1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
28 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
36 from xml
.etree
import ElementTree
as XmlElementTree
37 from lxml
import etree
as lxmlElementTree
40 from pyvcloud
import Http
41 from pyvcloud
.vcloudair
import VCA
42 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
43 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
44 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
45 from xml
.sax
.saxutils
import escape
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
48 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
49 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
50 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
62 # global variable for vcd connector type
63 STANDALONE
= 'standalone'
65 # key for flavor dicts
66 FLAVOR_RAM_KEY
= 'ram'
67 FLAVOR_VCPUS_KEY
= 'vcpus'
68 FLAVOR_DISK_KEY
= 'disk'
69 DEFAULT_IP_PROFILE
= {'gateway_address':"192.168.1.1",
71 'subnet_address':"192.168.1.0/24",
73 'dhcp_start_address':"192.168.1.3",
75 'dns_address':"192.168.1.2"
77 # global variable for wait time
83 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
84 __date__
= "$12-Jan-2017 11:09:29$"
87 # -1: "Could not be created",
93 # 5: "Waiting for user input",
95 # 7: "Unrecognized state",
97 # 9: "Inconsistent state",
98 # 10: "Children do not all have the same status",
99 # 11: "Upload initiated, OVF descriptor pending",
100 # 12: "Upload initiated, copying contents",
101 # 13: "Upload initiated , disk contents pending",
102 # 14: "Upload has been quarantined",
103 # 15: "Upload quarantine period has expired"
105 # mapping vCD status to MANO
106 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
115 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
116 'ERROR': 'ERROR', 'DELETED': 'DELETED'
119 # dict used to store flavor in memory
123 class vimconnector(vimconn
.vimconnector
):
124 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
125 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
127 Constructor create vmware connector to vCloud director.
129 By default construct doesn't validate connection state. So client can create object with None arguments.
130 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
132 a) It initialize organization UUID
133 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
136 uuid - is organization uuid.
137 name - is organization name that must be presented in vCloud director.
138 tenant_id - is VDC uuid it must be presented in vCloud director
139 tenant_name - is VDC name.
140 url - is hostname or ip address of vCloud director
141 url_admin - same as above.
142 user - is user that administrator for organization. Caller must make sure that
143 username has right privileges.
145 password - is password for a user.
147 VMware connector also requires PVDC administrative privileges and separate account.
148 This variables must be passed via config argument dict contains keys
150 dict['admin_username']
151 dict['admin_password']
157 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
158 url_admin
, user
, passwd
, log_level
, config
)
160 self
.logger
= logging
.getLogger('openmano.vim.vmware')
161 self
.logger
.setLevel(10)
166 self
.url_admin
= url_admin
167 self
.tenant_id
= tenant_id
168 self
.tenant_name
= tenant_name
172 self
.admin_password
= None
173 self
.admin_user
= None
176 if tenant_name
is not None:
177 orgnameandtenant
= tenant_name
.split(":")
178 if len(orgnameandtenant
) == 2:
179 self
.tenant_name
= orgnameandtenant
[1]
180 self
.org_name
= orgnameandtenant
[0]
182 self
.tenant_name
= tenant_name
183 if "orgname" in config
:
184 self
.org_name
= config
['orgname']
187 self
.logger
.setLevel(getattr(logging
, log_level
))
190 self
.admin_user
= config
['admin_username']
191 self
.admin_password
= config
['admin_password']
193 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
199 raise vimconn
.vimconnException('url param can not be NoneType')
201 if not self
.url_admin
: # try to use normal url
202 self
.url_admin
= self
.url
204 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
205 self
.tenant_id
, self
.tenant_name
))
206 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
207 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
209 # initialize organization
210 if self
.user
is not None and self
.passwd
is not None and self
.url
:
211 self
.init_organization()
213 def __getitem__(self
, index
):
216 if index
== 'tenant_id':
217 return self
.tenant_id
218 if index
== 'tenant_name':
219 return self
.tenant_name
222 elif index
== 'org_name':
224 elif index
== 'org_uuid':
226 elif index
== 'user':
228 elif index
== 'passwd':
232 elif index
== 'url_admin':
233 return self
.url_admin
234 elif index
== "config":
237 raise KeyError("Invalid key '%s'" % str(index
))
239 def __setitem__(self
, index
, value
):
242 if index
== 'tenant_id':
243 self
.tenant_id
= value
244 if index
== 'tenant_name':
245 self
.tenant_name
= value
248 elif index
== 'org_name':
249 self
.org_name
= value
250 elif index
== 'org_uuid':
251 self
.org_uuid
= value
252 elif index
== 'user':
254 elif index
== 'passwd':
258 elif index
== 'url_admin':
259 self
.url_admin
= value
261 raise KeyError("Invalid key '%s'" % str(index
))
263 def connect_as_admin(self
):
264 """ Method connect as pvdc admin user to vCloud director.
265 There are certain action that can be done only by provider vdc admin user.
266 Organization creation / provider network creation etc.
269 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
272 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
274 vca_admin
= VCA(host
=self
.url
,
275 username
=self
.admin_user
,
276 service_type
=STANDALONE
,
280 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
282 raise vimconn
.vimconnConnectionException(
283 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
284 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
287 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
292 """ Method connect as normal user to vCloud director.
295 The return vca object that letter can be used to connect to vCloud director as admin for VDC
299 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
302 vca
= VCA(host
=self
.url
,
304 service_type
=STANDALONE
,
309 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
311 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
312 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
315 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
318 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
319 "{} as user: {}".format(self
.org_name
, self
.user
))
323 def init_organization(self
):
324 """ Method initialize organization UUID and VDC parameters.
326 At bare minimum client must provide organization name that present in vCloud director and VDC.
328 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
329 The Org - UUID will be initialized at the run time if data center present in vCloud director.
332 The return vca object that letter can be used to connect to vcloud direct as admin
335 if self
.org_uuid
is None:
336 org_dict
= self
.get_org_list()
338 # we set org UUID at the init phase but we can do it only when we have valid credential.
339 if org_dict
[org
] == self
.org_name
:
341 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
344 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
346 # if well good we require for org details
347 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
349 # we have two case if we want to initialize VDC ID or VDC name at run time
350 # tenant_name provided but no tenant id
351 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
352 vdcs_dict
= org_details_dict
['vdcs']
353 for vdc
in vdcs_dict
:
354 if vdcs_dict
[vdc
] == self
.tenant_name
:
356 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
360 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
361 # case two we have tenant_id but we don't have tenant name so we find and set it.
362 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
363 vdcs_dict
= org_details_dict
['vdcs']
364 for vdc
in vdcs_dict
:
365 if vdc
== self
.tenant_id
:
366 self
.tenant_name
= vdcs_dict
[vdc
]
367 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
371 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
372 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
374 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
375 self
.logger
.debug(traceback
.format_exc())
378 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
379 """ Method adds a new tenant to VIM with this name.
380 This action requires access to create VDC action in vCloud director.
383 tenant_name is tenant_name to be created.
384 tenant_description not used for this call
387 returns the tenant identifier in UUID format.
388 If action is failed method will throw vimconn.vimconnException method
390 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
391 if vdc_task
is not None:
392 vdc_uuid
, value
= vdc_task
.popitem()
393 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
396 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
398 def delete_tenant(self
, tenant_id
=None):
399 """Delete a tenant from VIM"""
400 'Returns the tenant identifier'
401 raise vimconn
.vimconnNotImplemented("Should have implemented this")
403 def get_tenant_list(self
, filter_dict
={}):
404 """Obtain tenants of VIM
405 filter_dict can contain the following keys:
406 name: filter by tenant name
407 id: filter by tenant uuid/id
409 Returns the tenant list of dictionaries:
410 [{'name':'<name>, 'id':'<id>, ...}, ...]
413 org_dict
= self
.get_org(self
.org_uuid
)
414 vdcs_dict
= org_dict
['vdcs']
419 entry
= {'name': vdcs_dict
[k
], 'id': k
}
420 # if caller didn't specify dictionary we return all tenants.
421 if filter_dict
is not None and filter_dict
:
422 filtered_entry
= entry
.copy()
423 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
424 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
425 if filter_dict
== entry
:
426 vdclist
.append(filtered_entry
)
428 vdclist
.append(entry
)
430 self
.logger
.debug("Error in get_tenant_list()")
431 self
.logger
.debug(traceback
.format_exc())
432 raise vimconn
.vimconnException("Incorrect state. {}")
436 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
437 """Adds a tenant network to VIM
439 net_type can be 'bridge','data'.'ptp'.
440 ip_profile is a dict containing the IP parameters of the network
442 Returns the network identifier"""
444 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
445 .format(net_name
, net_type
, ip_profile
, shared
))
451 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
452 ip_profile
=ip_profile
, isshared
=isshared
)
453 if network_uuid
is not None:
456 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
458 def get_vcd_network_list(self
):
459 """ Method available organization for a logged in tenant
462 The return vca object that letter can be used to connect to vcloud direct as admin
465 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
468 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
470 if not self
.tenant_name
:
471 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
473 vdc
= vca
.get_vdc(self
.tenant_name
)
475 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
477 vdc_uuid
= vdc
.get_id().split(":")[3]
478 networks
= vca
.get_networks(vdc
.get_name())
481 for network
in networks
:
483 netid
= network
.get_id().split(":")
487 filter_dict
["name"] = network
.get_name()
488 filter_dict
["id"] = netid
[3]
489 filter_dict
["shared"] = network
.get_IsShared()
490 filter_dict
["tenant_id"] = vdc_uuid
491 if network
.get_status() == 1:
492 filter_dict
["admin_state_up"] = True
494 filter_dict
["admin_state_up"] = False
495 filter_dict
["status"] = "ACTIVE"
496 filter_dict
["type"] = "bridge"
497 network_list
.append(filter_dict
)
498 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
500 self
.logger
.debug("Error in get_vcd_network_list")
501 self
.logger
.debug(traceback
.format_exc())
504 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
507 def get_network_list(self
, filter_dict
={}):
508 """Obtain tenant networks of VIM
510 name: network name OR/AND
511 id: network uuid OR/AND
512 shared: boolean OR/AND
513 tenant_id: tenant OR/AND
514 admin_state_up: boolean
517 [{key : value , key : value}]
519 Returns the network list of dictionaries:
520 [{<the fields at Filter_dict plus some VIM specific>}, ...]
524 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
527 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
529 if not self
.tenant_name
:
530 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
532 vdc
= vca
.get_vdc(self
.tenant_name
)
534 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
536 vdcid
= vdc
.get_id().split(":")[3]
537 networks
= vca
.get_networks(vdc
.get_name())
541 for network
in networks
:
543 net_uuid
= network
.get_id().split(":")
544 if len(net_uuid
) != 4:
547 net_uuid
= net_uuid
[3]
549 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
552 filter_entry
["name"] = network
.get_name()
553 filter_entry
["id"] = net_uuid
554 filter_entry
["shared"] = network
.get_IsShared()
555 filter_entry
["tenant_id"] = vdcid
556 if network
.get_status() == 1:
557 filter_entry
["admin_state_up"] = True
559 filter_entry
["admin_state_up"] = False
560 filter_entry
["status"] = "ACTIVE"
561 filter_entry
["type"] = "bridge"
562 filtered_entry
= filter_entry
.copy()
564 if filter_dict
is not None and filter_dict
:
565 # we remove all the key : value we don't care and match only
567 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
568 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
569 if filter_dict
== filter_entry
:
570 network_list
.append(filtered_entry
)
572 network_list
.append(filtered_entry
)
574 self
.logger
.debug("Error in get_vcd_network_list")
575 self
.logger
.debug(traceback
.format_exc())
577 self
.logger
.debug("Returning {}".format(network_list
))
580 def get_network(self
, net_id
):
581 """Method obtains network details of net_id VIM network
582 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
586 raise vimconn
.vimconnConnectionException("self.connect() is failed")
588 vdc
= vca
.get_vdc(self
.tenant_name
)
589 vdc_id
= vdc
.get_id().split(":")[3]
591 networks
= vca
.get_networks(vdc
.get_name())
595 for network
in networks
:
596 vdc_network_id
= network
.get_id().split(":")
597 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
598 filter_dict
["name"] = network
.get_name()
599 filter_dict
["id"] = vdc_network_id
[3]
600 filter_dict
["shared"] = network
.get_IsShared()
601 filter_dict
["tenant_id"] = vdc_id
602 if network
.get_status() == 1:
603 filter_dict
["admin_state_up"] = True
605 filter_dict
["admin_state_up"] = False
606 filter_dict
["status"] = "ACTIVE"
607 filter_dict
["type"] = "bridge"
608 self
.logger
.debug("Returning {}".format(filter_dict
))
611 self
.logger
.debug("Error in get_network")
612 self
.logger
.debug(traceback
.format_exc())
616 def delete_network(self
, net_id
):
618 Method Deletes a tenant network from VIM, provide the network id.
620 Returns the network identifier or raise an exception
625 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
627 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
628 if vcd_network
is not None and vcd_network
:
629 if self
.delete_network_action(network_uuid
=net_id
):
632 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
634 def refresh_nets_status(self
, net_list
):
635 """Get the status of the networks
636 Params: the list of network identifiers
637 Returns a dictionary with:
638 net_id: #VIM id of this network
639 status: #Mandatory. Text with one of:
640 # DELETED (not found at vim)
641 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
642 # OTHER (Vim reported other status not understood)
643 # ERROR (VIM indicates an ERROR status)
644 # ACTIVE, INACTIVE, DOWN (admin down),
645 # BUILD (on building process)
647 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
648 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
654 raise vimconn
.vimconnConnectionException("self.connect() is failed")
660 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
661 if vcd_network
is not None and vcd_network
:
662 if vcd_network
['status'] == '1':
668 errormsg
= 'Network not found.'
670 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
671 'vim_info': yaml
.safe_dump(vcd_network
)}
673 self
.logger
.debug("Error in refresh_nets_status")
674 self
.logger
.debug(traceback
.format_exc())
678 def get_flavor(self
, flavor_id
):
679 """Obtain flavor details from the VIM
680 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
682 if flavor_id
not in flavorlist
:
683 raise vimconn
.vimconnNotFoundException("Flavor not found.")
684 return flavorlist
[flavor_id
]
686 def new_flavor(self
, flavor_data
):
687 """Adds a tenant flavor to VIM
688 flavor_data contains a dictionary with information, keys:
690 ram: memory (cloud type) in MBytes
691 vpcus: cpus (cloud type)
692 extended: EPA parameters
693 - numas: #items requested in same NUMA
694 memory: number of 1G huge pages memory
695 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
696 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
697 - name: interface name
698 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
699 bandwidth: X Gbps; requested guarantee bandwidth
700 vpci: requested virtual PCI address
704 Returns the flavor identifier"""
706 # generate a new uuid put to internal dict and return it.
707 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
708 new_flavor
=flavor_data
709 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
710 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
711 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
713 extended_flv
= flavor_data
.get("extended")
715 numas
=extended_flv
.get("numas")
718 #overwrite ram and vcpus
719 ram
= numa
['memory']*1024
720 if 'paired-threads' in numa
:
721 cpu
= numa
['paired-threads']*2
722 elif 'cores' in numa
:
724 elif 'threads' in numa
:
725 cpu
= numa
['threads']
727 new_flavor
[FLAVOR_RAM_KEY
] = ram
728 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
729 new_flavor
[FLAVOR_DISK_KEY
] = disk
730 # generate a new uuid put to internal dict and return it.
731 flavor_id
= uuid
.uuid4()
732 flavorlist
[str(flavor_id
)] = new_flavor
733 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
735 return str(flavor_id
)
737 def delete_flavor(self
, flavor_id
):
738 """Deletes a tenant flavor from VIM identify by its id
740 Returns the used id or raise an exception
742 if flavor_id
not in flavorlist
:
743 raise vimconn
.vimconnNotFoundException("Flavor not found.")
745 flavorlist
.pop(flavor_id
, None)
748 def new_image(self
, image_dict
):
750 Adds a tenant image to VIM
752 200, image-id if the image is created
753 <0, message if there is an error
756 return self
.get_image_id_from_path(image_dict
['location'])
758 def delete_image(self
, image_id
):
765 raise vimconn
.vimconnNotImplemented("Should have implemented this")
767 def catalog_exists(self
, catalog_name
, catalogs
):
774 for catalog
in catalogs
:
775 if catalog
.name
== catalog_name
:
779 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
780 """ Create new catalog entry in vCloud director.
783 vca: vCloud director.
784 catalog_name catalog that client wish to create. Note no validation done for a name.
785 Client must make sure that provide valid string representation.
787 Return (bool) True if catalog created.
791 task
= vca
.create_catalog(catalog_name
, catalog_name
)
792 result
= vca
.block_until_completed(task
)
795 catalogs
= vca
.get_catalogs()
798 return self
.catalog_exists(catalog_name
, catalogs
)
800 # noinspection PyIncorrectDocstring
801 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
802 description
='', progress
=False, chunk_bytes
=128 * 1024):
804 Uploads a OVF file to a vCloud catalog
811 :param catalog_name: (str): The name of the catalog to upload the media.
812 :param media_file_name: (str): The name of the local media file to upload.
813 :return: (bool) True if the media file was successfully uploaded, false otherwise.
815 os
.path
.isfile(media_file_name
)
816 statinfo
= os
.stat(media_file_name
)
818 # find a catalog entry where we upload OVF.
819 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
821 # if VCD can parse OVF we upload VMDK file
822 for catalog
in vca
.get_catalogs():
823 if catalog_name
!= catalog
.name
:
825 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
826 link
.get_rel() == 'add', catalog
.get_Link())
827 assert len(link
) == 1
829 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
830 """ % (escape(catalog_name
), escape(description
))
831 headers
= vca
.vcloud_session
.get_vcloud_headers()
832 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
833 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
834 if response
.status_code
== requests
.codes
.created
:
835 catalogItem
= XmlElementTree
.fromstring(response
.content
)
836 entity
= [child
for child
in catalogItem
if
837 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
838 href
= entity
.get('href')
840 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
841 verify
=vca
.verify
, logger
=self
.logger
)
843 if response
.status_code
== requests
.codes
.ok
:
844 media
= mediaType
.parseString(response
.content
, True)
845 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
846 media
.get_Files().get_File()[0].get_Link())[0]
847 headers
= vca
.vcloud_session
.get_vcloud_headers()
848 headers
['Content-Type'] = 'Content-Type text/xml'
849 response
= Http
.put(link
.get_href(),
850 data
=open(media_file_name
, 'rb'),
852 verify
=vca
.verify
, logger
=self
.logger
)
853 if response
.status_code
!= requests
.codes
.ok
:
855 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
859 # TODO fix this with aync block
862 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
864 # uploading VMDK file
865 # check status of OVF upload and upload remaining files.
866 response
= Http
.get(template
,
867 headers
=vca
.vcloud_session
.get_vcloud_headers(),
871 if response
.status_code
== requests
.codes
.ok
:
872 media
= mediaType
.parseString(response
.content
, True)
873 number_of_files
= len(media
.get_Files().get_File())
874 for index
in xrange(0, number_of_files
):
875 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
876 media
.get_Files().get_File()[index
].get_Link())
877 for link
in links_list
:
878 # we skip ovf since it already uploaded.
879 if 'ovf' in link
.get_href():
881 # The OVF file and VMDK must be in a same directory
882 head
, tail
= os
.path
.split(media_file_name
)
883 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
884 if not os
.path
.isfile(file_vmdk
):
886 statinfo
= os
.stat(file_vmdk
)
887 if statinfo
.st_size
== 0:
889 hrefvmdk
= link
.get_href()
892 print("Uploading file: {}".format(file_vmdk
))
894 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
896 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
898 bytes_transferred
= 0
899 f
= open(file_vmdk
, 'rb')
900 while bytes_transferred
< statinfo
.st_size
:
901 my_bytes
= f
.read(chunk_bytes
)
902 if len(my_bytes
) <= chunk_bytes
:
903 headers
= vca
.vcloud_session
.get_vcloud_headers()
904 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
905 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
906 headers
['Content-Length'] = str(len(my_bytes
))
907 response
= Http
.put(hrefvmdk
,
913 if response
.status_code
== requests
.codes
.ok
:
914 bytes_transferred
+= len(my_bytes
)
916 progress_bar
.update(bytes_transferred
)
919 'file upload failed with error: [%s] %s' % (response
.status_code
,
926 progress_bar
.finish()
930 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
931 format(catalog_name
, media_file_name
))
934 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
937 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
938 """Upload media file"""
939 # TODO add named parameters for readability
941 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
942 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
944 def validate_uuid4(self
, uuid_string
=None):
945 """ Method validate correct format of UUID.
947 Return: true if string represent valid uuid
950 val
= uuid
.UUID(uuid_string
, version
=4)
955 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
956 """ Method check catalog and return catalog ID in UUID format.
959 catalog_name: catalog name as string
960 catalogs: list of catalogs.
962 Return: catalogs uuid
965 for catalog
in catalogs
:
966 if catalog
.name
== catalog_name
:
967 catalog_id
= catalog
.get_id().split(":")
971 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
972 """ Method check catalog and return catalog name lookup done by catalog UUID.
975 catalog_name: catalog name as string
976 catalogs: list of catalogs.
978 Return: catalogs name or None
981 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
984 for catalog
in catalogs
:
985 catalog_id
= catalog
.get_id().split(":")[3]
986 if catalog_id
== catalog_uuid
:
990 def get_image_id_from_path(self
, path
=None, progress
=False):
991 """ Method upload OVF image to vCloud director.
993 Each OVF image represented as single catalog entry in vcloud director.
994 The method check for existing catalog entry. The check done by file name without file extension.
996 if given catalog name already present method will respond with existing catalog uuid otherwise
997 it will create new catalog entry and upload OVF file to newly created catalog.
999 If method can't create catalog entry or upload a file it will throw exception.
1001 Method accept boolean flag progress that will output progress bar. It useful method
1002 for standalone upload use case. In case to test large file upload.
1005 path: - valid path to OVF file.
1006 progress - boolean progress bar show progress bar.
1008 Return: if image uploaded correct method will provide image catalog UUID.
1010 vca
= self
.connect()
1012 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1015 raise vimconn
.vimconnException("Image path can't be None.")
1017 if not os
.path
.isfile(path
):
1018 raise vimconn
.vimconnException("Can't read file. File not found.")
1020 if not os
.access(path
, os
.R_OK
):
1021 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1023 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1025 dirpath
, filename
= os
.path
.split(path
)
1026 flname
, file_extension
= os
.path
.splitext(path
)
1027 if file_extension
!= '.ovf':
1028 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1029 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1031 catalog_name
= os
.path
.splitext(filename
)[0]
1032 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1033 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1034 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1036 catalogs
= vca
.get_catalogs()
1037 if len(catalogs
) == 0:
1038 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1039 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1041 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1042 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1043 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1045 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1046 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1048 for catalog
in catalogs
:
1049 # search for existing catalog if we find same name we return ID
1050 # TODO optimize this
1051 if catalog
.name
== catalog_md5_name
:
1052 self
.logger
.debug("Found existing catalog entry for {} "
1053 "catalog id {}".format(catalog_name
,
1054 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1055 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1057 # if we didn't find existing catalog we create a new one and upload image.
1058 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1059 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1061 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1063 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1064 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1066 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1068 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1070 def get_vappid(self
, vdc
=None, vapp_name
=None):
1071 """ Method takes vdc object and vApp name and returns vapp uuid or None
1074 vdc: The VDC object.
1075 vapp_name: is application vappp name identifier
1078 The return vApp name otherwise None
1080 if vdc
is None or vapp_name
is None:
1082 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1084 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1085 vdc
.ResourceEntities
.ResourceEntity
)
1087 return refs
[0].href
.split("vapp")[1][1:]
1088 except Exception as e
:
1089 self
.logger
.exception(e
)
1093 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1094 """ Method Method returns True or False if vapp deployed in vCloud director
1097 vca: Connector to VCA
1098 vdc: The VDC object.
1099 vappid: vappid is application identifier
1102 The return True if vApp deployed
1107 refs
= filter(lambda ref
:
1108 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1109 vdc
.ResourceEntities
.ResourceEntity
)
1111 vappid
= ref
.href
.split("vapp")[1][1:]
1112 # find vapp with respected vapp uuid
1113 if vappid
== vapp_uuid
:
1115 except Exception as e
:
1116 self
.logger
.exception(e
)
1120 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1121 """Method returns vApp name from vCD and lookup done by vapp_id.
1124 vca: Connector to VCA
1125 vdc: The VDC object.
1126 vapp_uuid: vappid is application identifier
1129 The return vApp name otherwise None
1133 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1134 vdc
.ResourceEntities
.ResourceEntity
)
1136 # we care only about UUID the rest doesn't matter
1137 vappid
= ref
.href
.split("vapp")[1][1:]
1138 if vappid
== vapp_uuid
:
1139 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1141 tree
= XmlElementTree
.fromstring(response
.content
)
1142 return tree
.attrib
['name']
1143 except Exception as e
:
1144 self
.logger
.exception(e
)
1148 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1149 cloud_config
=None, disk_list
=None):
1150 """Adds a VM instance to VIM
1152 start: indicates if VM must start or boot in pause mode. Ignored
1153 image_id,flavor_id: image and flavor uuid
1154 net_list: list of interfaces, each one is a dictionary with:
1156 net_id: network uuid to connect
1157 vpci: virtual vcpi to assign
1158 model: interface model, virtio, e2000, ...
1160 use: 'data', 'bridge', 'mgmt'
1161 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1162 vim_id: filled/added by this function
1163 cloud_config: can be a text script to be passed directly to cloud-init,
1164 or an object to inject users and ssh keys with format:
1165 key-pairs: [] list of keys to install to the default user
1166 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1167 #TODO ip, security groups
1168 Returns >=0, the instance identifier
1172 self
.logger
.info("Creating new instance for entry {}".format(name
))
1173 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1174 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1175 vca
= self
.connect()
1177 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1179 #new vm name = vmname + tenant_id + uuid
1180 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1181 vmname_andid
= ''.join(new_vm_name
)
1183 # if vm already deployed we return existing uuid
1184 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1185 # if vapp_uuid is not None:
1188 # we check for presence of VDC, Catalog entry and Flavor.
1189 vdc
= vca
.get_vdc(self
.tenant_name
)
1191 raise vimconn
.vimconnNotFoundException(
1192 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1193 catalogs
= vca
.get_catalogs()
1194 if catalogs
is None:
1195 raise vimconn
.vimconnNotFoundException(
1196 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1198 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1199 if catalog_hash_name
:
1200 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1202 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1203 "(Failed retrieve catalog information {})".format(name
, image_id
))
1206 # Set vCPU and Memory based on flavor.
1212 if flavor_id
is not None:
1213 if flavor_id
not in flavorlist
:
1214 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1215 "Failed retrieve flavor information "
1216 "flavor id {}".format(name
, flavor_id
))
1219 flavor
= flavorlist
[flavor_id
]
1220 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1221 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1222 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1225 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1227 # image upload creates template name as catalog name space Template.
1228 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1233 # client must provide at least one entry in net_list if not we report error
1234 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1235 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1237 primary_netname
= None
1238 network_mode
= 'bridged'
1239 if net_list
is not None and len(net_list
) > 0:
1240 for net
in net_list
:
1241 if 'use' in net
and net
['use'] == 'mgmt':
1243 if primary_net
is None:
1244 primary_net
= net_list
[0]
1247 primary_net_id
= primary_net
['net_id']
1248 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1249 if 'name' in network_dict
:
1250 primary_netname
= network_dict
['name']
1253 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1255 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1257 # use: 'data', 'bridge', 'mgmt'
1258 # create vApp. Set vcpu and ram based on flavor id.
1259 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1260 self
.get_catalogbyid(image_id
, catalogs
),
1261 network_name
=None, # None while creating vapp
1262 network_mode
=network_mode
,
1263 vm_name
=vmname_andid
,
1264 vm_cpus
=vm_cpus
, # can be None if flavor is None
1265 vm_memory
=vm_memory
) # can be None if flavor is None
1267 if vapptask
is None or vapptask
is False:
1268 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1269 if type(vapptask
) is VappTask
:
1270 vca
.block_until_completed(vapptask
)
1272 # we should have now vapp in undeployed state.
1273 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1274 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1276 raise vimconn
.vimconnUnexpectedResponse(
1277 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1282 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1283 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1285 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1287 # add NICs & connect to networks in netlist
1289 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1291 primary_nic_index
= 0
1292 for net
in net_list
:
1293 # openmano uses network id in UUID format.
1294 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1295 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1296 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1298 if 'net_id' not in net
:
1301 interface_net_id
= net
['net_id']
1302 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1303 interface_network_mode
= net
['use']
1305 if interface_network_mode
== 'mgmt':
1306 primary_nic_index
= nicIndex
1308 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1309 - DHCP (The IP address is obtained from a DHCP service.)
1310 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1311 - NONE (No IP addressing mode specified.)"""
1313 if primary_netname
is not None:
1314 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1316 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1317 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1318 if type(task
) is GenericTask
:
1319 vca
.block_until_completed(task
)
1320 # connect network to VM - with all DHCP by default
1321 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1322 task
= vapp
.connect_vms(nets
[0].name
,
1323 connection_index
=nicIndex
,
1324 connections_primary_index
=primary_nic_index
,
1325 ip_allocation_mode
='DHCP')
1326 if type(task
) is GenericTask
:
1327 vca
.block_until_completed(task
)
1330 # it might be a case if specific mandatory entry in dict is empty
1331 self
.logger
.debug("Key error {}".format(KeyError.message
))
1332 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1334 # deploy and power on vm
1335 task
= vapp
.poweron()
1336 if type(task
) is TaskType
:
1337 vca
.block_until_completed(task
)
1338 deploytask
= vapp
.deploy(powerOn
='True')
1339 if type(task
) is TaskType
:
1340 vca
.block_until_completed(deploytask
)
1342 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1345 while wait_time
<= MAX_WAIT_TIME
:
1346 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1347 if vapp
and vapp
.me
.deployed
:
1348 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1351 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1352 time
.sleep(INTERVAL_TIME
)
1354 wait_time
+=INTERVAL_TIME
1356 if vapp_uuid
is not None:
1359 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1363 ## based on current discussion
1367 # created: '2016-09-08T11:51:58'
1368 # description: simple-instance.linux1.1
1369 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1370 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1371 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1376 def get_vminstance(self
, vim_vm_uuid
=None):
1377 """Returns the VM instance information from VIM"""
1379 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1380 vca
= self
.connect()
1382 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1384 vdc
= vca
.get_vdc(self
.tenant_name
)
1386 raise vimconn
.vimconnConnectionException(
1387 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1389 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1390 if not vm_info_dict
:
1391 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1392 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1394 status_key
= vm_info_dict
['status']
1397 vm_dict
= {'created': vm_info_dict
['created'],
1398 'description': vm_info_dict
['name'],
1399 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1400 'hostId': vm_info_dict
['vmuuid'],
1402 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1404 if 'interfaces' in vm_info_dict
:
1405 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1407 vm_dict
['interfaces'] = []
1409 vm_dict
= {'created': '',
1411 'status': vcdStatusCode2manoFormat
[int(-1)],
1412 'hostId': vm_info_dict
['vmuuid'],
1413 'error_msg': "Inconsistency state",
1414 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1418 def delete_vminstance(self
, vm__vim_uuid
):
1419 """Method poweroff and remove VM instance from vcloud director network.
1422 vm__vim_uuid: VM UUID
1425 Returns the instance identifier
1428 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1429 vca
= self
.connect()
1431 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1433 vdc
= vca
.get_vdc(self
.tenant_name
)
1435 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1437 raise vimconn
.vimconnException(
1438 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1441 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1442 if vapp_name
is None:
1443 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1444 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1446 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1448 # Delete vApp and wait for status change if task executed and vApp is None.
1449 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1452 if vapp
.me
.deployed
:
1453 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1457 while wait_time
<= MAX_WAIT_TIME
:
1458 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1460 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1461 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1463 power_off_task
= vapp
.poweroff()
1464 if type(power_off_task
) is GenericTask
:
1465 result
= vca
.block_until_completed(power_off_task
)
1470 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1471 time
.sleep(INTERVAL_TIME
)
1473 wait_time
+=INTERVAL_TIME
1475 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1477 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1480 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1483 while wait_time
<= MAX_WAIT_TIME
:
1484 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1486 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1487 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1488 undeploy_task
= vapp
.undeploy(action
='powerOff')
1490 if type(undeploy_task
) is GenericTask
:
1491 result
= vca
.block_until_completed(undeploy_task
)
1496 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1497 time
.sleep(INTERVAL_TIME
)
1499 wait_time
+=INTERVAL_TIME
1502 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1505 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1506 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1508 if vapp
is not None:
1512 while wait_time
<= MAX_WAIT_TIME
:
1513 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1515 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1516 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1518 delete_task
= vapp
.delete()
1520 if type(delete_task
) is GenericTask
:
1521 vca
.block_until_completed(delete_task
)
1522 result
= vca
.block_until_completed(delete_task
)
1526 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1527 time
.sleep(INTERVAL_TIME
)
1529 wait_time
+=INTERVAL_TIME
1532 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1535 self
.logger
.debug(traceback
.format_exc())
1536 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1538 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1539 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1542 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1544 def refresh_vms_status(self
, vm_list
):
1545 """Get the status of the virtual machines and their interfaces/ports
1546 Params: the list of VM identifiers
1547 Returns a dictionary with:
1548 vm_id: #VIM id of this Virtual Machine
1549 status: #Mandatory. Text with one of:
1550 # DELETED (not found at vim)
1551 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1552 # OTHER (Vim reported other status not understood)
1553 # ERROR (VIM indicates an ERROR status)
1554 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1555 # CREATING (on building process), ERROR
1556 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1558 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1559 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1561 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1562 mac_address: #Text format XX:XX:XX:XX:XX:XX
1563 vim_net_id: #network id where this interface is connected
1564 vim_interface_id: #interface/port VIM id
1565 ip_address: #null, or text with IPv4, IPv6 address
1568 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1569 vca
= self
.connect()
1571 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1573 vdc
= vca
.get_vdc(self
.tenant_name
)
1575 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1578 for vmuuid
in vm_list
:
1579 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1580 if vmname
is not None:
1582 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1583 vm_info
= the_vapp
.get_vms_details()
1584 vm_status
= vm_info
[0]['status']
1586 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1587 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1588 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1592 vm_app_networks
= the_vapp
.get_vms_network_info()
1593 for vapp_network
in vm_app_networks
:
1594 for vm_network
in vapp_network
:
1595 if vm_network
['name'] == vmname
:
1596 interface
= {"mac_address": vm_network
['mac'],
1597 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1598 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1599 'ip_address': vm_network
['ip']}
1600 # interface['vim_info'] = yaml.safe_dump(vm_network)
1601 vm_dict
["interfaces"].append(interface
)
1602 # add a vm to vm dict
1603 vms_dict
.setdefault(vmuuid
, vm_dict
)
1605 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1606 self
.logger
.debug(traceback
.format_exc())
1610 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1611 """Send and action over a VM instance from VIM
1612 Returns the vm_id if the action was successfully sent to the VIM"""
1614 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1615 if vm__vim_uuid
is None or action_dict
is None:
1616 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1618 vca
= self
.connect()
1620 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1622 vdc
= vca
.get_vdc(self
.tenant_name
)
1624 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1626 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1627 if vapp_name
is None:
1628 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1629 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1631 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1634 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1635 # TODO fix all status
1636 if "start" in action_dict
:
1637 if action_dict
["start"] == "rebuild":
1638 the_vapp
.deploy(powerOn
=True)
1640 vm_info
= the_vapp
.get_vms_details()
1641 vm_status
= vm_info
[0]['status']
1642 if vm_status
== "Suspended":
1644 elif vm_status
.status
== "Powered off":
1646 elif "pause" in action_dict
:
1649 elif "resume" in action_dict
:
1652 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1654 elif "forceOff" in action_dict
:
1656 elif "terminate" in action_dict
:
1658 # elif "createImage" in action_dict:
1659 # server.create_image()
1665 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1667 Get a console for the virtual machine
1669 vm_id: uuid of the VM
1670 console_type, can be:
1671 "novnc" (by default), "xvpvnc" for VNC types,
1672 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1673 Returns dict with the console parameters:
1674 protocol: ssh, ftp, http, https, ...
1675 server: usually ip address
1676 port: the http, ssh, ... port
1677 suffix: extra text, e.g. the http path and query string
1679 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1681 # NOT USED METHODS in current version
1683 def host_vim2gui(self
, host
, server_dict
):
1684 """Transform host dictionary from VIM format to GUI format,
1685 and append to the server_dict
1687 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1689 def get_hosts_info(self
):
1690 """Get the information of deployed hosts
1691 Returns the hosts content"""
1692 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1694 def get_hosts(self
, vim_tenant
):
1695 """Get the hosts and deployed instances
1696 Returns the hosts content"""
1697 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1699 def get_processor_rankings(self
):
1700 """Get the processor rankings in the VIM database"""
1701 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1703 def new_host(self
, host_data
):
1704 """Adds a new host to VIM"""
1705 '''Returns status code of the VIM response'''
1706 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1708 def new_external_port(self
, port_data
):
1709 """Adds a external port to VIM"""
1710 '''Returns the port identifier'''
1711 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1713 def new_external_network(self
, net_name
, net_type
):
1714 """Adds a external network to VIM (shared)"""
1715 '''Returns the network identifier'''
1716 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1718 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1719 """Connects a external port to a network"""
1720 '''Returns status code of the VIM response'''
1721 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1723 def new_vminstancefromJSON(self
, vm_data
):
1724 """Adds a VM instance to VIM"""
1725 '''Returns the instance identifier'''
1726 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1728 def get_network_name_by_id(self
, network_uuid
=None):
1729 """Method gets vcloud director network named based on supplied uuid.
1732 network_uuid: network_id
1735 The return network name.
1738 vca
= self
.connect()
1740 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1742 if not network_uuid
:
1746 org_dict
= self
.get_org(self
.org_uuid
)
1747 if 'networks' in org_dict
:
1748 org_network_dict
= org_dict
['networks']
1749 for net_uuid
in org_network_dict
:
1750 if net_uuid
== network_uuid
:
1751 return org_network_dict
[net_uuid
]
1753 self
.logger
.debug("Exception in get_network_name_by_id")
1754 self
.logger
.debug(traceback
.format_exc())
1758 def get_network_id_by_name(self
, network_name
=None):
1759 """Method gets vcloud director network uuid based on supplied name.
1762 network_name: network_name
1764 The return network uuid.
1765 network_uuid: network_id
1768 vca
= self
.connect()
1770 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1772 if not network_name
:
1773 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1777 org_dict
= self
.get_org(self
.org_uuid
)
1778 if org_dict
and 'networks' in org_dict
:
1779 org_network_dict
= org_dict
['networks']
1780 for net_uuid
,net_name
in org_network_dict
.iteritems():
1781 if net_name
== network_name
:
1784 except KeyError as exp
:
1785 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1789 def list_org_action(self
):
1791 Method leverages vCloud director and query for available organization for particular user
1794 vca - is active VCA connection.
1795 vdc_name - is a vdc name that will be used to query vms action
1798 The return XML respond
1801 vca
= self
.connect()
1803 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1805 url_list
= [vca
.host
, '/api/org']
1806 vm_list_rest_call
= ''.join(url_list
)
1808 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1809 response
= Http
.get(url
=vm_list_rest_call
,
1810 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1813 if response
.status_code
== requests
.codes
.ok
:
1814 return response
.content
1818 def get_org_action(self
, org_uuid
=None):
1820 Method leverages vCloud director and retrieve available object fdr organization.
1823 vca - is active VCA connection.
1824 vdc_name - is a vdc name that will be used to query vms action
1827 The return XML respond
1830 vca
= self
.connect()
1832 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1834 if org_uuid
is None:
1837 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1838 vm_list_rest_call
= ''.join(url_list
)
1840 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1841 response
= Http
.get(url
=vm_list_rest_call
,
1842 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1845 if response
.status_code
== requests
.codes
.ok
:
1846 return response
.content
1850 def get_org(self
, org_uuid
=None):
1852 Method retrieves available organization in vCloud Director
1855 org_uuid - is a organization uuid.
1858 The return dictionary with following key
1859 "network" - for network list under the org
1860 "catalogs" - for network list under the org
1861 "vdcs" - for vdc list under org
1865 vca
= self
.connect()
1867 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1869 if org_uuid
is None:
1872 content
= self
.get_org_action(org_uuid
=org_uuid
)
1877 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1878 for child
in vm_list_xmlroot
:
1879 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1880 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1881 org_dict
['vdcs'] = vdc_list
1882 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1883 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1884 org_dict
['networks'] = network_list
1885 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1886 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1887 org_dict
['catalogs'] = catalog_list
1893 def get_org_list(self
):
1895 Method retrieves available organization in vCloud Director
1898 vca - is active VCA connection.
1901 The return dictionary and key for each entry VDC UUID
1905 vca
= self
.connect()
1907 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1909 content
= self
.list_org_action()
1911 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1912 for vm_xml
in vm_list_xmlroot
:
1913 if vm_xml
.tag
.split("}")[1] == 'Org':
1914 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1915 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1921 def vms_view_action(self
, vdc_name
=None):
1922 """ Method leverages vCloud director vms query call
1925 vca - is active VCA connection.
1926 vdc_name - is a vdc name that will be used to query vms action
1929 The return XML respond
1931 vca
= self
.connect()
1932 if vdc_name
is None:
1935 url_list
= [vca
.host
, '/api/vms/query']
1936 vm_list_rest_call
= ''.join(url_list
)
1938 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1939 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1940 vca
.vcloud_session
.organization
.Link
)
1942 response
= Http
.get(url
=vm_list_rest_call
,
1943 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1946 if response
.status_code
== requests
.codes
.ok
:
1947 return response
.content
1951 def get_vapp_list(self
, vdc_name
=None):
1953 Method retrieves vApp list deployed vCloud director and returns a dictionary
1954 contains a list of all vapp deployed for queried VDC.
1955 The key for a dictionary is vApp UUID
1959 vca - is active VCA connection.
1960 vdc_name - is a vdc name that will be used to query vms action
1963 The return dictionary and key for each entry vapp UUID
1967 if vdc_name
is None:
1970 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1972 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1973 for vm_xml
in vm_list_xmlroot
:
1974 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1975 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1976 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1977 if 'vappTemplate-' in rawuuid
[0]:
1978 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1979 # vm and use raw UUID as key
1980 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1986 def get_vm_list(self
, vdc_name
=None):
1988 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1989 contains a list of all VM's deployed for queried VDC.
1990 The key for a dictionary is VM UUID
1994 vca - is active VCA connection.
1995 vdc_name - is a vdc name that will be used to query vms action
1998 The return dictionary and key for each entry vapp UUID
2002 if vdc_name
is None:
2005 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2007 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2008 for vm_xml
in vm_list_xmlroot
:
2009 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2010 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2011 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2012 if 'vm-' in rawuuid
[0]:
2013 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2014 # vm and use raw UUID as key
2015 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2021 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2023 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2024 contains a list of all VM's deployed for queried VDC.
2025 The key for a dictionary is VM UUID
2029 vca - is active VCA connection.
2030 vdc_name - is a vdc name that will be used to query vms action
2033 The return dictionary and key for each entry vapp UUID
2036 vca
= self
.connect()
2038 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2040 if vdc_name
is None:
2043 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2045 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2046 for vm_xml
in vm_list_xmlroot
:
2047 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2048 # lookup done by UUID
2050 if vapp_name
in vm_xml
.attrib
['container']:
2051 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2052 if 'vm-' in rawuuid
[0]:
2053 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2055 # lookup done by Name
2057 if vapp_name
in vm_xml
.attrib
['name']:
2058 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2059 if 'vm-' in rawuuid
[0]:
2060 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2067 def get_network_action(self
, network_uuid
=None):
2069 Method leverages vCloud director and query network based on network uuid
2072 vca - is active VCA connection.
2073 network_uuid - is a network uuid
2076 The return XML respond
2079 vca
= self
.connect()
2081 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2083 if network_uuid
is None:
2086 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2087 vm_list_rest_call
= ''.join(url_list
)
2089 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2090 response
= Http
.get(url
=vm_list_rest_call
,
2091 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2094 if response
.status_code
== requests
.codes
.ok
:
2095 return response
.content
2099 def get_vcd_network(self
, network_uuid
=None):
2101 Method retrieves available network from vCloud Director
2104 network_uuid - is VCD network UUID
2106 Each element serialized as key : value pair
2108 Following keys available for access. network_configuration['Gateway'}
2112 <IsInherited>true</IsInherited>
2113 <Gateway>172.16.252.100</Gateway>
2114 <Netmask>255.255.255.0</Netmask>
2115 <Dns1>172.16.254.201</Dns1>
2116 <Dns2>172.16.254.202</Dns2>
2117 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2118 <IsEnabled>true</IsEnabled>
2121 <StartAddress>172.16.252.1</StartAddress>
2122 <EndAddress>172.16.252.99</EndAddress>
2127 <FenceMode>bridged</FenceMode>
2130 The return dictionary and key for each entry vapp UUID
2133 network_configuration
= {}
2134 if network_uuid
is None:
2137 content
= self
.get_network_action(network_uuid
=network_uuid
)
2139 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2141 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2142 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2143 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2145 for child
in vm_list_xmlroot
:
2146 if child
.tag
.split("}")[1] == 'IsShared':
2147 network_configuration
['isShared'] = child
.text
.strip()
2148 if child
.tag
.split("}")[1] == 'Configuration':
2149 for configuration
in child
.iter():
2150 tagKey
= configuration
.tag
.split("}")[1].strip()
2152 network_configuration
[tagKey
] = configuration
.text
.strip()
2153 return network_configuration
2157 return network_configuration
2159 def delete_network_action(self
, network_uuid
=None):
2161 Method delete given network from vCloud director
2164 network_uuid - is a network uuid that client wish to delete
2167 The return None or XML respond or false
2170 vca
= self
.connect_as_admin()
2172 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2173 if network_uuid
is None:
2176 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2177 vm_list_rest_call
= ''.join(url_list
)
2179 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2180 response
= Http
.delete(url
=vm_list_rest_call
,
2181 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2185 if response
.status_code
== 202:
2190 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2191 ip_profile
=None, isshared
='true'):
2193 Method create network in vCloud director
2196 network_name - is network name to be created.
2197 net_type - can be 'bridge','data','ptp','mgmt'.
2198 ip_profile is a dict containing the IP parameters of the network
2199 isshared - is a boolean
2200 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2201 It optional attribute. by default if no parent network indicate the first available will be used.
2204 The return network uuid or return None
2207 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2208 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2209 ip_profile
=ip_profile
,
2211 parent_network_uuid
=parent_network_uuid
,
2214 self
.logger
.debug("Failed create network {}.".format(network_name
))
2218 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2219 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2220 if len(vcd_uuid
) == 4:
2221 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2224 self
.logger
.debug("Failed create network {}".format(network_name
))
2227 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2228 ip_profile
=None, isshared
='true'):
2230 Method create network in vCloud director
2233 network_name - is network name to be created.
2234 net_type - can be 'bridge','data','ptp','mgmt'.
2235 ip_profile is a dict containing the IP parameters of the network
2236 isshared - is a boolean
2237 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2238 It optional attribute. by default if no parent network indicate the first available will be used.
2241 The return network uuid or return None
2244 vca
= self
.connect_as_admin()
2246 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2247 if network_name
is None:
2250 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2251 vm_list_rest_call
= ''.join(url_list
)
2252 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2253 response
= Http
.get(url
=vm_list_rest_call
,
2254 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2258 provider_network
= None
2259 available_networks
= None
2260 add_vdc_rest_url
= None
2262 if response
.status_code
!= requests
.codes
.ok
:
2263 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2264 response
.status_code
))
2268 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2269 for child
in vm_list_xmlroot
:
2270 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2271 provider_network
= child
.attrib
.get('href')
2272 # application/vnd.vmware.admin.providervdc+xml
2273 if child
.tag
.split("}")[1] == 'Link':
2274 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2275 and child
.attrib
.get('rel') == 'add':
2276 add_vdc_rest_url
= child
.attrib
.get('href')
2278 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2279 self
.logger
.debug("Respond body {}".format(response
.content
))
2282 # find pvdc provided available network
2283 response
= Http
.get(url
=provider_network
,
2284 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2287 if response
.status_code
!= requests
.codes
.ok
:
2288 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2289 response
.status_code
))
2292 # available_networks.split("/")[-1]
2294 if parent_network_uuid
is None:
2296 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2297 for child
in vm_list_xmlroot
.iter():
2298 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2299 for networks
in child
.iter():
2300 # application/vnd.vmware.admin.network+xml
2301 if networks
.attrib
.get('href') is not None:
2302 available_networks
= networks
.attrib
.get('href')
2307 #Configure IP profile of the network
2308 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2310 gateway_address
=ip_profile
['gateway_address']
2311 dhcp_count
=int(ip_profile
['dhcp_count'])
2312 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2314 if ip_profile
['dhcp_enabled']==True:
2317 dhcp_enabled
='false'
2318 dhcp_start_address
=ip_profile
['dhcp_start_address']
2320 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2321 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2322 end_ip_int
+= dhcp_count
- 1
2323 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2325 ip_version
=ip_profile
['ip_version']
2326 dns_address
=ip_profile
['dns_address']
2328 # either use client provided UUID or search for a first available
2329 # if both are not defined we return none
2330 if parent_network_uuid
is not None:
2331 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2332 add_vdc_rest_url
= ''.join(url_list
)
2335 fence_mode
="isolated"
2337 is_inherited
='false'
2338 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2339 <Description>Openmano created</Description>
2343 <IsInherited>{1:s}</IsInherited>
2344 <Gateway>{2:s}</Gateway>
2345 <Netmask>{3:s}</Netmask>
2347 <IsEnabled>{5:s}</IsEnabled>
2350 <StartAddress>{6:s}</StartAddress>
2351 <EndAddress>{7:s}</EndAddress>
2356 <FenceMode>{8:s}</FenceMode>
2358 <IsShared>{9:s}</IsShared>
2359 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2360 subnet_address
, dns_address
, dhcp_enabled
,
2361 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2364 fence_mode
="bridged"
2365 is_inherited
='false'
2366 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2367 <Description>Openmano created</Description>
2371 <IsInherited>{1:s}</IsInherited>
2372 <Gateway>{2:s}</Gateway>
2373 <Netmask>{3:s}</Netmask>
2375 <IsEnabled>{5:s}</IsEnabled>
2378 <StartAddress>{6:s}</StartAddress>
2379 <EndAddress>{7:s}</EndAddress>
2384 <ParentNetwork href="{8:s}"/>
2385 <FenceMode>{9:s}</FenceMode>
2387 <IsShared>{10:s}</IsShared>
2388 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2389 subnet_address
, dns_address
, dhcp_enabled
,
2390 dhcp_start_address
, dhcp_end_address
, available_networks
,
2391 fence_mode
, isshared
)
2393 headers
= vca
.vcloud_session
.get_vcloud_headers()
2394 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2396 response
= Http
.post(url
=add_vdc_rest_url
,
2402 if response
.status_code
!= 201:
2403 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2404 .format(response
.status_code
))
2406 network
= networkType
.parseString(response
.content
, True)
2407 create_nw_task
= network
.get_Tasks().get_Task()[0]
2409 # if we all ok we respond with content after network creation completes
2410 # otherwise by default return None
2411 if create_nw_task
is not None:
2412 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2413 status
= vca
.block_until_completed(create_nw_task
)
2415 return response
.content
2417 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2418 .format(response
.content
))
2419 except Exception as exp
:
2420 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2424 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2426 Method sets convert CIDR netmask address to normal IP format
2428 cidr_ip : CIDR IP address
2430 netmask : Converted netmask
2432 if cidr_ip
is not None:
2434 network
, net_bits
= cidr_ip
.split('/')
2435 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2441 def get_provider_rest(self
, vca
=None):
2443 Method gets provider vdc view from vcloud director
2446 network_name - is network name to be created.
2447 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2448 It optional attribute. by default if no parent network indicate the first available will be used.
2451 The return xml content of respond or None
2454 url_list
= [vca
.host
, '/api/admin']
2455 response
= Http
.get(url
=''.join(url_list
),
2456 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2460 if response
.status_code
== requests
.codes
.ok
:
2461 return response
.content
2464 def create_vdc(self
, vdc_name
=None):
2468 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2469 if xml_content
is not None:
2471 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2472 for child
in task_resp_xmlroot
:
2473 if child
.tag
.split("}")[1] == 'Owner':
2474 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2475 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2478 self
.logger
.debug("Respond body {}".format(xml_content
))
2482 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2484 Method create vdc in vCloud director based on VDC template.
2485 it uses pre-defined template that must be named openmano
2488 vdc_name - name of a new vdc.
2491 The return xml content of respond or None
2494 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2495 vca
= self
.connect()
2497 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2498 if vdc_name
is None:
2501 url_list
= [vca
.host
, '/api/vdcTemplates']
2502 vm_list_rest_call
= ''.join(url_list
)
2503 response
= Http
.get(url
=vm_list_rest_call
,
2504 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2508 # container url to a template
2509 vdc_template_ref
= None
2511 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2512 for child
in vm_list_xmlroot
:
2513 # application/vnd.vmware.admin.providervdc+xml
2514 # we need find a template from witch we instantiate VDC
2515 if child
.tag
.split("}")[1] == 'VdcTemplate':
2516 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2517 'name') == 'openmano':
2518 vdc_template_ref
= child
.attrib
.get('href')
2520 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2521 self
.logger
.debug("Respond body {}".format(response
.content
))
2524 # if we didn't found required pre defined template we return None
2525 if vdc_template_ref
is None:
2530 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2531 vm_list_rest_call
= ''.join(url_list
)
2532 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2533 <Source href="{1:s}"></Source>
2534 <Description>opnemano</Description>
2535 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2536 headers
= vca
.vcloud_session
.get_vcloud_headers()
2537 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2538 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2540 # if we all ok we respond with content otherwise by default None
2541 if response
.status_code
>= 200 and response
.status_code
< 300:
2542 return response
.content
2545 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2546 self
.logger
.debug("Respond body {}".format(response
.content
))
2550 def create_vdc_rest(self
, vdc_name
=None):
2552 Method create network in vCloud director
2555 network_name - is network name to be created.
2556 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2557 It optional attribute. by default if no parent network indicate the first available will be used.
2560 The return network uuid or return None
2563 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2565 vca
= self
.connect_as_admin()
2567 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2568 if vdc_name
is None:
2571 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2572 vm_list_rest_call
= ''.join(url_list
)
2573 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2574 response
= Http
.get(url
=vm_list_rest_call
,
2575 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2579 provider_vdc_ref
= None
2580 add_vdc_rest_url
= None
2581 available_networks
= None
2583 if response
.status_code
!= requests
.codes
.ok
:
2584 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2585 response
.status_code
))
2589 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2590 for child
in vm_list_xmlroot
:
2591 # application/vnd.vmware.admin.providervdc+xml
2592 if child
.tag
.split("}")[1] == 'Link':
2593 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2594 and child
.attrib
.get('rel') == 'add':
2595 add_vdc_rest_url
= child
.attrib
.get('href')
2597 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2598 self
.logger
.debug("Respond body {}".format(response
.content
))
2601 response
= self
.get_provider_rest(vca
=vca
)
2603 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2604 for child
in vm_list_xmlroot
:
2605 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2606 for sub_child
in child
:
2607 provider_vdc_ref
= sub_child
.attrib
.get('href')
2609 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2610 self
.logger
.debug("Respond body {}".format(response
))
2613 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2614 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2615 <AllocationModel>ReservationPool</AllocationModel>
2616 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2617 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2618 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2619 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2620 <ProviderVdcReference
2621 name="Main Provider"
2623 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2627 headers
= vca
.vcloud_session
.get_vcloud_headers()
2628 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2629 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2632 # if we all ok we respond with content otherwise by default None
2633 if response
.status_code
== 201:
2634 return response
.content
2637 def get_vapp_details_rest(self
, vapp_uuid
=None):
2639 Method retrieve vapp detail from vCloud director
2642 vapp_uuid - is vapp identifier.
2645 The return network uuid or return None
2650 vca
= self
.connect()
2652 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2653 if vapp_uuid
is None:
2656 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2657 get_vapp_restcall
= ''.join(url_list
)
2658 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2659 response
= Http
.get(url
=get_vapp_restcall
,
2660 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2664 if response
.status_code
!= requests
.codes
.ok
:
2665 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2666 response
.status_code
))
2667 return parsed_respond
2670 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2671 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2673 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2674 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2675 'vmw': 'http://www.vmware.com/schema/ovf',
2676 'vm': 'http://www.vmware.com/vcloud/v1.5',
2677 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2678 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2679 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2682 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2683 if created_section
is not None:
2684 parsed_respond
['created'] = created_section
.text
2686 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2687 if network_section
is not None and 'networkName' in network_section
.attrib
:
2688 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2690 ipscopes_section
= \
2691 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2693 if ipscopes_section
is not None:
2694 for ipscope
in ipscopes_section
:
2695 for scope
in ipscope
:
2696 tag_key
= scope
.tag
.split("}")[1]
2697 if tag_key
== 'IpRanges':
2698 ip_ranges
= scope
.getchildren()
2699 for ipblock
in ip_ranges
:
2700 for block
in ipblock
:
2701 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2703 parsed_respond
[tag_key
] = scope
.text
2705 # parse children section for other attrib
2706 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2707 if children_section
is not None:
2708 parsed_respond
['name'] = children_section
.attrib
['name']
2709 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2710 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2711 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2712 parsed_respond
['status'] = children_section
.attrib
['status']
2713 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2714 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2716 for adapters
in network_adapter
:
2717 adapter_key
= adapters
.tag
.split("}")[1]
2718 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2719 parsed_respond
['primarynetwork'] = adapters
.text
2720 if adapter_key
== 'NetworkConnection':
2722 if 'network' in adapters
.attrib
:
2723 vnic
['network'] = adapters
.attrib
['network']
2724 for adapter
in adapters
:
2725 setting_key
= adapter
.tag
.split("}")[1]
2726 vnic
[setting_key
] = adapter
.text
2727 nic_list
.append(vnic
)
2729 for link
in children_section
:
2730 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2731 if link
.attrib
['rel'] == 'screen:acquireTicket':
2732 parsed_respond
['acquireTicket'] = link
.attrib
2733 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2734 parsed_respond
['acquireMksTicket'] = link
.attrib
2736 parsed_respond
['interfaces'] = nic_list
2738 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2739 vm_virtual_hardware_info
= {}
2740 if virtual_hardware_section
is not None:
2741 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2742 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2743 disk_size
= item
.find("rasd:HostResource" ,namespaces
2744 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2746 vm_virtual_hardware_info
["disk_size"]= disk_size
2749 for link
in virtual_hardware_section
:
2750 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2751 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2752 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2755 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2756 except Exception as exp
:
2757 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2758 return parsed_respond
2760 def acuire_console(self
, vm_uuid
=None):
2762 vca
= self
.connect()
2764 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2768 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2769 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2770 console_dict
= vm_dict
['acquireTicket']
2771 console_rest_call
= console_dict
['href']
2773 response
= Http
.post(url
=console_rest_call
,
2774 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2778 if response
.status_code
== requests
.codes
.ok
:
2779 return response
.content
2783 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2785 Method retrieve vm disk details
2788 vapp_uuid - is vapp identifier.
2789 flavor_disk - disk size as specified in VNFD (flavor)
2792 The return network uuid or return None
2796 #Flavor disk is in GB convert it into MB
2797 flavor_disk
= int(flavor_disk
) * 1024
2798 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2800 vm_name
= vm_details
["name"]
2801 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2803 if vm_details
and "vm_virtual_hardware" in vm_details
:
2804 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2805 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2807 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2809 if flavor_disk
> vm_disk
:
2810 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2811 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2812 vm_disk
, flavor_disk
))
2815 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
2818 except Exception as exp
:
2819 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
2822 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
2824 Method retrieve modify vm disk size
2827 disk_href - vCD API URL to GET and PUT disk data
2828 disk_size - disk size as specified in VNFD (flavor)
2831 The return network uuid or return None
2833 vca
= self
.connect()
2835 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2836 if disk_href
is None or disk_size
is None:
2839 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2840 response
= Http
.get(url
=disk_href
,
2841 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2845 if response
.status_code
!= requests
.codes
.ok
:
2846 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
2847 response
.status_code
))
2850 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
2851 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
2852 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2854 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
2855 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2856 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
2857 if disk_item
is not None:
2858 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
2861 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
2862 xml_declaration
=True)
2864 #Send PUT request to modify disk size
2865 headers
= vca
.vcloud_session
.get_vcloud_headers()
2866 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2868 response
= Http
.put(url
=disk_href
,
2871 verify
=vca
.verify
, logger
=self
.logger
)
2873 if response
.status_code
!= 202:
2874 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
2875 response
.status_code
))
2877 modify_disk_task
= taskType
.parseString(response
.content
, True)
2878 if type(modify_disk_task
) is GenericTask
:
2879 status
= vca
.block_until_completed(modify_disk_task
)
2884 except Exception as exp
:
2885 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))