1 # -*- coding: utf-8 -*-
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
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: osslegalrouting@vmware.com
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
28 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
38 from pyVmomi
import vim
, vmodl
39 from pyVim
.connect
import SmartConnect
, Disconnect
41 from xml
.etree
import ElementTree
as XmlElementTree
42 from lxml
import etree
as lxmlElementTree
45 from pyvcloud
.vcd
.client
import BasicLoginCredentials
,Client
,VcdTaskException
46 from pyvcloud
.vcd
.vdc
import VDC
47 from pyvcloud
.vcd
.org
import Org
49 from pyvcloud
.vcd
.vapp
import VApp
50 from xml
.sax
.saxutils
import escape
64 # global variable for vcd connector type
65 STANDALONE
= 'standalone'
67 # key for flavor dicts
68 FLAVOR_RAM_KEY
= 'ram'
69 FLAVOR_VCPUS_KEY
= 'vcpus'
70 FLAVOR_DISK_KEY
= 'disk'
71 DEFAULT_IP_PROFILE
= {'dhcp_count':50,
75 # global variable for wait time
81 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare, Prakash Kasar"
82 __date__
= "$09-Mar-2018 11:09:29$"
85 # -1: "Could not be created",
91 # 5: "Waiting for user input",
93 # 7: "Unrecognized state",
95 # 9: "Inconsistent state",
96 # 10: "Children do not all have the same status",
97 # 11: "Upload initiated, OVF descriptor pending",
98 # 12: "Upload initiated, copying contents",
99 # 13: "Upload initiated , disk contents pending",
100 # 14: "Upload has been quarantined",
101 # 15: "Upload quarantine period has expired"
103 # mapping vCD status to MANO
104 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
113 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
114 'ERROR': 'ERROR', 'DELETED': 'DELETED'
117 class vimconnector(vimconn
.vimconnector
):
118 # dict used to store flavor in memory
121 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
122 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}, persistent_info
={}):
124 Constructor create vmware connector to vCloud director.
126 By default construct doesn't validate connection state. So client can create object with None arguments.
127 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
129 a) It initialize organization UUID
130 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
133 uuid - is organization uuid.
134 name - is organization name that must be presented in vCloud director.
135 tenant_id - is VDC uuid it must be presented in vCloud director
136 tenant_name - is VDC name.
137 url - is hostname or ip address of vCloud director
138 url_admin - same as above.
139 user - is user that administrator for organization. Caller must make sure that
140 username has right privileges.
142 password - is password for a user.
144 VMware connector also requires PVDC administrative privileges and separate account.
145 This variables must be passed via config argument dict contains keys
147 dict['admin_username']
148 dict['admin_password']
149 config - Provide NSX and vCenter information
155 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
156 url_admin
, user
, passwd
, log_level
, config
)
158 self
.logger
= logging
.getLogger('openmano.vim.vmware')
159 self
.logger
.setLevel(10)
160 self
.persistent_info
= persistent_info
165 self
.url_admin
= url_admin
166 self
.tenant_id
= tenant_id
167 self
.tenant_name
= tenant_name
171 self
.admin_password
= None
172 self
.admin_user
= None
174 self
.nsx_manager
= None
176 self
.nsx_password
= None
177 self
.availability_zone
= None
179 # Disable warnings from self-signed certificates.
180 requests
.packages
.urllib3
.disable_warnings()
182 if tenant_name
is not None:
183 orgnameandtenant
= tenant_name
.split(":")
184 if len(orgnameandtenant
) == 2:
185 self
.tenant_name
= orgnameandtenant
[1]
186 self
.org_name
= orgnameandtenant
[0]
188 self
.tenant_name
= tenant_name
189 if "orgname" in config
:
190 self
.org_name
= config
['orgname']
193 self
.logger
.setLevel(getattr(logging
, log_level
))
196 self
.admin_user
= config
['admin_username']
197 self
.admin_password
= config
['admin_password']
199 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
202 self
.nsx_manager
= config
['nsx_manager']
203 self
.nsx_user
= config
['nsx_user']
204 self
.nsx_password
= config
['nsx_password']
206 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
208 self
.vcenter_ip
= config
.get("vcenter_ip", None)
209 self
.vcenter_port
= config
.get("vcenter_port", None)
210 self
.vcenter_user
= config
.get("vcenter_user", None)
211 self
.vcenter_password
= config
.get("vcenter_password", None)
213 #Set availability zone for Affinity rules
214 self
.availability_zone
= self
.set_availability_zones()
216 # ############# Stub code for SRIOV #################
218 # self.dvs_name = config['dv_switch_name']
220 # raise vimconn.vimconnException(message="Error: distributed virtaul switch name is empty in Config")
222 # self.vlanID_range = config.get("vlanID_range", None)
228 raise vimconn
.vimconnException('url param can not be NoneType')
230 if not self
.url_admin
: # try to use normal url
231 self
.url_admin
= self
.url
233 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
234 self
.tenant_id
, self
.tenant_name
))
235 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
236 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
238 # initialize organization
239 if self
.user
is not None and self
.passwd
is not None and self
.url
:
240 self
.init_organization()
242 def __getitem__(self
, index
):
245 if index
== 'tenant_id':
246 return self
.tenant_id
247 if index
== 'tenant_name':
248 return self
.tenant_name
251 elif index
== 'org_name':
253 elif index
== 'org_uuid':
255 elif index
== 'user':
257 elif index
== 'passwd':
261 elif index
== 'url_admin':
262 return self
.url_admin
263 elif index
== "config":
266 raise KeyError("Invalid key '%s'" % str(index
))
268 def __setitem__(self
, index
, value
):
271 if index
== 'tenant_id':
272 self
.tenant_id
= value
273 if index
== 'tenant_name':
274 self
.tenant_name
= value
277 elif index
== 'org_name':
278 self
.org_name
= value
279 elif index
== 'org_uuid':
280 self
.org_uuid
= value
281 elif index
== 'user':
283 elif index
== 'passwd':
287 elif index
== 'url_admin':
288 self
.url_admin
= value
290 raise KeyError("Invalid key '%s'" % str(index
))
292 def connect_as_admin(self
):
293 """ Method connect as pvdc admin user to vCloud director.
294 There are certain action that can be done only by provider vdc admin user.
295 Organization creation / provider network creation etc.
298 The return client object that latter can be used to connect to vcloud director as admin for provider vdc
300 self
.logger
.debug("Logging into vCD {} as admin.".format(self
.org_name
))
305 client_as_admin
= Client(host
, verify_ssl_certs
=False)
306 client_as_admin
.set_highest_supported_version()
307 client_as_admin
.set_credentials(BasicLoginCredentials(self
.admin_user
, org
, self
.admin_password
))
308 except Exception as e
:
309 raise vimconn
.vimconnException(
310 "Can't connect to a vCloud director as: {} with exception {}".format(self
.admin_user
, e
))
312 return client_as_admin
315 """ Method connect as normal user to vCloud director.
318 The return client object that latter can be used to connect to vCloud director as admin for VDC
321 self
.logger
.debug("Logging into vCD {} as {} to datacenter {}.".format(self
.org_name
,
325 client
= Client(host
, verify_ssl_certs
=False)
326 client
.set_highest_supported_version()
327 client
.set_credentials(BasicLoginCredentials(self
.user
, self
.org_name
, self
.passwd
))
329 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
330 "{} as user: {}".format(self
.org_name
, self
.user
))
334 def init_organization(self
):
335 """ Method initialize organization UUID and VDC parameters.
337 At bare minimum client must provide organization name that present in vCloud director and VDC.
339 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
340 The Org - UUID will be initialized at the run time if data center present in vCloud director.
343 The return vca object that letter can be used to connect to vcloud direct as admin
345 client
= self
.connect()
347 raise vimconn
.vimconnConnectionException("Failed to connect vCD.")
351 if self
.org_uuid
is None:
352 org_list
= client
.get_org_list()
353 for org
in org_list
.Org
:
354 # we set org UUID at the init phase but we can do it only when we have valid credential.
355 if org
.get('name') == self
.org_name
:
356 self
.org_uuid
= org
.get('href').split('/')[-1]
357 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
360 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
362 # if well good we require for org details
363 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
365 # we have two case if we want to initialize VDC ID or VDC name at run time
366 # tenant_name provided but no tenant id
367 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
368 vdcs_dict
= org_details_dict
['vdcs']
369 for vdc
in vdcs_dict
:
370 if vdcs_dict
[vdc
] == self
.tenant_name
:
372 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
376 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
377 # case two we have tenant_id but we don't have tenant name so we find and set it.
378 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
379 vdcs_dict
= org_details_dict
['vdcs']
380 for vdc
in vdcs_dict
:
381 if vdc
== self
.tenant_id
:
382 self
.tenant_name
= vdcs_dict
[vdc
]
383 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
387 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
388 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
390 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
391 self
.logger
.debug(traceback
.format_exc())
394 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
395 """ Method adds a new tenant to VIM with this name.
396 This action requires access to create VDC action in vCloud director.
399 tenant_name is tenant_name to be created.
400 tenant_description not used for this call
403 returns the tenant identifier in UUID format.
404 If action is failed method will throw vimconn.vimconnException method
406 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
407 if vdc_task
is not None:
408 vdc_uuid
, value
= vdc_task
.popitem()
409 self
.logger
.info("Created new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
412 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
414 def delete_tenant(self
, tenant_id
=None):
415 """ Delete a tenant from VIM
417 tenant_id is tenant_id to be deleted.
420 returns the tenant identifier in UUID format.
421 If action is failed method will throw exception
423 vca
= self
.connect_as_admin()
425 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
427 if tenant_id
is not None:
430 url_list
= [self
.url
, '/api/vdc/', tenant_id
]
431 orgvdc_herf
= ''.join(url_list
)
433 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
434 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
435 response
= self
.perform_request(req_type
='GET',
439 if response
.status_code
!= requests
.codes
.ok
:
440 self
.logger
.debug("delete_tenant():GET REST API call {} failed. "\
441 "Return status code {}".format(orgvdc_herf
,
442 response
.status_code
))
443 raise vimconn
.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id
))
445 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
446 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
448 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
449 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
450 vdc_remove_href
= lxmlroot_respond
.find("xmlns:Link[@rel='remove']",namespaces
).attrib
['href']
451 vdc_remove_href
= vdc_remove_href
+ '?recursive=true&force=true'
453 response
= self
.perform_request(req_type
='DELETE',
457 if response
.status_code
== 202:
461 self
.logger
.debug("delete_tenant(): DELETE REST API call {} failed. "\
462 "Return status code {}".format(vdc_remove_href
,
463 response
.status_code
))
464 raise vimconn
.vimconnException("Fail to delete tenant with ID {}".format(tenant_id
))
466 self
.logger
.debug("delete_tenant():Incorrect tenant ID {}".format(tenant_id
))
467 raise vimconn
.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id
))
470 def get_tenant_list(self
, filter_dict
={}):
471 """Obtain tenants of VIM
472 filter_dict can contain the following keys:
473 name: filter by tenant name
474 id: filter by tenant uuid/id
476 Returns the tenant list of dictionaries:
477 [{'name':'<name>, 'id':'<id>, ...}, ...]
480 org_dict
= self
.get_org(self
.org_uuid
)
481 vdcs_dict
= org_dict
['vdcs']
486 entry
= {'name': vdcs_dict
[k
], 'id': k
}
487 # if caller didn't specify dictionary we return all tenants.
488 if filter_dict
is not None and filter_dict
:
489 filtered_entry
= entry
.copy()
490 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
491 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
492 if filter_dict
== entry
:
493 vdclist
.append(filtered_entry
)
495 vdclist
.append(entry
)
497 self
.logger
.debug("Error in get_tenant_list()")
498 self
.logger
.debug(traceback
.format_exc())
499 raise vimconn
.vimconnException("Incorrect state. {}")
503 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False, vlan
=None):
504 """Adds a tenant network to VIM
506 'net_name': name of the network
508 'bridge': overlay isolated network
509 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
510 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
511 'ip_profile': is a dict containing the IP parameters of the network
512 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
513 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
514 'gateway_address': (Optional) ip_schema, that is X.X.X.X
515 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
516 'dhcp_enabled': True or False
517 'dhcp_start_address': ip_schema, first IP to grant
518 'dhcp_count': number of IPs to grant.
519 'shared': if this network can be seen/use by other tenants/organization
520 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
521 Returns a tuple with the network identifier and created_items, or raises an exception on error
522 created_items can be None or a dictionary where this method can include key-values that will be passed to
523 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
524 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
528 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
529 .format(net_name
, net_type
, ip_profile
, shared
))
536 # ############# Stub code for SRIOV #################
537 # if net_type == "data" or net_type == "ptp":
538 # if self.config.get('dv_switch_name') == None:
539 # raise vimconn.vimconnConflictException("You must provide 'dv_switch_name' at config value")
540 # network_uuid = self.create_dvPort_group(net_name)
542 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
543 ip_profile
=ip_profile
, isshared
=isshared
)
544 if network_uuid
is not None:
545 return network_uuid
, created_items
547 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
549 def get_vcd_network_list(self
):
550 """ Method available organization for a logged in tenant
553 The return vca object that letter can be used to connect to vcloud direct as admin
556 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
558 if not self
.tenant_name
:
559 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
561 org
, vdc
= self
.get_vdc_details()
563 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
565 vdc_uuid
= vdc
.get('id').split(":")[3]
566 if self
.client
._session
:
567 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
568 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
569 response
= self
.perform_request(req_type
='GET',
572 if response
.status_code
!= 200:
573 self
.logger
.error("Failed to get vdc content")
574 raise vimconn
.vimconnNotFoundException("Failed to get vdc content")
576 content
= XmlElementTree
.fromstring(response
.content
)
581 if item
.tag
.split('}')[-1] == 'AvailableNetworks':
583 response
= self
.perform_request(req_type
='GET',
587 if response
.status_code
!= 200:
588 self
.logger
.error("Failed to get network content")
589 raise vimconn
.vimconnNotFoundException("Failed to get network content")
591 net_details
= XmlElementTree
.fromstring(response
.content
)
594 net_uuid
= net_details
.get('id').split(":")
595 if len(net_uuid
) != 4:
598 net_uuid
= net_uuid
[3]
600 self
.logger
.debug("get_vcd_network_list(): Adding network {} "
601 "to a list vcd id {} network {}".format(net_uuid
,
603 net_details
.get('name')))
604 filter_dict
["name"] = net_details
.get('name')
605 filter_dict
["id"] = net_uuid
606 if [i
.text
for i
in net_details
if i
.tag
.split('}')[-1] == 'IsShared'][0] == 'true':
610 filter_dict
["shared"] = shared
611 filter_dict
["tenant_id"] = vdc_uuid
612 if int(net_details
.get('status')) == 1:
613 filter_dict
["admin_state_up"] = True
615 filter_dict
["admin_state_up"] = False
616 filter_dict
["status"] = "ACTIVE"
617 filter_dict
["type"] = "bridge"
618 network_list
.append(filter_dict
)
619 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
621 self
.logger
.debug("Error in get_vcd_network_list", exc_info
=True)
624 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
627 def get_network_list(self
, filter_dict
={}):
628 """Obtain tenant networks of VIM
630 name: network name OR/AND
631 id: network uuid OR/AND
632 shared: boolean OR/AND
633 tenant_id: tenant OR/AND
634 admin_state_up: boolean
637 [{key : value , key : value}]
639 Returns the network list of dictionaries:
640 [{<the fields at Filter_dict plus some VIM specific>}, ...]
644 self
.logger
.debug("get_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
646 if not self
.tenant_name
:
647 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
649 org
, vdc
= self
.get_vdc_details()
651 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
654 vdcid
= vdc
.get('id').split(":")[3]
656 if self
.client
._session
:
657 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
658 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
659 response
= self
.perform_request(req_type
='GET',
662 if response
.status_code
!= 200:
663 self
.logger
.error("Failed to get vdc content")
664 raise vimconn
.vimconnNotFoundException("Failed to get vdc content")
666 content
= XmlElementTree
.fromstring(response
.content
)
670 if item
.tag
.split('}')[-1] == 'AvailableNetworks':
672 response
= self
.perform_request(req_type
='GET',
676 if response
.status_code
!= 200:
677 self
.logger
.error("Failed to get network content")
678 raise vimconn
.vimconnNotFoundException("Failed to get network content")
680 net_details
= XmlElementTree
.fromstring(response
.content
)
683 net_uuid
= net_details
.get('id').split(":")
684 if len(net_uuid
) != 4:
687 net_uuid
= net_uuid
[3]
689 self
.logger
.debug("get_network_list(): Adding net {}"
690 " to a list vcd id {} network {}".format(net_uuid
,
692 net_details
.get('name')))
693 filter_entry
["name"] = net_details
.get('name')
694 filter_entry
["id"] = net_uuid
695 if [i
.text
for i
in net_details
if i
.tag
.split('}')[-1] == 'IsShared'][0] == 'true':
699 filter_entry
["shared"] = shared
700 filter_entry
["tenant_id"] = vdcid
701 if int(net_details
.get('status')) == 1:
702 filter_entry
["admin_state_up"] = True
704 filter_entry
["admin_state_up"] = False
705 filter_entry
["status"] = "ACTIVE"
706 filter_entry
["type"] = "bridge"
707 filtered_entry
= filter_entry
.copy()
709 if filter_dict
is not None and filter_dict
:
710 # we remove all the key : value we don't care and match only
712 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
713 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
714 if filter_dict
== filter_entry
:
715 network_list
.append(filtered_entry
)
717 network_list
.append(filtered_entry
)
718 except Exception as e
:
719 self
.logger
.debug("Error in get_network_list",exc_info
=True)
720 if isinstance(e
, vimconn
.vimconnException
):
723 raise vimconn
.vimconnNotFoundException("Failed : Networks list not found {} ".format(e
))
725 self
.logger
.debug("Returning {}".format(network_list
))
728 def get_network(self
, net_id
):
729 """Method obtains network details of net_id VIM network
730 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
733 org
, vdc
= self
.get_vdc_details()
734 vdc_id
= vdc
.get('id').split(":")[3]
735 if self
.client
._session
:
736 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
737 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
738 response
= self
.perform_request(req_type
='GET',
741 if response
.status_code
!= 200:
742 self
.logger
.error("Failed to get vdc content")
743 raise vimconn
.vimconnNotFoundException("Failed to get vdc content")
745 content
= XmlElementTree
.fromstring(response
.content
)
750 if item
.tag
.split('}')[-1] == 'AvailableNetworks':
752 response
= self
.perform_request(req_type
='GET',
756 if response
.status_code
!= 200:
757 self
.logger
.error("Failed to get network content")
758 raise vimconn
.vimconnNotFoundException("Failed to get network content")
760 net_details
= XmlElementTree
.fromstring(response
.content
)
762 vdc_network_id
= net_details
.get('id').split(":")
763 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
764 filter_dict
["name"] = net_details
.get('name')
765 filter_dict
["id"] = vdc_network_id
[3]
766 if [i
.text
for i
in net_details
if i
.tag
.split('}')[-1] == 'IsShared'][0] == 'true':
770 filter_dict
["shared"] = shared
771 filter_dict
["tenant_id"] = vdc_id
772 if int(net_details
.get('status')) == 1:
773 filter_dict
["admin_state_up"] = True
775 filter_dict
["admin_state_up"] = False
776 filter_dict
["status"] = "ACTIVE"
777 filter_dict
["type"] = "bridge"
778 self
.logger
.debug("Returning {}".format(filter_dict
))
781 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
782 except Exception as e
:
783 self
.logger
.debug("Error in get_network")
784 self
.logger
.debug(traceback
.format_exc())
785 if isinstance(e
, vimconn
.vimconnException
):
788 raise vimconn
.vimconnNotFoundException("Failed : Network not found {} ".format(e
))
792 def delete_network(self
, net_id
, created_items
=None):
794 Removes a tenant network from VIM and its associated elements
795 :param net_id: VIM identifier of the network, provided by method new_network
796 :param created_items: dictionary with extra items to be deleted. provided by method new_network
797 Returns the network identifier or raises an exception upon error or when network is not found
800 # ############# Stub code for SRIOV #################
801 # dvport_group = self.get_dvport_group(net_id)
804 # status = self.destroy_dvport_group(net_id)
806 # # Remove vlanID from persistent info
807 # if net_id in self.persistent_info["used_vlanIDs"]:
808 # del self.persistent_info["used_vlanIDs"][net_id]
812 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
813 if vcd_network
is not None and vcd_network
:
814 if self
.delete_network_action(network_uuid
=net_id
):
817 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
819 def refresh_nets_status(self
, net_list
):
820 """Get the status of the networks
821 Params: the list of network identifiers
822 Returns a dictionary with:
823 net_id: #VIM id of this network
824 status: #Mandatory. Text with one of:
825 # DELETED (not found at vim)
826 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
827 # OTHER (Vim reported other status not understood)
828 # ERROR (VIM indicates an ERROR status)
829 # ACTIVE, INACTIVE, DOWN (admin down),
830 # BUILD (on building process)
832 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
833 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
841 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
842 if vcd_network
is not None and vcd_network
:
843 if vcd_network
['status'] == '1':
849 errormsg
= 'Network not found.'
851 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
852 'vim_info': yaml
.safe_dump(vcd_network
)}
854 self
.logger
.debug("Error in refresh_nets_status")
855 self
.logger
.debug(traceback
.format_exc())
859 def get_flavor(self
, flavor_id
):
860 """Obtain flavor details from the VIM
861 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
863 if flavor_id
not in vimconnector
.flavorlist
:
864 raise vimconn
.vimconnNotFoundException("Flavor not found.")
865 return vimconnector
.flavorlist
[flavor_id
]
867 def new_flavor(self
, flavor_data
):
868 """Adds a tenant flavor to VIM
869 flavor_data contains a dictionary with information, keys:
871 ram: memory (cloud type) in MBytes
872 vpcus: cpus (cloud type)
873 extended: EPA parameters
874 - numas: #items requested in same NUMA
875 memory: number of 1G huge pages memory
876 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
877 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
878 - name: interface name
879 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
880 bandwidth: X Gbps; requested guarantee bandwidth
881 vpci: requested virtual PCI address
885 Returns the flavor identifier"""
887 # generate a new uuid put to internal dict and return it.
888 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
889 new_flavor
=flavor_data
890 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
891 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
892 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 0)
894 if not isinstance(ram
, int):
895 raise vimconn
.vimconnException("Non-integer value for ram")
896 elif not isinstance(cpu
, int):
897 raise vimconn
.vimconnException("Non-integer value for cpu")
898 elif not isinstance(disk
, int):
899 raise vimconn
.vimconnException("Non-integer value for disk")
901 extended_flv
= flavor_data
.get("extended")
903 numas
=extended_flv
.get("numas")
906 #overwrite ram and vcpus
908 ram
= numa
['memory']*1024
909 if 'paired-threads' in numa
:
910 cpu
= numa
['paired-threads']*2
911 elif 'cores' in numa
:
913 elif 'threads' in numa
:
914 cpu
= numa
['threads']
916 new_flavor
[FLAVOR_RAM_KEY
] = ram
917 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
918 new_flavor
[FLAVOR_DISK_KEY
] = disk
919 # generate a new uuid put to internal dict and return it.
920 flavor_id
= uuid
.uuid4()
921 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
922 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
924 return str(flavor_id
)
926 def delete_flavor(self
, flavor_id
):
927 """Deletes a tenant flavor from VIM identify by its id
929 Returns the used id or raise an exception
931 if flavor_id
not in vimconnector
.flavorlist
:
932 raise vimconn
.vimconnNotFoundException("Flavor not found.")
934 vimconnector
.flavorlist
.pop(flavor_id
, None)
937 def new_image(self
, image_dict
):
939 Adds a tenant image to VIM
941 200, image-id if the image is created
942 <0, message if there is an error
945 return self
.get_image_id_from_path(image_dict
['location'])
947 def delete_image(self
, image_id
):
949 Deletes a tenant image from VIM
951 image_id is ID of Image to be deleted
953 returns the image identifier in UUID format or raises an exception on error
955 conn
= self
.connect_as_admin()
957 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
958 # Get Catalog details
959 url_list
= [self
.url
, '/api/catalog/', image_id
]
960 catalog_herf
= ''.join(url_list
)
962 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
963 'x-vcloud-authorization': conn
._session
.headers
['x-vcloud-authorization']}
965 response
= self
.perform_request(req_type
='GET',
969 if response
.status_code
!= requests
.codes
.ok
:
970 self
.logger
.debug("delete_image():GET REST API call {} failed. "\
971 "Return status code {}".format(catalog_herf
,
972 response
.status_code
))
973 raise vimconn
.vimconnNotFoundException("Fail to get image {}".format(image_id
))
975 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
976 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
978 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
979 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
981 catalogItems_section
= lxmlroot_respond
.find("xmlns:CatalogItems",namespaces
)
982 catalogItems
= catalogItems_section
.iterfind("xmlns:CatalogItem",namespaces
)
983 for catalogItem
in catalogItems
:
984 catalogItem_href
= catalogItem
.attrib
['href']
986 response
= self
.perform_request(req_type
='GET',
987 url
=catalogItem_href
,
990 if response
.status_code
!= requests
.codes
.ok
:
991 self
.logger
.debug("delete_image():GET REST API call {} failed. "\
992 "Return status code {}".format(catalog_herf
,
993 response
.status_code
))
994 raise vimconn
.vimconnNotFoundException("Fail to get catalogItem {} for catalog {}".format(
998 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
999 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
1001 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
1002 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
1003 catalogitem_remove_href
= lxmlroot_respond
.find("xmlns:Link[@rel='remove']",namespaces
).attrib
['href']
1006 response
= self
.perform_request(req_type
='DELETE',
1007 url
=catalogitem_remove_href
,
1009 if response
.status_code
== requests
.codes
.no_content
:
1010 self
.logger
.debug("Deleted Catalog item {}".format(catalogItem
))
1012 raise vimconn
.vimconnException("Fail to delete Catalog Item {}".format(catalogItem
))
1015 url_list
= [self
.url
, '/api/admin/catalog/', image_id
]
1016 catalog_remove_herf
= ''.join(url_list
)
1017 response
= self
.perform_request(req_type
='DELETE',
1018 url
=catalog_remove_herf
,
1021 if response
.status_code
== requests
.codes
.no_content
:
1022 self
.logger
.debug("Deleted Catalog {}".format(image_id
))
1025 raise vimconn
.vimconnException("Fail to delete Catalog {}".format(image_id
))
1028 def catalog_exists(self
, catalog_name
, catalogs
):
1031 :param catalog_name:
1035 for catalog
in catalogs
:
1036 if catalog
['name'] == catalog_name
:
1040 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
1041 """ Create new catalog entry in vCloud director.
1044 vca: vCloud director.
1045 catalog_name catalog that client wish to create. Note no validation done for a name.
1046 Client must make sure that provide valid string representation.
1048 Return (bool) True if catalog created.
1052 result
= vca
.create_catalog(catalog_name
, catalog_name
)
1053 if result
is not None:
1055 catalogs
= vca
.list_catalogs()
1058 return self
.catalog_exists(catalog_name
, catalogs
)
1060 # noinspection PyIncorrectDocstring
1061 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
1062 description
='', progress
=False, chunk_bytes
=128 * 1024):
1064 Uploads a OVF file to a vCloud catalog
1071 :param catalog_name: (str): The name of the catalog to upload the media.
1072 :param media_file_name: (str): The name of the local media file to upload.
1073 :return: (bool) True if the media file was successfully uploaded, false otherwise.
1075 os
.path
.isfile(media_file_name
)
1076 statinfo
= os
.stat(media_file_name
)
1078 # find a catalog entry where we upload OVF.
1079 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
1081 # if VCD can parse OVF we upload VMDK file
1083 for catalog
in vca
.list_catalogs():
1084 if catalog_name
!= catalog
['name']:
1086 catalog_href
= "{}/api/catalog/{}/action/upload".format(self
.url
, catalog
['id'])
1088 <UploadVAppTemplateParams name="{}" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>{} vApp Template</Description></UploadVAppTemplateParams>
1089 """.format(catalog_name
, description
)
1092 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
1093 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
1094 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
1096 response
= self
.perform_request(req_type
='POST',
1101 if response
.status_code
== requests
.codes
.created
:
1102 catalogItem
= XmlElementTree
.fromstring(response
.content
)
1103 entity
= [child
for child
in catalogItem
if
1104 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
1105 href
= entity
.get('href')
1108 response
= self
.perform_request(req_type
='GET',
1112 if response
.status_code
== requests
.codes
.ok
:
1113 headers
['Content-Type'] = 'Content-Type text/xml'
1114 result
= re
.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"',response
.content
)
1116 transfer_href
= result
.group(1)
1118 response
= self
.perform_request(req_type
='PUT',
1121 data
=open(media_file_name
, 'rb'))
1122 if response
.status_code
!= requests
.codes
.ok
:
1124 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
1128 # TODO fix this with aync block
1131 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
1133 # uploading VMDK file
1134 # check status of OVF upload and upload remaining files.
1135 response
= self
.perform_request(req_type
='GET',
1139 if response
.status_code
== requests
.codes
.ok
:
1140 result
= re
.search('rel="upload:default"\s*href="(.*?vmdk)"',response
.content
)
1142 link_href
= result
.group(1)
1143 # we skip ovf since it already uploaded.
1144 if 'ovf' in link_href
:
1146 # The OVF file and VMDK must be in a same directory
1147 head
, tail
= os
.path
.split(media_file_name
)
1148 file_vmdk
= head
+ '/' + link_href
.split("/")[-1]
1149 if not os
.path
.isfile(file_vmdk
):
1151 statinfo
= os
.stat(file_vmdk
)
1152 if statinfo
.st_size
== 0:
1154 hrefvmdk
= link_href
1157 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
1158 FileTransferSpeed()]
1159 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
1161 bytes_transferred
= 0
1162 f
= open(file_vmdk
, 'rb')
1163 while bytes_transferred
< statinfo
.st_size
:
1164 my_bytes
= f
.read(chunk_bytes
)
1165 if len(my_bytes
) <= chunk_bytes
:
1166 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
1167 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
1168 headers
['Content-Length'] = str(len(my_bytes
))
1169 response
= requests
.put(url
=hrefvmdk
,
1173 if response
.status_code
== requests
.codes
.ok
:
1174 bytes_transferred
+= len(my_bytes
)
1176 progress_bar
.update(bytes_transferred
)
1179 'file upload failed with error: [%s] %s' % (response
.status_code
,
1186 progress_bar
.finish()
1190 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
1191 format(catalog_name
, media_file_name
))
1193 except Exception as exp
:
1194 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1195 .format(catalog_name
,media_file_name
, exp
))
1196 raise vimconn
.vimconnException(
1197 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1198 .format(catalog_name
,media_file_name
, exp
))
1200 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
1203 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
1204 """Upload media file"""
1205 # TODO add named parameters for readability
1207 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
1208 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
1210 def validate_uuid4(self
, uuid_string
=None):
1211 """ Method validate correct format of UUID.
1213 Return: true if string represent valid uuid
1216 val
= uuid
.UUID(uuid_string
, version
=4)
1221 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
1222 """ Method check catalog and return catalog ID in UUID format.
1225 catalog_name: catalog name as string
1226 catalogs: list of catalogs.
1228 Return: catalogs uuid
1231 for catalog
in catalogs
:
1232 if catalog
['name'] == catalog_name
:
1233 catalog_id
= catalog
['id']
1237 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1238 """ Method check catalog and return catalog name lookup done by catalog UUID.
1241 catalog_name: catalog name as string
1242 catalogs: list of catalogs.
1244 Return: catalogs name or None
1247 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1250 for catalog
in catalogs
:
1251 catalog_id
= catalog
.get('id')
1252 if catalog_id
== catalog_uuid
:
1253 return catalog
.get('name')
1256 def get_catalog_obj(self
, catalog_uuid
=None, catalogs
=None):
1257 """ Method check catalog and return catalog name lookup done by catalog UUID.
1260 catalog_name: catalog name as string
1261 catalogs: list of catalogs.
1263 Return: catalogs name or None
1266 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1269 for catalog
in catalogs
:
1270 catalog_id
= catalog
.get('id')
1271 if catalog_id
== catalog_uuid
:
1275 def get_image_id_from_path(self
, path
=None, progress
=False):
1276 """ Method upload OVF image to vCloud director.
1278 Each OVF image represented as single catalog entry in vcloud director.
1279 The method check for existing catalog entry. The check done by file name without file extension.
1281 if given catalog name already present method will respond with existing catalog uuid otherwise
1282 it will create new catalog entry and upload OVF file to newly created catalog.
1284 If method can't create catalog entry or upload a file it will throw exception.
1286 Method accept boolean flag progress that will output progress bar. It useful method
1287 for standalone upload use case. In case to test large file upload.
1290 path: - valid path to OVF file.
1291 progress - boolean progress bar show progress bar.
1293 Return: if image uploaded correct method will provide image catalog UUID.
1297 raise vimconn
.vimconnException("Image path can't be None.")
1299 if not os
.path
.isfile(path
):
1300 raise vimconn
.vimconnException("Can't read file. File not found.")
1302 if not os
.access(path
, os
.R_OK
):
1303 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1305 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1307 dirpath
, filename
= os
.path
.split(path
)
1308 flname
, file_extension
= os
.path
.splitext(path
)
1309 if file_extension
!= '.ovf':
1310 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1311 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1313 catalog_name
= os
.path
.splitext(filename
)[0]
1314 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1315 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1316 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1319 org
,vdc
= self
.get_vdc_details()
1320 catalogs
= org
.list_catalogs()
1321 except Exception as exp
:
1322 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1323 raise vimconn
.vimconnException("Failed get catalogs() with Exception {} ".format(exp
))
1325 if len(catalogs
) == 0:
1326 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1327 result
= self
.create_vimcatalog(org
, catalog_md5_name
)
1329 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1331 result
= self
.upload_vimimage(vca
=org
, catalog_name
=catalog_md5_name
,
1332 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1334 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1335 return self
.get_catalogid(catalog_name
, catalogs
)
1337 for catalog
in catalogs
:
1338 # search for existing catalog if we find same name we return ID
1339 # TODO optimize this
1340 if catalog
['name'] == catalog_md5_name
:
1341 self
.logger
.debug("Found existing catalog entry for {} "
1342 "catalog id {}".format(catalog_name
,
1343 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1344 return self
.get_catalogid(catalog_md5_name
, catalogs
)
1346 # if we didn't find existing catalog we create a new one and upload image.
1347 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1348 result
= self
.create_vimcatalog(org
, catalog_md5_name
)
1350 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1352 result
= self
.upload_vimimage(vca
=org
, catalog_name
=catalog_md5_name
,
1353 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1355 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1357 return self
.get_catalogid(catalog_md5_name
, org
.list_catalogs())
1359 def get_image_list(self
, filter_dict
={}):
1360 '''Obtain tenant images from VIM
1364 checksum: image checksum
1365 location: image path
1366 Returns the image list of dictionaries:
1367 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1372 org
, vdc
= self
.get_vdc_details()
1374 catalogs
= org
.list_catalogs()
1375 if len(catalogs
) == 0:
1378 for catalog
in catalogs
:
1379 catalog_uuid
= catalog
.get('id')
1380 name
= catalog
.get('name')
1382 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1384 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1386 filtered_dict
["name"] = name
1387 filtered_dict
["id"] = catalog_uuid
1388 image_list
.append(filtered_dict
)
1390 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1392 except Exception as exp
:
1393 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1395 def get_vappid(self
, vdc
=None, vapp_name
=None):
1396 """ Method takes vdc object and vApp name and returns vapp uuid or None
1399 vdc: The VDC object.
1400 vapp_name: is application vappp name identifier
1403 The return vApp name otherwise None
1405 if vdc
is None or vapp_name
is None:
1407 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1409 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1410 vdc
.ResourceEntities
.ResourceEntity
)
1412 #refs = [ref for ref in vdc.ResourceEntities.ResourceEntity\
1413 # if ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1415 return refs
[0].href
.split("vapp")[1][1:]
1416 except Exception as e
:
1417 self
.logger
.exception(e
)
1421 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1422 """ Method Method returns True or False if vapp deployed in vCloud director
1425 vca: Connector to VCA
1426 vdc: The VDC object.
1427 vappid: vappid is application identifier
1430 The return True if vApp deployed
1435 refs
= filter(lambda ref
:
1436 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1437 vdc
.ResourceEntities
.ResourceEntity
)
1439 #refs = [ref for ref in vdc.ResourceEntities.ResourceEntity\
1440 # if ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1442 vappid
= ref
.href
.split("vapp")[1][1:]
1443 # find vapp with respected vapp uuid
1444 if vappid
== vapp_uuid
:
1446 except Exception as e
:
1447 self
.logger
.exception(e
)
1451 def get_namebyvappid(self
, vapp_uuid
=None):
1452 """Method returns vApp name from vCD and lookup done by vapp_id.
1455 vapp_uuid: vappid is application identifier
1458 The return vApp name otherwise None
1461 if self
.client
and vapp_uuid
:
1462 vapp_call
= "{}/api/vApp/vapp-{}".format(self
.url
, vapp_uuid
)
1463 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
1464 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
1466 response
= self
.perform_request(req_type
='GET',
1469 #Retry login if session expired & retry sending request
1470 if response
.status_code
== 403:
1471 response
= self
.retry_rest('GET', vapp_call
)
1473 tree
= XmlElementTree
.fromstring(response
.content
)
1474 return tree
.attrib
['name']
1475 except Exception as e
:
1476 self
.logger
.exception(e
)
1480 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
=[],
1481 cloud_config
=None, disk_list
=None, availability_zone_index
=None, availability_zone_list
=None):
1482 """Adds a VM instance to VIM
1484 'start': (boolean) indicates if VM must start or created in pause mode.
1485 'image_id','flavor_id': image and flavor VIM id to use for the VM
1486 'net_list': list of interfaces, each one is a dictionary with:
1487 'name': (optional) name for the interface.
1488 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
1489 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
1490 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
1491 'mac_address': (optional) mac address to assign to this interface
1492 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
1493 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
1494 'type': (mandatory) can be one of:
1495 'virtual', in this case always connected to a network of type 'net_type=bridge'
1496 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
1497 can created unconnected
1498 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
1499 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
1500 are allocated on the same physical NIC
1501 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
1502 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
1503 or True, it must apply the default VIM behaviour
1504 After execution the method will add the key:
1505 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
1506 interface. 'net_list' is modified
1507 'cloud_config': (optional) dictionary with:
1508 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1509 'users': (optional) list of users to be inserted, each item is a dict with:
1510 'name': (mandatory) user name,
1511 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1512 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
1513 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
1514 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1515 'dest': (mandatory) string with the destination absolute path
1516 'encoding': (optional, by default text). Can be one of:
1517 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1518 'content' (mandatory): string with the content of the file
1519 'permissions': (optional) string with file permissions, typically octal notation '0644'
1520 'owner': (optional) file owner, string with the format 'owner:group'
1521 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
1522 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1523 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1524 'size': (mandatory) string with the size of the disk in GB
1525 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1526 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1527 availability_zone_index is None
1528 Returns a tuple with the instance identifier and created_items or raises an exception on error
1529 created_items can be None or a dictionary where this method can include key-values that will be passed to
1530 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1531 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1534 self
.logger
.info("Creating new instance for entry {}".format(name
))
1535 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {} "\
1536 "availability_zone_index {} availability_zone_list {}"\
1537 .format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
, disk_list
,\
1538 availability_zone_index
, availability_zone_list
))
1540 #new vm name = vmname + tenant_id + uuid
1541 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1542 vmname_andid
= ''.join(new_vm_name
)
1544 for net
in net_list
:
1545 if net
['type'] == "PCI-PASSTHROUGH":
1546 raise vimconn
.vimconnNotSupportedException(
1547 "Current vCD version does not support type : {}".format(net
['type']))
1549 if len(net_list
) > 10:
1550 raise vimconn
.vimconnNotSupportedException(
1551 "The VM hardware versions 7 and above support upto 10 NICs only")
1553 # if vm already deployed we return existing uuid
1554 # we check for presence of VDC, Catalog entry and Flavor.
1555 org
, vdc
= self
.get_vdc_details()
1557 raise vimconn
.vimconnNotFoundException(
1558 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1559 catalogs
= org
.list_catalogs()
1560 if catalogs
is None:
1561 #Retry once, if failed by refreshing token
1563 org
= Org(self
.client
, resource
=self
.client
.get_org())
1564 catalogs
= org
.list_catalogs()
1565 if catalogs
is None:
1566 raise vimconn
.vimconnNotFoundException(
1567 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1569 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1570 if catalog_hash_name
:
1571 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1573 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1574 "(Failed retrieve catalog information {})".format(name
, image_id
))
1576 # Set vCPU and Memory based on flavor.
1582 if flavor_id
is not None:
1583 if flavor_id
not in vimconnector
.flavorlist
:
1584 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1585 "Failed retrieve flavor information "
1586 "flavor id {}".format(name
, flavor_id
))
1589 flavor
= vimconnector
.flavorlist
[flavor_id
]
1590 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1591 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1592 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1593 extended
= flavor
.get("extended", None)
1595 numas
=extended
.get("numas", None)
1597 except Exception as exp
:
1598 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1600 # image upload creates template name as catalog name space Template.
1601 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1606 # client must provide at least one entry in net_list if not we report error
1607 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1608 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1610 primary_netname
= None
1611 primary_net_href
= None
1612 network_mode
= 'bridged'
1613 if net_list
is not None and len(net_list
) > 0:
1614 for net
in net_list
:
1615 if 'use' in net
and net
['use'] == 'mgmt' and not primary_net
:
1617 if primary_net
is None:
1618 primary_net
= net_list
[0]
1621 primary_net_id
= primary_net
['net_id']
1622 url_list
= [self
.url
, '/api/network/', primary_net_id
]
1623 primary_net_href
= ''.join(url_list
)
1624 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1625 if 'name' in network_dict
:
1626 primary_netname
= network_dict
['name']
1629 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1631 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1633 # use: 'data', 'bridge', 'mgmt'
1634 # create vApp. Set vcpu and ram based on flavor id.
1636 vdc_obj
= VDC(self
.client
, resource
=org
.get_vdc(self
.tenant_name
))
1638 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed to get VDC object")
1641 items
= org
.get_catalog_item(catalog_hash_name
, catalog_hash_name
)
1642 catalog_items
= [items
.attrib
]
1644 if len(catalog_items
) == 1:
1646 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
1647 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
1649 response
= self
.perform_request(req_type
='GET',
1650 url
=catalog_items
[0].get('href'),
1652 catalogItem
= XmlElementTree
.fromstring(response
.content
)
1653 entity
= [child
for child
in catalogItem
if child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
1654 vapp_tempalte_href
= entity
.get("href")
1656 response
= self
.perform_request(req_type
='GET',
1657 url
=vapp_tempalte_href
,
1659 if response
.status_code
!= requests
.codes
.ok
:
1660 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vapp_tempalte_href
,
1661 response
.status_code
))
1663 result
= (response
.content
).replace("\n"," ")
1665 src
= re
.search('<Vm goldMaster="false"\sstatus="\d+"\sname="(.*?)"\s'
1666 'id="(\w+:\w+:vm:.*?)"\shref="(.*?)"\s'
1667 'type="application/vnd\.vmware\.vcloud\.vm\+xml',result
)
1669 vm_name
= src
.group(1)
1670 vm_id
= src
.group(2)
1671 vm_href
= src
.group(3)
1673 cpus
= re
.search('<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>',result
).group(1)
1674 memory_mb
= re
.search('<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>',result
).group(1)
1675 cores
= re
.search('<vmw:CoresPerSocket ovf:required.*?>(\d+)</vmw:CoresPerSocket>',result
).group(1)
1677 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'
1678 vdc_id
= vdc
.get('id').split(':')[-1]
1679 instantiate_vapp_href
= "{}/api/vdc/{}/action/instantiateVAppTemplate".format(self
.url
,
1681 data
= """<?xml version="1.0" encoding="UTF-8"?>
1682 <InstantiateVAppTemplateParams
1683 xmlns="http://www.vmware.com/vcloud/v1.5"
1687 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1688 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1">
1689 <Description>Vapp instantiation</Description>
1690 <InstantiationParams>
1691 <NetworkConfigSection>
1692 <ovf:Info>Configuration parameters for logical networks</ovf:Info>
1693 <NetworkConfig networkName="{}">
1695 <ParentNetwork href="{}" />
1696 <FenceMode>bridged</FenceMode>
1699 </NetworkConfigSection>
1700 <LeaseSettingsSection
1701 type="application/vnd.vmware.vcloud.leaseSettingsSection+xml">
1702 <ovf:Info>Lease Settings</ovf:Info>
1703 <StorageLeaseInSeconds>172800</StorageLeaseInSeconds>
1704 <StorageLeaseExpiration>2014-04-25T08:08:16.438-07:00</StorageLeaseExpiration>
1705 </LeaseSettingsSection>
1706 </InstantiationParams>
1709 <Source href="{}" id="{}" name="{}"
1710 type="application/vnd.vmware.vcloud.vm+xml"/>
1712 <NeedsCustomization>false</NeedsCustomization>
1714 <InstantiationParams>
1715 <NetworkConnectionSection>
1716 <ovf:Info>Specifies the available VM network connections</ovf:Info>
1717 <NetworkConnection network="{}">
1718 <NetworkConnectionIndex>0</NetworkConnectionIndex>
1719 <IsConnected>true</IsConnected>
1720 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
1721 </NetworkConnection>
1722 </NetworkConnectionSection><ovf:VirtualHardwareSection>
1723 <ovf:Info>Virtual hardware requirements</ovf:Info>
1724 <ovf:Item xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
1725 xmlns:vmw="http://www.vmware.com/schema/ovf">
1726 <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
1727 <rasd:Description>Number of Virtual CPUs</rasd:Description>
1728 <rasd:ElementName xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="str">{cpu} virtual CPU(s)</rasd:ElementName>
1729 <rasd:InstanceID>4</rasd:InstanceID>
1730 <rasd:Reservation>0</rasd:Reservation>
1731 <rasd:ResourceType>3</rasd:ResourceType>
1732 <rasd:VirtualQuantity xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="int">{cpu}</rasd:VirtualQuantity>
1733 <rasd:Weight>0</rasd:Weight>
1734 <vmw:CoresPerSocket ovf:required="false">{core}</vmw:CoresPerSocket>
1735 </ovf:Item><ovf:Item xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">
1736 <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
1737 <rasd:Description>Memory Size</rasd:Description>
1738 <rasd:ElementName xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="str">{memory} MB of memory</rasd:ElementName>
1739 <rasd:InstanceID>5</rasd:InstanceID>
1740 <rasd:Reservation>0</rasd:Reservation>
1741 <rasd:ResourceType>4</rasd:ResourceType>
1742 <rasd:VirtualQuantity xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="int">{memory}</rasd:VirtualQuantity>
1743 <rasd:Weight>0</rasd:Weight>
1745 </ovf:VirtualHardwareSection>
1746 </InstantiationParams>
1748 <AllEULAsAccepted>false</AllEULAsAccepted>
1749 </InstantiateVAppTemplateParams>""".format(vmname_andid
,
1761 response
= self
.perform_request(req_type
='POST',
1762 url
=instantiate_vapp_href
,
1766 if response
.status_code
!= 201:
1767 self
.logger
.error("REST call {} failed reason : {}"\
1768 "status code : {}".format(instantiate_vapp_href
,
1770 response
.status_code
))
1771 raise vimconn
.vimconnException("new_vminstance(): Failed to create"\
1772 "vAapp {}".format(vmname_andid
))
1774 vapptask
= self
.get_task_from_response(response
.content
)
1776 if vapptask
is None and retry
==1:
1777 self
.get_token() # Retry getting token
1782 if vapptask
is None or vapptask
is False:
1783 raise vimconn
.vimconnUnexpectedResponse(
1784 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1786 # wait for task to complete
1787 result
= self
.client
.get_task_monitor().wait_for_success(task
=vapptask
)
1789 if result
.get('status') == 'success':
1790 self
.logger
.debug("new_vminstance(): Sucessfully created Vapp {}".format(vmname_andid
))
1792 raise vimconn
.vimconnUnexpectedResponse(
1793 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1795 except Exception as exp
:
1796 raise vimconn
.vimconnUnexpectedResponse(
1797 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid
, exp
))
1799 # we should have now vapp in undeployed state.
1801 vdc_obj
= VDC(self
.client
, href
=vdc
.get('href'))
1802 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
1803 vapp_uuid
= vapp_resource
.get('id').split(':')[-1]
1804 vapp
= VApp(self
.client
, resource
=vapp_resource
)
1806 except Exception as exp
:
1807 raise vimconn
.vimconnUnexpectedResponse(
1808 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1809 .format(vmname_andid
, exp
))
1811 if vapp_uuid
is None:
1812 raise vimconn
.vimconnUnexpectedResponse(
1813 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1816 #Add PCI passthrough/SRIOV configrations
1818 pci_devices_info
= []
1819 reserve_memory
= False
1821 for net
in net_list
:
1822 if net
["type"] == "PF" or net
["type"] == "PCI-PASSTHROUGH":
1823 pci_devices_info
.append(net
)
1824 elif (net
["type"] == "VF" or net
["type"] == "SR-IOV" or net
["type"] == "VFnotShared") and 'net_id'in net
:
1825 reserve_memory
= True
1828 if len(pci_devices_info
) > 0:
1829 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1831 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1834 if PCI_devices_status
:
1835 self
.logger
.info("Added PCI devives {} to VM {}".format(
1839 reserve_memory
= True
1841 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1848 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1849 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1851 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1853 #Add new or existing disks to vApp
1855 added_existing_disk
= False
1856 for disk
in disk_list
:
1857 if 'device_type' in disk
and disk
['device_type'] == 'cdrom':
1858 image_id
= disk
['image_id']
1859 # Adding CD-ROM to VM
1860 # will revisit code once specification ready to support this feature
1861 self
.insert_media_to_vm(vapp
, image_id
)
1862 elif "image_id" in disk
and disk
["image_id"] is not None:
1863 self
.logger
.debug("Adding existing disk from image {} to vm {} ".format(
1864 disk
["image_id"] , vapp_uuid
))
1865 self
.add_existing_disk(catalogs
=catalogs
,
1866 image_id
=disk
["image_id"],
1867 size
= disk
["size"],
1868 template_name
=templateName
,
1871 added_existing_disk
= True
1873 #Wait till added existing disk gets reflected into vCD database/API
1874 if added_existing_disk
:
1876 added_existing_disk
= False
1877 self
.add_new_disk(vapp_uuid
, disk
['size'])
1880 # Assigning numa affinity setting
1882 if 'paired-threads-id' in numa
:
1883 paired_threads_id
= numa
['paired-threads-id']
1884 self
.set_numa_affinity(vapp_uuid
, paired_threads_id
)
1886 # add NICs & connect to networks in netlist
1888 vdc_obj
= VDC(self
.client
, href
=vdc
.get('href'))
1889 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
1890 vapp
= VApp(self
.client
, resource
=vapp_resource
)
1891 vapp_id
= vapp_resource
.get('id').split(':')[-1]
1893 self
.logger
.info("Removing primary NIC: ")
1894 # First remove all NICs so that NIC properties can be adjusted as needed
1895 self
.remove_primary_network_adapter_from_all_vms(vapp
)
1897 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1898 primary_nic_index
= 0
1900 for net
in net_list
:
1901 # openmano uses network id in UUID format.
1902 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1903 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1904 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1906 if 'net_id' not in net
:
1909 #Using net_id as a vim_id i.e. vim interface id, as do not have saperate vim interface id
1910 #Same will be returned in refresh_vms_status() as vim_interface_id
1911 net
['vim_id'] = net
['net_id'] # Provide the same VIM identifier as the VIM network
1913 interface_net_id
= net
['net_id']
1914 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1915 interface_network_mode
= net
['use']
1917 if interface_network_mode
== 'mgmt':
1918 primary_nic_index
= nicIndex
1920 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1921 - DHCP (The IP address is obtained from a DHCP service.)
1922 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1923 - NONE (No IP addressing mode specified.)"""
1925 if primary_netname
is not None:
1926 self
.logger
.debug("new_vminstance(): Filtering by net name {}".format(interface_net_name
))
1927 nets
= filter(lambda n
: n
.get('name') == interface_net_name
, self
.get_network_list())
1929 #nets = [n for n in self.get_network_list() if n.get('name') == interface_net_name]
1931 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].get('name')))
1933 if interface_net_name
!= primary_netname
:
1934 # connect network to VM - with all DHCP by default
1935 self
.logger
.info("new_vminstance(): Attaching net {} to vapp".format(interface_net_name
))
1936 self
.connect_vapp_to_org_vdc_network(vapp_id
, nets
[0].get('name'))
1938 type_list
= ('PF', 'PCI-PASSTHROUGH', 'VFnotShared')
1939 nic_type
= 'VMXNET3'
1940 if 'type' in net
and net
['type'] not in type_list
:
1941 # fetching nic type from vnf
1943 if net
['model'] is not None:
1944 if net
['model'].lower() == 'paravirt' or net
['model'].lower() == 'virtio':
1945 nic_type
= 'VMXNET3'
1947 nic_type
= net
['model']
1949 self
.logger
.info("new_vminstance(): adding network adapter "\
1950 "to a network {}".format(nets
[0].get('name')))
1951 self
.add_network_adapter_to_vms(vapp
, nets
[0].get('name'),
1957 self
.logger
.info("new_vminstance(): adding network adapter "\
1958 "to a network {}".format(nets
[0].get('name')))
1959 if net
['type'] in ['SR-IOV', 'VF']:
1960 nic_type
= net
['type']
1961 self
.add_network_adapter_to_vms(vapp
, nets
[0].get('name'),
1968 # cloud-init for ssh-key injection
1970 self
.cloud_init(vapp
,cloud_config
)
1972 # If VM has PCI devices or SRIOV reserve memory for VM
1974 self
.reserve_memory_for_all_vms(vapp
, memory_mb
)
1976 self
.logger
.debug("new_vminstance(): starting power on vApp {} ".format(vmname_andid
))
1978 poweron_task
= self
.power_on_vapp(vapp_id
, vmname_andid
)
1979 result
= self
.client
.get_task_monitor().wait_for_success(task
=poweron_task
)
1980 if result
.get('status') == 'success':
1981 self
.logger
.info("new_vminstance(): Successfully power on "\
1982 "vApp {}".format(vmname_andid
))
1984 self
.logger
.error("new_vminstance(): failed to power on vApp "\
1985 "{}".format(vmname_andid
))
1987 except Exception as exp
:
1988 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1989 self
.logger
.error("new_vminstance(): Failed create new vm instance {} with exception {}"
1991 raise vimconn
.vimconnException("new_vminstance(): Failed create new vm instance {} with exception {}"
1994 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1997 while wait_time
<= MAX_WAIT_TIME
:
1999 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
2000 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2001 except Exception as exp
:
2002 raise vimconn
.vimconnUnexpectedResponse(
2003 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
2004 .format(vmname_andid
, exp
))
2006 #if vapp and vapp.me.deployed:
2007 if vapp
and vapp_resource
.get('deployed') == 'true':
2008 vapp_uuid
= vapp_resource
.get('id').split(':')[-1]
2011 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
2012 time
.sleep(INTERVAL_TIME
)
2014 wait_time
+=INTERVAL_TIME
2016 #SET Affinity Rule for VM
2017 #Pre-requisites: User has created Hosh Groups in vCenter with respective Hosts to be used
2018 #While creating VIM account user has to pass the Host Group names in availability_zone list
2019 #"availability_zone" is a part of VIM "config" parameters
2020 #For example, in VIM config: "availability_zone":["HG_170","HG_174","HG_175"]
2021 #Host groups are referred as availability zones
2022 #With following procedure, deployed VM will be added into a VM group.
2023 #Then A VM to Host Affinity rule will be created using the VM group & Host group.
2024 if(availability_zone_list
):
2025 self
.logger
.debug("Existing Host Groups in VIM {}".format(self
.config
.get('availability_zone')))
2026 #Admin access required for creating Affinity rules
2027 client
= self
.connect_as_admin()
2029 raise vimconn
.vimconnConnectionException("Failed to connect vCD as admin")
2031 self
.client
= client
2033 headers
= {'Accept':'application/*+xml;version=27.0',
2034 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
2035 #Step1: Get provider vdc details from organization
2036 pvdc_href
= self
.get_pvdc_for_org(self
.tenant_name
, headers
)
2037 if pvdc_href
is not None:
2038 #Step2: Found required pvdc, now get resource pool information
2039 respool_href
= self
.get_resource_pool_details(pvdc_href
, headers
)
2040 if respool_href
is None:
2041 #Raise error if respool_href not found
2042 msg
= "new_vminstance():Error in finding resource pool details in pvdc {}"\
2044 self
.log_message(msg
)
2046 #Step3: Verify requested availability zone(hostGroup) is present in vCD
2047 # get availability Zone
2048 vm_az
= self
.get_vm_availability_zone(availability_zone_index
, availability_zone_list
)
2049 # check if provided av zone(hostGroup) is present in vCD VIM
2050 status
= self
.check_availibility_zone(vm_az
, respool_href
, headers
)
2052 msg
= "new_vminstance(): Error in finding availability zone(Host Group): {} in "\
2053 "resource pool {} status: {}".format(vm_az
,respool_href
,status
)
2054 self
.log_message(msg
)
2056 self
.logger
.debug ("new_vminstance(): Availability zone {} found in VIM".format(vm_az
))
2058 #Step4: Find VM group references to create vm group
2059 vmgrp_href
= self
.find_vmgroup_reference(respool_href
, headers
)
2060 if vmgrp_href
== None:
2061 msg
= "new_vminstance(): No reference to VmGroup found in resource pool"
2062 self
.log_message(msg
)
2064 #Step5: Create a VmGroup with name az_VmGroup
2065 vmgrp_name
= vm_az
+ "_" + name
#Formed VM Group name = Host Group name + VM name
2066 status
= self
.create_vmgroup(vmgrp_name
, vmgrp_href
, headers
)
2067 if status
is not True:
2068 msg
= "new_vminstance(): Error in creating VM group {}".format(vmgrp_name
)
2069 self
.log_message(msg
)
2071 #VM Group url to add vms to vm group
2072 vmgrpname_url
= self
.url
+ "/api/admin/extension/vmGroup/name/"+ vmgrp_name
2074 #Step6: Add VM to VM Group
2075 #Find VM uuid from vapp_uuid
2076 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2077 vm_uuid
= vm_details
['vmuuid']
2079 status
= self
.add_vm_to_vmgroup(vm_uuid
, vmgrpname_url
, vmgrp_name
, headers
)
2080 if status
is not True:
2081 msg
= "new_vminstance(): Error in adding VM to VM group {}".format(vmgrp_name
)
2082 self
.log_message(msg
)
2084 #Step7: Create VM to Host affinity rule
2085 addrule_href
= self
.get_add_rule_reference (respool_href
, headers
)
2086 if addrule_href
is None:
2087 msg
= "new_vminstance(): Error in finding href to add rule in resource pool: {}"\
2088 .format(respool_href
)
2089 self
.log_message(msg
)
2091 status
= self
.create_vm_to_host_affinity_rule(addrule_href
, vmgrp_name
, vm_az
, "Affinity", headers
)
2093 msg
= "new_vminstance(): Error in creating affinity rule for VM {} in Host group {}"\
2094 .format(name
, vm_az
)
2095 self
.log_message(msg
)
2097 self
.logger
.debug("new_vminstance(): Affinity rule created successfully. Added {} in Host group {}"\
2098 .format(name
, vm_az
))
2099 #Reset token to a normal user to perform other operations
2102 if vapp_uuid
is not None:
2103 return vapp_uuid
, None
2105 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
2108 def get_vcd_availibility_zones(self
,respool_href
, headers
):
2109 """ Method to find presence of av zone is VIM resource pool
2112 respool_href - resource pool href
2113 headers - header information
2116 vcd_az - list of azone present in vCD
2120 resp
= self
.perform_request(req_type
='GET',url
=respool_href
, headers
=headers
)
2122 if resp
.status_code
!= requests
.codes
.ok
:
2123 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(url
, resp
.status_code
))
2125 #Get the href to hostGroups and find provided hostGroup is present in it
2126 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2127 for child
in resp_xml
:
2128 if 'VMWProviderVdcResourcePool' in child
.tag
:
2129 for schild
in child
:
2130 if 'Link' in schild
.tag
:
2131 if schild
.attrib
.get('type') == "application/vnd.vmware.admin.vmwHostGroupsType+xml":
2132 hostGroup
= schild
.attrib
.get('href')
2133 hg_resp
= self
.perform_request(req_type
='GET',url
=hostGroup
, headers
=headers
)
2134 if hg_resp
.status_code
!= requests
.codes
.ok
:
2135 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(hostGroup
, hg_resp
.status_code
))
2137 hg_resp_xml
= XmlElementTree
.fromstring(hg_resp
.content
)
2138 for hostGroup
in hg_resp_xml
:
2139 if 'HostGroup' in hostGroup
.tag
:
2140 #append host group name to the list
2141 vcd_az
.append(hostGroup
.attrib
.get("name"))
2145 def set_availability_zones(self
):
2147 Set vim availability zone
2150 vim_availability_zones
= None
2151 availability_zone
= None
2152 if 'availability_zone' in self
.config
:
2153 vim_availability_zones
= self
.config
.get('availability_zone')
2154 if isinstance(vim_availability_zones
, str):
2155 availability_zone
= [vim_availability_zones
]
2156 elif isinstance(vim_availability_zones
, list):
2157 availability_zone
= vim_availability_zones
2159 return availability_zone
2161 return availability_zone
2164 def get_vm_availability_zone(self
, availability_zone_index
, availability_zone_list
):
2166 Return the availability zone to be used by the created VM.
2167 returns: The VIM availability zone to be used or None
2169 if availability_zone_index
is None:
2170 if not self
.config
.get('availability_zone'):
2172 elif isinstance(self
.config
.get('availability_zone'), str):
2173 return self
.config
['availability_zone']
2175 return self
.config
['availability_zone'][0]
2177 vim_availability_zones
= self
.availability_zone
2179 # check if VIM offer enough availability zones describe in the VNFD
2180 if vim_availability_zones
and len(availability_zone_list
) <= len(vim_availability_zones
):
2181 # check if all the names of NFV AV match VIM AV names
2182 match_by_index
= False
2183 for av
in availability_zone_list
:
2184 if av
not in vim_availability_zones
:
2185 match_by_index
= True
2188 self
.logger
.debug("Required Availability zone or Host Group not found in VIM config")
2189 self
.logger
.debug("Input Availability zone list: {}".format(availability_zone_list
))
2190 self
.logger
.debug("VIM configured Availability zones: {}".format(vim_availability_zones
))
2191 self
.logger
.debug("VIM Availability zones will be used by index")
2192 return vim_availability_zones
[availability_zone_index
]
2194 return availability_zone_list
[availability_zone_index
]
2196 raise vimconn
.vimconnConflictException("No enough availability zones at VIM for this deployment")
2199 def create_vm_to_host_affinity_rule(self
, addrule_href
, vmgrpname
, hostgrpname
, polarity
, headers
):
2200 """ Method to create VM to Host Affinity rule in vCD
2203 addrule_href - href to make a POST request
2204 vmgrpname - name of the VM group created
2205 hostgrpnmae - name of the host group created earlier
2206 polarity - Affinity or Anti-affinity (default: Affinity)
2207 headers - headers to make REST call
2210 True- if rule is created
2211 False- Failed to create rule due to some error
2215 rule_name
= polarity
+ "_" + vmgrpname
2216 payload
= """<?xml version="1.0" encoding="UTF-8"?>
2217 <vmext:VMWVmHostAffinityRule
2218 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
2219 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
2220 type="application/vnd.vmware.admin.vmwVmHostAffinityRule+xml">
2221 <vcloud:Name>{}</vcloud:Name>
2222 <vcloud:IsEnabled>true</vcloud:IsEnabled>
2223 <vcloud:IsMandatory>true</vcloud:IsMandatory>
2224 <vcloud:Polarity>{}</vcloud:Polarity>
2225 <vmext:HostGroupName>{}</vmext:HostGroupName>
2226 <vmext:VmGroupName>{}</vmext:VmGroupName>
2227 </vmext:VMWVmHostAffinityRule>""".format(rule_name
, polarity
, hostgrpname
, vmgrpname
)
2229 resp
= self
.perform_request(req_type
='POST',url
=addrule_href
, headers
=headers
, data
=payload
)
2231 if resp
.status_code
!= requests
.codes
.accepted
:
2232 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(addrule_href
, resp
.status_code
))
2236 affinity_task
= self
.get_task_from_response(resp
.content
)
2237 self
.logger
.debug ("affinity_task: {}".format(affinity_task
))
2238 if affinity_task
is None or affinity_task
is False:
2239 raise vimconn
.vimconnUnexpectedResponse("failed to find affinity task")
2240 # wait for task to complete
2241 result
= self
.client
.get_task_monitor().wait_for_success(task
=affinity_task
)
2242 if result
.get('status') == 'success':
2243 self
.logger
.debug("Successfully created affinity rule {}".format(rule_name
))
2246 raise vimconn
.vimconnUnexpectedResponse(
2247 "failed to create affinity rule {}".format(rule_name
))
2250 def get_add_rule_reference (self
, respool_href
, headers
):
2251 """ This method finds href to add vm to host affinity rule to vCD
2254 respool_href- href to resource pool
2255 headers- header information to make REST call
2258 None - if no valid href to add rule found or
2259 addrule_href - href to add vm to host affinity rule of resource pool
2262 resp
= self
.perform_request(req_type
='GET',url
=respool_href
, headers
=headers
)
2264 if resp
.status_code
!= requests
.codes
.ok
:
2265 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(respool_href
, resp
.status_code
))
2268 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2269 for child
in resp_xml
:
2270 if 'VMWProviderVdcResourcePool' in child
.tag
:
2271 for schild
in child
:
2272 if 'Link' in schild
.tag
:
2273 if schild
.attrib
.get('type') == "application/vnd.vmware.admin.vmwVmHostAffinityRule+xml" and \
2274 schild
.attrib
.get('rel') == "add":
2275 addrule_href
= schild
.attrib
.get('href')
2281 def add_vm_to_vmgroup(self
, vm_uuid
, vmGroupNameURL
, vmGroup_name
, headers
):
2282 """ Method to add deployed VM to newly created VM Group.
2283 This is required to create VM to Host affinity in vCD
2286 vm_uuid- newly created vm uuid
2287 vmGroupNameURL- URL to VM Group name
2288 vmGroup_name- Name of VM group created
2289 headers- Headers for REST request
2292 True- if VM added to VM group successfully
2293 False- if any error encounter
2296 addvm_resp
= self
.perform_request(req_type
='GET',url
=vmGroupNameURL
, headers
=headers
)#, data=payload)
2298 if addvm_resp
.status_code
!= requests
.codes
.ok
:
2299 self
.logger
.debug ("REST API call to get VM Group Name url {} failed. Return status code {}"\
2300 .format(vmGroupNameURL
, addvm_resp
.status_code
))
2303 resp_xml
= XmlElementTree
.fromstring(addvm_resp
.content
)
2304 for child
in resp_xml
:
2305 if child
.tag
.split('}')[1] == 'Link':
2306 if child
.attrib
.get("rel") == "addVms":
2307 addvmtogrpURL
= child
.attrib
.get("href")
2310 url_list
= [self
.url
, '/api/vApp/vm-',vm_uuid
]
2311 vmdetailsURL
= ''.join(url_list
)
2313 resp
= self
.perform_request(req_type
='GET',url
=vmdetailsURL
, headers
=headers
)
2315 if resp
.status_code
!= requests
.codes
.ok
:
2316 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(vmdetailsURL
, resp
.status_code
))
2320 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2321 if resp_xml
.tag
.split('}')[1] == "Vm":
2322 vm_id
= resp_xml
.attrib
.get("id")
2323 vm_name
= resp_xml
.attrib
.get("name")
2324 vm_href
= resp_xml
.attrib
.get("href")
2325 #print vm_id, vm_name, vm_href
2326 #Add VM into VMgroup
2327 payload
= """<?xml version="1.0" encoding="UTF-8"?>\
2328 <ns2:Vms xmlns:ns2="http://www.vmware.com/vcloud/v1.5" \
2329 xmlns="http://www.vmware.com/vcloud/versions" \
2330 xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" \
2331 xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" \
2332 xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" \
2333 xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" \
2334 xmlns:ns7="http://www.vmware.com/schema/ovf" \
2335 xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" \
2336 xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">\
2337 <ns2:VmReference href="{}" id="{}" name="{}" \
2338 type="application/vnd.vmware.vcloud.vm+xml" />\
2339 </ns2:Vms>""".format(vm_href
, vm_id
, vm_name
)
2341 addvmtogrp_resp
= self
.perform_request(req_type
='POST',url
=addvmtogrpURL
, headers
=headers
, data
=payload
)
2343 if addvmtogrp_resp
.status_code
!= requests
.codes
.accepted
:
2344 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(addvmtogrpURL
, addvmtogrp_resp
.status_code
))
2347 self
.logger
.debug ("Done adding VM {} to VMgroup {}".format(vm_name
, vmGroup_name
))
2351 def create_vmgroup(self
, vmgroup_name
, vmgroup_href
, headers
):
2352 """Method to create a VM group in vCD
2355 vmgroup_name : Name of VM group to be created
2356 vmgroup_href : href for vmgroup
2357 headers- Headers for REST request
2359 #POST to add URL with required data
2360 vmgroup_status
= False
2361 payload
= """<VMWVmGroup xmlns="http://www.vmware.com/vcloud/extension/v1.5" \
2362 xmlns:vcloud_v1.5="http://www.vmware.com/vcloud/v1.5" name="{}">\
2363 <vmCount>1</vmCount>\
2364 </VMWVmGroup>""".format(vmgroup_name
)
2365 resp
= self
.perform_request(req_type
='POST',url
=vmgroup_href
, headers
=headers
, data
=payload
)
2367 if resp
.status_code
!= requests
.codes
.accepted
:
2368 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(vmgroup_href
, resp
.status_code
))
2369 return vmgroup_status
2371 vmgroup_task
= self
.get_task_from_response(resp
.content
)
2372 if vmgroup_task
is None or vmgroup_task
is False:
2373 raise vimconn
.vimconnUnexpectedResponse(
2374 "create_vmgroup(): failed to create VM group {}".format(vmgroup_name
))
2376 # wait for task to complete
2377 result
= self
.client
.get_task_monitor().wait_for_success(task
=vmgroup_task
)
2379 if result
.get('status') == 'success':
2380 self
.logger
.debug("create_vmgroup(): Successfully created VM group {}".format(vmgroup_name
))
2382 vmgroup_status
= True
2383 return vmgroup_status
2385 raise vimconn
.vimconnUnexpectedResponse(\
2386 "create_vmgroup(): failed to create VM group {}".format(vmgroup_name
))
2389 def find_vmgroup_reference(self
, url
, headers
):
2390 """ Method to create a new VMGroup which is required to add created VM
2392 url- resource pool href
2393 headers- header information
2396 returns href to VM group to create VM group
2398 #Perform GET on resource pool to find 'add' link to create VMGroup
2399 #https://vcd-ip/api/admin/extension/providervdc/<providervdc id>/resourcePools
2401 resp
= self
.perform_request(req_type
='GET',url
=url
, headers
=headers
)
2403 if resp
.status_code
!= requests
.codes
.ok
:
2404 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(url
, resp
.status_code
))
2406 #Get the href to add vmGroup to vCD
2407 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2408 for child
in resp_xml
:
2409 if 'VMWProviderVdcResourcePool' in child
.tag
:
2410 for schild
in child
:
2411 if 'Link' in schild
.tag
:
2412 #Find href with type VMGroup and rel with add
2413 if schild
.attrib
.get('type') == "application/vnd.vmware.admin.vmwVmGroupType+xml"\
2414 and schild
.attrib
.get('rel') == "add":
2415 vmgrp_href
= schild
.attrib
.get('href')
2419 def check_availibility_zone(self
, az
, respool_href
, headers
):
2420 """ Method to verify requested av zone is present or not in provided
2424 az - name of hostgroup (availibility_zone)
2425 respool_href - Resource Pool href
2426 headers - Headers to make REST call
2428 az_found - True if availibility_zone is found else False
2431 headers
['Accept']='application/*+xml;version=27.0'
2432 resp
= self
.perform_request(req_type
='GET',url
=respool_href
, headers
=headers
)
2434 if resp
.status_code
!= requests
.codes
.ok
:
2435 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(respool_href
, resp
.status_code
))
2437 #Get the href to hostGroups and find provided hostGroup is present in it
2438 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2440 for child
in resp_xml
:
2441 if 'VMWProviderVdcResourcePool' in child
.tag
:
2442 for schild
in child
:
2443 if 'Link' in schild
.tag
:
2444 if schild
.attrib
.get('type') == "application/vnd.vmware.admin.vmwHostGroupsType+xml":
2445 hostGroup_href
= schild
.attrib
.get('href')
2446 hg_resp
= self
.perform_request(req_type
='GET',url
=hostGroup_href
, headers
=headers
)
2447 if hg_resp
.status_code
!= requests
.codes
.ok
:
2448 self
.logger
.debug ("REST API call {} failed. Return status code {}".format(hostGroup_href
, hg_resp
.status_code
))
2450 hg_resp_xml
= XmlElementTree
.fromstring(hg_resp
.content
)
2451 for hostGroup
in hg_resp_xml
:
2452 if 'HostGroup' in hostGroup
.tag
:
2453 if hostGroup
.attrib
.get("name") == az
:
2459 def get_pvdc_for_org(self
, org_vdc
, headers
):
2460 """ This method gets provider vdc references from organisation
2463 org_vdc - name of the organisation VDC to find pvdc
2464 headers - headers to make REST call
2467 None - if no pvdc href found else
2468 pvdc_href - href to pvdc
2471 #Get provider VDC references from vCD
2473 #url = '<vcd url>/api/admin/extension/providerVdcReferences'
2474 url_list
= [self
.url
, '/api/admin/extension/providerVdcReferences']
2475 url
= ''.join(url_list
)
2477 response
= self
.perform_request(req_type
='GET',url
=url
, headers
=headers
)
2478 if response
.status_code
!= requests
.codes
.ok
:
2479 self
.logger
.debug ("REST API call {} failed. Return status code {}"\
2480 .format(url
, response
.status_code
))
2482 xmlroot_response
= XmlElementTree
.fromstring(response
.content
)
2483 for child
in xmlroot_response
:
2484 if 'ProviderVdcReference' in child
.tag
:
2485 pvdc_href
= child
.attrib
.get('href')
2486 #Get vdcReferences to find org
2487 pvdc_resp
= self
.perform_request(req_type
='GET',url
=pvdc_href
, headers
=headers
)
2488 if pvdc_resp
.status_code
!= requests
.codes
.ok
:
2489 raise vimconn
.vimconnException("REST API call {} failed. "\
2490 "Return status code {}"\
2491 .format(url
, pvdc_resp
.status_code
))
2493 pvdc_resp_xml
= XmlElementTree
.fromstring(pvdc_resp
.content
)
2494 for child
in pvdc_resp_xml
:
2495 if 'Link' in child
.tag
:
2496 if child
.attrib
.get('type') == "application/vnd.vmware.admin.vdcReferences+xml":
2497 vdc_href
= child
.attrib
.get('href')
2499 #Check if provided org is present in vdc
2500 vdc_resp
= self
.perform_request(req_type
='GET',
2503 if vdc_resp
.status_code
!= requests
.codes
.ok
:
2504 raise vimconn
.vimconnException("REST API call {} failed. "\
2505 "Return status code {}"\
2506 .format(url
, vdc_resp
.status_code
))
2507 vdc_resp_xml
= XmlElementTree
.fromstring(vdc_resp
.content
)
2508 for child
in vdc_resp_xml
:
2509 if 'VdcReference' in child
.tag
:
2510 if child
.attrib
.get('name') == org_vdc
:
2514 def get_resource_pool_details(self
, pvdc_href
, headers
):
2515 """ Method to get resource pool information.
2516 Host groups are property of resource group.
2517 To get host groups, we need to GET details of resource pool.
2520 pvdc_href: href to pvdc details
2524 respool_href - Returns href link reference to resource pool
2527 resp
= self
.perform_request(req_type
='GET',url
=pvdc_href
, headers
=headers
)
2529 if resp
.status_code
!= requests
.codes
.ok
:
2530 self
.logger
.debug ("REST API call {} failed. Return status code {}"\
2531 .format(pvdc_href
, resp
.status_code
))
2533 respool_resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2534 for child
in respool_resp_xml
:
2535 if 'Link' in child
.tag
:
2536 if child
.attrib
.get('type') == "application/vnd.vmware.admin.vmwProviderVdcResourcePoolSet+xml":
2537 respool_href
= child
.attrib
.get("href")
2542 def log_message(self
, msg
):
2544 Method to log error messages related to Affinity rule creation
2545 in new_vminstance & raise Exception
2547 msg - Error message to be logged
2550 #get token to connect vCD as a normal user
2552 self
.logger
.debug(msg
)
2553 raise vimconn
.vimconnException(msg
)
2558 ## based on current discussion
2562 # created: '2016-09-08T11:51:58'
2563 # description: simple-instance.linux1.1
2564 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
2565 # hostId: e836c036-74e7-11e6-b249-0800273e724c
2566 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
2571 def get_vminstance(self
, vim_vm_uuid
=None):
2572 """Returns the VM instance information from VIM"""
2574 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
2576 org
, vdc
= self
.get_vdc_details()
2578 raise vimconn
.vimconnConnectionException(
2579 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
2581 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
2582 if not vm_info_dict
:
2583 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
2584 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
2586 status_key
= vm_info_dict
['status']
2589 vm_dict
= {'created': vm_info_dict
['created'],
2590 'description': vm_info_dict
['name'],
2591 'status': vcdStatusCode2manoFormat
[int(status_key
)],
2592 'hostId': vm_info_dict
['vmuuid'],
2594 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
2596 if 'interfaces' in vm_info_dict
:
2597 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
2599 vm_dict
['interfaces'] = []
2601 vm_dict
= {'created': '',
2603 'status': vcdStatusCode2manoFormat
[int(-1)],
2604 'hostId': vm_info_dict
['vmuuid'],
2605 'error_msg': "Inconsistency state",
2606 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
2610 def delete_vminstance(self
, vm__vim_uuid
, created_items
=None):
2611 """Method poweroff and remove VM instance from vcloud director network.
2614 vm__vim_uuid: VM UUID
2617 Returns the instance identifier
2620 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
2622 org
, vdc
= self
.get_vdc_details()
2623 vdc_obj
= VDC(self
.client
, href
=vdc
.get('href'))
2625 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
2627 raise vimconn
.vimconnException(
2628 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
2631 vapp_name
= self
.get_namebyvappid(vm__vim_uuid
)
2632 if vapp_name
is None:
2633 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2634 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
2635 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
2636 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
2637 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2639 # Delete vApp and wait for status change if task executed and vApp is None.
2642 if vapp_resource
.get('deployed') == 'true':
2643 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
2647 while wait_time
<= MAX_WAIT_TIME
:
2648 power_off_task
= vapp
.power_off()
2649 result
= self
.client
.get_task_monitor().wait_for_success(task
=power_off_task
)
2651 if result
.get('status') == 'success':
2655 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
2656 time
.sleep(INTERVAL_TIME
)
2658 wait_time
+=INTERVAL_TIME
2660 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
2662 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
2665 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
2668 while wait_time
<= MAX_WAIT_TIME
:
2669 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2671 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2672 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
2673 undeploy_task
= vapp
.undeploy()
2675 result
= self
.client
.get_task_monitor().wait_for_success(task
=undeploy_task
)
2676 if result
.get('status') == 'success':
2680 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
2681 time
.sleep(INTERVAL_TIME
)
2683 wait_time
+=INTERVAL_TIME
2686 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
2689 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
2691 if vapp
is not None:
2695 while wait_time
<= MAX_WAIT_TIME
:
2696 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2698 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2699 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
2701 delete_task
= vdc_obj
.delete_vapp(vapp
.name
, force
=True)
2703 result
= self
.client
.get_task_monitor().wait_for_success(task
=delete_task
)
2704 if result
.get('status') == 'success':
2707 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
2708 time
.sleep(INTERVAL_TIME
)
2710 wait_time
+=INTERVAL_TIME
2713 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
2715 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
2718 self
.logger
.debug(traceback
.format_exc())
2719 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
2722 def refresh_vms_status(self
, vm_list
):
2723 """Get the status of the virtual machines and their interfaces/ports
2724 Params: the list of VM identifiers
2725 Returns a dictionary with:
2726 vm_id: #VIM id of this Virtual Machine
2727 status: #Mandatory. Text with one of:
2728 # DELETED (not found at vim)
2729 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2730 # OTHER (Vim reported other status not understood)
2731 # ERROR (VIM indicates an ERROR status)
2732 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
2733 # CREATING (on building process), ERROR
2734 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
2736 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2737 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2739 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2740 mac_address: #Text format XX:XX:XX:XX:XX:XX
2741 vim_net_id: #network id where this interface is connected
2742 vim_interface_id: #interface/port VIM id
2743 ip_address: #null, or text with IPv4, IPv6 address
2746 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
2748 org
,vdc
= self
.get_vdc_details()
2750 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
2754 for vmuuid
in vm_list
:
2755 vapp_name
= self
.get_namebyvappid(vmuuid
)
2756 if vapp_name
is not None:
2759 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
2760 vdc_obj
= VDC(self
.client
, href
=vdc
.get('href'))
2761 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
2762 the_vapp
= VApp(self
.client
, resource
=vapp_resource
)
2765 for vm
in the_vapp
.get_all_vms():
2766 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
2767 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
2768 response
= self
.perform_request(req_type
='GET',
2772 if response
.status_code
!= 200:
2773 self
.logger
.error("refresh_vms_status : REST call {} failed reason : {}"\
2774 "status code : {}".format(vm
.get('href'),
2776 response
.status_code
))
2777 raise vimconn
.vimconnException("refresh_vms_status : Failed to get "\
2779 xmlroot
= XmlElementTree
.fromstring(response
.content
)
2782 result
= response
.content
.replace("\n"," ")
2783 hdd_match
= re
.search('vcloud:capacity="(\d+)"\svcloud:storageProfileOverrideVmDefault=',result
)
2785 hdd_mb
= hdd_match
.group(1)
2786 vm_details
['hdd_mb'] = int(hdd_mb
) if hdd_mb
else None
2787 cpus_match
= re
.search('<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>',result
)
2789 cpus
= cpus_match
.group(1)
2790 vm_details
['cpus'] = int(cpus
) if cpus
else None
2791 memory_mb
= re
.search('<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>',result
).group(1)
2792 vm_details
['memory_mb'] = int(memory_mb
) if memory_mb
else None
2793 vm_details
['status'] = vcdStatusCode2manoFormat
[int(xmlroot
.get('status'))]
2794 vm_details
['id'] = xmlroot
.get('id')
2795 vm_details
['name'] = xmlroot
.get('name')
2796 vm_info
= [vm_details
]
2798 vm_info
[0].update(vm_pci_details
)
2800 vm_dict
= {'status': vcdStatusCode2manoFormat
[int(vapp_resource
.get('status'))],
2801 'error_msg': vcdStatusCode2manoFormat
[int(vapp_resource
.get('status'))],
2802 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
2807 networks
= re
.findall('<NetworkConnection needsCustomization=.*?</NetworkConnection>',result
)
2808 for network
in networks
:
2809 mac_s
= re
.search('<MACAddress>(.*?)</MACAddress>',network
)
2810 vm_mac
= mac_s
.group(1) if mac_s
else None
2811 ip_s
= re
.search('<IpAddress>(.*?)</IpAddress>',network
)
2812 vm_ip
= ip_s
.group(1) if ip_s
else None
2815 if not nsx_edge_list
:
2816 nsx_edge_list
= self
.get_edge_details()
2817 if nsx_edge_list
is None:
2818 raise vimconn
.vimconnException("refresh_vms_status:"\
2819 "Failed to get edge details from NSX Manager")
2820 if vm_mac
is not None:
2821 vm_ip
= self
.get_ipaddr_from_NSXedge(nsx_edge_list
, vm_mac
)
2823 net_s
= re
.search('network="(.*?)"',network
)
2824 network_name
= net_s
.group(1) if net_s
else None
2826 vm_net_id
= self
.get_network_id_by_name(network_name
)
2827 interface
= {"mac_address": vm_mac
,
2828 "vim_net_id": vm_net_id
,
2829 "vim_interface_id": vm_net_id
,
2830 "ip_address": vm_ip
}
2832 vm_dict
["interfaces"].append(interface
)
2834 # add a vm to vm dict
2835 vms_dict
.setdefault(vmuuid
, vm_dict
)
2836 self
.logger
.debug("refresh_vms_status : vm info {}".format(vm_dict
))
2837 except Exception as exp
:
2838 self
.logger
.debug("Error in response {}".format(exp
))
2839 self
.logger
.debug(traceback
.format_exc())
2844 def get_edge_details(self
):
2845 """Get the NSX edge list from NSX Manager
2846 Returns list of NSX edges
2849 rheaders
= {'Content-Type': 'application/xml'}
2850 nsx_api_url
= '/api/4.0/edges'
2852 self
.logger
.debug("Get edge details from NSX Manager {} {}".format(self
.nsx_manager
, nsx_api_url
))
2855 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
2856 auth
= (self
.nsx_user
, self
.nsx_password
),
2857 verify
= False, headers
= rheaders
)
2858 if resp
.status_code
== requests
.codes
.ok
:
2859 paged_Edge_List
= XmlElementTree
.fromstring(resp
.text
)
2860 for edge_pages
in paged_Edge_List
:
2861 if edge_pages
.tag
== 'edgePage':
2862 for edge_summary
in edge_pages
:
2863 if edge_summary
.tag
== 'pagingInfo':
2864 for element
in edge_summary
:
2865 if element
.tag
== 'totalCount' and element
.text
== '0':
2866 raise vimconn
.vimconnException("get_edge_details: No NSX edges details found: {}"
2867 .format(self
.nsx_manager
))
2869 if edge_summary
.tag
== 'edgeSummary':
2870 for element
in edge_summary
:
2871 if element
.tag
== 'id':
2872 edge_list
.append(element
.text
)
2874 raise vimconn
.vimconnException("get_edge_details: No NSX edge details found: {}"
2875 .format(self
.nsx_manager
))
2878 raise vimconn
.vimconnException("get_edge_details: "\
2879 "No NSX edge details found: {}"
2880 .format(self
.nsx_manager
))
2882 self
.logger
.debug("get_edge_details: Found NSX edges {}".format(edge_list
))
2885 self
.logger
.debug("get_edge_details: "
2886 "Failed to get NSX edge details from NSX Manager: {}"
2887 .format(resp
.content
))
2890 except Exception as exp
:
2891 self
.logger
.debug("get_edge_details: "\
2892 "Failed to get NSX edge details from NSX Manager: {}"
2894 raise vimconn
.vimconnException("get_edge_details: "\
2895 "Failed to get NSX edge details from NSX Manager: {}"
2899 def get_ipaddr_from_NSXedge(self
, nsx_edges
, mac_address
):
2900 """Get IP address details from NSX edges, using the MAC address
2901 PARAMS: nsx_edges : List of NSX edges
2902 mac_address : Find IP address corresponding to this MAC address
2903 Returns: IP address corrresponding to the provided MAC address
2907 rheaders
= {'Content-Type': 'application/xml'}
2909 self
.logger
.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
2912 for edge
in nsx_edges
:
2913 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
2915 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
2916 auth
= (self
.nsx_user
, self
.nsx_password
),
2917 verify
= False, headers
= rheaders
)
2919 if resp
.status_code
== requests
.codes
.ok
:
2920 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
2921 for child
in dhcp_leases
:
2922 if child
.tag
== 'dhcpLeaseInfo':
2923 dhcpLeaseInfo
= child
2924 for leaseInfo
in dhcpLeaseInfo
:
2925 for elem
in leaseInfo
:
2926 if (elem
.tag
)=='macAddress':
2927 edge_mac_addr
= elem
.text
2928 if (elem
.tag
)=='ipAddress':
2930 if edge_mac_addr
is not None:
2931 if edge_mac_addr
== mac_address
:
2932 self
.logger
.debug("Found ip addr {} for mac {} at NSX edge {}"
2933 .format(ip_addr
, mac_address
,edge
))
2936 self
.logger
.debug("get_ipaddr_from_NSXedge: "\
2937 "Error occurred while getting DHCP lease info from NSX Manager: {}"
2938 .format(resp
.content
))
2940 self
.logger
.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
2943 except XmlElementTree
.ParseError
as Err
:
2944 self
.logger
.debug("ParseError in response from NSX Manager {}".format(Err
.message
), exc_info
=True)
2947 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None, created_items
={}):
2948 """Send and action over a VM instance from VIM
2949 Returns the vm_id if the action was successfully sent to the VIM"""
2951 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
2952 if vm__vim_uuid
is None or action_dict
is None:
2953 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
2955 org
, vdc
= self
.get_vdc_details()
2957 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
2959 vapp_name
= self
.get_namebyvappid(vm__vim_uuid
)
2960 if vapp_name
is None:
2961 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2962 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
2964 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
2967 vdc_obj
= VDC(self
.client
, href
=vdc
.get('href'))
2968 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
2969 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2970 if "start" in action_dict
:
2971 self
.logger
.info("action_vminstance: Power on vApp: {}".format(vapp_name
))
2972 poweron_task
= self
.power_on_vapp(vm__vim_uuid
, vapp_name
)
2973 result
= self
.client
.get_task_monitor().wait_for_success(task
=poweron_task
)
2974 self
.instance_actions_result("start", result
, vapp_name
)
2975 elif "rebuild" in action_dict
:
2976 self
.logger
.info("action_vminstance: Rebuild vApp: {}".format(vapp_name
))
2977 rebuild_task
= vapp
.deploy(power_on
=True)
2978 result
= self
.client
.get_task_monitor().wait_for_success(task
=rebuild_task
)
2979 self
.instance_actions_result("rebuild", result
, vapp_name
)
2980 elif "pause" in action_dict
:
2981 self
.logger
.info("action_vminstance: pause vApp: {}".format(vapp_name
))
2982 pause_task
= vapp
.undeploy(action
='suspend')
2983 result
= self
.client
.get_task_monitor().wait_for_success(task
=pause_task
)
2984 self
.instance_actions_result("pause", result
, vapp_name
)
2985 elif "resume" in action_dict
:
2986 self
.logger
.info("action_vminstance: resume vApp: {}".format(vapp_name
))
2987 poweron_task
= self
.power_on_vapp(vm__vim_uuid
, vapp_name
)
2988 result
= self
.client
.get_task_monitor().wait_for_success(task
=poweron_task
)
2989 self
.instance_actions_result("resume", result
, vapp_name
)
2990 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
2991 action_name
, value
= action_dict
.items()[0]
2993 #action_name , value = list(action_dict.items())[0]
2994 self
.logger
.info("action_vminstance: {} vApp: {}".format(action_name
, vapp_name
))
2995 shutdown_task
= vapp
.shutdown()
2996 result
= self
.client
.get_task_monitor().wait_for_success(task
=shutdown_task
)
2997 if action_name
== "shutdown":
2998 self
.instance_actions_result("shutdown", result
, vapp_name
)
3000 self
.instance_actions_result("shutoff", result
, vapp_name
)
3001 elif "forceOff" in action_dict
:
3002 result
= vapp
.undeploy(action
='powerOff')
3003 self
.instance_actions_result("forceOff", result
, vapp_name
)
3004 elif "reboot" in action_dict
:
3005 self
.logger
.info("action_vminstance: reboot vApp: {}".format(vapp_name
))
3006 reboot_task
= vapp
.reboot()
3007 self
.client
.get_task_monitor().wait_for_success(task
=reboot_task
)
3009 raise vimconn
.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict
))
3011 except Exception as exp
:
3012 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
3013 raise vimconn
.vimconnException("action_vminstance: Failed with Exception {}".format(exp
))
3015 def instance_actions_result(self
, action
, result
, vapp_name
):
3016 if result
.get('status') == 'success':
3017 self
.logger
.info("action_vminstance: Sucessfully {} the vApp: {}".format(action
, vapp_name
))
3019 self
.logger
.error("action_vminstance: Failed to {} vApp: {}".format(action
, vapp_name
))
3021 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
3023 Get a console for the virtual machine
3025 vm_id: uuid of the VM
3026 console_type, can be:
3027 "novnc" (by default), "xvpvnc" for VNC types,
3028 "rdp-html5" for RDP types, "spice-html5" for SPICE types
3029 Returns dict with the console parameters:
3030 protocol: ssh, ftp, http, https, ...
3031 server: usually ip address
3032 port: the http, ssh, ... port
3033 suffix: extra text, e.g. the http path and query string
3035 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3037 # NOT USED METHODS in current version
3039 def host_vim2gui(self
, host
, server_dict
):
3040 """Transform host dictionary from VIM format to GUI format,
3041 and append to the server_dict
3043 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3045 def get_hosts_info(self
):
3046 """Get the information of deployed hosts
3047 Returns the hosts content"""
3048 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3050 def get_hosts(self
, vim_tenant
):
3051 """Get the hosts and deployed instances
3052 Returns the hosts content"""
3053 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3055 def get_processor_rankings(self
):
3056 """Get the processor rankings in the VIM database"""
3057 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3059 def new_host(self
, host_data
):
3060 """Adds a new host to VIM"""
3061 '''Returns status code of the VIM response'''
3062 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3064 def new_external_port(self
, port_data
):
3065 """Adds a external port to VIM"""
3066 '''Returns the port identifier'''
3067 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3069 def new_external_network(self
, net_name
, net_type
):
3070 """Adds a external network to VIM (shared)"""
3071 '''Returns the network identifier'''
3072 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3074 def connect_port_network(self
, port_id
, network_id
, admin
=False):
3075 """Connects a external port to a network"""
3076 '''Returns status code of the VIM response'''
3077 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3079 def new_vminstancefromJSON(self
, vm_data
):
3080 """Adds a VM instance to VIM"""
3081 '''Returns the instance identifier'''
3082 raise vimconn
.vimconnNotImplemented("Should have implemented this")
3084 def get_network_name_by_id(self
, network_uuid
=None):
3085 """Method gets vcloud director network named based on supplied uuid.
3088 network_uuid: network_id
3091 The return network name.
3094 if not network_uuid
:
3098 org_dict
= self
.get_org(self
.org_uuid
)
3099 if 'networks' in org_dict
:
3100 org_network_dict
= org_dict
['networks']
3101 for net_uuid
in org_network_dict
:
3102 if net_uuid
== network_uuid
:
3103 return org_network_dict
[net_uuid
]
3105 self
.logger
.debug("Exception in get_network_name_by_id")
3106 self
.logger
.debug(traceback
.format_exc())
3110 def get_network_id_by_name(self
, network_name
=None):
3111 """Method gets vcloud director network uuid based on supplied name.
3114 network_name: network_name
3116 The return network uuid.
3117 network_uuid: network_id
3120 if not network_name
:
3121 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
3125 org_dict
= self
.get_org(self
.org_uuid
)
3126 if org_dict
and 'networks' in org_dict
:
3127 org_network_dict
= org_dict
['networks']
3128 for net_uuid
,net_name
in org_network_dict
.iteritems():
3130 #for net_uuid,net_name in org_network_dict.items():
3131 if net_name
== network_name
:
3134 except KeyError as exp
:
3135 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
3139 def list_org_action(self
):
3141 Method leverages vCloud director and query for available organization for particular user
3144 vca - is active VCA connection.
3145 vdc_name - is a vdc name that will be used to query vms action
3148 The return XML respond
3150 url_list
= [self
.url
, '/api/org']
3151 vm_list_rest_call
= ''.join(url_list
)
3153 if self
.client
._session
:
3154 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3155 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
3157 response
= self
.perform_request(req_type
='GET',
3158 url
=vm_list_rest_call
,
3161 if response
.status_code
== 403:
3162 response
= self
.retry_rest('GET', vm_list_rest_call
)
3164 if response
.status_code
== requests
.codes
.ok
:
3165 return response
.content
3169 def get_org_action(self
, org_uuid
=None):
3171 Method leverages vCloud director and retrieve available object for organization.
3174 org_uuid - vCD organization uuid
3175 self.client - is active connection.
3178 The return XML respond
3181 if org_uuid
is None:
3184 url_list
= [self
.url
, '/api/org/', org_uuid
]
3185 vm_list_rest_call
= ''.join(url_list
)
3187 if self
.client
._session
:
3188 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3189 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
3191 #response = requests.get(vm_list_rest_call, headers=headers, verify=False)
3192 response
= self
.perform_request(req_type
='GET',
3193 url
=vm_list_rest_call
,
3195 if response
.status_code
== 403:
3196 response
= self
.retry_rest('GET', vm_list_rest_call
)
3198 if response
.status_code
== requests
.codes
.ok
:
3199 return response
.content
3202 def get_org(self
, org_uuid
=None):
3204 Method retrieves available organization in vCloud Director
3207 org_uuid - is a organization uuid.
3210 The return dictionary with following key
3211 "network" - for network list under the org
3212 "catalogs" - for network list under the org
3213 "vdcs" - for vdc list under org
3218 if org_uuid
is None:
3221 content
= self
.get_org_action(org_uuid
=org_uuid
)
3226 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3227 for child
in vm_list_xmlroot
:
3228 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
3229 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
3230 org_dict
['vdcs'] = vdc_list
3231 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
3232 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
3233 org_dict
['networks'] = network_list
3234 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
3235 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
3236 org_dict
['catalogs'] = catalog_list
3242 def get_org_list(self
):
3244 Method retrieves available organization in vCloud Director
3247 vca - is active VCA connection.
3250 The return dictionary and key for each entry VDC UUID
3255 content
= self
.list_org_action()
3257 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3258 for vm_xml
in vm_list_xmlroot
:
3259 if vm_xml
.tag
.split("}")[1] == 'Org':
3260 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
3261 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
3267 def vms_view_action(self
, vdc_name
=None):
3268 """ Method leverages vCloud director vms query call
3271 vca - is active VCA connection.
3272 vdc_name - is a vdc name that will be used to query vms action
3275 The return XML respond
3277 vca
= self
.connect()
3278 if vdc_name
is None:
3281 url_list
= [vca
.host
, '/api/vms/query']
3282 vm_list_rest_call
= ''.join(url_list
)
3284 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
3285 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
3286 vca
.vcloud_session
.organization
.Link
)
3288 #refs = [ref for ref in vca.vcloud_session.organization.Link if ref.name == vdc_name and\
3289 # ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml']
3291 response
= Http
.get(url
=vm_list_rest_call
,
3292 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3295 if response
.status_code
== requests
.codes
.ok
:
3296 return response
.content
3300 def get_vapp_list(self
, vdc_name
=None):
3302 Method retrieves vApp list deployed vCloud director and returns a dictionary
3303 contains a list of all vapp deployed for queried VDC.
3304 The key for a dictionary is vApp UUID
3308 vca - is active VCA connection.
3309 vdc_name - is a vdc name that will be used to query vms action
3312 The return dictionary and key for each entry vapp UUID
3316 if vdc_name
is None:
3319 content
= self
.vms_view_action(vdc_name
=vdc_name
)
3321 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3322 for vm_xml
in vm_list_xmlroot
:
3323 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
3324 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
3325 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
3326 if 'vappTemplate-' in rawuuid
[0]:
3327 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
3328 # vm and use raw UUID as key
3329 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
3335 def get_vm_list(self
, vdc_name
=None):
3337 Method retrieves VM's list deployed vCloud director. It returns a dictionary
3338 contains a list of all VM's deployed for queried VDC.
3339 The key for a dictionary is VM UUID
3343 vca - is active VCA connection.
3344 vdc_name - is a vdc name that will be used to query vms action
3347 The return dictionary and key for each entry vapp UUID
3351 if vdc_name
is None:
3354 content
= self
.vms_view_action(vdc_name
=vdc_name
)
3356 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3357 for vm_xml
in vm_list_xmlroot
:
3358 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
3359 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
3360 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
3361 if 'vm-' in rawuuid
[0]:
3362 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
3363 # vm and use raw UUID as key
3364 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
3370 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
3372 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
3373 contains a list of all VM's deployed for queried VDC.
3374 The key for a dictionary is VM UUID
3378 vca - is active VCA connection.
3379 vdc_name - is a vdc name that will be used to query vms action
3382 The return dictionary and key for each entry vapp UUID
3385 vca
= self
.connect()
3387 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3389 if vdc_name
is None:
3392 content
= self
.vms_view_action(vdc_name
=vdc_name
)
3394 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3395 for vm_xml
in vm_list_xmlroot
:
3396 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
3397 # lookup done by UUID
3399 if vapp_name
in vm_xml
.attrib
['container']:
3400 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
3401 if 'vm-' in rawuuid
[0]:
3402 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
3404 # lookup done by Name
3406 if vapp_name
in vm_xml
.attrib
['name']:
3407 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
3408 if 'vm-' in rawuuid
[0]:
3409 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
3416 def get_network_action(self
, network_uuid
=None):
3418 Method leverages vCloud director and query network based on network uuid
3421 vca - is active VCA connection.
3422 network_uuid - is a network uuid
3425 The return XML respond
3428 if network_uuid
is None:
3431 url_list
= [self
.url
, '/api/network/', network_uuid
]
3432 vm_list_rest_call
= ''.join(url_list
)
3434 if self
.client
._session
:
3435 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3436 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
3438 response
= self
.perform_request(req_type
='GET',
3439 url
=vm_list_rest_call
,
3441 #Retry login if session expired & retry sending request
3442 if response
.status_code
== 403:
3443 response
= self
.retry_rest('GET', vm_list_rest_call
)
3445 if response
.status_code
== requests
.codes
.ok
:
3446 return response
.content
3450 def get_vcd_network(self
, network_uuid
=None):
3452 Method retrieves available network from vCloud Director
3455 network_uuid - is VCD network UUID
3457 Each element serialized as key : value pair
3459 Following keys available for access. network_configuration['Gateway'}
3463 <IsInherited>true</IsInherited>
3464 <Gateway>172.16.252.100</Gateway>
3465 <Netmask>255.255.255.0</Netmask>
3466 <Dns1>172.16.254.201</Dns1>
3467 <Dns2>172.16.254.202</Dns2>
3468 <DnsSuffix>vmwarelab.edu</DnsSuffix>
3469 <IsEnabled>true</IsEnabled>
3472 <StartAddress>172.16.252.1</StartAddress>
3473 <EndAddress>172.16.252.99</EndAddress>
3478 <FenceMode>bridged</FenceMode>
3481 The return dictionary and key for each entry vapp UUID
3484 network_configuration
= {}
3485 if network_uuid
is None:
3489 content
= self
.get_network_action(network_uuid
=network_uuid
)
3490 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3492 network_configuration
['status'] = vm_list_xmlroot
.get("status")
3493 network_configuration
['name'] = vm_list_xmlroot
.get("name")
3494 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
3496 for child
in vm_list_xmlroot
:
3497 if child
.tag
.split("}")[1] == 'IsShared':
3498 network_configuration
['isShared'] = child
.text
.strip()
3499 if child
.tag
.split("}")[1] == 'Configuration':
3500 for configuration
in child
.iter():
3501 tagKey
= configuration
.tag
.split("}")[1].strip()
3503 network_configuration
[tagKey
] = configuration
.text
.strip()
3504 return network_configuration
3505 except Exception as exp
:
3506 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
3507 raise vimconn
.vimconnException("get_vcd_network: Failed with Exception {}".format(exp
))
3509 return network_configuration
3511 def delete_network_action(self
, network_uuid
=None):
3513 Method delete given network from vCloud director
3516 network_uuid - is a network uuid that client wish to delete
3519 The return None or XML respond or false
3521 client
= self
.connect_as_admin()
3523 raise vimconn
.vimconnConnectionException("Failed to connect vCD as admin")
3524 if network_uuid
is None:
3527 url_list
= [self
.url
, '/api/admin/network/', network_uuid
]
3528 vm_list_rest_call
= ''.join(url_list
)
3531 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3532 'x-vcloud-authorization': client
._session
.headers
['x-vcloud-authorization']}
3533 response
= self
.perform_request(req_type
='DELETE',
3534 url
=vm_list_rest_call
,
3536 if response
.status_code
== 202:
3541 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
3542 ip_profile
=None, isshared
='true'):
3544 Method create network in vCloud director
3547 network_name - is network name to be created.
3548 net_type - can be 'bridge','data','ptp','mgmt'.
3549 ip_profile is a dict containing the IP parameters of the network
3550 isshared - is a boolean
3551 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3552 It optional attribute. by default if no parent network indicate the first available will be used.
3555 The return network uuid or return None
3558 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
3559 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
3560 ip_profile
=ip_profile
,
3562 parent_network_uuid
=parent_network_uuid
,
3565 self
.logger
.debug("Failed create network {}.".format(network_name
))
3569 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
3570 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
3571 if len(vcd_uuid
) == 4:
3572 self
.logger
.info("Created new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
3575 self
.logger
.debug("Failed create network {}".format(network_name
))
3578 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
3579 ip_profile
=None, isshared
='true'):
3581 Method create network in vCloud director
3584 network_name - is network name to be created.
3585 net_type - can be 'bridge','data','ptp','mgmt'.
3586 ip_profile is a dict containing the IP parameters of the network
3587 isshared - is a boolean
3588 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3589 It optional attribute. by default if no parent network indicate the first available will be used.
3592 The return network uuid or return None
3594 client_as_admin
= self
.connect_as_admin()
3595 if not client_as_admin
:
3596 raise vimconn
.vimconnConnectionException("Failed to connect vCD.")
3597 if network_name
is None:
3600 url_list
= [self
.url
, '/api/admin/vdc/', self
.tenant_id
]
3601 vm_list_rest_call
= ''.join(url_list
)
3603 if client_as_admin
._session
:
3604 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3605 'x-vcloud-authorization': client_as_admin
._session
.headers
['x-vcloud-authorization']}
3607 response
= self
.perform_request(req_type
='GET',
3608 url
=vm_list_rest_call
,
3611 provider_network
= None
3612 available_networks
= None
3613 add_vdc_rest_url
= None
3615 if response
.status_code
!= requests
.codes
.ok
:
3616 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
3617 response
.status_code
))
3621 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
3622 for child
in vm_list_xmlroot
:
3623 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
3624 provider_network
= child
.attrib
.get('href')
3625 # application/vnd.vmware.admin.providervdc+xml
3626 if child
.tag
.split("}")[1] == 'Link':
3627 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
3628 and child
.attrib
.get('rel') == 'add':
3629 add_vdc_rest_url
= child
.attrib
.get('href')
3631 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3632 self
.logger
.debug("Respond body {}".format(response
.content
))
3635 # find pvdc provided available network
3636 response
= self
.perform_request(req_type
='GET',
3637 url
=provider_network
,
3639 if response
.status_code
!= requests
.codes
.ok
:
3640 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
3641 response
.status_code
))
3644 if parent_network_uuid
is None:
3646 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
3647 for child
in vm_list_xmlroot
.iter():
3648 if child
.tag
.split("}")[1] == 'AvailableNetworks':
3649 for networks
in child
.iter():
3650 # application/vnd.vmware.admin.network+xml
3651 if networks
.attrib
.get('href') is not None:
3652 available_networks
= networks
.attrib
.get('href')
3658 #Configure IP profile of the network
3659 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
3661 if 'subnet_address' not in ip_profile
or ip_profile
['subnet_address'] is None:
3662 subnet_rand
= random
.randint(0, 255)
3663 ip_base
= "192.168.{}.".format(subnet_rand
)
3664 ip_profile
['subnet_address'] = ip_base
+ "0/24"
3666 ip_base
= ip_profile
['subnet_address'].rsplit('.',1)[0] + '.'
3668 if 'gateway_address' not in ip_profile
or ip_profile
['gateway_address'] is None:
3669 ip_profile
['gateway_address']=ip_base
+ "1"
3670 if 'dhcp_count' not in ip_profile
or ip_profile
['dhcp_count'] is None:
3671 ip_profile
['dhcp_count']=DEFAULT_IP_PROFILE
['dhcp_count']
3672 if 'dhcp_enabled' not in ip_profile
or ip_profile
['dhcp_enabled'] is None:
3673 ip_profile
['dhcp_enabled']=DEFAULT_IP_PROFILE
['dhcp_enabled']
3674 if 'dhcp_start_address' not in ip_profile
or ip_profile
['dhcp_start_address'] is None:
3675 ip_profile
['dhcp_start_address']=ip_base
+ "3"
3676 if 'ip_version' not in ip_profile
or ip_profile
['ip_version'] is None:
3677 ip_profile
['ip_version']=DEFAULT_IP_PROFILE
['ip_version']
3678 if 'dns_address' not in ip_profile
or ip_profile
['dns_address'] is None:
3679 ip_profile
['dns_address']=ip_base
+ "2"
3681 gateway_address
=ip_profile
['gateway_address']
3682 dhcp_count
=int(ip_profile
['dhcp_count'])
3683 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
3685 if ip_profile
['dhcp_enabled']==True:
3688 dhcp_enabled
='false'
3689 dhcp_start_address
=ip_profile
['dhcp_start_address']
3691 #derive dhcp_end_address from dhcp_start_address & dhcp_count
3692 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
3693 end_ip_int
+= dhcp_count
- 1
3694 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
3696 ip_version
=ip_profile
['ip_version']
3697 dns_address
=ip_profile
['dns_address']
3698 except KeyError as exp
:
3699 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
3700 raise vimconn
.vimconnException("Create Network REST: Key error{}".format(exp
))
3702 # either use client provided UUID or search for a first available
3703 # if both are not defined we return none
3704 if parent_network_uuid
is not None:
3705 provider_network
= None
3706 available_networks
= None
3707 add_vdc_rest_url
= None
3709 url_list
= [self
.url
, '/api/admin/vdc/', self
.tenant_id
, '/networks']
3710 add_vdc_rest_url
= ''.join(url_list
)
3712 url_list
= [self
.url
, '/api/admin/network/', parent_network_uuid
]
3713 available_networks
= ''.join(url_list
)
3715 #Creating all networks as Direct Org VDC type networks.
3716 #Unused in case of Underlay (data/ptp) network interface.
3717 fence_mode
="isolated"
3718 is_inherited
='false'
3719 dns_list
= dns_address
.split(";")
3722 if len(dns_list
) >= 2:
3723 dns2_text
= "\n <Dns2>{}</Dns2>\n".format(dns_list
[1])
3724 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
3725 <Description>Openmano created</Description>
3729 <IsInherited>{1:s}</IsInherited>
3730 <Gateway>{2:s}</Gateway>
3731 <Netmask>{3:s}</Netmask>
3732 <Dns1>{4:s}</Dns1>{5:s}
3733 <IsEnabled>{6:s}</IsEnabled>
3736 <StartAddress>{7:s}</StartAddress>
3737 <EndAddress>{8:s}</EndAddress>
3742 <FenceMode>{9:s}</FenceMode>
3744 <IsShared>{10:s}</IsShared>
3745 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
3746 subnet_address
, dns1
, dns2_text
, dhcp_enabled
,
3747 dhcp_start_address
, dhcp_end_address
,
3748 fence_mode
, isshared
)
3750 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
3752 response
= self
.perform_request(req_type
='POST',
3753 url
=add_vdc_rest_url
,
3757 if response
.status_code
!= 201:
3758 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
3759 .format(response
.status_code
,response
.content
))
3761 network_task
= self
.get_task_from_response(response
.content
)
3762 self
.logger
.debug("Create Network REST : Waiting for Network creation complete")
3764 result
= self
.client
.get_task_monitor().wait_for_success(task
=network_task
)
3765 if result
.get('status') == 'success':
3766 return response
.content
3768 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
3769 .format(response
.content
))
3770 except Exception as exp
:
3771 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
3775 def convert_cidr_to_netmask(self
, cidr_ip
=None):
3777 Method sets convert CIDR netmask address to normal IP format
3779 cidr_ip : CIDR IP address
3781 netmask : Converted netmask
3783 if cidr_ip
is not None:
3785 network
, net_bits
= cidr_ip
.split('/')
3786 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
3792 def get_provider_rest(self
, vca
=None):
3794 Method gets provider vdc view from vcloud director
3797 network_name - is network name to be created.
3798 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3799 It optional attribute. by default if no parent network indicate the first available will be used.
3802 The return xml content of respond or None
3805 url_list
= [self
.url
, '/api/admin']
3807 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3808 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
3809 response
= self
.perform_request(req_type
='GET',
3810 url
=''.join(url_list
),
3813 if response
.status_code
== requests
.codes
.ok
:
3814 return response
.content
3817 def create_vdc(self
, vdc_name
=None):
3821 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
3822 if xml_content
is not None:
3824 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
3825 for child
in task_resp_xmlroot
:
3826 if child
.tag
.split("}")[1] == 'Owner':
3827 vdc_id
= child
.attrib
.get('href').split("/")[-1]
3828 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
3831 self
.logger
.debug("Respond body {}".format(xml_content
))
3835 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
3837 Method create vdc in vCloud director based on VDC template.
3838 it uses pre-defined template.
3841 vdc_name - name of a new vdc.
3844 The return xml content of respond or None
3846 # pre-requesite atleast one vdc template should be available in vCD
3847 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
3848 vca
= self
.connect_as_admin()
3850 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
3851 if vdc_name
is None:
3854 url_list
= [self
.url
, '/api/vdcTemplates']
3855 vm_list_rest_call
= ''.join(url_list
)
3857 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3858 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
3859 response
= self
.perform_request(req_type
='GET',
3860 url
=vm_list_rest_call
,
3863 # container url to a template
3864 vdc_template_ref
= None
3866 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
3867 for child
in vm_list_xmlroot
:
3868 # application/vnd.vmware.admin.providervdc+xml
3869 # we need find a template from witch we instantiate VDC
3870 if child
.tag
.split("}")[1] == 'VdcTemplate':
3871 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml':
3872 vdc_template_ref
= child
.attrib
.get('href')
3874 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3875 self
.logger
.debug("Respond body {}".format(response
.content
))
3878 # if we didn't found required pre defined template we return None
3879 if vdc_template_ref
is None:
3884 url_list
= [self
.url
, '/api/org/', self
.org_uuid
, '/action/instantiate']
3885 vm_list_rest_call
= ''.join(url_list
)
3886 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
3887 <Source href="{1:s}"></Source>
3888 <Description>opnemano</Description>
3889 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
3891 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
3893 response
= self
.perform_request(req_type
='POST',
3894 url
=vm_list_rest_call
,
3898 vdc_task
= self
.get_task_from_response(response
.content
)
3899 self
.client
.get_task_monitor().wait_for_success(task
=vdc_task
)
3901 # if we all ok we respond with content otherwise by default None
3902 if response
.status_code
>= 200 and response
.status_code
< 300:
3903 return response
.content
3906 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3907 self
.logger
.debug("Respond body {}".format(response
.content
))
3911 def create_vdc_rest(self
, vdc_name
=None):
3913 Method create network in vCloud director
3916 vdc_name - vdc name to be created
3921 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
3923 vca
= self
.connect_as_admin()
3925 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
3926 if vdc_name
is None:
3929 url_list
= [self
.url
, '/api/admin/org/', self
.org_uuid
]
3930 vm_list_rest_call
= ''.join(url_list
)
3933 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
3934 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
3935 response
= self
.perform_request(req_type
='GET',
3936 url
=vm_list_rest_call
,
3939 provider_vdc_ref
= None
3940 add_vdc_rest_url
= None
3941 available_networks
= None
3943 if response
.status_code
!= requests
.codes
.ok
:
3944 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
3945 response
.status_code
))
3949 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
3950 for child
in vm_list_xmlroot
:
3951 # application/vnd.vmware.admin.providervdc+xml
3952 if child
.tag
.split("}")[1] == 'Link':
3953 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
3954 and child
.attrib
.get('rel') == 'add':
3955 add_vdc_rest_url
= child
.attrib
.get('href')
3957 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3958 self
.logger
.debug("Respond body {}".format(response
.content
))
3961 response
= self
.get_provider_rest(vca
=vca
)
3963 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
3964 for child
in vm_list_xmlroot
:
3965 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
3966 for sub_child
in child
:
3967 provider_vdc_ref
= sub_child
.attrib
.get('href')
3969 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
3970 self
.logger
.debug("Respond body {}".format(response
))
3973 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
3974 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
3975 <AllocationModel>ReservationPool</AllocationModel>
3976 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
3977 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
3978 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
3979 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
3980 <ProviderVdcReference
3981 name="Main Provider"
3983 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
3987 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
3989 response
= self
.perform_request(req_type
='POST',
3990 url
=add_vdc_rest_url
,
3994 # if we all ok we respond with content otherwise by default None
3995 if response
.status_code
== 201:
3996 return response
.content
3999 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
4001 Method retrieve vapp detail from vCloud director
4004 vapp_uuid - is vapp identifier.
4007 The return network uuid or return None
4013 if need_admin_access
:
4014 vca
= self
.connect_as_admin()
4019 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
4020 if vapp_uuid
is None:
4023 url_list
= [self
.url
, '/api/vApp/vapp-', vapp_uuid
]
4024 get_vapp_restcall
= ''.join(url_list
)
4027 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4028 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
4029 response
= self
.perform_request(req_type
='GET',
4030 url
=get_vapp_restcall
,
4033 if response
.status_code
== 403:
4034 if need_admin_access
== False:
4035 response
= self
.retry_rest('GET', get_vapp_restcall
)
4037 if response
.status_code
!= requests
.codes
.ok
:
4038 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
4039 response
.status_code
))
4040 return parsed_respond
4043 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
4044 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
4046 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
4047 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
4048 'vmw': 'http://www.vmware.com/schema/ovf',
4049 'vm': 'http://www.vmware.com/vcloud/v1.5',
4050 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
4051 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
4052 "xmlns":"http://www.vmware.com/vcloud/v1.5"
4055 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
4056 if created_section
is not None:
4057 parsed_respond
['created'] = created_section
.text
4059 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
4060 if network_section
is not None and 'networkName' in network_section
.attrib
:
4061 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
4063 ipscopes_section
= \
4064 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
4066 if ipscopes_section
is not None:
4067 for ipscope
in ipscopes_section
:
4068 for scope
in ipscope
:
4069 tag_key
= scope
.tag
.split("}")[1]
4070 if tag_key
== 'IpRanges':
4071 ip_ranges
= scope
.getchildren()
4072 for ipblock
in ip_ranges
:
4073 for block
in ipblock
:
4074 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
4076 parsed_respond
[tag_key
] = scope
.text
4078 # parse children section for other attrib
4079 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
4080 if children_section
is not None:
4081 parsed_respond
['name'] = children_section
.attrib
['name']
4082 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
4083 if "nestedHypervisorEnabled" in children_section
.attrib
else None
4084 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
4085 parsed_respond
['status'] = children_section
.attrib
['status']
4086 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
4087 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
4089 for adapters
in network_adapter
:
4090 adapter_key
= adapters
.tag
.split("}")[1]
4091 if adapter_key
== 'PrimaryNetworkConnectionIndex':
4092 parsed_respond
['primarynetwork'] = adapters
.text
4093 if adapter_key
== 'NetworkConnection':
4095 if 'network' in adapters
.attrib
:
4096 vnic
['network'] = adapters
.attrib
['network']
4097 for adapter
in adapters
:
4098 setting_key
= adapter
.tag
.split("}")[1]
4099 vnic
[setting_key
] = adapter
.text
4100 nic_list
.append(vnic
)
4102 for link
in children_section
:
4103 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
4104 if link
.attrib
['rel'] == 'screen:acquireTicket':
4105 parsed_respond
['acquireTicket'] = link
.attrib
4106 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
4107 parsed_respond
['acquireMksTicket'] = link
.attrib
4109 parsed_respond
['interfaces'] = nic_list
4110 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
4111 if vCloud_extension_section
is not None:
4112 vm_vcenter_info
= {}
4113 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
4114 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
4115 if vmext
is not None:
4116 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
4117 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
4119 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
4120 vm_virtual_hardware_info
= {}
4121 if virtual_hardware_section
is not None:
4122 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
4123 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
4124 disk_size
= item
.find("rasd:HostResource" ,namespaces
4125 ).attrib
["{"+namespaces
['vm']+"}capacity"]
4127 vm_virtual_hardware_info
["disk_size"]= disk_size
4130 for link
in virtual_hardware_section
:
4131 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
4132 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
4133 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
4136 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
4137 except Exception as exp
:
4138 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
4139 return parsed_respond
4141 def acquire_console(self
, vm_uuid
=None):
4145 if self
.client
._session
:
4146 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4147 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4148 vm_dict
= self
.get_vapp_details_rest(vapp_uuid
=vm_uuid
)
4149 console_dict
= vm_dict
['acquireTicket']
4150 console_rest_call
= console_dict
['href']
4152 response
= self
.perform_request(req_type
='POST',
4153 url
=console_rest_call
,
4156 if response
.status_code
== 403:
4157 response
= self
.retry_rest('POST', console_rest_call
)
4159 if response
.status_code
== requests
.codes
.ok
:
4160 return response
.content
4164 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
4166 Method retrieve vm disk details
4169 vapp_uuid - is vapp identifier.
4170 flavor_disk - disk size as specified in VNFD (flavor)
4173 The return network uuid or return None
4177 #Flavor disk is in GB convert it into MB
4178 flavor_disk
= int(flavor_disk
) * 1024
4179 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
4181 vm_name
= vm_details
["name"]
4182 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
4184 if vm_details
and "vm_virtual_hardware" in vm_details
:
4185 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
4186 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
4188 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
4190 if flavor_disk
> vm_disk
:
4191 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
4192 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
4193 vm_disk
, flavor_disk
))
4196 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
4199 except Exception as exp
:
4200 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
4203 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
4205 Method retrieve modify vm disk size
4208 disk_href - vCD API URL to GET and PUT disk data
4209 disk_size - disk size as specified in VNFD (flavor)
4212 The return network uuid or return None
4214 if disk_href
is None or disk_size
is None:
4217 if self
.client
._session
:
4218 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4219 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4220 response
= self
.perform_request(req_type
='GET',
4224 if response
.status_code
== 403:
4225 response
= self
.retry_rest('GET', disk_href
)
4227 if response
.status_code
!= requests
.codes
.ok
:
4228 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
4229 response
.status_code
))
4232 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
4233 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
4235 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
4236 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4238 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
4239 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
4240 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
4241 if disk_item
is not None:
4242 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
4245 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
4246 xml_declaration
=True)
4248 #Send PUT request to modify disk size
4249 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
4251 response
= self
.perform_request(req_type
='PUT',
4255 if response
.status_code
== 403:
4256 add_headers
= {'Content-Type': headers
['Content-Type']}
4257 response
= self
.retry_rest('PUT', disk_href
, add_headers
, data
)
4259 if response
.status_code
!= 202:
4260 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
4261 response
.status_code
))
4263 modify_disk_task
= self
.get_task_from_response(response
.content
)
4264 result
= self
.client
.get_task_monitor().wait_for_success(task
=modify_disk_task
)
4265 if result
.get('status') == 'success':
4271 except Exception as exp
:
4272 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
4275 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
4277 Method to attach pci devices to VM
4280 vapp_uuid - uuid of vApp/VM
4281 pci_devices - pci devices infromation as specified in VNFD (flavor)
4284 The status of add pci device task , vm object and
4285 vcenter_conect object
4288 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
4289 vcenter_conect
, content
= self
.get_vcenter_content()
4290 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
4294 no_of_pci_devices
= len(pci_devices
)
4295 if no_of_pci_devices
> 0:
4296 #Get VM and its host
4297 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
4298 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
4299 if host_obj
and vm_obj
:
4300 #get PCI devies from host on which vapp is currently installed
4301 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
4303 if avilable_pci_devices
is None:
4304 #find other hosts with active pci devices
4305 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
4310 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
4311 #Migrate vm to the host where PCI devices are availble
4312 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
4313 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
4314 if task
is not None:
4315 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4316 self
.logger
.info("Migrate VM status: {}".format(result
))
4317 host_obj
= new_host_obj
4319 self
.logger
.info("Fail to migrate VM : {}".format(result
))
4320 raise vimconn
.vimconnNotFoundException(
4321 "Fail to migrate VM : {} to host {}".format(
4326 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
4327 #Add PCI devices one by one
4328 for pci_device
in avilable_pci_devices
:
4329 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
4331 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
4333 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
4335 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
4336 return True, vm_obj
, vcenter_conect
4338 self
.logger
.error("Currently there is no host with"\
4339 " {} number of avaialble PCI devices required for VM {}".format(
4343 raise vimconn
.vimconnNotFoundException(
4344 "Currently there is no host with {} "\
4345 "number of avaialble PCI devices required for VM {}".format(
4349 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
4351 except vmodl
.MethodFault
as error
:
4352 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
4353 return None, vm_obj
, vcenter_conect
4355 def get_vm_obj(self
, content
, mob_id
):
4357 Method to get the vsphere VM object associated with a given morf ID
4359 vapp_uuid - uuid of vApp/VM
4360 content - vCenter content object
4361 mob_id - mob_id of VM
4369 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
4370 [vim
.VirtualMachine
], True
4372 for vm
in container
.view
:
4373 mobID
= vm
._GetMoId
()
4376 host_obj
= vm_obj
.runtime
.host
4378 except Exception as exp
:
4379 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
4380 return host_obj
, vm_obj
4382 def get_pci_devices(self
, host
, need_devices
):
4384 Method to get the details of pci devices on given host
4386 host - vSphere host object
4387 need_devices - number of pci devices needed on host
4390 array of pci devices
4394 used_devices_ids
= []
4398 pciPassthruInfo
= host
.config
.pciPassthruInfo
4399 pciDevies
= host
.hardware
.pciDevice
4401 for pci_status
in pciPassthruInfo
:
4402 if pci_status
.passthruActive
:
4403 for device
in pciDevies
:
4404 if device
.id == pci_status
.id:
4405 all_device_ids
.append(device
.id)
4406 all_devices
.append(device
)
4408 #check if devices are in use
4409 avalible_devices
= all_devices
4411 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
4412 vm_devices
= vm
.config
.hardware
.device
4413 for device
in vm_devices
:
4414 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
4415 if device
.backing
.id in all_device_ids
:
4416 for use_device
in avalible_devices
:
4417 if use_device
.id == device
.backing
.id:
4418 avalible_devices
.remove(use_device
)
4419 used_devices_ids
.append(device
.backing
.id)
4420 self
.logger
.debug("Device {} from devices {}"\
4421 "is in use".format(device
.backing
.id,
4424 if len(avalible_devices
) < need_devices
:
4425 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
4427 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
4431 required_devices
= avalible_devices
[:need_devices
]
4432 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
4433 len(avalible_devices
),
4436 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
4438 return required_devices
4440 except Exception as exp
:
4441 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
4445 def get_host_and_PCIdevices(self
, content
, need_devices
):
4447 Method to get the details of pci devices infromation on all hosts
4450 content - vSphere host object
4451 need_devices - number of pci devices needed on host
4454 array of pci devices and host object
4457 pci_device_objs
= None
4460 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
4461 [vim
.HostSystem
], True)
4462 for host
in container
.view
:
4463 devices
= self
.get_pci_devices(host
, need_devices
)
4466 pci_device_objs
= devices
4468 except Exception as exp
:
4469 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
4471 return host_obj
,pci_device_objs
4473 def relocate_vm(self
, dest_host
, vm
) :
4475 Method to get the relocate VM to new host
4478 dest_host - vSphere host object
4479 vm - vSphere VM object
4486 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
4487 task
= vm
.Relocate(relocate_spec
)
4488 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
4489 except Exception as exp
:
4490 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
4491 dest_host
, vm
, exp
))
4494 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
4496 Waits and provides updates on a vSphere task
4498 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
4501 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
4502 if task
.info
.result
is not None and not hideResult
:
4503 self
.logger
.info('{} completed successfully, result: {}'.format(
4507 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
4509 self
.logger
.error('{} did not complete successfully: {} '.format(
4514 return task
.info
.result
4516 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
4518 Method to add pci device in given VM
4521 host_object - vSphere host object
4522 vm_object - vSphere VM object
4523 host_pci_dev - host_pci_dev must be one of the devices from the
4524 host_object.hardware.pciDevice list
4525 which is configured as a PCI passthrough device
4531 if vm_object
and host_object
and host_pci_dev
:
4533 #Add PCI device to VM
4534 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
4535 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
4537 if host_pci_dev
.id not in systemid_by_pciid
:
4538 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
4541 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
4542 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
4544 systemId
=systemid_by_pciid
[host_pci_dev
.id],
4545 vendorId
=host_pci_dev
.vendorId
,
4546 deviceName
=host_pci_dev
.deviceName
)
4548 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
4550 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
4551 new_device_config
.operation
= "add"
4552 vmConfigSpec
= vim
.vm
.ConfigSpec()
4553 vmConfigSpec
.deviceChange
= [new_device_config
]
4555 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
4556 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
4557 host_pci_dev
, vm_object
, host_object
)
4559 except Exception as exp
:
4560 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
4566 def get_vm_vcenter_info(self
):
4568 Method to get details of vCenter and vm
4571 vapp_uuid - uuid of vApp or VM
4574 Moref Id of VM and deails of vCenter
4576 vm_vcenter_info
= {}
4578 if self
.vcenter_ip
is not None:
4579 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
4581 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
4582 " Please provide vCenter IP while attaching datacenter to tenant in --config")
4583 if self
.vcenter_port
is not None:
4584 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
4586 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
4587 " Please provide vCenter port while attaching datacenter to tenant in --config")
4588 if self
.vcenter_user
is not None:
4589 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
4591 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
4592 " Please provide vCenter user while attaching datacenter to tenant in --config")
4594 if self
.vcenter_password
is not None:
4595 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
4597 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
4598 " Please provide vCenter user password while attaching datacenter to tenant in --config")
4600 return vm_vcenter_info
4603 def get_vm_pci_details(self
, vmuuid
):
4605 Method to get VM PCI device details from vCenter
4608 vm_obj - vSphere VM object
4611 dict of PCI devives attached to VM
4614 vm_pci_devices_info
= {}
4616 vcenter_conect
, content
= self
.get_vcenter_content()
4617 vm_moref_id
= self
.get_vm_moref_id(vmuuid
)
4619 #Get VM and its host
4621 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
4622 if host_obj
and vm_obj
:
4623 vm_pci_devices_info
["host_name"]= host_obj
.name
4624 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
4625 for device
in vm_obj
.config
.hardware
.device
:
4626 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
4627 device_details
={'devide_id':device
.backing
.id,
4628 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
4630 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
4632 self
.logger
.error("Can not connect to vCenter while getting "\
4633 "PCI devices infromationn")
4634 return vm_pci_devices_info
4635 except Exception as exp
:
4636 self
.logger
.error("Error occurred while getting VM infromationn"\
4637 " for VM : {}".format(exp
))
4638 raise vimconn
.vimconnException(message
=exp
)
4641 def reserve_memory_for_all_vms(self
, vapp
, memory_mb
):
4643 Method to reserve memory for all VMs
4646 memory_mb - Memory in MB
4651 self
.logger
.info("Reserve memory for all VMs")
4652 for vms
in vapp
.get_all_vms():
4653 vm_id
= vms
.get('id').split(':')[-1]
4655 url_rest_call
= "{}/api/vApp/vm-{}/virtualHardwareSection/memory".format(self
.url
, vm_id
)
4657 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4658 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4659 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItem+xml'
4660 response
= self
.perform_request(req_type
='GET',
4664 if response
.status_code
== 403:
4665 response
= self
.retry_rest('GET', url_rest_call
)
4667 if response
.status_code
!= 200:
4668 self
.logger
.error("REST call {} failed reason : {}"\
4669 "status code : {}".format(url_rest_call
,
4671 response
.status_code
))
4672 raise vimconn
.vimconnException("reserve_memory_for_all_vms : Failed to get "\
4675 bytexml
= bytes(bytearray(response
.content
, encoding
='utf-8'))
4676 contentelem
= lxmlElementTree
.XML(bytexml
)
4677 namespaces
= {prefix
:uri
for prefix
,uri
in contentelem
.nsmap
.iteritems() if prefix
}
4678 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4680 # Find the reservation element in the response
4681 memelem_list
= contentelem
.findall(".//rasd:Reservation", namespaces
)
4682 for memelem
in memelem_list
:
4683 memelem
.text
= str(memory_mb
)
4685 newdata
= lxmlElementTree
.tostring(contentelem
, pretty_print
=True)
4687 response
= self
.perform_request(req_type
='PUT',
4692 if response
.status_code
== 403:
4693 add_headers
= {'Content-Type': headers
['Content-Type']}
4694 response
= self
.retry_rest('PUT', url_rest_call
, add_headers
, newdata
)
4696 if response
.status_code
!= 202:
4697 self
.logger
.error("REST call {} failed reason : {}"\
4698 "status code : {} ".format(url_rest_call
,
4700 response
.status_code
))
4701 raise vimconn
.vimconnException("reserve_memory_for_all_vms : Failed to update "\
4702 "virtual hardware memory section")
4704 mem_task
= self
.get_task_from_response(response
.content
)
4705 result
= self
.client
.get_task_monitor().wait_for_success(task
=mem_task
)
4706 if result
.get('status') == 'success':
4707 self
.logger
.info("reserve_memory_for_all_vms(): VM {} succeeded "\
4710 self
.logger
.error("reserve_memory_for_all_vms(): VM {} failed "\
4713 def connect_vapp_to_org_vdc_network(self
, vapp_id
, net_name
):
4715 Configure VApp network config with org vdc network
4722 self
.logger
.info("Connecting vapp {} to org vdc network {}".
4723 format(vapp_id
, net_name
))
4725 url_rest_call
= "{}/api/vApp/vapp-{}/networkConfigSection/".format(self
.url
, vapp_id
)
4727 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4728 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4729 response
= self
.perform_request(req_type
='GET',
4733 if response
.status_code
== 403:
4734 response
= self
.retry_rest('GET', url_rest_call
)
4736 if response
.status_code
!= 200:
4737 self
.logger
.error("REST call {} failed reason : {}"\
4738 "status code : {}".format(url_rest_call
,
4740 response
.status_code
))
4741 raise vimconn
.vimconnException("connect_vapp_to_org_vdc_network : Failed to get "\
4742 "network config section")
4744 data
= response
.content
4745 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConfigSection+xml'
4746 net_id
= self
.get_network_id_by_name(net_name
)
4748 raise vimconn
.vimconnException("connect_vapp_to_org_vdc_network : Failed to find "\
4751 bytexml
= bytes(bytearray(data
, encoding
='utf-8'))
4752 newelem
= lxmlElementTree
.XML(bytexml
)
4753 namespaces
= {prefix
: uri
for prefix
, uri
in newelem
.nsmap
.iteritems() if prefix
}
4754 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
4755 nwcfglist
= newelem
.findall(".//xmlns:NetworkConfig", namespaces
)
4757 newstr
= """<NetworkConfig networkName="{}">
4759 <ParentNetwork href="{}/api/network/{}"/>
4760 <FenceMode>bridged</FenceMode>
4763 """.format(net_name
, self
.url
, net_id
)
4764 newcfgelem
= lxmlElementTree
.fromstring(newstr
)
4766 nwcfglist
[0].addnext(newcfgelem
)
4768 newdata
= lxmlElementTree
.tostring(newelem
, pretty_print
=True)
4770 response
= self
.perform_request(req_type
='PUT',
4775 if response
.status_code
== 403:
4776 add_headers
= {'Content-Type': headers
['Content-Type']}
4777 response
= self
.retry_rest('PUT', url_rest_call
, add_headers
, newdata
)
4779 if response
.status_code
!= 202:
4780 self
.logger
.error("REST call {} failed reason : {}"\
4781 "status code : {} ".format(url_rest_call
,
4783 response
.status_code
))
4784 raise vimconn
.vimconnException("connect_vapp_to_org_vdc_network : Failed to update "\
4785 "network config section")
4787 vapp_task
= self
.get_task_from_response(response
.content
)
4788 result
= self
.client
.get_task_monitor().wait_for_success(task
=vapp_task
)
4789 if result
.get('status') == 'success':
4790 self
.logger
.info("connect_vapp_to_org_vdc_network(): Vapp {} connected to "\
4791 "network {}".format(vapp_id
, net_name
))
4793 self
.logger
.error("connect_vapp_to_org_vdc_network(): Vapp {} failed to "\
4794 "connect to network {}".format(vapp_id
, net_name
))
4796 def remove_primary_network_adapter_from_all_vms(self
, vapp
):
4798 Method to remove network adapter type to vm
4805 self
.logger
.info("Removing network adapter from all VMs")
4806 for vms
in vapp
.get_all_vms():
4807 vm_id
= vms
.get('id').split(':')[-1]
4809 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(self
.url
, vm_id
)
4811 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4812 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4813 response
= self
.perform_request(req_type
='GET',
4817 if response
.status_code
== 403:
4818 response
= self
.retry_rest('GET', url_rest_call
)
4820 if response
.status_code
!= 200:
4821 self
.logger
.error("REST call {} failed reason : {}"\
4822 "status code : {}".format(url_rest_call
,
4824 response
.status_code
))
4825 raise vimconn
.vimconnException("remove_primary_network_adapter : Failed to get "\
4826 "network connection section")
4828 data
= response
.content
4829 data
= data
.split('<Link rel="edit"')[0]
4831 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
4833 newdata
= """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
4834 <NetworkConnectionSection xmlns="http://www.vmware.com/vcloud/v1.5"
4835 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
4836 xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
4837 xmlns:common="http://schemas.dmtf.org/wbem/wscim/1/common"
4838 xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
4839 xmlns:vmw="http://www.vmware.com/schema/ovf"
4840 xmlns:ovfenv="http://schemas.dmtf.org/ovf/environment/1"
4841 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
4842 xmlns:ns9="http://www.vmware.com/vcloud/versions"
4843 href="{url}" type="application/vnd.vmware.vcloud.networkConnectionSection+xml" ovf:required="false">
4844 <ovf:Info>Specifies the available VM network connections</ovf:Info>
4845 <PrimaryNetworkConnectionIndex>0</PrimaryNetworkConnectionIndex>
4846 <Link rel="edit" href="{url}" type="application/vnd.vmware.vcloud.networkConnectionSection+xml"/>
4847 </NetworkConnectionSection>""".format(url
=url_rest_call
)
4848 response
= self
.perform_request(req_type
='PUT',
4853 if response
.status_code
== 403:
4854 add_headers
= {'Content-Type': headers
['Content-Type']}
4855 response
= self
.retry_rest('PUT', url_rest_call
, add_headers
, newdata
)
4857 if response
.status_code
!= 202:
4858 self
.logger
.error("REST call {} failed reason : {}"\
4859 "status code : {} ".format(url_rest_call
,
4861 response
.status_code
))
4862 raise vimconn
.vimconnException("remove_primary_network_adapter : Failed to update "\
4863 "network connection section")
4865 nic_task
= self
.get_task_from_response(response
.content
)
4866 result
= self
.client
.get_task_monitor().wait_for_success(task
=nic_task
)
4867 if result
.get('status') == 'success':
4868 self
.logger
.info("remove_primary_network_adapter(): VM {} conneced to "\
4869 "default NIC type".format(vm_id
))
4871 self
.logger
.error("remove_primary_network_adapter(): VM {} failed to "\
4872 "connect NIC type".format(vm_id
))
4874 def add_network_adapter_to_vms(self
, vapp
, network_name
, primary_nic_index
, nicIndex
, net
, nic_type
=None):
4876 Method to add network adapter type to vm
4878 network_name - name of network
4879 primary_nic_index - int value for primary nic index
4880 nicIndex - int value for nic index
4881 nic_type - specify model name to which add to vm
4886 self
.logger
.info("Add network adapter to VM: network_name {} nicIndex {} nic_type {}".\
4887 format(network_name
, nicIndex
, nic_type
))
4892 if 'floating_ip' in net
: floating_ip
= net
['floating_ip']
4894 # Stub for ip_address feature
4895 if 'ip_address' in net
: ip_address
= net
['ip_address']
4897 if 'mac_address' in net
: mac_address
= net
['mac_address']
4900 allocation_mode
= "POOL"
4902 allocation_mode
= "MANUAL"
4904 allocation_mode
= "DHCP"
4907 for vms
in vapp
.get_all_vms():
4908 vm_id
= vms
.get('id').split(':')[-1]
4910 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(self
.url
, vm_id
)
4912 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
4913 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
4914 response
= self
.perform_request(req_type
='GET',
4918 if response
.status_code
== 403:
4919 response
= self
.retry_rest('GET', url_rest_call
)
4921 if response
.status_code
!= 200:
4922 self
.logger
.error("REST call {} failed reason : {}"\
4923 "status code : {}".format(url_rest_call
,
4925 response
.status_code
))
4926 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
4927 "network connection section")
4929 data
= response
.content
4930 data
= data
.split('<Link rel="edit"')[0]
4931 if '<PrimaryNetworkConnectionIndex>' not in data
:
4932 self
.logger
.debug("add_network_adapter PrimaryNIC not in data")
4933 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
4934 <NetworkConnection network="{}">
4935 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
4936 <IsConnected>true</IsConnected>
4937 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
4938 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
4940 # Stub for ip_address feature
4942 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
4943 item
= item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
4946 mac_tag
= '<MACAddress>{}</MACAddress>'.format(mac_address
)
4947 item
= item
.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag
))
4949 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n</NetworkConnectionSection>'.format(item
))
4951 self
.logger
.debug("add_network_adapter PrimaryNIC in data")
4952 new_item
= """<NetworkConnection network="{}">
4953 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
4954 <IsConnected>true</IsConnected>
4955 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
4956 </NetworkConnection>""".format(network_name
, nicIndex
,
4958 # Stub for ip_address feature
4960 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
4961 new_item
= new_item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
4964 mac_tag
= '<MACAddress>{}</MACAddress>'.format(mac_address
)
4965 new_item
= new_item
.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag
))
4967 data
= data
+ new_item
+ '</NetworkConnectionSection>'
4969 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
4971 response
= self
.perform_request(req_type
='PUT',
4976 if response
.status_code
== 403:
4977 add_headers
= {'Content-Type': headers
['Content-Type']}
4978 response
= self
.retry_rest('PUT', url_rest_call
, add_headers
, data
)
4980 if response
.status_code
!= 202:
4981 self
.logger
.error("REST call {} failed reason : {}"\
4982 "status code : {} ".format(url_rest_call
,
4984 response
.status_code
))
4985 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
4986 "network connection section")
4988 nic_task
= self
.get_task_from_response(response
.content
)
4989 result
= self
.client
.get_task_monitor().wait_for_success(task
=nic_task
)
4990 if result
.get('status') == 'success':
4991 self
.logger
.info("add_network_adapter_to_vms(): VM {} conneced to "\
4992 "default NIC type".format(vm_id
))
4994 self
.logger
.error("add_network_adapter_to_vms(): VM {} failed to "\
4995 "connect NIC type".format(vm_id
))
4997 for vms
in vapp
.get_all_vms():
4998 vm_id
= vms
.get('id').split(':')[-1]
5000 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(self
.url
, vm_id
)
5002 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
5003 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
5004 response
= self
.perform_request(req_type
='GET',
5008 if response
.status_code
== 403:
5009 response
= self
.retry_rest('GET', url_rest_call
)
5011 if response
.status_code
!= 200:
5012 self
.logger
.error("REST call {} failed reason : {}"\
5013 "status code : {}".format(url_rest_call
,
5015 response
.status_code
))
5016 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
5017 "network connection section")
5018 data
= response
.content
5019 data
= data
.split('<Link rel="edit"')[0]
5020 vcd_netadapter_type
= nic_type
5021 if nic_type
in ['SR-IOV', 'VF']:
5022 vcd_netadapter_type
= "SRIOVETHERNETCARD"
5024 if '<PrimaryNetworkConnectionIndex>' not in data
:
5025 self
.logger
.debug("add_network_adapter PrimaryNIC not in data nic_type {}".format(nic_type
))
5026 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
5027 <NetworkConnection network="{}">
5028 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
5029 <IsConnected>true</IsConnected>
5030 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
5031 <NetworkAdapterType>{}</NetworkAdapterType>
5032 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
5033 allocation_mode
, vcd_netadapter_type
)
5034 # Stub for ip_address feature
5036 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
5037 item
= item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
5040 mac_tag
= '<MACAddress>{}</MACAddress>'.format(mac_address
)
5041 item
= item
.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag
))
5043 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n</NetworkConnectionSection>'.format(item
))
5045 self
.logger
.debug("add_network_adapter PrimaryNIC in data nic_type {}".format(nic_type
))
5046 new_item
= """<NetworkConnection network="{}">
5047 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
5048 <IsConnected>true</IsConnected>
5049 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
5050 <NetworkAdapterType>{}</NetworkAdapterType>
5051 </NetworkConnection>""".format(network_name
, nicIndex
,
5052 allocation_mode
, vcd_netadapter_type
)
5053 # Stub for ip_address feature
5055 ip_tag
= '<IpAddress>{}</IpAddress>'.format(ip_address
)
5056 new_item
= new_item
.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag
))
5059 mac_tag
= '<MACAddress>{}</MACAddress>'.format(mac_address
)
5060 new_item
= new_item
.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag
))
5062 data
= data
+ new_item
+ '</NetworkConnectionSection>'
5064 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
5066 response
= self
.perform_request(req_type
='PUT',
5071 if response
.status_code
== 403:
5072 add_headers
= {'Content-Type': headers
['Content-Type']}
5073 response
= self
.retry_rest('PUT', url_rest_call
, add_headers
, data
)
5075 if response
.status_code
!= 202:
5076 self
.logger
.error("REST call {} failed reason : {}"\
5077 "status code : {}".format(url_rest_call
,
5079 response
.status_code
))
5080 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
5081 "network connection section")
5083 nic_task
= self
.get_task_from_response(response
.content
)
5084 result
= self
.client
.get_task_monitor().wait_for_success(task
=nic_task
)
5085 if result
.get('status') == 'success':
5086 self
.logger
.info("add_network_adapter_to_vms(): VM {} "\
5087 "conneced to NIC type {}".format(vm_id
, nic_type
))
5089 self
.logger
.error("add_network_adapter_to_vms(): VM {} "\
5090 "failed to connect NIC type {}".format(vm_id
, nic_type
))
5091 except Exception as exp
:
5092 self
.logger
.error("add_network_adapter_to_vms() : exception occurred "\
5093 "while adding Network adapter")
5094 raise vimconn
.vimconnException(message
=exp
)
5097 def set_numa_affinity(self
, vmuuid
, paired_threads_id
):
5099 Method to assign numa affinity in vm configuration parammeters
5102 paired_threads_id - one or more virtual processor
5108 vcenter_conect
, content
= self
.get_vcenter_content()
5109 vm_moref_id
= self
.get_vm_moref_id(vmuuid
)
5111 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
5113 config_spec
= vim
.vm
.ConfigSpec()
5114 config_spec
.extraConfig
= []
5115 opt
= vim
.option
.OptionValue()
5116 opt
.key
= 'numa.nodeAffinity'
5117 opt
.value
= str(paired_threads_id
)
5118 config_spec
.extraConfig
.append(opt
)
5119 task
= vm_obj
.ReconfigVM_Task(config_spec
)
5121 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
5122 extra_config
= vm_obj
.config
.extraConfig
5124 for opts
in extra_config
:
5125 if 'numa.nodeAffinity' in opts
.key
:
5127 self
.logger
.info("set_numa_affinity: Sucessfully assign numa affinity "\
5128 "value {} for vm {}".format(opt
.value
, vm_obj
))
5132 self
.logger
.error("set_numa_affinity: Failed to assign numa affinity")
5133 except Exception as exp
:
5134 self
.logger
.error("set_numa_affinity : exception occurred while setting numa affinity "\
5135 "for VM {} : {}".format(vm_obj
, vm_moref_id
))
5136 raise vimconn
.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
5137 "affinity".format(exp
))
5140 def cloud_init(self
, vapp
, cloud_config
):
5142 Method to inject ssh-key
5144 cloud_config a dictionary with:
5145 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
5146 'users': (optional) list of users to be inserted, each item is a dict with:
5147 'name': (mandatory) user name,
5148 'key-pairs': (optional) list of strings with the public key to be inserted to the user
5149 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
5150 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
5151 'config-files': (optional). List of files to be transferred. Each item is a dict with:
5152 'dest': (mandatory) string with the destination absolute path
5153 'encoding': (optional, by default text). Can be one of:
5154 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
5155 'content' (mandatory): string with the content of the file
5156 'permissions': (optional) string with file permissions, typically octal notation '0644'
5157 'owner': (optional) file owner, string with the format 'owner:group'
5158 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
5161 if not isinstance(cloud_config
, dict):
5162 raise Exception("cloud_init : parameter cloud_config is not a dictionary")
5166 if "key-pairs" in cloud_config
:
5167 key_pairs
= cloud_config
["key-pairs"]
5169 if "users" in cloud_config
:
5170 userdata
= cloud_config
["users"]
5172 self
.logger
.debug("cloud_init : Guest os customization started..")
5173 customize_script
= self
.format_script(key_pairs
=key_pairs
, users_list
=userdata
)
5174 customize_script
= customize_script
.replace("&","&")
5175 self
.guest_customization(vapp
, customize_script
)
5177 except Exception as exp
:
5178 self
.logger
.error("cloud_init : exception occurred while injecting "\
5180 raise vimconn
.vimconnException("cloud_init : Error {} failed to inject "\
5181 "ssh-key".format(exp
))
5183 def format_script(self
, key_pairs
=[], users_list
=[]):
5184 bash_script
= """#!/bin/sh
5185 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
5186 if [ "$1" = "precustomization" ];then
5187 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
5190 keys
= "\n".join(key_pairs
)
5193 if [ ! -d /root/.ssh ];then
5195 chown root:root /root/.ssh
5196 chmod 700 /root/.ssh
5197 touch /root/.ssh/authorized_keys
5198 chown root:root /root/.ssh/authorized_keys
5199 chmod 600 /root/.ssh/authorized_keys
5200 # make centos with selinux happy
5201 which restorecon && restorecon -Rv /root/.ssh
5203 touch /root/.ssh/authorized_keys
5204 chown root:root /root/.ssh/authorized_keys
5205 chmod 600 /root/.ssh/authorized_keys
5207 echo '{key}' >> /root/.ssh/authorized_keys
5208 """.format(key
=keys
)
5210 bash_script
+= keys_data
5212 for user
in users_list
:
5213 if 'name' in user
: user_name
= user
['name']
5214 if 'key-pairs' in user
:
5215 user_keys
= "\n".join(user
['key-pairs'])
5220 useradd -d /home/{user_name} -m -g users -s /bin/bash {user_name}
5221 """.format(user_name
=user_name
)
5223 bash_script
+= add_user_name
5226 user_keys_data
= """
5227 mkdir /home/{user_name}/.ssh
5228 chown {user_name}:{user_name} /home/{user_name}/.ssh
5229 chmod 700 /home/{user_name}/.ssh
5230 touch /home/{user_name}/.ssh/authorized_keys
5231 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
5232 chmod 600 /home/{user_name}/.ssh/authorized_keys
5233 # make centos with selinux happy
5234 which restorecon && restorecon -Rv /home/{user_name}/.ssh
5235 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
5236 """.format(user_name
=user_name
,user_key
=user_keys
)
5238 bash_script
+= user_keys_data
5240 return bash_script
+"\n\tfi"
5242 def guest_customization(self
, vapp
, customize_script
):
5244 Method to customize guest os
5246 customize_script - Customize script to be run at first boot of VM.
5248 for vm
in vapp
.get_all_vms():
5249 vm_id
= vm
.get('id').split(':')[-1]
5250 vm_name
= vm
.get('name')
5251 vm_name
= vm_name
.replace('_','-')
5253 vm_customization_url
= "{}/api/vApp/vm-{}/guestCustomizationSection/".format(self
.url
, vm_id
)
5254 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
5255 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
5257 headers
['Content-Type'] = "application/vnd.vmware.vcloud.guestCustomizationSection+xml"
5259 data
= """<GuestCustomizationSection
5260 xmlns="http://www.vmware.com/vcloud/v1.5"
5261 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
5262 ovf:required="false" href="{}" type="application/vnd.vmware.vcloud.guestCustomizationSection+xml">
5263 <ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
5264 <Enabled>true</Enabled>
5265 <ChangeSid>false</ChangeSid>
5266 <VirtualMachineId>{}</VirtualMachineId>
5267 <JoinDomainEnabled>false</JoinDomainEnabled>
5268 <UseOrgSettings>false</UseOrgSettings>
5269 <AdminPasswordEnabled>false</AdminPasswordEnabled>
5270 <AdminPasswordAuto>true</AdminPasswordAuto>
5271 <AdminAutoLogonEnabled>false</AdminAutoLogonEnabled>
5272 <AdminAutoLogonCount>0</AdminAutoLogonCount>
5273 <ResetPasswordRequired>false</ResetPasswordRequired>
5274 <CustomizationScript>{}</CustomizationScript>
5275 <ComputerName>{}</ComputerName>
5276 <Link href="{}" type="application/vnd.vmware.vcloud.guestCustomizationSection+xml" rel="edit"/>
5277 </GuestCustomizationSection>
5278 """.format(vm_customization_url
,
5282 vm_customization_url
)
5284 response
= self
.perform_request(req_type
='PUT',
5285 url
=vm_customization_url
,
5288 if response
.status_code
== 202:
5289 guest_task
= self
.get_task_from_response(response
.content
)
5290 self
.client
.get_task_monitor().wait_for_success(task
=guest_task
)
5291 self
.logger
.info("guest_customization : customized guest os task "\
5292 "completed for VM {}".format(vm_name
))
5294 self
.logger
.error("guest_customization : task for customized guest os"\
5295 "failed for VM {}".format(vm_name
))
5296 raise vimconn
.vimconnException("guest_customization : failed to perform"\
5297 "guest os customization on VM {}".format(vm_name
))
5299 def add_new_disk(self
, vapp_uuid
, disk_size
):
5301 Method to create an empty vm disk
5304 vapp_uuid - is vapp identifier.
5305 disk_size - size of disk to be created in GB
5313 #Disk size in GB, convert it into MB
5314 if disk_size
is not None:
5315 disk_size_mb
= int(disk_size
) * 1024
5316 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
5318 if vm_details
and "vm_virtual_hardware" in vm_details
:
5319 self
.logger
.info("Adding disk to VM: {} disk size:{}GB".format(vm_details
["name"], disk_size
))
5320 disk_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
5321 status
= self
.add_new_disk_rest(disk_href
, disk_size_mb
)
5323 except Exception as exp
:
5324 msg
= "Error occurred while creating new disk {}.".format(exp
)
5325 self
.rollback_newvm(vapp_uuid
, msg
)
5328 self
.logger
.info("Added new disk to VM: {} disk size:{}GB".format(vm_details
["name"], disk_size
))
5330 #If failed to add disk, delete VM
5331 msg
= "add_new_disk: Failed to add new disk to {}".format(vm_details
["name"])
5332 self
.rollback_newvm(vapp_uuid
, msg
)
5335 def add_new_disk_rest(self
, disk_href
, disk_size_mb
):
5337 Retrives vApp Disks section & add new empty disk
5340 disk_href: Disk section href to addd disk
5341 disk_size_mb: Disk size in MB
5343 Returns: Status of add new disk task
5346 if self
.client
._session
:
5347 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
5348 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
5349 response
= self
.perform_request(req_type
='GET',
5353 if response
.status_code
== 403:
5354 response
= self
.retry_rest('GET', disk_href
)
5356 if response
.status_code
!= requests
.codes
.ok
:
5357 self
.logger
.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}"
5358 .format(disk_href
, response
.status_code
))
5361 #Find but type & max of instance IDs assigned to disks
5362 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
5363 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
5365 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
5366 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
5368 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
5369 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
5370 inst_id
= int(item
.find("rasd:InstanceID" ,namespaces
).text
)
5371 if inst_id
> instance_id
:
5372 instance_id
= inst_id
5373 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
5374 bus_subtype
= disk_item
.attrib
["{"+namespaces
['xmlns']+"}busSubType"]
5375 bus_type
= disk_item
.attrib
["{"+namespaces
['xmlns']+"}busType"]
5377 instance_id
= instance_id
+ 1
5378 new_item
= """<Item>
5379 <rasd:Description>Hard disk</rasd:Description>
5380 <rasd:ElementName>New disk</rasd:ElementName>
5382 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
5383 vcloud:capacity="{}"
5384 vcloud:busSubType="{}"
5385 vcloud:busType="{}"></rasd:HostResource>
5386 <rasd:InstanceID>{}</rasd:InstanceID>
5387 <rasd:ResourceType>17</rasd:ResourceType>
5388 </Item>""".format(disk_size_mb
, bus_subtype
, bus_type
, instance_id
)
5390 new_data
= response
.content
5391 #Add new item at the bottom
5392 new_data
= new_data
.replace('</Item>\n</RasdItemsList>', '</Item>\n{}\n</RasdItemsList>'.format(new_item
))
5394 # Send PUT request to modify virtual hardware section with new disk
5395 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
5397 response
= self
.perform_request(req_type
='PUT',
5402 if response
.status_code
== 403:
5403 add_headers
= {'Content-Type': headers
['Content-Type']}
5404 response
= self
.retry_rest('PUT', disk_href
, add_headers
, new_data
)
5406 if response
.status_code
!= 202:
5407 self
.logger
.error("PUT REST API call {} failed. Return status code {}. Response Content:{}"
5408 .format(disk_href
, response
.status_code
, response
.content
))
5410 add_disk_task
= self
.get_task_from_response(response
.content
)
5411 result
= self
.client
.get_task_monitor().wait_for_success(task
=add_disk_task
)
5412 if result
.get('status') == 'success':
5415 self
.logger
.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb
))
5417 except Exception as exp
:
5418 self
.logger
.error("Error occurred calling rest api for creating new disk {}".format(exp
))
5423 def add_existing_disk(self
, catalogs
=None, image_id
=None, size
=None, template_name
=None, vapp_uuid
=None):
5425 Method to add existing disk to vm
5427 catalogs - List of VDC catalogs
5428 image_id - Catalog ID
5429 template_name - Name of template in catalog
5430 vapp_uuid - UUID of vApp
5435 vcenter_conect
, content
= self
.get_vcenter_content()
5436 #find moref-id of vm in image
5437 catalog_vm_info
= self
.get_vapp_template_details(catalogs
=catalogs
,
5441 if catalog_vm_info
and "vm_vcenter_info" in catalog_vm_info
:
5442 if "vm_moref_id" in catalog_vm_info
["vm_vcenter_info"]:
5443 catalog_vm_moref_id
= catalog_vm_info
["vm_vcenter_info"].get("vm_moref_id", None)
5444 if catalog_vm_moref_id
:
5445 self
.logger
.info("Moref_id of VM in catalog : {}" .format(catalog_vm_moref_id
))
5446 host
, catalog_vm_obj
= self
.get_vm_obj(content
, catalog_vm_moref_id
)
5449 disk_info
= self
.find_disk(catalog_vm_obj
)
5451 exp_msg
= "No VM with image id {} found".format(image_id
)
5452 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
5454 exp_msg
= "No Image found with image ID {} ".format(image_id
)
5455 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
5458 self
.logger
.info("Existing disk_info : {}".format(disk_info
))
5460 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
5461 host
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
5463 status
= self
.add_disk(vcenter_conect
=vcenter_conect
,
5465 disk_info
=disk_info
,
5470 self
.logger
.info("Disk from image id {} added to {}".format(image_id
,
5474 msg
= "No disk found with image id {} to add in VM {}".format(
5477 self
.rollback_newvm(vapp_uuid
, msg
, exp_type
="NotFound")
5480 def find_disk(self
, vm_obj
):
5482 Method to find details of existing disk in VM
5484 vm_obj - vCenter object of VM
5485 image_id - Catalog ID
5487 disk_info : dict of disk details
5492 devices
= vm_obj
.config
.hardware
.device
5493 for device
in devices
:
5494 if type(device
) is vim
.vm
.device
.VirtualDisk
:
5495 if isinstance(device
.backing
,vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo
) and hasattr(device
.backing
, 'fileName'):
5496 disk_info
["full_path"] = device
.backing
.fileName
5497 disk_info
["datastore"] = device
.backing
.datastore
5498 disk_info
["capacityKB"] = device
.capacityInKB
5500 except Exception as exp
:
5501 self
.logger
.error("find_disk() : exception occurred while "\
5502 "getting existing disk details :{}".format(exp
))
5506 def add_disk(self
, vcenter_conect
=None, vm
=None, size
=None, vapp_uuid
=None, disk_info
={}):
5508 Method to add existing disk in VM
5510 vcenter_conect - vCenter content object
5511 vm - vCenter vm object
5512 disk_info : dict of disk details
5514 status : status of add disk task
5516 datastore
= disk_info
["datastore"] if "datastore" in disk_info
else None
5517 fullpath
= disk_info
["full_path"] if "full_path" in disk_info
else None
5518 capacityKB
= disk_info
["capacityKB"] if "capacityKB" in disk_info
else None
5519 if size
is not None:
5520 #Convert size from GB to KB
5521 sizeKB
= int(size
) * 1024 * 1024
5522 #compare size of existing disk and user given size.Assign whicherver is greater
5523 self
.logger
.info("Add Existing disk : sizeKB {} , capacityKB {}".format(
5524 sizeKB
, capacityKB
))
5525 if sizeKB
> capacityKB
:
5528 if datastore
and fullpath
and capacityKB
:
5530 spec
= vim
.vm
.ConfigSpec()
5531 # get all disks on a VM, set unit_number to the next available
5533 for dev
in vm
.config
.hardware
.device
:
5534 if hasattr(dev
.backing
, 'fileName'):
5535 unit_number
= int(dev
.unitNumber
) + 1
5536 # unit_number 7 reserved for scsi controller
5537 if unit_number
== 7:
5539 if isinstance(dev
, vim
.vm
.device
.VirtualDisk
):
5540 #vim.vm.device.VirtualSCSIController
5541 controller_key
= dev
.controllerKey
5543 self
.logger
.info("Add Existing disk : unit number {} , controller key {}".format(
5544 unit_number
, controller_key
))
5547 disk_spec
= vim
.vm
.device
.VirtualDeviceSpec()
5548 disk_spec
.operation
= vim
.vm
.device
.VirtualDeviceSpec
.Operation
.add
5549 disk_spec
.device
= vim
.vm
.device
.VirtualDisk()
5550 disk_spec
.device
.backing
= \
5551 vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo()
5552 disk_spec
.device
.backing
.thinProvisioned
= True
5553 disk_spec
.device
.backing
.diskMode
= 'persistent'
5554 disk_spec
.device
.backing
.datastore
= datastore
5555 disk_spec
.device
.backing
.fileName
= fullpath
5557 disk_spec
.device
.unitNumber
= unit_number
5558 disk_spec
.device
.capacityInKB
= capacityKB
5559 disk_spec
.device
.controllerKey
= controller_key
5560 dev_changes
.append(disk_spec
)
5561 spec
.deviceChange
= dev_changes
5562 task
= vm
.ReconfigVM_Task(spec
=spec
)
5563 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
5565 except Exception as exp
:
5566 exp_msg
= "add_disk() : exception {} occurred while adding disk "\
5567 "{} to vm {}".format(exp
,
5570 self
.rollback_newvm(vapp_uuid
, exp_msg
)
5572 msg
= "add_disk() : Can not add disk to VM with disk info {} ".format(disk_info
)
5573 self
.rollback_newvm(vapp_uuid
, msg
)
5576 def get_vcenter_content(self
):
5578 Get the vsphere content object
5581 vm_vcenter_info
= self
.get_vm_vcenter_info()
5582 except Exception as exp
:
5583 self
.logger
.error("Error occurred while getting vCenter infromationn"\
5584 " for VM : {}".format(exp
))
5585 raise vimconn
.vimconnException(message
=exp
)
5588 if hasattr(ssl
, '_create_unverified_context'):
5589 context
= ssl
._create
_unverified
_context
()
5591 vcenter_conect
= SmartConnect(
5592 host
=vm_vcenter_info
["vm_vcenter_ip"],
5593 user
=vm_vcenter_info
["vm_vcenter_user"],
5594 pwd
=vm_vcenter_info
["vm_vcenter_password"],
5595 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
5598 atexit
.register(Disconnect
, vcenter_conect
)
5599 content
= vcenter_conect
.RetrieveContent()
5600 return vcenter_conect
, content
5603 def get_vm_moref_id(self
, vapp_uuid
):
5605 Get the moref_id of given VM
5609 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
5610 if vm_details
and "vm_vcenter_info" in vm_details
:
5611 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
5614 except Exception as exp
:
5615 self
.logger
.error("Error occurred while getting VM moref ID "\
5616 " for VM : {}".format(exp
))
5620 def get_vapp_template_details(self
, catalogs
=None, image_id
=None , template_name
=None):
5622 Method to get vApp template details
5624 catalogs - list of VDC catalogs
5625 image_id - Catalog ID to find
5626 template_name : template name in catalog
5628 parsed_respond : dict of vApp tempalte details
5630 parsed_response
= {}
5632 vca
= self
.connect_as_admin()
5634 raise vimconn
.vimconnConnectionException("Failed to connect vCD")
5637 org
, vdc
= self
.get_vdc_details()
5638 catalog
= self
.get_catalog_obj(image_id
, catalogs
)
5640 items
= org
.get_catalog_item(catalog
.get('name'), catalog
.get('name'))
5641 catalog_items
= [items
.attrib
]
5643 if len(catalog_items
) == 1:
5644 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
5645 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
5647 response
= self
.perform_request(req_type
='GET',
5648 url
=catalog_items
[0].get('href'),
5650 catalogItem
= XmlElementTree
.fromstring(response
.content
)
5651 entity
= [child
for child
in catalogItem
if child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
5652 vapp_tempalte_href
= entity
.get("href")
5653 #get vapp details and parse moref id
5655 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
5656 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
5657 'vmw': 'http://www.vmware.com/schema/ovf',
5658 'vm': 'http://www.vmware.com/vcloud/v1.5',
5659 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
5660 'vmext':"http://www.vmware.com/vcloud/extension/v1.5",
5661 'xmlns':"http://www.vmware.com/vcloud/v1.5"
5665 response
= self
.perform_request(req_type
='GET',
5666 url
=vapp_tempalte_href
,
5669 if response
.status_code
!= requests
.codes
.ok
:
5670 self
.logger
.debug("REST API call {} failed. Return status code {}".format(
5671 vapp_tempalte_href
, response
.status_code
))
5674 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
5675 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
5676 if children_section
is not None:
5677 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
5678 if vCloud_extension_section
is not None:
5679 vm_vcenter_info
= {}
5680 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
5681 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
5682 if vmext
is not None:
5683 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
5684 parsed_response
["vm_vcenter_info"]= vm_vcenter_info
5686 except Exception as exp
:
5687 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
5689 return parsed_response
5692 def rollback_newvm(self
, vapp_uuid
, msg
, exp_type
="Genric"):
5694 Method to delete vApp
5696 vapp_uuid - vApp UUID
5697 msg - Error message to be logged
5698 exp_type : Exception type
5703 status
= self
.delete_vminstance(vapp_uuid
)
5706 self
.logger
.error(msg
)
5707 if exp_type
== "Genric":
5708 raise vimconn
.vimconnException(msg
)
5709 elif exp_type
== "NotFound":
5710 raise vimconn
.vimconnNotFoundException(message
=msg
)
5712 def add_sriov(self
, vapp_uuid
, sriov_nets
, vmname_andid
):
5714 Method to attach SRIOV adapters to VM
5717 vapp_uuid - uuid of vApp/VM
5718 sriov_nets - SRIOV devices infromation as specified in VNFD (flavor)
5719 vmname_andid - vmname
5722 The status of add SRIOV adapter task , vm object and
5723 vcenter_conect object
5726 vcenter_conect
, content
= self
.get_vcenter_content()
5727 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
5731 no_of_sriov_devices
= len(sriov_nets
)
5732 if no_of_sriov_devices
> 0:
5733 #Get VM and its host
5734 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
5735 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
5736 if host_obj
and vm_obj
:
5737 #get SRIOV devies from host on which vapp is currently installed
5738 avilable_sriov_devices
= self
.get_sriov_devices(host_obj
,
5739 no_of_sriov_devices
,
5742 if len(avilable_sriov_devices
) == 0:
5743 #find other hosts with active pci devices
5744 new_host_obj
, avilable_sriov_devices
= self
.get_host_and_sriov_devices(
5746 no_of_sriov_devices
,
5749 if new_host_obj
is not None and len(avilable_sriov_devices
)> 0:
5750 #Migrate vm to the host where SRIOV devices are available
5751 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
,
5753 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
5754 if task
is not None:
5755 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
5756 self
.logger
.info("Migrate VM status: {}".format(result
))
5757 host_obj
= new_host_obj
5759 self
.logger
.info("Fail to migrate VM : {}".format(result
))
5760 raise vimconn
.vimconnNotFoundException(
5761 "Fail to migrate VM : {} to host {}".format(
5766 if host_obj
is not None and avilable_sriov_devices
is not None and len(avilable_sriov_devices
)> 0:
5767 #Add SRIOV devices one by one
5768 for sriov_net
in sriov_nets
:
5769 network_name
= sriov_net
.get('net_id')
5770 dvs_portgr_name
= self
.create_dvPort_group(network_name
)
5771 if sriov_net
.get('type') == "VF" or sriov_net
.get('type') == "SR-IOV":
5772 #add vlan ID ,Modify portgroup for vlan ID
5773 self
.configure_vlanID(content
, vcenter_conect
, network_name
)
5775 task
= self
.add_sriov_to_vm(content
,
5779 avilable_sriov_devices
[0]
5782 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
5784 self
.logger
.info("Added SRIOV {} to VM {}".format(
5785 no_of_sriov_devices
,
5788 self
.logger
.error("Fail to add SRIOV {} to VM {}".format(
5789 no_of_sriov_devices
,
5791 raise vimconn
.vimconnUnexpectedResponse(
5792 "Fail to add SRIOV adapter in VM ".format(str(vm_obj
))
5794 return True, vm_obj
, vcenter_conect
5796 self
.logger
.error("Currently there is no host with"\
5797 " {} number of avaialble SRIOV "\
5798 "VFs required for VM {}".format(
5799 no_of_sriov_devices
,
5802 raise vimconn
.vimconnNotFoundException(
5803 "Currently there is no host with {} "\
5804 "number of avaialble SRIOV devices required for VM {}".format(
5805 no_of_sriov_devices
,
5808 self
.logger
.debug("No infromation about SRIOV devices {} ",sriov_nets
)
5810 except vmodl
.MethodFault
as error
:
5811 self
.logger
.error("Error occurred while adding SRIOV {} ",error
)
5812 return None, vm_obj
, vcenter_conect
5815 def get_sriov_devices(self
,host
, no_of_vfs
):
5817 Method to get the details of SRIOV devices on given host
5819 host - vSphere host object
5820 no_of_vfs - number of VFs needed on host
5823 array of SRIOV devices
5827 for device
in host
.config
.pciPassthruInfo
:
5828 if isinstance(device
,vim
.host
.SriovInfo
) and device
.sriovActive
:
5829 if device
.numVirtualFunction
>= no_of_vfs
:
5830 sriovInfo
.append(device
)
5835 def get_host_and_sriov_devices(self
, content
, no_of_vfs
):
5837 Method to get the details of SRIOV devices infromation on all hosts
5840 content - vSphere host object
5841 no_of_vfs - number of pci VFs needed on host
5844 array of SRIOV devices and host object
5847 sriov_device_objs
= None
5850 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
5851 [vim
.HostSystem
], True)
5852 for host
in container
.view
:
5853 devices
= self
.get_sriov_devices(host
, no_of_vfs
)
5856 sriov_device_objs
= devices
5858 except Exception as exp
:
5859 self
.logger
.error("Error {} occurred while finding SRIOV devices on host: {}".format(exp
, host_obj
))
5861 return host_obj
,sriov_device_objs
5864 def add_sriov_to_vm(self
,content
, vm_obj
, host_obj
, network_name
, sriov_device
):
5866 Method to add SRIOV adapter to vm
5869 host_obj - vSphere host object
5870 vm_obj - vSphere vm object
5871 content - vCenter content object
5872 network_name - name of distributed virtaul portgroup
5873 sriov_device - SRIOV device info
5879 vnic_label
= "sriov nic"
5881 dvs_portgr
= self
.get_dvport_group(network_name
)
5882 network_name
= dvs_portgr
.name
5883 nic
= vim
.vm
.device
.VirtualDeviceSpec()
5885 nic
.operation
= vim
.vm
.device
.VirtualDeviceSpec
.Operation
.add
5886 nic
.device
= vim
.vm
.device
.VirtualSriovEthernetCard()
5887 nic
.device
.addressType
= 'assigned'
5888 #nic.device.key = 13016
5889 nic
.device
.deviceInfo
= vim
.Description()
5890 nic
.device
.deviceInfo
.label
= vnic_label
5891 nic
.device
.deviceInfo
.summary
= network_name
5892 nic
.device
.backing
= vim
.vm
.device
.VirtualEthernetCard
.NetworkBackingInfo()
5894 nic
.device
.backing
.network
= self
.get_obj(content
, [vim
.Network
], network_name
)
5895 nic
.device
.backing
.deviceName
= network_name
5896 nic
.device
.backing
.useAutoDetect
= False
5897 nic
.device
.connectable
= vim
.vm
.device
.VirtualDevice
.ConnectInfo()
5898 nic
.device
.connectable
.startConnected
= True
5899 nic
.device
.connectable
.allowGuestControl
= True
5901 nic
.device
.sriovBacking
= vim
.vm
.device
.VirtualSriovEthernetCard
.SriovBackingInfo()
5902 nic
.device
.sriovBacking
.physicalFunctionBacking
= vim
.vm
.device
.VirtualPCIPassthrough
.DeviceBackingInfo()
5903 nic
.device
.sriovBacking
.physicalFunctionBacking
.id = sriov_device
.id
5906 vmconf
= vim
.vm
.ConfigSpec(deviceChange
=devices
)
5907 task
= vm_obj
.ReconfigVM_Task(vmconf
)
5909 except Exception as exp
:
5910 self
.logger
.error("Error {} occurred while adding SRIOV adapter in VM: {}".format(exp
, vm_obj
))
5914 def create_dvPort_group(self
, network_name
):
5916 Method to create disributed virtual portgroup
5919 network_name - name of network/portgroup
5925 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
5926 network_name
=''.join(new_network_name
)
5927 vcenter_conect
, content
= self
.get_vcenter_content()
5929 dv_switch
= self
.get_obj(content
, [vim
.DistributedVirtualSwitch
], self
.dvs_name
)
5931 dv_pg_spec
= vim
.dvs
.DistributedVirtualPortgroup
.ConfigSpec()
5932 dv_pg_spec
.name
= network_name
5934 dv_pg_spec
.type = vim
.dvs
.DistributedVirtualPortgroup
.PortgroupType
.earlyBinding
5935 dv_pg_spec
.defaultPortConfig
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VmwarePortConfigPolicy()
5936 dv_pg_spec
.defaultPortConfig
.securityPolicy
= vim
.dvs
.VmwareDistributedVirtualSwitch
.SecurityPolicy()
5937 dv_pg_spec
.defaultPortConfig
.securityPolicy
.allowPromiscuous
= vim
.BoolPolicy(value
=False)
5938 dv_pg_spec
.defaultPortConfig
.securityPolicy
.forgedTransmits
= vim
.BoolPolicy(value
=False)
5939 dv_pg_spec
.defaultPortConfig
.securityPolicy
.macChanges
= vim
.BoolPolicy(value
=False)
5941 task
= dv_switch
.AddDVPortgroup_Task([dv_pg_spec
])
5942 self
.wait_for_vcenter_task(task
, vcenter_conect
)
5944 dvPort_group
= self
.get_obj(content
, [vim
.dvs
.DistributedVirtualPortgroup
], network_name
)
5946 self
.logger
.info("Created disributed virtaul port group: {}".format(dvPort_group
))
5947 return dvPort_group
.key
5949 self
.logger
.debug("No disributed virtual switch found with name {}".format(network_name
))
5951 except Exception as exp
:
5952 self
.logger
.error("Error occurred while creating disributed virtaul port group {}"\
5953 " : {}".format(network_name
, exp
))
5956 def reconfig_portgroup(self
, content
, dvPort_group_name
, config_info
={}):
5958 Method to reconfigure disributed virtual portgroup
5961 dvPort_group_name - name of disributed virtual portgroup
5962 content - vCenter content object
5963 config_info - disributed virtual portgroup configuration
5969 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
5971 dv_pg_spec
= vim
.dvs
.DistributedVirtualPortgroup
.ConfigSpec()
5972 dv_pg_spec
.configVersion
= dvPort_group
.config
.configVersion
5973 dv_pg_spec
.defaultPortConfig
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VmwarePortConfigPolicy()
5974 if "vlanID" in config_info
:
5975 dv_pg_spec
.defaultPortConfig
.vlan
= vim
.dvs
.VmwareDistributedVirtualSwitch
.VlanIdSpec()
5976 dv_pg_spec
.defaultPortConfig
.vlan
.vlanId
= config_info
.get('vlanID')
5978 task
= dvPort_group
.ReconfigureDVPortgroup_Task(spec
=dv_pg_spec
)
5982 except Exception as exp
:
5983 self
.logger
.error("Error occurred while reconfiguraing disributed virtaul port group {}"\
5984 " : {}".format(dvPort_group_name
, exp
))
5988 def destroy_dvport_group(self
, dvPort_group_name
):
5990 Method to destroy disributed virtual portgroup
5993 network_name - name of network/portgroup
5996 True if portgroup successfully got deleted else false
5998 vcenter_conect
, content
= self
.get_vcenter_content()
6001 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
6003 task
= dvPort_group
.Destroy_Task()
6004 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
6006 except vmodl
.MethodFault
as exp
:
6007 self
.logger
.error("Caught vmodl fault {} while deleting disributed virtaul port group {}".format(
6008 exp
, dvPort_group_name
))
6012 def get_dvport_group(self
, dvPort_group_name
):
6014 Method to get disributed virtual portgroup
6017 network_name - name of network/portgroup
6022 vcenter_conect
, content
= self
.get_vcenter_content()
6025 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
, [vim
.dvs
.DistributedVirtualPortgroup
], True)
6026 for item
in container
.view
:
6027 if item
.key
== dvPort_group_name
:
6031 except vmodl
.MethodFault
as exp
:
6032 self
.logger
.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
6033 exp
, dvPort_group_name
))
6036 def get_vlanID_from_dvs_portgr(self
, dvPort_group_name
):
6038 Method to get disributed virtual portgroup vlanID
6041 network_name - name of network/portgroup
6048 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
6050 vlanId
= dvPort_group
.config
.defaultPortConfig
.vlan
.vlanId
6051 except vmodl
.MethodFault
as exp
:
6052 self
.logger
.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
6053 exp
, dvPort_group_name
))
6057 def configure_vlanID(self
, content
, vcenter_conect
, dvPort_group_name
):
6059 Method to configure vlanID in disributed virtual portgroup vlanID
6062 network_name - name of network/portgroup
6067 vlanID
= self
.get_vlanID_from_dvs_portgr(dvPort_group_name
)
6070 vlanID
= self
.genrate_vlanID(dvPort_group_name
)
6071 config
= {"vlanID":vlanID
}
6072 task
= self
.reconfig_portgroup(content
, dvPort_group_name
,
6075 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
6077 self
.logger
.info("Reconfigured Port group {} for vlan ID {}".format(
6078 dvPort_group_name
,vlanID
))
6080 self
.logger
.error("Fail reconfigure portgroup {} for vlanID{}".format(
6081 dvPort_group_name
, vlanID
))
6084 def genrate_vlanID(self
, network_name
):
6086 Method to get unused vlanID
6088 network_name - name of network/portgroup
6094 if self
.config
.get('vlanID_range') == None:
6095 raise vimconn
.vimconnConflictException("You must provide a 'vlanID_range' "\
6096 "at config value before creating sriov network with vlan tag")
6097 if "used_vlanIDs" not in self
.persistent_info
:
6098 self
.persistent_info
["used_vlanIDs"] = {}
6100 used_ids
= self
.persistent_info
["used_vlanIDs"].values()
6102 #used_ids = list(self.persistent_info["used_vlanIDs"].values())
6104 for vlanID_range
in self
.config
.get('vlanID_range'):
6105 start_vlanid
, end_vlanid
= vlanID_range
.split("-")
6106 if start_vlanid
> end_vlanid
:
6107 raise vimconn
.vimconnConflictException("Invalid vlan ID range {}".format(
6110 for id in xrange(int(start_vlanid
), int(end_vlanid
) + 1):
6112 #for id in range(int(start_vlanid), int(end_vlanid) + 1):
6113 if id not in used_ids
:
6115 self
.persistent_info
["used_vlanIDs"][network_name
] = vlan_id
6118 raise vimconn
.vimconnConflictException("All Vlan IDs are in use")
6121 def get_obj(self
, content
, vimtype
, name
):
6123 Get the vsphere object associated with a given text name
6126 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
, vimtype
, True)
6127 for item
in container
.view
:
6128 if item
.name
== name
:
6134 def insert_media_to_vm(self
, vapp
, image_id
):
6136 Method to insert media CD-ROM (ISO image) from catalog to vm.
6137 vapp - vapp object to get vm id
6138 Image_id - image id for cdrom to be inerted to vm
6140 # create connection object
6141 vca
= self
.connect()
6143 # fetching catalog details
6144 rest_url
= "{}/api/catalog/{}".format(self
.url
, image_id
)
6146 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
6147 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
6148 response
= self
.perform_request(req_type
='GET',
6152 if response
.status_code
!= 200:
6153 self
.logger
.error("REST call {} failed reason : {}"\
6154 "status code : {}".format(url_rest_call
,
6156 response
.status_code
))
6157 raise vimconn
.vimconnException("insert_media_to_vm(): Failed to get "\
6159 # searching iso name and id
6160 iso_name
,media_id
= self
.get_media_details(vca
, response
.content
)
6162 if iso_name
and media_id
:
6163 data
="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
6164 <ns6:MediaInsertOrEjectParams
6165 xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns6="http://www.vmware.com/vcloud/v1.5" xmlns:ns7="http://www.vmware.com/schema/ovf" xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
6167 type="application/vnd.vmware.vcloud.media+xml"
6169 id="urn:vcloud:media:{}"
6170 href="https://{}/api/media/{}"/>
6171 </ns6:MediaInsertOrEjectParams>""".format(iso_name
, media_id
,
6174 for vms
in vapp
.get_all_vms():
6175 vm_id
= vms
.get('id').split(':')[-1]
6177 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
6178 rest_url
= "{}/api/vApp/vm-{}/media/action/insertMedia".format(self
.url
,vm_id
)
6180 response
= self
.perform_request(req_type
='POST',
6185 if response
.status_code
!= 202:
6186 self
.logger
.error("Failed to insert CD-ROM to vm")
6187 raise vimconn
.vimconnException("insert_media_to_vm() : Failed to insert"\
6190 task
= self
.get_task_from_response(response
.content
)
6191 result
= self
.client
.get_task_monitor().wait_for_success(task
=task
)
6192 if result
.get('status') == 'success':
6193 self
.logger
.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
6194 " image to vm {}".format(vm_id
))
6196 except Exception as exp
:
6197 self
.logger
.error("insert_media_to_vm() : exception occurred "\
6198 "while inserting media CD-ROM")
6199 raise vimconn
.vimconnException(message
=exp
)
6202 def get_media_details(self
, vca
, content
):
6204 Method to get catalog item details
6205 vca - connection object
6206 content - Catalog details
6207 Return - Media name, media id
6209 cataloghref_list
= []
6212 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
6213 for child
in vm_list_xmlroot
.iter():
6214 if 'CatalogItem' in child
.tag
:
6215 cataloghref_list
.append(child
.attrib
.get('href'))
6216 if cataloghref_list
is not None:
6217 for href
in cataloghref_list
:
6219 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
6220 'x-vcloud-authorization': vca
._session
.headers
['x-vcloud-authorization']}
6221 response
= self
.perform_request(req_type
='GET',
6224 if response
.status_code
!= 200:
6225 self
.logger
.error("REST call {} failed reason : {}"\
6226 "status code : {}".format(href
,
6228 response
.status_code
))
6229 raise vimconn
.vimconnException("get_media_details : Failed to get "\
6230 "catalogitem details")
6231 list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
6232 for child
in list_xmlroot
.iter():
6233 if 'Entity' in child
.tag
:
6234 if 'media' in child
.attrib
.get('href'):
6235 name
= child
.attrib
.get('name')
6236 media_id
= child
.attrib
.get('href').split('/').pop()
6237 return name
,media_id
6239 self
.logger
.debug("Media name and id not found")
6241 except Exception as exp
:
6242 self
.logger
.error("get_media_details : exception occurred "\
6243 "getting media details")
6244 raise vimconn
.vimconnException(message
=exp
)
6247 def retry_rest(self
, method
, url
, add_headers
=None, data
=None):
6248 """ Method to get Token & retry respective REST request
6250 api - REST API - Can be one of 'GET' or 'PUT' or 'POST'
6251 url - request url to be used
6252 add_headers - Additional headers (optional)
6253 data - Request payload data to be passed in request
6255 response - Response of request
6262 if self
.client
._session
:
6263 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
6264 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
6267 headers
.update(add_headers
)
6270 response
= self
.perform_request(req_type
='GET',
6273 elif method
== 'PUT':
6274 response
= self
.perform_request(req_type
='PUT',
6278 elif method
== 'POST':
6279 response
= self
.perform_request(req_type
='POST',
6283 elif method
== 'DELETE':
6284 response
= self
.perform_request(req_type
='DELETE',
6290 def get_token(self
):
6291 """ Generate a new token if expired
6294 The return client object that letter can be used to connect to vCloud director as admin for VDC
6297 self
.logger
.debug("Generate token for vca {} as {} to datacenter {}.".format(self
.org_name
,
6301 client
= Client(host
, verify_ssl_certs
=False)
6302 client
.set_credentials(BasicLoginCredentials(self
.user
, self
.org_name
, self
.passwd
))
6304 self
.client
= client
6307 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
6308 "{} as user: {}".format(self
.org_name
, self
.user
))
6311 raise vimconn
.vimconnConnectionException("Failed while reconnecting vCD")
6314 def get_vdc_details(self
):
6315 """ Get VDC details using pyVcloud Lib
6317 Returns org and vdc object
6321 org
= Org(self
.client
, resource
=self
.client
.get_org())
6322 vdc
= org
.get_vdc(self
.tenant_name
)
6323 except Exception as e
:
6324 # pyvcloud not giving a specific exception, Refresh nevertheless
6325 self
.logger
.debug("Received exception {}, refreshing token ".format(str(e
)))
6327 #Retry once, if failed by refreshing token
6330 org
= Org(self
.client
, resource
=self
.client
.get_org())
6331 vdc
= org
.get_vdc(self
.tenant_name
)
6336 def perform_request(self
, req_type
, url
, headers
=None, data
=None):
6337 """Perform the POST/PUT/GET/DELETE request."""
6339 #Log REST request details
6340 self
.log_request(req_type
, url
=url
, headers
=headers
, data
=data
)
6341 # perform request and return its result
6342 if req_type
== 'GET':
6343 response
= requests
.get(url
=url
,
6346 elif req_type
== 'PUT':
6347 response
= requests
.put(url
=url
,
6351 elif req_type
== 'POST':
6352 response
= requests
.post(url
=url
,
6356 elif req_type
== 'DELETE':
6357 response
= requests
.delete(url
=url
,
6360 #Log the REST response
6361 self
.log_response(response
)
6366 def log_request(self
, req_type
, url
=None, headers
=None, data
=None):
6367 """Logs REST request details"""
6369 if req_type
is not None:
6370 self
.logger
.debug("Request type: {}".format(req_type
))
6373 self
.logger
.debug("Request url: {}".format(url
))
6375 if headers
is not None:
6376 for header
in headers
:
6377 self
.logger
.debug("Request header: {}: {}".format(header
, headers
[header
]))
6379 if data
is not None:
6380 self
.logger
.debug("Request data: {}".format(data
))
6383 def log_response(self
, response
):
6384 """Logs REST response details"""
6386 self
.logger
.debug("Response status code: {} ".format(response
.status_code
))
6389 def get_task_from_response(self
, content
):
6391 content - API response content(response.content)
6394 xmlroot
= XmlElementTree
.fromstring(content
)
6395 if xmlroot
.tag
.split('}')[1] == "Task":
6399 if ele
.tag
.split("}")[1] == "Tasks":
6405 def power_on_vapp(self
,vapp_id
, vapp_name
):
6408 vapp_name - vAapp name
6409 return - Task object
6411 headers
= {'Accept':'application/*+xml;version=' + API_VERSION
,
6412 'x-vcloud-authorization': self
.client
._session
.headers
['x-vcloud-authorization']}
6414 poweron_href
= "{}/api/vApp/vapp-{}/power/action/powerOn".format(self
.url
,
6416 response
= self
.perform_request(req_type
='POST',
6420 if response
.status_code
!= 202:
6421 self
.logger
.error("REST call {} failed reason : {}"\
6422 "status code : {} ".format(poweron_href
,
6424 response
.status_code
))
6425 raise vimconn
.vimconnException("power_on_vapp() : Failed to power on "\
6426 "vApp {}".format(vapp_name
))
6428 poweron_task
= self
.get_task_from_response(response
.content
)