1 # -*- coding: utf-8 -*-
4 # Copyright 2016-2019 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.
44 from xml
.etree
import ElementTree
as XmlElementTree
45 from xml
.sax
.saxutils
import escape
47 from lxml
import etree
as lxmlElementTree
49 from osm_ro_plugin
import vimconn
50 from progressbar
import Bar
, ETA
, FileTransferSpeed
, Percentage
, ProgressBar
51 from pyvcloud
.vcd
.client
import BasicLoginCredentials
, Client
52 from pyvcloud
.vcd
.org
import Org
53 from pyvcloud
.vcd
.vapp
import VApp
54 from pyvcloud
.vcd
.vdc
import VDC
55 from pyVim
.connect
import Disconnect
, SmartConnect
56 from pyVmomi
import vim
, vmodl
# @UnresolvedImport
60 # global variable for vcd connector type
61 STANDALONE
= "standalone"
63 # key for flavor dicts
64 FLAVOR_RAM_KEY
= "ram"
65 FLAVOR_VCPUS_KEY
= "vcpus"
66 FLAVOR_DISK_KEY
= "disk"
67 DEFAULT_IP_PROFILE
= {"dhcp_count": 50, "dhcp_enabled": True, "ip_version": "IPv4"}
68 # global variable for wait time
74 # -1: "Could not be created",
80 # 5: "Waiting for user input",
82 # 7: "Unrecognized state",
84 # 9: "Inconsistent state",
85 # 10: "Children do not all have the same status",
86 # 11: "Upload initiated, OVF descriptor pending",
87 # 12: "Upload initiated, copying contents",
88 # 13: "Upload initiated , disk contents pending",
89 # 14: "Upload has been quarantined",
90 # 15: "Upload quarantine period has expired"
92 # mapping vCD status to MANO
93 vcdStatusCode2manoFormat
= {
104 netStatus2manoFormat
= {
107 "INACTIVE": "INACTIVE",
110 "DELETED": "DELETED",
114 class vimconnector(vimconn
.VimConnector
):
115 # dict used to store flavor in memory
133 Constructor create vmware connector to vCloud director.
135 By default construct doesn't validate connection state. So client can create object with None arguments.
136 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
138 a) It initialize organization UUID
139 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
142 uuid - is organization uuid.
143 name - is organization name that must be presented in vCloud director.
144 tenant_id - is VDC uuid it must be presented in vCloud director
145 tenant_name - is VDC name.
146 url - is hostname or ip address of vCloud director
147 url_admin - same as above.
148 user - is user that administrator for organization. Caller must make sure that
149 username has right privileges.
151 password - is password for a user.
153 VMware connector also requires PVDC administrative privileges and separate account.
154 This variables must be passed via config argument dict contains keys
156 dict['admin_username']
157 dict['admin_password']
158 config - Provide NSX and vCenter information
164 vimconn
.VimConnector
.__init
__(
178 self
.logger
= logging
.getLogger("ro.vim.vmware")
179 self
.logger
.setLevel(10)
180 self
.persistent_info
= persistent_info
185 self
.url_admin
= url_admin
186 self
.tenant_id
= tenant_id
187 self
.tenant_name
= tenant_name
191 self
.admin_password
= None
192 self
.admin_user
= None
194 self
.nsx_manager
= None
196 self
.nsx_password
= None
197 self
.availability_zone
= None
199 # Disable warnings from self-signed certificates.
200 requests
.packages
.urllib3
.disable_warnings()
202 if tenant_name
is not None:
203 orgnameandtenant
= tenant_name
.split(":")
205 if len(orgnameandtenant
) == 2:
206 self
.tenant_name
= orgnameandtenant
[1]
207 self
.org_name
= orgnameandtenant
[0]
209 self
.tenant_name
= tenant_name
211 if "orgname" in config
:
212 self
.org_name
= config
["orgname"]
215 self
.logger
.setLevel(getattr(logging
, log_level
))
218 self
.admin_user
= config
["admin_username"]
219 self
.admin_password
= config
["admin_password"]
221 raise vimconn
.VimConnException(
222 message
="Error admin username or admin password is empty."
226 self
.nsx_manager
= config
["nsx_manager"]
227 self
.nsx_user
= config
["nsx_user"]
228 self
.nsx_password
= config
["nsx_password"]
230 raise vimconn
.VimConnException(
231 message
="Error: nsx manager or nsx user or nsx password is empty in Config"
234 self
.vcenter_ip
= config
.get("vcenter_ip", None)
235 self
.vcenter_port
= config
.get("vcenter_port", None)
236 self
.vcenter_user
= config
.get("vcenter_user", None)
237 self
.vcenter_password
= config
.get("vcenter_password", None)
239 # Set availability zone for Affinity rules
240 self
.availability_zone
= self
.set_availability_zones()
242 # ############# Stub code for SRIOV #################
244 # self.dvs_name = config['dv_switch_name']
246 # raise vimconn.VimConnException(message="Error:
247 # distributed virtaul switch name is empty in Config")
249 # self.vlanID_range = config.get("vlanID_range", None)
255 raise vimconn
.VimConnException("url param can not be NoneType")
257 if not self
.url_admin
: # try to use normal url
258 self
.url_admin
= self
.url
261 "UUID: {} name: {} tenant_id: {} tenant name {}".format(
262 self
.id, self
.org_name
, self
.tenant_id
, self
.tenant_name
266 "vcd url {} vcd username: {} vcd password: {}".format(
267 self
.url
, self
.user
, self
.passwd
271 "vcd admin username {} vcd admin passowrd {}".format(
272 self
.admin_user
, self
.admin_password
276 # initialize organization
277 if self
.user
is not None and self
.passwd
is not None and self
.url
:
278 self
.init_organization()
280 def __getitem__(self
, index
):
284 if index
== "tenant_id":
285 return self
.tenant_id
287 if index
== "tenant_name":
288 return self
.tenant_name
291 elif index
== "org_name":
293 elif index
== "org_uuid":
295 elif index
== "user":
297 elif index
== "passwd":
301 elif index
== "url_admin":
302 return self
.url_admin
303 elif index
== "config":
306 raise KeyError("Invalid key '{}'".format(index
))
308 def __setitem__(self
, index
, value
):
312 if index
== "tenant_id":
313 self
.tenant_id
= value
315 if index
== "tenant_name":
316 self
.tenant_name
= value
319 elif index
== "org_name":
320 self
.org_name
= value
321 elif index
== "org_uuid":
322 self
.org_uuid
= value
323 elif index
== "user":
325 elif index
== "passwd":
329 elif index
== "url_admin":
330 self
.url_admin
= value
332 raise KeyError("Invalid key '{}'".format(index
))
334 def connect_as_admin(self
):
335 """Method connect as pvdc admin user to vCloud director.
336 There are certain action that can be done only by provider vdc admin user.
337 Organization creation / provider network creation etc.
340 The return client object that latter can be used to connect to vcloud director as admin for provider vdc
342 self
.logger
.debug("Logging into vCD {} as admin.".format(self
.org_name
))
347 client_as_admin
= Client(
348 host
, verify_ssl_certs
=False, api_version
=API_VERSION
350 client_as_admin
.set_credentials(
351 BasicLoginCredentials(self
.admin_user
, org
, self
.admin_password
)
353 except Exception as e
:
354 raise vimconn
.VimConnException(
355 "Can't connect to vCloud director as: {} with exception {}".format(
360 return client_as_admin
363 """Method connect as normal user to vCloud director.
366 The return client object that latter can be used to connect to vCloud director as admin for VDC
370 "Logging into vCD {} as {} to datacenter {}.".format(
371 self
.org_name
, self
.user
, self
.org_name
375 client
= Client(host
, verify_ssl_certs
=False, api_version
=API_VERSION
)
376 client
.set_credentials(
377 BasicLoginCredentials(self
.user
, self
.org_name
, self
.passwd
)
379 except Exception as e
:
380 raise vimconn
.VimConnConnectionException(
381 "Can't connect to vCloud director org: "
382 "{} as user {} with exception: {}".format(self
.org_name
, self
.user
, e
)
387 def init_organization(self
):
388 """Method initialize organization UUID and VDC parameters.
390 At bare minimum client must provide organization name that present in vCloud director and VDC.
392 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
393 The Org - UUID will be initialized at the run time if data center present in vCloud director.
396 The return vca object that letter can be used to connect to vcloud direct as admin
398 client
= self
.connect()
401 raise vimconn
.VimConnConnectionException("Failed to connect vCD.")
405 if self
.org_uuid
is None:
406 org_list
= client
.get_org_list()
407 for org
in org_list
.Org
:
408 # we set org UUID at the init phase but we can do it only when we have valid credential.
409 if org
.get("name") == self
.org_name
:
410 self
.org_uuid
= org
.get("href").split("/")[-1]
412 "Setting organization UUID {}".format(self
.org_uuid
)
416 raise vimconn
.VimConnException(
417 "Vcloud director organization {} not found".format(
422 # if well good we require for org details
423 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
425 # we have two case if we want to initialize VDC ID or VDC name at run time
426 # tenant_name provided but no tenant id
428 self
.tenant_id
is None
429 and self
.tenant_name
is not None
430 and "vdcs" in org_details_dict
432 vdcs_dict
= org_details_dict
["vdcs"]
433 for vdc
in vdcs_dict
:
434 if vdcs_dict
[vdc
] == self
.tenant_name
:
437 "Setting vdc uuid {} for organization UUID {}".format(
438 self
.tenant_id
, self
.org_name
443 raise vimconn
.VimConnException(
444 "Tenant name indicated but not present in vcloud director."
447 # case two we have tenant_id but we don't have tenant name so we find and set it.
449 self
.tenant_id
is not None
450 and self
.tenant_name
is None
451 and "vdcs" in org_details_dict
453 vdcs_dict
= org_details_dict
["vdcs"]
454 for vdc
in vdcs_dict
:
455 if vdc
== self
.tenant_id
:
456 self
.tenant_name
= vdcs_dict
[vdc
]
458 "Setting vdc uuid {} for organization UUID {}".format(
459 self
.tenant_id
, self
.org_name
464 raise vimconn
.VimConnException(
465 "Tenant id indicated but not present in vcloud director"
468 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
469 except Exception as e
:
471 "Failed initialize organization UUID for org {}: {}".format(
475 self
.logger
.debug(traceback
.format_exc())
478 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
479 """Method adds a new tenant to VIM with this name.
480 This action requires access to create VDC action in vCloud director.
483 tenant_name is tenant_name to be created.
484 tenant_description not used for this call
487 returns the tenant identifier in UUID format.
488 If action is failed method will throw vimconn.VimConnException method
490 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
491 if vdc_task
is not None:
492 vdc_uuid
, _
= vdc_task
.popitem()
494 "Created new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
)
499 raise vimconn
.VimConnException(
500 "Failed create tenant {}".format(tenant_name
)
503 def delete_tenant(self
, tenant_id
=None):
504 """Delete a tenant from VIM
506 tenant_id is tenant_id to be deleted.
509 returns the tenant identifier in UUID format.
510 If action is failed method will throw exception
512 vca
= self
.connect_as_admin()
514 raise vimconn
.VimConnConnectionException("Failed to connect vCD")
516 if tenant_id
is not None:
519 url_list
= [self
.url
, "/api/vdc/", tenant_id
]
520 orgvdc_herf
= "".join(url_list
)
523 "Accept": "application/*+xml;version=" + API_VERSION
,
524 "x-vcloud-authorization": vca
._session
.headers
[
525 "x-vcloud-authorization"
528 response
= self
.perform_request(
529 req_type
="GET", url
=orgvdc_herf
, headers
=headers
532 if response
.status_code
!= requests
.codes
.ok
:
534 "delete_tenant():GET REST API call {} failed. "
535 "Return status code {}".format(
536 orgvdc_herf
, response
.status_code
540 raise vimconn
.VimConnNotFoundException(
541 "Fail to get tenant {}".format(tenant_id
)
544 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
547 for prefix
, uri
in lxmlroot_respond
.nsmap
.items()
550 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
551 vdc_remove_href
= lxmlroot_respond
.find(
552 "xmlns:Link[@rel='remove']", namespaces
554 vdc_remove_href
= vdc_remove_href
+ "?recursive=true&force=true"
556 response
= self
.perform_request(
557 req_type
="DELETE", url
=vdc_remove_href
, headers
=headers
560 if response
.status_code
== 202:
566 "delete_tenant(): DELETE REST API call {} failed. "
567 "Return status code {}".format(
568 vdc_remove_href
, response
.status_code
572 raise vimconn
.VimConnException(
573 "Fail to delete tenant with ID {}".format(tenant_id
)
577 "delete_tenant():Incorrect tenant ID {}".format(tenant_id
)
580 raise vimconn
.VimConnNotFoundException(
581 "Fail to get tenant {}".format(tenant_id
)
584 def get_tenant_list(self
, filter_dict
={}):
585 """Obtain tenants of VIM
586 filter_dict can contain the following keys:
587 name: filter by tenant name
588 id: filter by tenant uuid/id
590 Returns the tenant list of dictionaries:
591 [{'name':'<name>, 'id':'<id>, ...}, ...]
594 org_dict
= self
.get_org(self
.org_uuid
)
595 vdcs_dict
= org_dict
["vdcs"]
600 entry
= {"name": vdcs_dict
[k
], "id": k
}
601 # if caller didn't specify dictionary we return all tenants.
603 if filter_dict
is not None and filter_dict
:
604 filtered_entry
= entry
.copy()
605 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
607 for unwanted_key
in filtered_dict
:
608 del entry
[unwanted_key
]
610 if filter_dict
== entry
:
611 vdclist
.append(filtered_entry
)
613 vdclist
.append(entry
)
615 self
.logger
.debug("Error in get_tenant_list()")
616 self
.logger
.debug(traceback
.format_exc())
618 raise vimconn
.VimConnException("Incorrect state. {}")
628 provider_network_profile
=None,
630 """Adds a tenant network to VIM
632 'net_name': name of the network
634 'bridge': overlay isolated network
635 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
636 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
637 'ip_profile': is a dict containing the IP parameters of the network
638 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
639 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
640 'gateway_address': (Optional) ip_schema, that is X.X.X.X
641 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
642 'dhcp_enabled': True or False
643 'dhcp_start_address': ip_schema, first IP to grant
644 'dhcp_count': number of IPs to grant.
645 'shared': if this network can be seen/use by other tenants/organization
646 'provider_network_profile': (optional) contains {segmentation-id: vlan, provider-network: vim_netowrk}
647 Returns a tuple with the network identifier and created_items, or raises an exception on error
648 created_items can be None or a dictionary where this method can include key-values that will be passed to
649 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
650 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
655 "new_network tenant {} net_type {} ip_profile {} shared {} provider_network_profile {}".format(
656 net_name
, net_type
, ip_profile
, shared
, provider_network_profile
660 # if provider_network_profile:
661 # vlan = provider_network_profile.get("segmentation-id")
669 # ############# Stub code for SRIOV #################
670 # if net_type == "data" or net_type == "ptp":
671 # if self.config.get('dv_switch_name') == None:
672 # raise vimconn.VimConnConflictException("You must provide 'dv_switch_name' at config value")
673 # network_uuid = self.create_dvPort_group(net_name)
674 parent_network_uuid
= None
676 if provider_network_profile
is not None:
677 for k
, v
in provider_network_profile
.items():
678 if k
== "physical_network":
679 parent_network_uuid
= self
.get_physical_network_by_name(v
)
681 network_uuid
= self
.create_network(
682 network_name
=net_name
,
684 ip_profile
=ip_profile
,
686 parent_network_uuid
=parent_network_uuid
,
689 if network_uuid
is not None:
690 return network_uuid
, created_items
692 raise vimconn
.VimConnUnexpectedResponse(
693 "Failed create a new network {}".format(net_name
)
696 def get_network_list(self
, filter_dict
={}):
697 """Obtain tenant networks of VIM
699 name: network name OR/AND
700 id: network uuid OR/AND
701 shared: boolean OR/AND
702 tenant_id: tenant OR/AND
703 admin_state_up: boolean
706 [{key : value , key : value}]
708 Returns the network list of dictionaries:
709 [{<the fields at Filter_dict plus some VIM specific>}, ...]
714 "get_network_list(): retrieving network list for vcd {}".format(
719 if not self
.tenant_name
:
720 raise vimconn
.VimConnConnectionException("Tenant name is empty.")
722 _
, vdc
= self
.get_vdc_details()
724 raise vimconn
.VimConnConnectionException(
725 "Can't retrieve information for a VDC {}.".format(self
.tenant_name
)
729 vdcid
= vdc
.get("id").split(":")[3]
731 if self
.client
._session
:
733 "Accept": "application/*+xml;version=" + API_VERSION
,
734 "x-vcloud-authorization": self
.client
._session
.headers
[
735 "x-vcloud-authorization"
738 response
= self
.perform_request(
739 req_type
="GET", url
=vdc
.get("href"), headers
=headers
742 if response
.status_code
!= 200:
743 self
.logger
.error("Failed to get vdc content")
744 raise vimconn
.VimConnNotFoundException("Failed to get vdc content")
746 content
= XmlElementTree
.fromstring(response
.text
)
750 if item
.tag
.split("}")[-1] == "AvailableNetworks":
752 response
= self
.perform_request(
753 req_type
="GET", url
=net
.get("href"), headers
=headers
756 if response
.status_code
!= 200:
757 self
.logger
.error("Failed to get network content")
758 raise vimconn
.VimConnNotFoundException(
759 "Failed to get network content"
762 net_details
= XmlElementTree
.fromstring(response
.text
)
765 net_uuid
= net_details
.get("id").split(":")
767 if len(net_uuid
) != 4:
770 net_uuid
= net_uuid
[3]
773 "get_network_list(): Adding net {}"
774 " to a list vcd id {} network {}".format(
775 net_uuid
, vdcid
, net_details
.get("name")
778 filter_entry
["name"] = net_details
.get("name")
779 filter_entry
["id"] = net_uuid
784 if i
.tag
.split("}")[-1] == "IsShared"
790 filter_entry
["shared"] = shared
791 filter_entry
["tenant_id"] = vdcid
793 if int(net_details
.get("status")) == 1:
794 filter_entry
["admin_state_up"] = True
796 filter_entry
["admin_state_up"] = False
798 filter_entry
["status"] = "ACTIVE"
799 filter_entry
["type"] = "bridge"
800 filtered_entry
= filter_entry
.copy()
802 if filter_dict
is not None and filter_dict
:
803 # we remove all the key : value we don't care and match only
805 filtered_dict
= set(filter_entry
.keys()) - set(
809 for unwanted_key
in filtered_dict
:
810 del filter_entry
[unwanted_key
]
812 if filter_dict
== filter_entry
:
813 network_list
.append(filtered_entry
)
815 network_list
.append(filtered_entry
)
816 except Exception as e
:
817 self
.logger
.debug("Error in get_network_list", exc_info
=True)
819 if isinstance(e
, vimconn
.VimConnException
):
822 raise vimconn
.VimConnNotFoundException(
823 "Failed : Networks list not found {} ".format(e
)
826 self
.logger
.debug("Returning {}".format(network_list
))
830 def get_network(self
, net_id
):
831 """Method obtains network details of net_id VIM network
832 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]
835 _
, vdc
= self
.get_vdc_details()
836 vdc_id
= vdc
.get("id").split(":")[3]
838 if self
.client
._session
:
840 "Accept": "application/*+xml;version=" + API_VERSION
,
841 "x-vcloud-authorization": self
.client
._session
.headers
[
842 "x-vcloud-authorization"
845 response
= self
.perform_request(
846 req_type
="GET", url
=vdc
.get("href"), headers
=headers
849 if response
.status_code
!= 200:
850 self
.logger
.error("Failed to get vdc content")
851 raise vimconn
.VimConnNotFoundException("Failed to get vdc content")
853 content
= XmlElementTree
.fromstring(response
.text
)
858 if item
.tag
.split("}")[-1] == "AvailableNetworks":
860 response
= self
.perform_request(
861 req_type
="GET", url
=net
.get("href"), headers
=headers
864 if response
.status_code
!= 200:
865 self
.logger
.error("Failed to get network content")
866 raise vimconn
.VimConnNotFoundException(
867 "Failed to get network content"
870 net_details
= XmlElementTree
.fromstring(response
.text
)
872 vdc_network_id
= net_details
.get("id").split(":")
873 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
874 filter_dict
["name"] = net_details
.get("name")
875 filter_dict
["id"] = vdc_network_id
[3]
880 if i
.tag
.split("}")[-1] == "IsShared"
886 filter_dict
["shared"] = shared
887 filter_dict
["tenant_id"] = vdc_id
889 if int(net_details
.get("status")) == 1:
890 filter_dict
["admin_state_up"] = True
892 filter_dict
["admin_state_up"] = False
894 filter_dict
["status"] = "ACTIVE"
895 filter_dict
["type"] = "bridge"
896 self
.logger
.debug("Returning {}".format(filter_dict
))
900 raise vimconn
.VimConnNotFoundException(
901 "Network {} not found".format(net_id
)
903 except Exception as e
:
904 self
.logger
.debug("Error in get_network")
905 self
.logger
.debug(traceback
.format_exc())
907 if isinstance(e
, vimconn
.VimConnException
):
910 raise vimconn
.VimConnNotFoundException(
911 "Failed : Network not found {} ".format(e
)
916 def delete_network(self
, net_id
, created_items
=None):
918 Removes a tenant network from VIM and its associated elements
919 :param net_id: VIM identifier of the network, provided by method new_network
920 :param created_items: dictionary with extra items to be deleted. provided by method new_network
921 Returns the network identifier or raises an exception upon error or when network is not found
923 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
924 if vcd_network
is not None and vcd_network
:
925 if self
.delete_network_action(network_uuid
=net_id
):
928 raise vimconn
.VimConnNotFoundException(
929 "Network {} not found".format(net_id
)
932 def refresh_nets_status(self
, net_list
):
933 """Get the status of the networks
934 Params: the list of network identifiers
935 Returns a dictionary with:
936 net_id: #VIM id of this network
937 status: #Mandatory. Text with one of:
938 # DELETED (not found at vim)
939 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
940 # OTHER (Vim reported other status not understood)
941 # ERROR (VIM indicates an ERROR status)
942 # ACTIVE, INACTIVE, DOWN (admin down),
943 # BUILD (on building process)
945 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
946 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
953 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
954 if vcd_network
is not None and vcd_network
:
955 if vcd_network
["status"] == "1":
961 errormsg
= "Network not found."
965 "error_msg": errormsg
,
966 "vim_info": yaml
.safe_dump(vcd_network
),
969 self
.logger
.debug("Error in refresh_nets_status")
970 self
.logger
.debug(traceback
.format_exc())
974 def get_flavor(self
, flavor_id
):
975 """Obtain flavor details from the VIM
976 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
978 if flavor_id
not in vimconnector
.flavorlist
:
979 raise vimconn
.VimConnNotFoundException("Flavor not found.")
981 return vimconnector
.flavorlist
[flavor_id
]
983 def new_flavor(self
, flavor_data
):
984 """Adds a tenant flavor to VIM
985 flavor_data contains a dictionary with information, keys:
987 ram: memory (cloud type) in MBytes
988 vpcus: cpus (cloud type)
989 extended: EPA parameters
990 - numas: #items requested in same NUMA
991 memory: number of 1G huge pages memory
992 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual
994 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
995 - name: interface name
996 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
997 bandwidth: X Gbps; requested guarantee bandwidth
998 vpci: requested virtual PCI address
1002 Returns the flavor identifier"""
1004 # generate a new uuid put to internal dict and return it.
1005 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
1006 new_flavor
= flavor_data
1007 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
1008 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
1009 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 0)
1011 if not isinstance(ram
, int):
1012 raise vimconn
.VimConnException("Non-integer value for ram")
1013 elif not isinstance(cpu
, int):
1014 raise vimconn
.VimConnException("Non-integer value for cpu")
1015 elif not isinstance(disk
, int):
1016 raise vimconn
.VimConnException("Non-integer value for disk")
1018 extended_flv
= flavor_data
.get("extended")
1020 numas
= extended_flv
.get("numas")
1023 # overwrite ram and vcpus
1024 if "memory" in numa
:
1025 ram
= numa
["memory"] * 1024
1027 if "paired-threads" in numa
:
1028 cpu
= numa
["paired-threads"] * 2
1029 elif "cores" in numa
:
1031 elif "threads" in numa
:
1032 cpu
= numa
["threads"]
1034 new_flavor
[FLAVOR_RAM_KEY
] = ram
1035 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
1036 new_flavor
[FLAVOR_DISK_KEY
] = disk
1037 # generate a new uuid put to internal dict and return it.
1038 flavor_id
= uuid
.uuid4()
1039 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
1040 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
1042 return str(flavor_id
)
1044 def delete_flavor(self
, flavor_id
):
1045 """Deletes a tenant flavor from VIM identify by its id
1047 Returns the used id or raise an exception
1049 if flavor_id
not in vimconnector
.flavorlist
:
1050 raise vimconn
.VimConnNotFoundException("Flavor not found.")
1052 vimconnector
.flavorlist
.pop(flavor_id
, None)
1056 def new_image(self
, image_dict
):
1058 Adds a tenant image to VIM
1060 200, image-id if the image is created
1061 <0, message if there is an error
1063 return self
.get_image_id_from_path(image_dict
["location"])
1065 def delete_image(self
, image_id
):
1067 Deletes a tenant image from VIM
1069 image_id is ID of Image to be deleted
1071 returns the image identifier in UUID format or raises an exception on error
1073 conn
= self
.connect_as_admin()
1076 raise vimconn
.VimConnConnectionException("Failed to connect vCD")
1078 # Get Catalog details
1079 url_list
= [self
.url
, "/api/catalog/", image_id
]
1080 catalog_herf
= "".join(url_list
)
1083 "Accept": "application/*+xml;version=" + API_VERSION
,
1084 "x-vcloud-authorization": conn
._session
.headers
["x-vcloud-authorization"],
1087 response
= self
.perform_request(
1088 req_type
="GET", url
=catalog_herf
, headers
=headers
1091 if response
.status_code
!= requests
.codes
.ok
:
1093 "delete_image():GET REST API call {} failed. "
1094 "Return status code {}".format(catalog_herf
, response
.status_code
)
1097 raise vimconn
.VimConnNotFoundException(
1098 "Fail to get image {}".format(image_id
)
1101 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
1103 prefix
: uri
for prefix
, uri
in lxmlroot_respond
.nsmap
.items() if prefix
1105 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
1107 catalogItems_section
= lxmlroot_respond
.find("xmlns:CatalogItems", namespaces
)
1108 catalogItems
= catalogItems_section
.iterfind("xmlns:CatalogItem", namespaces
)
1110 for catalogItem
in catalogItems
:
1111 catalogItem_href
= catalogItem
.attrib
["href"]
1113 response
= self
.perform_request(
1114 req_type
="GET", url
=catalogItem_href
, headers
=headers
1117 if response
.status_code
!= requests
.codes
.ok
:
1119 "delete_image():GET REST API call {} failed. "
1120 "Return status code {}".format(catalog_herf
, response
.status_code
)
1122 raise vimconn
.VimConnNotFoundException(
1123 "Fail to get catalogItem {} for catalog {}".format(
1124 catalogItem
, image_id
1128 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
1130 prefix
: uri
for prefix
, uri
in lxmlroot_respond
.nsmap
.items() if prefix
1132 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
1133 catalogitem_remove_href
= lxmlroot_respond
.find(
1134 "xmlns:Link[@rel='remove']", namespaces
1137 # Remove catalogItem
1138 response
= self
.perform_request(
1139 req_type
="DELETE", url
=catalogitem_remove_href
, headers
=headers
1142 if response
.status_code
== requests
.codes
.no_content
:
1143 self
.logger
.debug("Deleted Catalog item {}".format(catalogItem
))
1145 raise vimconn
.VimConnException(
1146 "Fail to delete Catalog Item {}".format(catalogItem
)
1150 url_list
= [self
.url
, "/api/admin/catalog/", image_id
]
1151 catalog_remove_herf
= "".join(url_list
)
1152 response
= self
.perform_request(
1153 req_type
="DELETE", url
=catalog_remove_herf
, headers
=headers
1156 if response
.status_code
== requests
.codes
.no_content
:
1157 self
.logger
.debug("Deleted Catalog {}".format(image_id
))
1161 raise vimconn
.VimConnException("Fail to delete Catalog {}".format(image_id
))
1163 def catalog_exists(self
, catalog_name
, catalogs
):
1166 :param catalog_name:
1170 for catalog
in catalogs
:
1171 if catalog
["name"] == catalog_name
:
1172 return catalog
["id"]
1174 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
1175 """Create new catalog entry in vCloud director.
1178 vca: vCloud director.
1179 catalog_name catalog that client wish to create. Note no validation done for a name.
1180 Client must make sure that provide valid string representation.
1182 Returns catalog id if catalog created else None.
1186 lxml_catalog_element
= vca
.create_catalog(catalog_name
, catalog_name
)
1188 if lxml_catalog_element
:
1189 id_attr_value
= lxml_catalog_element
.get("id")
1190 return id_attr_value
.split(":")[-1]
1192 catalogs
= vca
.list_catalogs()
1193 except Exception as ex
:
1195 'create_vimcatalog(): Creation of catalog "{}" failed with error: {}'.format(
1200 return self
.catalog_exists(catalog_name
, catalogs
)
1202 # noinspection PyIncorrectDocstring
1208 media_file_name
=None,
1211 chunk_bytes
=128 * 1024,
1214 Uploads a OVF file to a vCloud catalog
1221 :param catalog_name: (str): The name of the catalog to upload the media.
1222 :param media_file_name: (str): The name of the local media file to upload.
1223 :return: (bool) True if the media file was successfully uploaded, false otherwise.
1225 os
.path
.isfile(media_file_name
)
1226 statinfo
= os
.stat(media_file_name
)
1228 # find a catalog entry where we upload OVF.
1229 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
1231 # if VCD can parse OVF we upload VMDK file
1233 for catalog
in vca
.list_catalogs():
1234 if catalog_name
!= catalog
["name"]:
1236 catalog_href
= "{}/api/catalog/{}/action/upload".format(
1237 self
.url
, catalog
["id"]
1240 <UploadVAppTemplateParams name="{}"
1241 xmlns="http://www.vmware.com/vcloud/v1.5"
1242 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1">
1243 <Description>{} vApp Template</Description>
1244 </UploadVAppTemplateParams>
1246 catalog_name
, description
1251 "Accept": "application/*+xml;version=" + API_VERSION
,
1252 "x-vcloud-authorization": self
.client
._session
.headers
[
1253 "x-vcloud-authorization"
1258 ] = "application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml"
1260 response
= self
.perform_request(
1261 req_type
="POST", url
=catalog_href
, headers
=headers
, data
=data
1264 if response
.status_code
== requests
.codes
.created
:
1265 catalogItem
= XmlElementTree
.fromstring(response
.text
)
1268 for child
in catalogItem
1269 if child
.get("type")
1270 == "application/vnd.vmware.vcloud.vAppTemplate+xml"
1272 href
= entity
.get("href")
1275 response
= self
.perform_request(
1276 req_type
="GET", url
=href
, headers
=headers
1279 if response
.status_code
== requests
.codes
.ok
:
1280 headers
["Content-Type"] = "Content-Type text/xml"
1282 'rel="upload:default"\shref="(.*?\/descriptor.ovf)"',
1287 transfer_href
= result
.group(1)
1289 response
= self
.perform_request(
1293 data
=open(media_file_name
, "rb"),
1296 if response
.status_code
!= requests
.codes
.ok
:
1298 "Failed create vApp template for catalog name {} and image {}".format(
1299 catalog_name
, media_file_name
1304 # TODO fix this with aync block
1308 "vApp template for catalog name {} and image {}".format(
1309 catalog_name
, media_file_name
1313 # uploading VMDK file
1314 # check status of OVF upload and upload remaining files.
1315 response
= self
.perform_request(
1316 req_type
="GET", url
=template
, headers
=headers
1319 if response
.status_code
== requests
.codes
.ok
:
1321 'rel="upload:default"\s*href="(.*?vmdk)"', response
.text
1325 link_href
= result
.group(1)
1327 # we skip ovf since it already uploaded.
1328 if "ovf" in link_href
:
1331 # The OVF file and VMDK must be in a same directory
1332 head
, _
= os
.path
.split(media_file_name
)
1333 file_vmdk
= head
+ "/" + link_href
.split("/")[-1]
1335 if not os
.path
.isfile(file_vmdk
):
1338 statinfo
= os
.stat(file_vmdk
)
1339 if statinfo
.st_size
== 0:
1342 hrefvmdk
= link_href
1353 FileTransferSpeed(),
1355 progress_bar
= ProgressBar(
1356 widgets
=widgets
, maxval
=statinfo
.st_size
1359 bytes_transferred
= 0
1360 f
= open(file_vmdk
, "rb")
1362 while bytes_transferred
< statinfo
.st_size
:
1363 my_bytes
= f
.read(chunk_bytes
)
1364 if len(my_bytes
) <= chunk_bytes
:
1365 headers
["Content-Range"] = "bytes {}-{}/{}".format(
1370 headers
["Content-Length"] = str(len(my_bytes
))
1371 response
= requests
.put(
1378 if response
.status_code
== requests
.codes
.ok
:
1379 bytes_transferred
+= len(my_bytes
)
1381 progress_bar
.update(bytes_transferred
)
1384 "file upload failed with error: [{}] {}".format(
1385 response
.status_code
, response
.text
1395 progress_bar
.finish()
1401 "Failed retrieve vApp template for catalog name {} for OVF {}".format(
1402 catalog_name
, media_file_name
1406 except Exception as exp
:
1408 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}".format(
1409 catalog_name
, media_file_name
, exp
1413 raise vimconn
.VimConnException(
1414 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}".format(
1415 catalog_name
, media_file_name
, exp
1420 "Failed retrieve catalog name {} for OVF file {}".format(
1421 catalog_name
, media_file_name
1427 def upload_vimimage(
1432 medial_file_name
=None,
1435 """Upload media file"""
1436 # TODO add named parameters for readability
1437 return self
.upload_ovf(
1439 catalog_name
=catalog_name
,
1440 image_name
=media_name
.split(".")[0],
1441 media_file_name
=medial_file_name
,
1442 description
="medial_file_name",
1446 def validate_uuid4(self
, uuid_string
=None):
1447 """Method validate correct format of UUID.
1449 Return: true if string represent valid uuid
1452 uuid
.UUID(uuid_string
, version
=4)
1458 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
1459 """Method check catalog and return catalog ID in UUID format.
1462 catalog_name: catalog name as string
1463 catalogs: list of catalogs.
1465 Return: catalogs uuid
1467 for catalog
in catalogs
:
1468 if catalog
["name"] == catalog_name
:
1469 catalog_id
= catalog
["id"]
1474 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1475 """Method check catalog and return catalog name lookup done by catalog UUID.
1478 catalog_name: catalog name as string
1479 catalogs: list of catalogs.
1481 Return: catalogs name or None
1483 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1486 for catalog
in catalogs
:
1487 catalog_id
= catalog
.get("id")
1489 if catalog_id
== catalog_uuid
:
1490 return catalog
.get("name")
1494 def get_catalog_obj(self
, catalog_uuid
=None, catalogs
=None):
1495 """Method check catalog and return catalog name lookup done by catalog UUID.
1498 catalog_name: catalog name as string
1499 catalogs: list of catalogs.
1501 Return: catalogs name or None
1503 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1506 for catalog
in catalogs
:
1507 catalog_id
= catalog
.get("id")
1509 if catalog_id
== catalog_uuid
:
1514 def get_image_id_from_path(self
, path
=None, progress
=False):
1515 """Method upload OVF image to vCloud director.
1517 Each OVF image represented as single catalog entry in vcloud director.
1518 The method check for existing catalog entry. The check done by file name without file extension.
1520 if given catalog name already present method will respond with existing catalog uuid otherwise
1521 it will create new catalog entry and upload OVF file to newly created catalog.
1523 If method can't create catalog entry or upload a file it will throw exception.
1525 Method accept boolean flag progress that will output progress bar. It useful method
1526 for standalone upload use case. In case to test large file upload.
1529 path: - valid path to OVF file.
1530 progress - boolean progress bar show progress bar.
1532 Return: if image uploaded correct method will provide image catalog UUID.
1535 raise vimconn
.VimConnException("Image path can't be None.")
1537 if not os
.path
.isfile(path
):
1538 raise vimconn
.VimConnException("Can't read file. File not found.")
1540 if not os
.access(path
, os
.R_OK
):
1541 raise vimconn
.VimConnException(
1542 "Can't read file. Check file permission to read."
1545 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1547 _
, filename
= os
.path
.split(path
)
1548 _
, file_extension
= os
.path
.splitext(path
)
1549 if file_extension
!= ".ovf":
1551 "Wrong file extension {} connector support only OVF container.".format(
1556 raise vimconn
.VimConnException(
1557 "Wrong container. vCloud director supports only OVF."
1560 catalog_name
= os
.path
.splitext(filename
)[0]
1561 catalog_md5_name
= hashlib
.md5(path
.encode("utf-8")).hexdigest()
1563 "File name {} Catalog Name {} file path {} "
1564 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
)
1568 org
, _
= self
.get_vdc_details()
1569 catalogs
= org
.list_catalogs()
1570 except Exception as exp
:
1571 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1573 raise vimconn
.VimConnException(
1574 "Failed get catalogs() with Exception {} ".format(exp
)
1577 if len(catalogs
) == 0:
1579 "Creating a new catalog entry {} in vcloud director".format(
1584 if self
.create_vimcatalog(org
, catalog_md5_name
) is None:
1585 raise vimconn
.VimConnException(
1586 "Failed create new catalog {} ".format(catalog_md5_name
)
1589 result
= self
.upload_vimimage(
1591 catalog_name
=catalog_md5_name
,
1592 media_name
=filename
,
1593 medial_file_name
=path
,
1598 raise vimconn
.VimConnException(
1599 "Failed create vApp template for catalog {} ".format(catalog_name
)
1602 return self
.get_catalogid(catalog_name
, catalogs
)
1604 for catalog
in catalogs
:
1605 # search for existing catalog if we find same name we return ID
1606 # TODO optimize this
1607 if catalog
["name"] == catalog_md5_name
:
1609 "Found existing catalog entry for {} "
1610 "catalog id {}".format(
1611 catalog_name
, self
.get_catalogid(catalog_md5_name
, catalogs
)
1615 return self
.get_catalogid(catalog_md5_name
, catalogs
)
1617 # if we didn't find existing catalog we create a new one and upload image.
1619 "Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
)
1621 if self
.create_vimcatalog(org
, catalog_md5_name
) is None:
1622 raise vimconn
.VimConnException(
1623 "Failed create new catalog {} ".format(catalog_md5_name
)
1626 result
= self
.upload_vimimage(
1628 catalog_name
=catalog_md5_name
,
1629 media_name
=filename
,
1630 medial_file_name
=path
,
1634 raise vimconn
.VimConnException(
1635 "Failed create vApp template for catalog {} ".format(catalog_md5_name
)
1638 return self
.get_catalogid(catalog_md5_name
, org
.list_catalogs())
1640 def get_image_list(self
, filter_dict
={}):
1641 """Obtain tenant images from VIM
1645 checksum: image checksum
1646 location: image path
1647 Returns the image list of dictionaries:
1648 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1652 org
, _
= self
.get_vdc_details()
1654 catalogs
= org
.list_catalogs()
1656 if len(catalogs
) == 0:
1659 for catalog
in catalogs
:
1660 catalog_uuid
= catalog
.get("id")
1661 name
= catalog
.get("name")
1664 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1667 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1670 filtered_dict
["name"] = name
1671 filtered_dict
["id"] = catalog_uuid
1672 image_list
.append(filtered_dict
)
1675 "List of already created catalog items: {}".format(image_list
)
1679 except Exception as exp
:
1680 raise vimconn
.VimConnException(
1681 "Exception occured while retriving catalog items {}".format(exp
)
1684 def get_namebyvappid(self
, vapp_uuid
=None):
1685 """Method returns vApp name from vCD and lookup done by vapp_id.
1688 vapp_uuid: vappid is application identifier
1691 The return vApp name otherwise None
1694 if self
.client
and vapp_uuid
:
1695 vapp_call
= "{}/api/vApp/vapp-{}".format(self
.url
, vapp_uuid
)
1697 "Accept": "application/*+xml;version=" + API_VERSION
,
1698 "x-vcloud-authorization": self
.client
._session
.headers
[
1699 "x-vcloud-authorization"
1703 response
= self
.perform_request(
1704 req_type
="GET", url
=vapp_call
, headers
=headers
1707 # Retry login if session expired & retry sending request
1708 if response
.status_code
== 403:
1709 response
= self
.retry_rest("GET", vapp_call
)
1711 tree
= XmlElementTree
.fromstring(response
.text
)
1713 return tree
.attrib
["name"] if "name" in tree
.attrib
else None
1714 except Exception as e
:
1715 self
.logger
.exception(e
)
1728 affinity_group_list
=[],
1732 availability_zone_index
=None,
1733 availability_zone_list
=None,
1735 """Adds a VM instance to VIM
1737 'start': (boolean) indicates if VM must start or created in pause mode.
1738 'image_id','flavor_id': image and flavor VIM id to use for the VM
1739 'net_list': list of interfaces, each one is a dictionary with:
1740 'name': (optional) name for the interface.
1741 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
1742 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM
1744 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
1745 'mac_address': (optional) mac address to assign to this interface
1746 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not
1747 provided, the VLAN tag to be used. In case net_id is provided, the internal network vlan is used
1749 'type': (mandatory) can be one of:
1750 'virtual', in this case always connected to a network of type 'net_type=bridge'
1751 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a
1752 data/ptp network or it can created unconnected
1753 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
1754 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
1755 are allocated on the same physical NIC
1756 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
1757 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
1758 or True, it must apply the default VIM behaviour
1759 After execution the method will add the key:
1760 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
1761 interface. 'net_list' is modified
1762 'cloud_config': (optional) dictionary with:
1763 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1764 'users': (optional) list of users to be inserted, each item is a dict with:
1765 'name': (mandatory) user name,
1766 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1767 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
1768 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
1769 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1770 'dest': (mandatory) string with the destination absolute path
1771 'encoding': (optional, by default text). Can be one of:
1772 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1773 'content' (mandatory): string with the content of the file
1774 'permissions': (optional) string with file permissions, typically octal notation '0644'
1775 'owner': (optional) file owner, string with the format 'owner:group'
1776 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
1777 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1778 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1779 'size': (mandatory) string with the size of the disk in GB
1780 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1781 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1782 availability_zone_index is None
1783 Returns a tuple with the instance identifier and created_items or raises an exception on error
1784 created_items can be None or a dictionary where this method can include key-values that will be passed to
1785 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1786 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1789 self
.logger
.info("Creating new instance for entry {}".format(name
))
1791 "desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {} "
1792 "availability_zone_index {} availability_zone_list {}".format(
1800 availability_zone_index
,
1801 availability_zone_list
,
1805 # new vm name = vmname + tenant_id + uuid
1806 new_vm_name
= [name
, "-", str(uuid
.uuid4())]
1807 vmname_andid
= "".join(new_vm_name
)
1809 for net
in net_list
:
1810 if net
["type"] == "PCI-PASSTHROUGH":
1811 raise vimconn
.VimConnNotSupportedException(
1812 "Current vCD version does not support type : {}".format(net
["type"])
1815 if len(net_list
) > 10:
1816 raise vimconn
.VimConnNotSupportedException(
1817 "The VM hardware versions 7 and above support upto 10 NICs only"
1820 # if vm already deployed we return existing uuid
1821 # we check for presence of VDC, Catalog entry and Flavor.
1822 org
, vdc
= self
.get_vdc_details()
1824 raise vimconn
.VimConnNotFoundException(
1825 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(
1830 catalogs
= org
.list_catalogs()
1831 if catalogs
is None:
1832 # Retry once, if failed by refreshing token
1834 org
= Org(self
.client
, resource
=self
.client
.get_org())
1835 catalogs
= org
.list_catalogs()
1837 if catalogs
is None:
1838 raise vimconn
.VimConnNotFoundException(
1839 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(
1844 catalog_hash_name
= self
.get_catalogbyid(
1845 catalog_uuid
=image_id
, catalogs
=catalogs
1847 if catalog_hash_name
:
1849 "Found catalog entry {} for image id {}".format(
1850 catalog_hash_name
, image_id
1854 raise vimconn
.VimConnNotFoundException(
1855 "new_vminstance(): Failed create vApp {}: "
1856 "(Failed retrieve catalog information {})".format(name
, image_id
)
1859 # Set vCPU and Memory based on flavor.
1865 if flavor_id
is not None:
1866 if flavor_id
not in vimconnector
.flavorlist
:
1867 raise vimconn
.VimConnNotFoundException(
1868 "new_vminstance(): Failed create vApp {}: "
1869 "Failed retrieve flavor information "
1870 "flavor id {}".format(name
, flavor_id
)
1874 flavor
= vimconnector
.flavorlist
[flavor_id
]
1875 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1876 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1877 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1878 extended
= flavor
.get("extended", None)
1881 numas
= extended
.get("numas", None)
1882 except Exception as exp
:
1883 raise vimconn
.VimConnException(
1884 "Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
)
1887 # image upload creates template name as catalog name space Template.
1888 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1889 # power_on = 'false'
1893 # client must provide at least one entry in net_list if not we report error
1894 # If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1895 # If no mgmt, then the 1st NN in netlist is considered as primary net.
1897 primary_netname
= None
1898 primary_net_href
= None
1899 # network_mode = 'bridged'
1900 if net_list
is not None and len(net_list
) > 0:
1901 for net
in net_list
:
1902 if "use" in net
and net
["use"] == "mgmt" and not primary_net
:
1905 if primary_net
is None:
1906 primary_net
= net_list
[0]
1909 primary_net_id
= primary_net
["net_id"]
1910 url_list
= [self
.url
, "/api/network/", primary_net_id
]
1911 primary_net_href
= "".join(url_list
)
1912 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1914 if "name" in network_dict
:
1915 primary_netname
= network_dict
["name"]
1917 raise vimconn
.VimConnException(
1918 "Corrupted flavor. {}".format(primary_net
)
1921 raise vimconn
.VimConnUnexpectedResponse(
1922 "new_vminstance(): Failed network list is empty."
1925 # use: 'data', 'bridge', 'mgmt'
1926 # create vApp. Set vcpu and ram based on flavor id.
1928 vdc_obj
= VDC(self
.client
, resource
=org
.get_vdc(self
.tenant_name
))
1930 raise vimconn
.VimConnNotFoundException(
1931 "new_vminstance(): Failed to get VDC object"
1934 for retry
in (1, 2):
1935 items
= org
.get_catalog_item(catalog_hash_name
, catalog_hash_name
)
1936 catalog_items
= [items
.attrib
]
1938 if len(catalog_items
) == 1:
1941 "Accept": "application/*+xml;version=" + API_VERSION
,
1942 "x-vcloud-authorization": self
.client
._session
.headers
[
1943 "x-vcloud-authorization"
1947 response
= self
.perform_request(
1949 url
=catalog_items
[0].get("href"),
1952 catalogItem
= XmlElementTree
.fromstring(response
.text
)
1955 for child
in catalogItem
1956 if child
.get("type")
1957 == "application/vnd.vmware.vcloud.vAppTemplate+xml"
1959 vapp_tempalte_href
= entity
.get("href")
1961 response
= self
.perform_request(
1962 req_type
="GET", url
=vapp_tempalte_href
, headers
=headers
1965 if response
.status_code
!= requests
.codes
.ok
:
1967 "REST API call {} failed. Return status code {}".format(
1968 vapp_tempalte_href
, response
.status_code
1972 result
= (response
.text
).replace("\n", " ")
1974 vapp_template_tree
= XmlElementTree
.fromstring(response
.text
)
1975 children_element
= [
1976 child
for child
in vapp_template_tree
if "Children" in child
.tag
1978 vm_element
= [child
for child
in children_element
if "Vm" in child
.tag
][
1981 vm_name
= vm_element
.get("name")
1982 vm_id
= vm_element
.get("id")
1983 vm_href
= vm_element
.get("href")
1985 # cpus = re.search('<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>',
1987 memory_mb
= re
.search(
1988 "<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>",
1991 # cores = re.search('<vmw:CoresPerSocket ovf:required.*?>(\d+)</vmw:CoresPerSocket>', result).group(1)
1995 ] = "application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml"
1996 vdc_id
= vdc
.get("id").split(":")[-1]
1997 instantiate_vapp_href
= (
1998 "{}/api/vdc/{}/action/instantiateVAppTemplate".format(
2005 os
.path
.dirname(__file__
), "InstantiateVAppTemplateParams.xml"
2011 data
= template
.format(
2025 response
= self
.perform_request(
2027 url
=instantiate_vapp_href
,
2032 if response
.status_code
!= 201:
2034 "REST call {} failed reason : {}"
2035 "status code : {}".format(
2036 instantiate_vapp_href
, response
.text
, response
.status_code
2039 raise vimconn
.VimConnException(
2040 "new_vminstance(): Failed to create"
2041 "vAapp {}".format(vmname_andid
)
2044 vapptask
= self
.get_task_from_response(response
.text
)
2046 if vapptask
is None and retry
== 1:
2047 self
.get_token() # Retry getting token
2052 if vapptask
is None or vapptask
is False:
2053 raise vimconn
.VimConnUnexpectedResponse(
2054 "new_vminstance(): failed to create vApp {}".format(vmname_andid
)
2057 # wait for task to complete
2058 result
= self
.client
.get_task_monitor().wait_for_success(task
=vapptask
)
2060 if result
.get("status") == "success":
2062 "new_vminstance(): Sucessfully created Vapp {}".format(vmname_andid
)
2065 raise vimconn
.VimConnUnexpectedResponse(
2066 "new_vminstance(): failed to create vApp {}".format(vmname_andid
)
2068 except Exception as exp
:
2069 raise vimconn
.VimConnUnexpectedResponse(
2070 "new_vminstance(): failed to create vApp {} with Exception:{}".format(
2075 # we should have now vapp in undeployed state.
2077 vdc_obj
= VDC(self
.client
, href
=vdc
.get("href"))
2078 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
2079 vapp_uuid
= vapp_resource
.get("id").split(":")[-1]
2080 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2081 except Exception as exp
:
2082 raise vimconn
.VimConnUnexpectedResponse(
2083 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}".format(
2088 if vapp_uuid
is None:
2089 raise vimconn
.VimConnUnexpectedResponse(
2090 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
2095 # Add PCI passthrough/SRIOV configrations
2096 pci_devices_info
= []
2097 reserve_memory
= False
2099 for net
in net_list
:
2100 if net
["type"] == "PF" or net
["type"] == "PCI-PASSTHROUGH":
2101 pci_devices_info
.append(net
)
2104 or net
["type"] == "SR-IOV"
2105 or net
["type"] == "VFnotShared"
2106 ) and "net_id" in net
:
2107 reserve_memory
= True
2110 if len(pci_devices_info
) > 0:
2112 "Need to add PCI devices {} into VM {}".format(
2113 pci_devices_info
, vmname_andid
2116 PCI_devices_status
, _
, _
= self
.add_pci_devices(
2117 vapp_uuid
, pci_devices_info
, vmname_andid
2120 if PCI_devices_status
:
2122 "Added PCI devives {} to VM {}".format(
2123 pci_devices_info
, vmname_andid
2126 reserve_memory
= True
2129 "Fail to add PCI devives {} to VM {}".format(
2130 pci_devices_info
, vmname_andid
2134 # Add serial console - this allows cloud images to boot as if we are running under OpenStack
2135 self
.add_serial_device(vapp_uuid
)
2138 # Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
2139 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
2141 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
2143 # Add new or existing disks to vApp
2145 added_existing_disk
= False
2146 for disk
in disk_list
:
2147 if "device_type" in disk
and disk
["device_type"] == "cdrom":
2148 image_id
= disk
["image_id"]
2149 # Adding CD-ROM to VM
2150 # will revisit code once specification ready to support this feature
2151 self
.insert_media_to_vm(vapp
, image_id
)
2152 elif "image_id" in disk
and disk
["image_id"] is not None:
2154 "Adding existing disk from image {} to vm {} ".format(
2155 disk
["image_id"], vapp_uuid
2158 self
.add_existing_disk(
2160 image_id
=disk
["image_id"],
2162 template_name
=templateName
,
2163 vapp_uuid
=vapp_uuid
,
2165 added_existing_disk
= True
2167 # Wait till added existing disk gets reflected into vCD database/API
2168 if added_existing_disk
:
2170 added_existing_disk
= False
2171 self
.add_new_disk(vapp_uuid
, disk
["size"])
2174 # Assigning numa affinity setting
2176 if "paired-threads-id" in numa
:
2177 paired_threads_id
= numa
["paired-threads-id"]
2178 self
.set_numa_affinity(vapp_uuid
, paired_threads_id
)
2180 # add NICs & connect to networks in netlist
2182 vdc_obj
= VDC(self
.client
, href
=vdc
.get("href"))
2183 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
2184 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2185 vapp_id
= vapp_resource
.get("id").split(":")[-1]
2187 self
.logger
.info("Removing primary NIC: ")
2188 # First remove all NICs so that NIC properties can be adjusted as needed
2189 self
.remove_primary_network_adapter_from_all_vms(vapp
)
2191 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
2192 primary_nic_index
= 0
2194 for net
in net_list
:
2195 # openmano uses network id in UUID format.
2196 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
2197 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
2198 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
2200 if "net_id" not in net
:
2203 # Using net_id as a vim_id i.e. vim interface id, as do not have saperate vim interface id
2204 # Same will be returned in refresh_vms_status() as vim_interface_id
2205 net
["vim_id"] = net
[
2207 ] # Provide the same VIM identifier as the VIM network
2209 interface_net_id
= net
["net_id"]
2210 interface_net_name
= self
.get_network_name_by_id(
2211 network_uuid
=interface_net_id
2213 interface_network_mode
= net
["use"]
2215 if interface_network_mode
== "mgmt":
2216 primary_nic_index
= nicIndex
2218 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
2219 - DHCP (The IP address is obtained from a DHCP service.)
2220 - MANUAL (The IP address is assigned manually in the IpAddress element.)
2221 - NONE (No IP addressing mode specified.)"""
2223 if primary_netname
is not None:
2225 "new_vminstance(): Filtering by net name {}".format(
2231 for n
in self
.get_network_list()
2232 if n
.get("name") == interface_net_name
2237 "new_vminstance(): Found requested network: {}".format(
2242 if interface_net_name
!= primary_netname
:
2243 # connect network to VM - with all DHCP by default
2245 "new_vminstance(): Attaching net {} to vapp".format(
2249 self
.connect_vapp_to_org_vdc_network(
2250 vapp_id
, nets
[0].get("name")
2253 type_list
= ("PF", "PCI-PASSTHROUGH", "VFnotShared")
2254 nic_type
= "VMXNET3"
2255 if "type" in net
and net
["type"] not in type_list
:
2256 # fetching nic type from vnf
2258 if net
["model"] is not None:
2260 net
["model"].lower() == "paravirt"
2261 or net
["model"].lower() == "virtio"
2263 nic_type
= "VMXNET3"
2265 nic_type
= net
["model"]
2268 "new_vminstance(): adding network adapter "
2269 "to a network {}".format(nets
[0].get("name"))
2271 self
.add_network_adapter_to_vms(
2273 nets
[0].get("name"),
2281 "new_vminstance(): adding network adapter "
2282 "to a network {}".format(nets
[0].get("name"))
2285 if net
["type"] in ["SR-IOV", "VF"]:
2286 nic_type
= net
["type"]
2287 self
.add_network_adapter_to_vms(
2289 nets
[0].get("name"),
2297 # cloud-init for ssh-key injection
2299 # Create a catalog which will be carrying the config drive ISO
2300 # This catalog is deleted during vApp deletion. The catalog name carries
2301 # vApp UUID and thats how it gets identified during its deletion.
2302 config_drive_catalog_name
= "cfg_drv-" + vapp_uuid
2304 'new_vminstance(): Creating catalog "{}" to carry config drive ISO'.format(
2305 config_drive_catalog_name
2308 config_drive_catalog_id
= self
.create_vimcatalog(
2309 org
, config_drive_catalog_name
2312 if config_drive_catalog_id
is None:
2314 "new_vminstance(): Failed to create new catalog '{}' to carry the config drive "
2315 "ISO".format(config_drive_catalog_name
)
2317 raise Exception(error_msg
)
2319 # Create config-drive ISO
2320 _
, userdata
= self
._create
_user
_data
(cloud_config
)
2321 # self.logger.debug('new_vminstance(): The userdata for cloud-init: {}'.format(userdata))
2322 iso_path
= self
.create_config_drive_iso(userdata
)
2324 "new_vminstance(): The ISO is successfully created. Path: {}".format(
2330 "new_vminstance(): uploading iso to catalog {}".format(
2331 config_drive_catalog_name
2334 self
.upload_iso_to_catalog(config_drive_catalog_id
, iso_path
)
2335 # Attach the config-drive ISO to the VM
2337 "new_vminstance(): Attaching the config-drive ISO to the VM"
2339 self
.insert_media_to_vm(vapp
, config_drive_catalog_id
)
2340 shutil
.rmtree(os
.path
.dirname(iso_path
), ignore_errors
=True)
2342 # If VM has PCI devices or SRIOV reserve memory for VM
2344 self
.reserve_memory_for_all_vms(vapp
, memory_mb
)
2347 "new_vminstance(): starting power on vApp {} ".format(vmname_andid
)
2350 poweron_task
= self
.power_on_vapp(vapp_id
, vmname_andid
)
2351 result
= self
.client
.get_task_monitor().wait_for_success(task
=poweron_task
)
2352 if result
.get("status") == "success":
2354 "new_vminstance(): Successfully power on "
2355 "vApp {}".format(vmname_andid
)
2359 "new_vminstance(): failed to power on vApp "
2360 "{}".format(vmname_andid
)
2363 except Exception as exp
:
2365 self
.delete_vminstance(vapp_uuid
)
2366 except Exception as exp2
:
2367 self
.logger
.error("new_vminstance rollback fail {}".format(exp2
))
2368 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
2370 "new_vminstance(): Failed create new vm instance {} with exception {}".format(
2374 raise vimconn
.VimConnException(
2375 "new_vminstance(): Failed create new vm instance {} with exception {}".format(
2379 # check if vApp deployed and if that the case return vApp UUID otherwise -1
2382 while wait_time
<= MAX_WAIT_TIME
:
2384 vapp_resource
= vdc_obj
.get_vapp(vmname_andid
)
2385 vapp
= VApp(self
.client
, resource
=vapp_resource
)
2386 except Exception as exp
:
2387 raise vimconn
.VimConnUnexpectedResponse(
2388 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}".format(
2393 # if vapp and vapp.me.deployed:
2394 if vapp
and vapp_resource
.get("deployed") == "true":
2395 vapp_uuid
= vapp_resource
.get("id").split(":")[-1]
2399 "new_vminstance(): Wait for vApp {} to deploy".format(name
)
2401 time
.sleep(INTERVAL_TIME
)
2403 wait_time
+= INTERVAL_TIME
2405 # SET Affinity Rule for VM
2406 # Pre-requisites: User has created Hosh Groups in vCenter with respective Hosts to be used
2407 # While creating VIM account user has to pass the Host Group names in availability_zone list
2408 # "availability_zone" is a part of VIM "config" parameters
2409 # For example, in VIM config: "availability_zone":["HG_170","HG_174","HG_175"]
2410 # Host groups are referred as availability zones
2411 # With following procedure, deployed VM will be added into a VM group.
2412 # Then A VM to Host Affinity rule will be created using the VM group & Host group.
2413 if availability_zone_list
:
2415 "Existing Host Groups in VIM {}".format(
2416 self
.config
.get("availability_zone")
2419 # Admin access required for creating Affinity rules
2420 client
= self
.connect_as_admin()
2423 raise vimconn
.VimConnConnectionException(
2424 "Failed to connect vCD as admin"
2427 self
.client
= client
2431 "Accept": "application/*+xml;version=27.0",
2432 "x-vcloud-authorization": self
.client
._session
.headers
[
2433 "x-vcloud-authorization"
2437 # Step1: Get provider vdc details from organization
2438 pvdc_href
= self
.get_pvdc_for_org(self
.tenant_name
, headers
)
2439 if pvdc_href
is not None:
2440 # Step2: Found required pvdc, now get resource pool information
2441 respool_href
= self
.get_resource_pool_details(pvdc_href
, headers
)
2442 if respool_href
is None:
2443 # Raise error if respool_href not found
2444 msg
= "new_vminstance():Error in finding resource pool details in pvdc {}".format(
2447 self
.log_message(msg
)
2449 # Step3: Verify requested availability zone(hostGroup) is present in vCD
2450 # get availability Zone
2451 vm_az
= self
.get_vm_availability_zone(
2452 availability_zone_index
, availability_zone_list
2455 # check if provided av zone(hostGroup) is present in vCD VIM
2456 status
= self
.check_availibility_zone(vm_az
, respool_href
, headers
)
2459 "new_vminstance(): Error in finding availability zone(Host Group): {} in "
2460 "resource pool {} status: {}"
2461 ).format(vm_az
, respool_href
, status
)
2462 self
.log_message(msg
)
2465 "new_vminstance(): Availability zone {} found in VIM".format(vm_az
)
2468 # Step4: Find VM group references to create vm group
2469 vmgrp_href
= self
.find_vmgroup_reference(respool_href
, headers
)
2470 if vmgrp_href
is None:
2471 msg
= "new_vminstance(): No reference to VmGroup found in resource pool"
2472 self
.log_message(msg
)
2474 # Step5: Create a VmGroup with name az_VmGroup
2477 ) # Formed VM Group name = Host Group name + VM name
2478 status
= self
.create_vmgroup(vmgrp_name
, vmgrp_href
, headers
)
2479 if status
is not True:
2480 msg
= "new_vminstance(): Error in creating VM group {}".format(
2483 self
.log_message(msg
)
2485 # VM Group url to add vms to vm group
2486 vmgrpname_url
= self
.url
+ "/api/admin/extension/vmGroup/name/" + vmgrp_name
2488 # Step6: Add VM to VM Group
2489 # Find VM uuid from vapp_uuid
2490 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2491 vm_uuid
= vm_details
["vmuuid"]
2493 status
= self
.add_vm_to_vmgroup(vm_uuid
, vmgrpname_url
, vmgrp_name
, headers
)
2494 if status
is not True:
2495 msg
= "new_vminstance(): Error in adding VM to VM group {}".format(
2498 self
.log_message(msg
)
2500 # Step7: Create VM to Host affinity rule
2501 addrule_href
= self
.get_add_rule_reference(respool_href
, headers
)
2502 if addrule_href
is None:
2503 msg
= "new_vminstance(): Error in finding href to add rule in resource pool: {}".format(
2506 self
.log_message(msg
)
2508 status
= self
.create_vm_to_host_affinity_rule(
2509 addrule_href
, vmgrp_name
, vm_az
, "Affinity", headers
2512 msg
= "new_vminstance(): Error in creating affinity rule for VM {} in Host group {}".format(
2515 self
.log_message(msg
)
2518 "new_vminstance(): Affinity rule created successfully. Added {} in Host group {}".format(
2522 # Reset token to a normal user to perform other operations
2525 if vapp_uuid
is not None:
2526 return vapp_uuid
, None
2528 raise vimconn
.VimConnUnexpectedResponse(
2529 "new_vminstance(): Failed create new vm instance {}".format(name
)
2532 def create_config_drive_iso(self
, user_data
):
2533 tmpdir
= tempfile
.mkdtemp()
2534 iso_path
= os
.path
.join(tmpdir
, "ConfigDrive.iso")
2535 latest_dir
= os
.path
.join(tmpdir
, "openstack", "latest")
2536 os
.makedirs(latest_dir
)
2538 os
.path
.join(latest_dir
, "meta_data.json"), "w"
2539 ) as meta_file_obj
, open(
2540 os
.path
.join(latest_dir
, "user_data"), "w"
2541 ) as userdata_file_obj
:
2542 userdata_file_obj
.write(user_data
)
2543 meta_file_obj
.write(
2546 "availability_zone": "nova",
2548 "name": "ConfigDrive",
2549 "uuid": str(uuid
.uuid4()),
2554 "genisoimage -J -r -V config-2 -o {iso_path} {source_dir_path}".format(
2555 iso_path
=iso_path
, source_dir_path
=tmpdir
2559 'create_config_drive_iso(): Creating ISO by running command "{}"'.format(
2565 FNULL
= open(os
.devnull
, "w")
2566 subprocess
.check_call(genisoimage_cmd
, shell
=True, stdout
=FNULL
)
2567 except subprocess
.CalledProcessError
as e
:
2568 shutil
.rmtree(tmpdir
, ignore_errors
=True)
2569 error_msg
= "create_config_drive_iso(): Exception while running genisoimage command: {}".format(
2572 self
.logger
.error(error_msg
)
2573 raise Exception(error_msg
)
2577 def upload_iso_to_catalog(self
, catalog_id
, iso_file_path
):
2578 if not os
.path
.isfile(iso_file_path
):
2579 error_msg
= "upload_iso_to_catalog(): Given iso file is not present. Given path: {}".format(
2582 self
.logger
.error(error_msg
)
2583 raise Exception(error_msg
)
2585 iso_file_stat
= os
.stat(iso_file_path
)
2586 xml_media_elem
= """<?xml version="1.0" encoding="UTF-8"?>
2588 xmlns="http://www.vmware.com/vcloud/v1.5"
2592 <Description>ISO image for config-drive</Description>
2594 iso_name
=os
.path
.basename(iso_file_path
), iso_size
=iso_file_stat
.st_size
2597 "Accept": "application/*+xml;version=" + API_VERSION
,
2598 "x-vcloud-authorization": self
.client
._session
.headers
[
2599 "x-vcloud-authorization"
2602 headers
["Content-Type"] = "application/vnd.vmware.vcloud.media+xml"
2603 catalog_href
= self
.url
+ "/api/catalog/" + catalog_id
+ "/action/upload"
2604 response
= self
.perform_request(
2605 req_type
="POST", url
=catalog_href
, headers
=headers
, data
=xml_media_elem
2608 if response
.status_code
!= 201:
2609 error_msg
= "upload_iso_to_catalog(): Failed to POST an action/upload request to {}".format(
2612 self
.logger
.error(error_msg
)
2613 raise Exception(error_msg
)
2615 catalogItem
= XmlElementTree
.fromstring(response
.text
)
2618 for child
in catalogItem
2619 if child
.get("type") == "application/vnd.vmware.vcloud.media+xml"
2621 entity_href
= entity
.get("href")
2623 response
= self
.perform_request(
2624 req_type
="GET", url
=entity_href
, headers
=headers
2626 if response
.status_code
!= 200:
2628 "upload_iso_to_catalog(): Failed to GET entity href {}".format(
2634 r
'<Files>\s+?<File.+?href="(.+?)"/>\s+?</File>\s+?</Files>',
2639 media_upload_href
= match
.group(1)
2642 "Could not parse the upload URL for the media file from the last response"
2644 upload_iso_task
= self
.get_task_from_response(response
.text
)
2645 headers
["Content-Type"] = "application/octet-stream"
2646 response
= self
.perform_request(
2648 url
=media_upload_href
,
2650 data
=open(iso_file_path
, "rb"),
2653 if response
.status_code
!= 200:
2654 raise Exception('PUT request to "{}" failed'.format(media_upload_href
))
2656 result
= self
.client
.get_task_monitor().wait_for_success(task
=upload_iso_task
)
2657 if result
.get("status") != "success":
2659 "The upload iso task failed with status {}".format(result
.get("status"))
2662 def set_availability_zones(self
):
2664 Set vim availability zone
2666 vim_availability_zones
= None
2667 availability_zone
= None
2669 if "availability_zone" in self
.config
:
2670 vim_availability_zones
= self
.config
.get("availability_zone")
2672 if isinstance(vim_availability_zones
, str):
2673 availability_zone
= [vim_availability_zones
]
2674 elif isinstance(vim_availability_zones
, list):
2675 availability_zone
= vim_availability_zones
2677 return availability_zone
2679 return availability_zone
2681 def get_vm_availability_zone(self
, availability_zone_index
, availability_zone_list
):
2683 Return the availability zone to be used by the created VM.
2684 returns: The VIM availability zone to be used or None
2686 if availability_zone_index
is None:
2687 if not self
.config
.get("availability_zone"):
2689 elif isinstance(self
.config
.get("availability_zone"), str):
2690 return self
.config
["availability_zone"]
2692 return self
.config
["availability_zone"][0]
2694 vim_availability_zones
= self
.availability_zone
2696 # check if VIM offer enough availability zones describe in the VNFD
2697 if vim_availability_zones
and len(availability_zone_list
) <= len(
2698 vim_availability_zones
2700 # check if all the names of NFV AV match VIM AV names
2701 match_by_index
= False
2702 for av
in availability_zone_list
:
2703 if av
not in vim_availability_zones
:
2704 match_by_index
= True
2709 "Required Availability zone or Host Group not found in VIM config"
2712 "Input Availability zone list: {}".format(availability_zone_list
)
2715 "VIM configured Availability zones: {}".format(
2716 vim_availability_zones
2719 self
.logger
.debug("VIM Availability zones will be used by index")
2720 return vim_availability_zones
[availability_zone_index
]
2722 return availability_zone_list
[availability_zone_index
]
2724 raise vimconn
.VimConnConflictException(
2725 "No enough availability zones at VIM for this deployment"
2728 def create_vm_to_host_affinity_rule(
2729 self
, addrule_href
, vmgrpname
, hostgrpname
, polarity
, headers
2731 """Method to create VM to Host Affinity rule in vCD
2734 addrule_href - href to make a POST request
2735 vmgrpname - name of the VM group created
2736 hostgrpnmae - name of the host group created earlier
2737 polarity - Affinity or Anti-affinity (default: Affinity)
2738 headers - headers to make REST call
2741 True- if rule is created
2742 False- Failed to create rule due to some error
2746 rule_name
= polarity
+ "_" + vmgrpname
2747 payload
= """<?xml version="1.0" encoding="UTF-8"?>
2748 <vmext:VMWVmHostAffinityRule
2749 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
2750 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
2751 type="application/vnd.vmware.admin.vmwVmHostAffinityRule+xml">
2752 <vcloud:Name>{}</vcloud:Name>
2753 <vcloud:IsEnabled>true</vcloud:IsEnabled>
2754 <vcloud:IsMandatory>true</vcloud:IsMandatory>
2755 <vcloud:Polarity>{}</vcloud:Polarity>
2756 <vmext:HostGroupName>{}</vmext:HostGroupName>
2757 <vmext:VmGroupName>{}</vmext:VmGroupName>
2758 </vmext:VMWVmHostAffinityRule>""".format(
2759 rule_name
, polarity
, hostgrpname
, vmgrpname
2762 resp
= self
.perform_request(
2763 req_type
="POST", url
=addrule_href
, headers
=headers
, data
=payload
2766 if resp
.status_code
!= requests
.codes
.accepted
:
2768 "REST API call {} failed. Return status code {}".format(
2769 addrule_href
, resp
.status_code
2776 affinity_task
= self
.get_task_from_response(resp
.content
)
2777 self
.logger
.debug("affinity_task: {}".format(affinity_task
))
2779 if affinity_task
is None or affinity_task
is False:
2780 raise vimconn
.VimConnUnexpectedResponse("failed to find affinity task")
2781 # wait for task to complete
2782 result
= self
.client
.get_task_monitor().wait_for_success(task
=affinity_task
)
2784 if result
.get("status") == "success":
2786 "Successfully created affinity rule {}".format(rule_name
)
2790 raise vimconn
.VimConnUnexpectedResponse(
2791 "failed to create affinity rule {}".format(rule_name
)
2794 def get_add_rule_reference(self
, respool_href
, headers
):
2795 """This method finds href to add vm to host affinity rule to vCD
2798 respool_href- href to resource pool
2799 headers- header information to make REST call
2802 None - if no valid href to add rule found or
2803 addrule_href - href to add vm to host affinity rule of resource pool
2806 resp
= self
.perform_request(req_type
="GET", url
=respool_href
, headers
=headers
)
2808 if resp
.status_code
!= requests
.codes
.ok
:
2810 "REST API call {} failed. Return status code {}".format(
2811 respool_href
, resp
.status_code
2815 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2816 for child
in resp_xml
:
2817 if "VMWProviderVdcResourcePool" in child
.tag
:
2818 for schild
in child
:
2819 if "Link" in schild
.tag
:
2821 schild
.attrib
.get("type")
2822 == "application/vnd.vmware.admin.vmwVmHostAffinityRule+xml"
2823 and schild
.attrib
.get("rel") == "add"
2825 addrule_href
= schild
.attrib
.get("href")
2830 def add_vm_to_vmgroup(self
, vm_uuid
, vmGroupNameURL
, vmGroup_name
, headers
):
2831 """Method to add deployed VM to newly created VM Group.
2832 This is required to create VM to Host affinity in vCD
2835 vm_uuid- newly created vm uuid
2836 vmGroupNameURL- URL to VM Group name
2837 vmGroup_name- Name of VM group created
2838 headers- Headers for REST request
2841 True- if VM added to VM group successfully
2842 False- if any error encounter
2844 addvm_resp
= self
.perform_request(
2845 req_type
="GET", url
=vmGroupNameURL
, headers
=headers
2848 if addvm_resp
.status_code
!= requests
.codes
.ok
:
2850 "REST API call to get VM Group Name url {} failed. Return status code {}".format(
2851 vmGroupNameURL
, addvm_resp
.status_code
2856 resp_xml
= XmlElementTree
.fromstring(addvm_resp
.content
)
2857 for child
in resp_xml
:
2858 if child
.tag
.split("}")[1] == "Link":
2859 if child
.attrib
.get("rel") == "addVms":
2860 addvmtogrpURL
= child
.attrib
.get("href")
2863 url_list
= [self
.url
, "/api/vApp/vm-", vm_uuid
]
2864 vmdetailsURL
= "".join(url_list
)
2866 resp
= self
.perform_request(req_type
="GET", url
=vmdetailsURL
, headers
=headers
)
2868 if resp
.status_code
!= requests
.codes
.ok
:
2870 "REST API call {} failed. Return status code {}".format(
2871 vmdetailsURL
, resp
.status_code
2877 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
2878 if resp_xml
.tag
.split("}")[1] == "Vm":
2879 vm_id
= resp_xml
.attrib
.get("id")
2880 vm_name
= resp_xml
.attrib
.get("name")
2881 vm_href
= resp_xml
.attrib
.get("href")
2882 # print vm_id, vm_name, vm_href
2884 # Add VM into VMgroup
2885 payload
= """<?xml version="1.0" encoding="UTF-8"?>\
2886 <ns2:Vms xmlns:ns2="http://www.vmware.com/vcloud/v1.5" \
2887 xmlns="http://www.vmware.com/vcloud/versions" \
2888 xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" \
2889 xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" \
2890 xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" \
2891 xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" \
2892 xmlns:ns7="http://www.vmware.com/schema/ovf" \
2893 xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" \
2894 xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">\
2895 <ns2:VmReference href="{}" id="{}" name="{}" \
2896 type="application/vnd.vmware.vcloud.vm+xml" />\
2897 </ns2:Vms>""".format(
2898 vm_href
, vm_id
, vm_name
2901 addvmtogrp_resp
= self
.perform_request(
2902 req_type
="POST", url
=addvmtogrpURL
, headers
=headers
, data
=payload
2905 if addvmtogrp_resp
.status_code
!= requests
.codes
.accepted
:
2907 "REST API call {} failed. Return status code {}".format(
2908 addvmtogrpURL
, addvmtogrp_resp
.status_code
2915 "Done adding VM {} to VMgroup {}".format(vm_name
, vmGroup_name
)
2920 def create_vmgroup(self
, vmgroup_name
, vmgroup_href
, headers
):
2921 """Method to create a VM group in vCD
2924 vmgroup_name : Name of VM group to be created
2925 vmgroup_href : href for vmgroup
2926 headers- Headers for REST request
2928 # POST to add URL with required data
2929 vmgroup_status
= False
2930 payload
= """<VMWVmGroup xmlns="http://www.vmware.com/vcloud/extension/v1.5" \
2931 xmlns:vcloud_v1.5="http://www.vmware.com/vcloud/v1.5" name="{}">\
2932 <vmCount>1</vmCount>\
2933 </VMWVmGroup>""".format(
2936 resp
= self
.perform_request(
2937 req_type
="POST", url
=vmgroup_href
, headers
=headers
, data
=payload
2940 if resp
.status_code
!= requests
.codes
.accepted
:
2942 "REST API call {} failed. Return status code {}".format(
2943 vmgroup_href
, resp
.status_code
2947 return vmgroup_status
2949 vmgroup_task
= self
.get_task_from_response(resp
.content
)
2950 if vmgroup_task
is None or vmgroup_task
is False:
2951 raise vimconn
.VimConnUnexpectedResponse(
2952 "create_vmgroup(): failed to create VM group {}".format(
2957 # wait for task to complete
2958 result
= self
.client
.get_task_monitor().wait_for_success(task
=vmgroup_task
)
2960 if result
.get("status") == "success":
2962 "create_vmgroup(): Successfully created VM group {}".format(
2967 vmgroup_status
= True
2969 return vmgroup_status
2971 raise vimconn
.VimConnUnexpectedResponse(
2972 "create_vmgroup(): failed to create VM group {}".format(
2977 def find_vmgroup_reference(self
, url
, headers
):
2978 """Method to create a new VMGroup which is required to add created VM
2980 url- resource pool href
2981 headers- header information
2984 returns href to VM group to create VM group
2986 # Perform GET on resource pool to find 'add' link to create VMGroup
2987 # https://vcd-ip/api/admin/extension/providervdc/<providervdc id>/resourcePools
2989 resp
= self
.perform_request(req_type
="GET", url
=url
, headers
=headers
)
2991 if resp
.status_code
!= requests
.codes
.ok
:
2993 "REST API call {} failed. Return status code {}".format(
2994 url
, resp
.status_code
2998 # Get the href to add vmGroup to vCD
2999 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
3000 for child
in resp_xml
:
3001 if "VMWProviderVdcResourcePool" in child
.tag
:
3002 for schild
in child
:
3003 if "Link" in schild
.tag
:
3004 # Find href with type VMGroup and rel with add
3006 schild
.attrib
.get("type")
3007 == "application/vnd.vmware.admin.vmwVmGroupType+xml"
3008 and schild
.attrib
.get("rel") == "add"
3010 vmgrp_href
= schild
.attrib
.get("href")
3014 def check_availibility_zone(self
, az
, respool_href
, headers
):
3015 """Method to verify requested av zone is present or not in provided
3019 az - name of hostgroup (availibility_zone)
3020 respool_href - Resource Pool href
3021 headers - Headers to make REST call
3023 az_found - True if availibility_zone is found else False
3026 headers
["Accept"] = "application/*+xml;version=27.0"
3027 resp
= self
.perform_request(req_type
="GET", url
=respool_href
, headers
=headers
)
3029 if resp
.status_code
!= requests
.codes
.ok
:
3031 "REST API call {} failed. Return status code {}".format(
3032 respool_href
, resp
.status_code
3036 # Get the href to hostGroups and find provided hostGroup is present in it
3037 resp_xml
= XmlElementTree
.fromstring(resp
.content
)
3039 for child
in resp_xml
:
3040 if "VMWProviderVdcResourcePool" in child
.tag
:
3041 for schild
in child
:
3042 if "Link" in schild
.tag
:
3044 schild
.attrib
.get("type")
3045 == "application/vnd.vmware.admin.vmwHostGroupsType+xml"
3047 hostGroup_href
= schild
.attrib
.get("href")
3048 hg_resp
= self
.perform_request(
3049 req_type
="GET", url
=hostGroup_href
, headers
=headers
3052 if hg_resp
.status_code
!= requests
.codes
.ok
:
3054 "REST API call {} failed. Return status code {}".format(
3055 hostGroup_href
, hg_resp
.status_code
3059 hg_resp_xml
= XmlElementTree
.fromstring(
3062 for hostGroup
in hg_resp_xml
:
3063 if "HostGroup" in hostGroup
.tag
:
3064 if hostGroup
.attrib
.get("name") == az
:
3070 def get_pvdc_for_org(self
, org_vdc
, headers
):
3071 """This method gets provider vdc references from organisation
3074 org_vdc - name of the organisation VDC to find pvdc
3075 headers - headers to make REST call
3078 None - if no pvdc href found else
3079 pvdc_href - href to pvdc
3081 # Get provider VDC references from vCD
3083 # url = '<vcd url>/api/admin/extension/providerVdcReferences'
3084 url_list
= [self
.url
, "/api/admin/extension/providerVdcReferences"]
3085 url
= "".join(url_list
)
3087 response
= self
.perform_request(req_type
="GET", url
=url
, headers
=headers
)
3088 if response
.status_code
!= requests
.codes
.ok
:
3090 "REST API call {} failed. Return status code {}".format(
3091 url
, response
.status_code
3095 xmlroot_response
= XmlElementTree
.fromstring(response
.text
)
3096 for child
in xmlroot_response
:
3097 if "ProviderVdcReference" in child
.tag
:
3098 pvdc_href
= child
.attrib
.get("href")
3099 # Get vdcReferences to find org
3100 pvdc_resp
= self
.perform_request(
3101 req_type
="GET", url
=pvdc_href
, headers
=headers
3104 if pvdc_resp
.status_code
!= requests
.codes
.ok
:
3105 raise vimconn
.VimConnException(
3106 "REST API call {} failed. "
3107 "Return status code {}".format(url
, pvdc_resp
.status_code
)
3110 pvdc_resp_xml
= XmlElementTree
.fromstring(pvdc_resp
.content
)
3111 for child
in pvdc_resp_xml
:
3112 if "Link" in child
.tag
:
3114 child
.attrib
.get("type")
3115 == "application/vnd.vmware.admin.vdcReferences+xml"
3117 vdc_href
= child
.attrib
.get("href")
3119 # Check if provided org is present in vdc
3120 vdc_resp
= self
.perform_request(
3121 req_type
="GET", url
=vdc_href
, headers
=headers
3124 if vdc_resp
.status_code
!= requests
.codes
.ok
:
3125 raise vimconn
.VimConnException(
3126 "REST API call {} failed. "
3127 "Return status code {}".format(
3128 url
, vdc_resp
.status_code
3131 vdc_resp_xml
= XmlElementTree
.fromstring(
3135 for child
in vdc_resp_xml
:
3136 if "VdcReference" in child
.tag
:
3137 if child
.attrib
.get("name") == org_vdc
:
3140 def get_resource_pool_details(self
, pvdc_href
, headers
):
3141 """Method to get resource pool information.
3142 Host groups are property of resource group.
3143 To get host groups, we need to GET details of resource pool.
3146 pvdc_href: href to pvdc details
3150 respool_href - Returns href link reference to resource pool
3153 resp
= self
.perform_request(req_type
="GET", url
=pvdc_href
, headers
=headers
)
3155 if resp
.status_code
!= requests
.codes
.ok
:
3157 "REST API call {} failed. Return status code {}".format(
3158 pvdc_href
, resp
.status_code
3162 respool_resp_xml
= XmlElementTree
.fromstring(resp
.content
)
3163 for child
in respool_resp_xml
:
3164 if "Link" in child
.tag
:
3166 child
.attrib
.get("type")
3167 == "application/vnd.vmware.admin.vmwProviderVdcResourcePoolSet+xml"
3169 respool_href
= child
.attrib
.get("href")
3174 def log_message(self
, msg
):
3176 Method to log error messages related to Affinity rule creation
3177 in new_vminstance & raise Exception
3179 msg - Error message to be logged
3182 # get token to connect vCD as a normal user
3184 self
.logger
.debug(msg
)
3186 raise vimconn
.VimConnException(msg
)
3188 def get_vminstance(self
, vim_vm_uuid
=None):
3189 """Returns the VM instance information from VIM"""
3190 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
3192 _
, vdc
= self
.get_vdc_details()
3194 raise vimconn
.VimConnConnectionException(
3195 "Failed to get a reference of VDC for a tenant {}".format(
3200 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
3201 if not vm_info_dict
:
3203 "get_vminstance(): Failed to get vApp name by UUID {}".format(
3207 raise vimconn
.VimConnNotFoundException(
3208 "Failed to get vApp name by UUID {}".format(vim_vm_uuid
)
3211 status_key
= vm_info_dict
["status"]
3215 "created": vm_info_dict
["created"],
3216 "description": vm_info_dict
["name"],
3217 "status": vcdStatusCode2manoFormat
[int(status_key
)],
3218 "hostId": vm_info_dict
["vmuuid"],
3220 "vim_info": yaml
.safe_dump(vm_info_dict
),
3224 if "interfaces" in vm_info_dict
:
3225 vm_dict
["interfaces"] = vm_info_dict
["interfaces"]
3227 vm_dict
["interfaces"] = []
3232 "status": vcdStatusCode2manoFormat
[int(-1)],
3233 "hostId": vm_info_dict
["vmuuid"],
3234 "error_msg": "Inconsistency state",
3235 "vim_info": yaml
.safe_dump(vm_info_dict
),
3241 def delete_vminstance(self
, vm_id
, created_items
=None, volumes_to_hold
=None):
3242 """Method poweroff and remove VM instance from vcloud director network.
3248 Returns the instance identifier
3250 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm_id
))
3252 _
, vdc
= self
.get_vdc_details()
3253 vdc_obj
= VDC(self
.client
, href
=vdc
.get("href"))
3256 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
3260 raise vimconn
.VimConnException(
3261 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
3267 vapp_name
= self
.get_namebyvappid(vm_id
)
3268 if vapp_name
is None:
3270 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3277 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3282 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm_id
))
3283 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
3284 vapp
= VApp(self
.client
, resource
=vapp_resource
)
3286 # Delete vApp and wait for status change if task executed and vApp is None.
3288 if vapp_resource
.get("deployed") == "true":
3289 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
3294 while wait_time
<= MAX_WAIT_TIME
:
3295 power_off_task
= vapp
.power_off()
3296 result
= self
.client
.get_task_monitor().wait_for_success(
3300 if result
.get("status") == "success":
3305 "Wait for vApp {} to power off".format(vapp_name
)
3307 time
.sleep(INTERVAL_TIME
)
3309 wait_time
+= INTERVAL_TIME
3313 "delete_vminstance(): Failed to power off VM instance {} ".format(
3319 "delete_vminstance(): Powered off VM instance {} ".format(
3325 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
3328 while wait_time
<= MAX_WAIT_TIME
:
3329 vapp
= VApp(self
.client
, resource
=vapp_resource
)
3332 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3339 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3344 undeploy_task
= vapp
.undeploy()
3345 result
= self
.client
.get_task_monitor().wait_for_success(
3349 if result
.get("status") == "success":
3354 "Wait for vApp {} to undeploy".format(vapp_name
)
3356 time
.sleep(INTERVAL_TIME
)
3358 wait_time
+= INTERVAL_TIME
3362 "delete_vminstance(): Failed to undeploy vApp {} ".format(
3368 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
3369 if vapp
is not None:
3373 while wait_time
<= MAX_WAIT_TIME
:
3374 vapp
= VApp(self
.client
, resource
=vapp_resource
)
3377 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3384 "delete_vminstance(): Failed to get vm by given {} vm uuid".format(
3389 delete_task
= vdc_obj
.delete_vapp(vapp
.name
, force
=True)
3390 result
= self
.client
.get_task_monitor().wait_for_success(
3393 if result
.get("status") == "success":
3397 "Wait for vApp {} to delete".format(vapp_name
)
3399 time
.sleep(INTERVAL_TIME
)
3401 wait_time
+= INTERVAL_TIME
3405 "delete_vminstance(): Failed delete uuid {} ".format(vm_id
)
3409 "Deleted vm instance {} successfully".format(vm_id
)
3411 config_drive_catalog_name
, config_drive_catalog_id
= (
3415 catalog_list
= self
.get_image_list()
3418 config_drive_catalog_id
= [
3420 for catalog_
in catalog_list
3421 if catalog_
["name"] == config_drive_catalog_name
3426 if config_drive_catalog_id
:
3428 "delete_vminstance(): Found a config drive catalog {} matching "
3429 'vapp_name"{}". Deleting it.'.format(
3430 config_drive_catalog_id
, vapp_name
3433 self
.delete_image(config_drive_catalog_id
)
3437 self
.logger
.debug(traceback
.format_exc())
3439 raise vimconn
.VimConnException(
3440 "delete_vminstance(): Failed delete vm instance {}".format(vm_id
)
3443 def refresh_vms_status(self
, vm_list
):
3444 """Get the status of the virtual machines and their interfaces/ports
3445 Params: the list of VM identifiers
3446 Returns a dictionary with:
3447 vm_id: #VIM id of this Virtual Machine
3448 status: #Mandatory. Text with one of:
3449 # DELETED (not found at vim)
3450 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
3451 # OTHER (Vim reported other status not understood)
3452 # ERROR (VIM indicates an ERROR status)
3453 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
3454 # CREATING (on building process), ERROR
3455 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
3457 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
3458 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
3460 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
3461 mac_address: #Text format XX:XX:XX:XX:XX:XX
3462 vim_net_id: #network id where this interface is connected
3463 vim_interface_id: #interface/port VIM id
3464 ip_address: #null, or text with IPv4, IPv6 address
3466 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
3468 _
, vdc
= self
.get_vdc_details()
3470 raise vimconn
.VimConnException(
3471 "Failed to get a reference of VDC for a tenant {}".format(
3478 for vmuuid
in vm_list
:
3479 vapp_name
= self
.get_namebyvappid(vmuuid
)
3480 if vapp_name
is not None:
3482 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
3483 vdc_obj
= VDC(self
.client
, href
=vdc
.get("href"))
3484 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
3485 the_vapp
= VApp(self
.client
, resource
=vapp_resource
)
3488 for vm
in the_vapp
.get_all_vms():
3490 "Accept": "application/*+xml;version=" + API_VERSION
,
3491 "x-vcloud-authorization": self
.client
._session
.headers
[
3492 "x-vcloud-authorization"
3495 response
= self
.perform_request(
3496 req_type
="GET", url
=vm
.get("href"), headers
=headers
3499 if response
.status_code
!= 200:
3501 "refresh_vms_status : REST call {} failed reason : {}"
3502 "status code : {}".format(
3503 vm
.get("href"), response
.text
, response
.status_code
3506 raise vimconn
.VimConnException(
3507 "refresh_vms_status : Failed to get VM details"
3510 xmlroot
= XmlElementTree
.fromstring(response
.text
)
3511 result
= response
.text
.replace("\n", " ")
3512 hdd_match
= re
.search(
3513 'vcloud:capacity="(\d+)"\svcloud:storageProfileOverrideVmDefault=',
3518 hdd_mb
= hdd_match
.group(1)
3519 vm_details
["hdd_mb"] = int(hdd_mb
) if hdd_mb
else None
3521 cpus_match
= re
.search(
3522 "<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>",
3527 cpus
= cpus_match
.group(1)
3528 vm_details
["cpus"] = int(cpus
) if cpus
else None
3530 memory_mb
= re
.search(
3531 "<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>",
3534 vm_details
["memory_mb"] = int(memory_mb
) if memory_mb
else None
3535 vm_details
["status"] = vcdStatusCode2manoFormat
[
3536 int(xmlroot
.get("status"))
3538 vm_details
["id"] = xmlroot
.get("id")
3539 vm_details
["name"] = xmlroot
.get("name")
3540 vm_info
= [vm_details
]
3543 vm_info
[0].update(vm_pci_details
)
3546 "status": vcdStatusCode2manoFormat
[
3547 int(vapp_resource
.get("status"))
3549 "error_msg": vcdStatusCode2manoFormat
[
3550 int(vapp_resource
.get("status"))
3552 "vim_info": yaml
.safe_dump(vm_info
),
3559 networks
= re
.findall(
3560 "<NetworkConnection needsCustomization=.*?</NetworkConnection>",
3564 for network
in networks
:
3565 mac_s
= re
.search("<MACAddress>(.*?)</MACAddress>", network
)
3566 vm_mac
= mac_s
.group(1) if mac_s
else None
3567 ip_s
= re
.search("<IpAddress>(.*?)</IpAddress>", network
)
3568 vm_ip
= ip_s
.group(1) if ip_s
else None
3571 if not nsx_edge_list
:
3572 nsx_edge_list
= self
.get_edge_details()
3573 if nsx_edge_list
is None:
3574 raise vimconn
.VimConnException(
3575 "refresh_vms_status:"
3576 "Failed to get edge details from NSX Manager"
3579 if vm_mac
is not None:
3580 vm_ip
= self
.get_ipaddr_from_NSXedge(
3581 nsx_edge_list
, vm_mac
3584 net_s
= re
.search('network="(.*?)"', network
)
3585 network_name
= net_s
.group(1) if net_s
else None
3586 vm_net_id
= self
.get_network_id_by_name(network_name
)
3588 "mac_address": vm_mac
,
3589 "vim_net_id": vm_net_id
,
3590 "vim_interface_id": vm_net_id
,
3591 "ip_address": vm_ip
,
3593 vm_dict
["interfaces"].append(interface
)
3595 # add a vm to vm dict
3596 vms_dict
.setdefault(vmuuid
, vm_dict
)
3597 self
.logger
.debug("refresh_vms_status : vm info {}".format(vm_dict
))
3598 except Exception as exp
:
3599 self
.logger
.debug("Error in response {}".format(exp
))
3600 self
.logger
.debug(traceback
.format_exc())
3604 def get_edge_details(self
):
3605 """Get the NSX edge list from NSX Manager
3606 Returns list of NSX edges
3609 rheaders
= {"Content-Type": "application/xml"}
3610 nsx_api_url
= "/api/4.0/edges"
3613 "Get edge details from NSX Manager {} {}".format(
3614 self
.nsx_manager
, nsx_api_url
3619 resp
= requests
.get(
3620 self
.nsx_manager
+ nsx_api_url
,
3621 auth
=(self
.nsx_user
, self
.nsx_password
),
3625 if resp
.status_code
== requests
.codes
.ok
:
3626 paged_Edge_List
= XmlElementTree
.fromstring(resp
.text
)
3627 for edge_pages
in paged_Edge_List
:
3628 if edge_pages
.tag
== "edgePage":
3629 for edge_summary
in edge_pages
:
3630 if edge_summary
.tag
== "pagingInfo":
3631 for element
in edge_summary
:
3633 element
.tag
== "totalCount"
3634 and element
.text
== "0"
3636 raise vimconn
.VimConnException(
3637 "get_edge_details: No NSX edges details found: {}".format(
3642 if edge_summary
.tag
== "edgeSummary":
3643 for element
in edge_summary
:
3644 if element
.tag
== "id":
3645 edge_list
.append(element
.text
)
3647 raise vimconn
.VimConnException(
3648 "get_edge_details: No NSX edge details found: {}".format(
3654 raise vimconn
.VimConnException(
3655 "get_edge_details: "
3656 "No NSX edge details found: {}".format(self
.nsx_manager
)
3660 "get_edge_details: Found NSX edges {}".format(edge_list
)
3666 "get_edge_details: "
3667 "Failed to get NSX edge details from NSX Manager: {}".format(
3674 except Exception as exp
:
3676 "get_edge_details: "
3677 "Failed to get NSX edge details from NSX Manager: {}".format(exp
)
3679 raise vimconn
.VimConnException(
3680 "get_edge_details: "
3681 "Failed to get NSX edge details from NSX Manager: {}".format(exp
)
3684 def get_ipaddr_from_NSXedge(self
, nsx_edges
, mac_address
):
3685 """Get IP address details from NSX edges, using the MAC address
3686 PARAMS: nsx_edges : List of NSX edges
3687 mac_address : Find IP address corresponding to this MAC address
3688 Returns: IP address corrresponding to the provided MAC address
3691 rheaders
= {"Content-Type": "application/xml"}
3693 self
.logger
.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
3696 for edge
in nsx_edges
:
3697 nsx_api_url
= "/api/4.0/edges/" + edge
+ "/dhcp/leaseInfo"
3699 resp
= requests
.get(
3700 self
.nsx_manager
+ nsx_api_url
,
3701 auth
=(self
.nsx_user
, self
.nsx_password
),
3706 if resp
.status_code
== requests
.codes
.ok
:
3707 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
3708 for child
in dhcp_leases
:
3709 if child
.tag
== "dhcpLeaseInfo":
3710 dhcpLeaseInfo
= child
3711 for leaseInfo
in dhcpLeaseInfo
:
3712 for elem
in leaseInfo
:
3713 if (elem
.tag
) == "macAddress":
3714 edge_mac_addr
= elem
.text
3716 if (elem
.tag
) == "ipAddress":
3719 if edge_mac_addr
is not None:
3720 if edge_mac_addr
== mac_address
:
3722 "Found ip addr {} for mac {} at NSX edge {}".format(
3723 ip_addr
, mac_address
, edge
3730 "get_ipaddr_from_NSXedge: "
3731 "Error occurred while getting DHCP lease info from NSX Manager: {}".format(
3737 "get_ipaddr_from_NSXedge: No IP addr found in any NSX edge"
3742 except XmlElementTree
.ParseError
as Err
:
3744 "ParseError in response from NSX Manager {}".format(Err
.message
),
3748 def action_vminstance(self
, vm_id
=None, action_dict
=None, created_items
={}):
3749 """Send and action over a VM instance from VIM
3750 Returns the vm_id if the action was successfully sent to the VIM"""
3753 "Received action for vm {} and action dict {}".format(vm_id
, action_dict
)
3756 if vm_id
is None or action_dict
is None:
3757 raise vimconn
.VimConnException("Invalid request. VM id or action is None.")
3759 _
, vdc
= self
.get_vdc_details()
3761 raise vimconn
.VimConnException(
3762 "Failed to get a reference of VDC for a tenant {}".format(
3767 vapp_name
= self
.get_namebyvappid(vm_id
)
3768 if vapp_name
is None:
3770 "action_vminstance(): Failed to get vm by given {} vm uuid".format(
3775 raise vimconn
.VimConnException(
3776 "Failed to get vm by given {} vm uuid".format(vm_id
)
3780 "Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm_id
)
3784 vdc_obj
= VDC(self
.client
, href
=vdc
.get("href"))
3785 vapp_resource
= vdc_obj
.get_vapp(vapp_name
)
3786 vapp
= VApp(self
.client
, resource
=vapp_resource
)
3788 if "start" in action_dict
:
3790 "action_vminstance: Power on vApp: {}".format(vapp_name
)
3792 poweron_task
= self
.power_on_vapp(vm_id
, vapp_name
)
3793 result
= self
.client
.get_task_monitor().wait_for_success(
3796 self
.instance_actions_result("start", result
, vapp_name
)
3797 elif "rebuild" in action_dict
:
3799 "action_vminstance: Rebuild vApp: {}".format(vapp_name
)
3801 rebuild_task
= vapp
.deploy(power_on
=True)
3802 result
= self
.client
.get_task_monitor().wait_for_success(
3805 self
.instance_actions_result("rebuild", result
, vapp_name
)
3806 elif "pause" in action_dict
:
3807 self
.logger
.info("action_vminstance: pause vApp: {}".format(vapp_name
))
3808 pause_task
= vapp
.undeploy(action
="suspend")
3809 result
= self
.client
.get_task_monitor().wait_for_success(
3812 self
.instance_actions_result("pause", result
, vapp_name
)
3813 elif "resume" in action_dict
:
3814 self
.logger
.info("action_vminstance: resume vApp: {}".format(vapp_name
))
3815 poweron_task
= self
.power_on_vapp(vm_id
, vapp_name
)
3816 result
= self
.client
.get_task_monitor().wait_for_success(
3819 self
.instance_actions_result("resume", result
, vapp_name
)
3820 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
3821 action_name
, _
= list(action_dict
.items())[0]
3823 "action_vminstance: {} vApp: {}".format(action_name
, vapp_name
)
3825 shutdown_task
= vapp
.shutdown()
3826 result
= self
.client
.get_task_monitor().wait_for_success(
3829 if action_name
== "shutdown":
3830 self
.instance_actions_result("shutdown", result
, vapp_name
)
3832 self
.instance_actions_result("shutoff", result
, vapp_name
)
3833 elif "forceOff" in action_dict
:
3834 result
= vapp
.undeploy(action
="powerOff")
3835 self
.instance_actions_result("forceOff", result
, vapp_name
)
3836 elif "reboot" in action_dict
:
3837 self
.logger
.info("action_vminstance: reboot vApp: {}".format(vapp_name
))
3838 reboot_task
= vapp
.reboot()
3839 self
.client
.get_task_monitor().wait_for_success(task
=reboot_task
)
3841 raise vimconn
.VimConnException(
3842 "action_vminstance: Invalid action {} or action is None.".format(
3848 except Exception as exp
:
3849 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
3851 raise vimconn
.VimConnException(
3852 "action_vminstance: Failed with Exception {}".format(exp
)
3855 def instance_actions_result(self
, action
, result
, vapp_name
):
3856 if result
.get("status") == "success":
3858 "action_vminstance: Sucessfully {} the vApp: {}".format(
3864 "action_vminstance: Failed to {} vApp: {}".format(action
, vapp_name
)
3867 def get_vminstance_console(self
, vm_id
, console_type
="novnc"):
3869 Get a console for the virtual machine
3871 vm_id: uuid of the VM
3872 console_type, can be:
3873 "novnc" (by default), "xvpvnc" for VNC types,
3874 "rdp-html5" for RDP types, "spice-html5" for SPICE types
3875 Returns dict with the console parameters:
3876 protocol: ssh, ftp, http, https, ...
3877 server: usually ip address
3878 port: the http, ssh, ... port
3879 suffix: extra text, e.g. the http path and query string
3883 if console_type
is None or console_type
== "novnc":
3884 url_rest_call
= "{}/api/vApp/vm-{}/screen/action/acquireMksTicket".format(
3888 "Accept": "application/*+xml;version=" + API_VERSION
,
3889 "x-vcloud-authorization": self
.client
._session
.headers
[
3890 "x-vcloud-authorization"
3893 response
= self
.perform_request(
3894 req_type
="POST", url
=url_rest_call
, headers
=headers
3897 if response
.status_code
== 403:
3898 response
= self
.retry_rest("GET", url_rest_call
)
3900 if response
.status_code
!= 200:
3902 "REST call {} failed reason : {}"
3903 "status code : {}".format(
3904 url_rest_call
, response
.text
, response
.status_code
3907 raise vimconn
.VimConnException(
3908 "get_vminstance_console : Failed to get " "VM Mks ticket details"
3911 s
= re
.search("<Host>(.*?)</Host>", response
.text
)
3912 console_dict
["server"] = s
.group(1) if s
else None
3913 s1
= re
.search("<Port>(\d+)</Port>", response
.text
)
3914 console_dict
["port"] = s1
.group(1) if s1
else None
3915 url_rest_call
= "{}/api/vApp/vm-{}/screen/action/acquireTicket".format(
3919 "Accept": "application/*+xml;version=" + API_VERSION
,
3920 "x-vcloud-authorization": self
.client
._session
.headers
[
3921 "x-vcloud-authorization"
3924 response
= self
.perform_request(
3925 req_type
="POST", url
=url_rest_call
, headers
=headers
3928 if response
.status_code
== 403:
3929 response
= self
.retry_rest("GET", url_rest_call
)
3931 if response
.status_code
!= 200:
3933 "REST call {} failed reason : {}"
3934 "status code : {}".format(
3935 url_rest_call
, response
.text
, response
.status_code
3938 raise vimconn
.VimConnException(
3939 "get_vminstance_console : Failed to get " "VM console details"
3942 s
= re
.search(">.*?/(vm-\d+.*)</", response
.text
)
3943 console_dict
["suffix"] = s
.group(1) if s
else None
3944 console_dict
["protocol"] = "https"
3948 def get_hosts_info(self
):
3949 """Get the information of deployed hosts
3950 Returns the hosts content"""
3951 raise vimconn
.VimConnNotImplemented("Should have implemented this")
3953 def get_hosts(self
, vim_tenant
):
3954 """Get the hosts and deployed instances
3955 Returns the hosts content"""
3956 raise vimconn
.VimConnNotImplemented("Should have implemented this")
3958 def get_network_name_by_id(self
, network_uuid
=None):
3959 """Method gets vcloud director network named based on supplied uuid.
3962 network_uuid: network_id
3965 The return network name.
3968 if not network_uuid
:
3972 org_dict
= self
.get_org(self
.org_uuid
)
3973 if "networks" in org_dict
:
3974 org_network_dict
= org_dict
["networks"]
3976 for net_uuid
in org_network_dict
:
3977 if net_uuid
== network_uuid
:
3978 return org_network_dict
[net_uuid
]
3980 self
.logger
.debug("Exception in get_network_name_by_id")
3981 self
.logger
.debug(traceback
.format_exc())
3985 def get_network_id_by_name(self
, network_name
=None):
3986 """Method gets vcloud director network uuid based on supplied name.
3989 network_name: network_name
3991 The return network uuid.
3992 network_uuid: network_id
3994 if not network_name
:
3995 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
3999 org_dict
= self
.get_org(self
.org_uuid
)
4000 if org_dict
and "networks" in org_dict
:
4001 org_network_dict
= org_dict
["networks"]
4003 for net_uuid
, net_name
in org_network_dict
.items():
4004 if net_name
== network_name
:
4007 except KeyError as exp
:
4008 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
4012 def get_physical_network_by_name(self
, physical_network_name
):
4014 Methos returns uuid of physical network which passed
4016 physical_network_name: physical network name
4018 UUID of physical_network_name
4021 client_as_admin
= self
.connect_as_admin()
4023 if not client_as_admin
:
4024 raise vimconn
.VimConnConnectionException("Failed to connect vCD.")
4026 url_list
= [self
.url
, "/api/admin/vdc/", self
.tenant_id
]
4027 vm_list_rest_call
= "".join(url_list
)
4029 if client_as_admin
._session
:
4031 "Accept": "application/*+xml;version=" + API_VERSION
,
4032 "x-vcloud-authorization": client_as_admin
._session
.headers
[
4033 "x-vcloud-authorization"
4036 response
= self
.perform_request(
4037 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4039 provider_network
= None
4040 available_network
= None
4041 # add_vdc_rest_url = None
4043 if response
.status_code
!= requests
.codes
.ok
:
4045 "REST API call {} failed. Return status code {}".format(
4046 vm_list_rest_call
, response
.status_code
4052 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
4053 for child
in vm_list_xmlroot
:
4054 if child
.tag
.split("}")[1] == "ProviderVdcReference":
4055 provider_network
= child
.attrib
.get("href")
4056 # application/vnd.vmware.admin.providervdc+xml
4058 if child
.tag
.split("}")[1] == "Link":
4060 child
.attrib
.get("type")
4061 == "application/vnd.vmware.vcloud.orgVdcNetwork+xml"
4062 and child
.attrib
.get("rel") == "add"
4064 child
.attrib
.get("href")
4067 "Failed parse respond for rest api call {}".format(
4071 self
.logger
.debug("Respond body {}".format(response
.text
))
4075 # find pvdc provided available network
4076 response
= self
.perform_request(
4077 req_type
="GET", url
=provider_network
, headers
=headers
4080 if response
.status_code
!= requests
.codes
.ok
:
4082 "REST API call {} failed. Return status code {}".format(
4083 vm_list_rest_call
, response
.status_code
4090 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
4091 for child
in vm_list_xmlroot
.iter():
4092 if child
.tag
.split("}")[1] == "AvailableNetworks":
4093 for networks
in child
.iter():
4095 networks
.attrib
.get("href") is not None
4096 and networks
.attrib
.get("name") is not None
4099 networks
.attrib
.get("name")
4100 == physical_network_name
4102 network_url
= networks
.attrib
.get("href")
4103 available_network
= network_url
[
4104 network_url
.rindex("/") + 1 :
4110 return available_network
4111 except Exception as e
:
4112 self
.logger
.error("Error while getting physical network: {}".format(e
))
4114 def list_org_action(self
):
4116 Method leverages vCloud director and query for available organization for particular user
4119 vca - is active VCA connection.
4120 vdc_name - is a vdc name that will be used to query vms action
4123 The return XML respond
4125 url_list
= [self
.url
, "/api/org"]
4126 vm_list_rest_call
= "".join(url_list
)
4128 if self
.client
._session
:
4130 "Accept": "application/*+xml;version=" + API_VERSION
,
4131 "x-vcloud-authorization": self
.client
._session
.headers
[
4132 "x-vcloud-authorization"
4136 response
= self
.perform_request(
4137 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4140 if response
.status_code
== 403:
4141 response
= self
.retry_rest("GET", vm_list_rest_call
)
4143 if response
.status_code
== requests
.codes
.ok
:
4144 return response
.text
4148 def get_org_action(self
, org_uuid
=None):
4150 Method leverages vCloud director and retrieve available object for organization.
4153 org_uuid - vCD organization uuid
4154 self.client - is active connection.
4157 The return XML respond
4160 if org_uuid
is None:
4163 url_list
= [self
.url
, "/api/org/", org_uuid
]
4164 vm_list_rest_call
= "".join(url_list
)
4166 if self
.client
._session
:
4168 "Accept": "application/*+xml;version=" + API_VERSION
,
4169 "x-vcloud-authorization": self
.client
._session
.headers
[
4170 "x-vcloud-authorization"
4174 # response = requests.get(vm_list_rest_call, headers=headers, verify=False)
4175 response
= self
.perform_request(
4176 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4179 if response
.status_code
== 403:
4180 response
= self
.retry_rest("GET", vm_list_rest_call
)
4182 if response
.status_code
== requests
.codes
.ok
:
4183 return response
.text
4187 def get_org(self
, org_uuid
=None):
4189 Method retrieves available organization in vCloud Director
4192 org_uuid - is a organization uuid.
4195 The return dictionary with following key
4196 "network" - for network list under the org
4197 "catalogs" - for network list under the org
4198 "vdcs" - for vdc list under org
4203 if org_uuid
is None:
4206 content
= self
.get_org_action(org_uuid
=org_uuid
)
4211 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4212 for child
in vm_list_xmlroot
:
4213 if child
.attrib
["type"] == "application/vnd.vmware.vcloud.vdc+xml":
4214 vdc_list
[child
.attrib
["href"].split("/")[-1:][0]] = child
.attrib
[
4217 org_dict
["vdcs"] = vdc_list
4220 child
.attrib
["type"]
4221 == "application/vnd.vmware.vcloud.orgNetwork+xml"
4224 child
.attrib
["href"].split("/")[-1:][0]
4225 ] = child
.attrib
["name"]
4226 org_dict
["networks"] = network_list
4228 if child
.attrib
["type"] == "application/vnd.vmware.vcloud.catalog+xml":
4230 child
.attrib
["href"].split("/")[-1:][0]
4231 ] = child
.attrib
["name"]
4232 org_dict
["catalogs"] = catalog_list
4238 def get_org_list(self
):
4240 Method retrieves available organization in vCloud Director
4243 vca - is active VCA connection.
4246 The return dictionary and key for each entry VDC UUID
4250 content
= self
.list_org_action()
4252 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4254 for vm_xml
in vm_list_xmlroot
:
4255 if vm_xml
.tag
.split("}")[1] == "Org":
4256 org_uuid
= vm_xml
.attrib
["href"].split("/")[-1:]
4257 org_dict
[org_uuid
[0]] = vm_xml
.attrib
["name"]
4263 def vms_view_action(self
, vdc_name
=None):
4264 """Method leverages vCloud director vms query call
4267 vca - is active VCA connection.
4268 vdc_name - is a vdc name that will be used to query vms action
4271 The return XML respond
4273 vca
= self
.connect()
4274 if vdc_name
is None:
4277 url_list
= [vca
.host
, "/api/vms/query"]
4278 vm_list_rest_call
= "".join(url_list
)
4280 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
4283 for ref
in vca
.vcloud_session
.organization
.Link
4284 if ref
.name
== vdc_name
4285 and ref
.type_
== "application/vnd.vmware.vcloud.vdc+xml"
4289 response
= self
.perform_request(
4291 url
=vm_list_rest_call
,
4292 headers
=vca
.vcloud_session
.get_vcloud_headers(),
4297 if response
.status_code
== requests
.codes
.ok
:
4298 return response
.text
4302 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
4304 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
4305 contains a list of all VM's deployed for queried VDC.
4306 The key for a dictionary is VM UUID
4310 vca - is active VCA connection.
4311 vdc_name - is a vdc name that will be used to query vms action
4314 The return dictionary and key for each entry vapp UUID
4317 vca
= self
.connect()
4320 raise vimconn
.VimConnConnectionException("self.connect() is failed")
4322 if vdc_name
is None:
4325 content
= self
.vms_view_action(vdc_name
=vdc_name
)
4327 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4328 for vm_xml
in vm_list_xmlroot
:
4330 vm_xml
.tag
.split("}")[1] == "VMRecord"
4331 and vm_xml
.attrib
["isVAppTemplate"] == "false"
4333 # lookup done by UUID
4335 if vapp_name
in vm_xml
.attrib
["container"]:
4336 rawuuid
= vm_xml
.attrib
["href"].split("/")[-1:]
4337 if "vm-" in rawuuid
[0]:
4338 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
4340 # lookup done by Name
4342 if vapp_name
in vm_xml
.attrib
["name"]:
4343 rawuuid
= vm_xml
.attrib
["href"].split("/")[-1:]
4344 if "vm-" in rawuuid
[0]:
4345 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
4352 def get_network_action(self
, network_uuid
=None):
4354 Method leverages vCloud director and query network based on network uuid
4357 vca - is active VCA connection.
4358 network_uuid - is a network uuid
4361 The return XML respond
4363 if network_uuid
is None:
4366 url_list
= [self
.url
, "/api/network/", network_uuid
]
4367 vm_list_rest_call
= "".join(url_list
)
4369 if self
.client
._session
:
4371 "Accept": "application/*+xml;version=" + API_VERSION
,
4372 "x-vcloud-authorization": self
.client
._session
.headers
[
4373 "x-vcloud-authorization"
4376 response
= self
.perform_request(
4377 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4380 # Retry login if session expired & retry sending request
4381 if response
.status_code
== 403:
4382 response
= self
.retry_rest("GET", vm_list_rest_call
)
4384 if response
.status_code
== requests
.codes
.ok
:
4385 return response
.text
4389 def get_vcd_network(self
, network_uuid
=None):
4391 Method retrieves available network from vCloud Director
4394 network_uuid - is VCD network UUID
4396 Each element serialized as key : value pair
4398 Following keys available for access. network_configuration['Gateway'}
4402 <IsInherited>true</IsInherited>
4403 <Gateway>172.16.252.100</Gateway>
4404 <Netmask>255.255.255.0</Netmask>
4405 <Dns1>172.16.254.201</Dns1>
4406 <Dns2>172.16.254.202</Dns2>
4407 <DnsSuffix>vmwarelab.edu</DnsSuffix>
4408 <IsEnabled>true</IsEnabled>
4411 <StartAddress>172.16.252.1</StartAddress>
4412 <EndAddress>172.16.252.99</EndAddress>
4417 <FenceMode>bridged</FenceMode>
4420 The return dictionary and key for each entry vapp UUID
4422 network_configuration
= {}
4424 if network_uuid
is None:
4428 content
= self
.get_network_action(network_uuid
=network_uuid
)
4429 if content
is not None:
4430 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4431 network_configuration
["status"] = vm_list_xmlroot
.get("status")
4432 network_configuration
["name"] = vm_list_xmlroot
.get("name")
4433 network_configuration
["uuid"] = vm_list_xmlroot
.get("id").split(":")[3]
4435 for child
in vm_list_xmlroot
:
4436 if child
.tag
.split("}")[1] == "IsShared":
4437 network_configuration
["isShared"] = child
.text
.strip()
4439 if child
.tag
.split("}")[1] == "Configuration":
4440 for configuration
in child
.iter():
4441 tagKey
= configuration
.tag
.split("}")[1].strip()
4443 network_configuration
[
4445 ] = configuration
.text
.strip()
4446 except Exception as exp
:
4447 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
4449 raise vimconn
.VimConnException(
4450 "get_vcd_network: Failed with Exception {}".format(exp
)
4453 return network_configuration
4455 def delete_network_action(self
, network_uuid
=None):
4457 Method delete given network from vCloud director
4460 network_uuid - is a network uuid that client wish to delete
4463 The return None or XML respond or false
4465 client
= self
.connect_as_admin()
4468 raise vimconn
.VimConnConnectionException("Failed to connect vCD as admin")
4470 if network_uuid
is None:
4473 url_list
= [self
.url
, "/api/admin/network/", network_uuid
]
4474 vm_list_rest_call
= "".join(url_list
)
4478 "Accept": "application/*+xml;version=" + API_VERSION
,
4479 "x-vcloud-authorization": client
._session
.headers
[
4480 "x-vcloud-authorization"
4483 response
= self
.perform_request(
4484 req_type
="DELETE", url
=vm_list_rest_call
, headers
=headers
4487 if response
.status_code
== 202:
4496 parent_network_uuid
=None,
4501 Method create network in vCloud director
4504 network_name - is network name to be created.
4505 net_type - can be 'bridge','data','ptp','mgmt'.
4506 ip_profile is a dict containing the IP parameters of the network
4507 isshared - is a boolean
4508 parent_network_uuid - is parent provider vdc network that will be used for mapping.
4509 It optional attribute. by default if no parent network indicate the first available will be used.
4512 The return network uuid or return None
4514 new_network_name
= [network_name
, "-", str(uuid
.uuid4())]
4515 content
= self
.create_network_rest(
4516 network_name
="".join(new_network_name
),
4517 ip_profile
=ip_profile
,
4519 parent_network_uuid
=parent_network_uuid
,
4524 self
.logger
.debug("Failed create network {}.".format(network_name
))
4529 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
4530 vcd_uuid
= vm_list_xmlroot
.get("id").split(":")
4531 if len(vcd_uuid
) == 4:
4533 "Created new network name: {} uuid: {}".format(
4534 network_name
, vcd_uuid
[3]
4540 self
.logger
.debug("Failed create network {}".format(network_name
))
4544 def create_network_rest(
4548 parent_network_uuid
=None,
4553 Method create network in vCloud director
4556 network_name - is network name to be created.
4557 net_type - can be 'bridge','data','ptp','mgmt'.
4558 ip_profile is a dict containing the IP parameters of the network
4559 isshared - is a boolean
4560 parent_network_uuid - is parent provider vdc network that will be used for mapping.
4561 It optional attribute. by default if no parent network indicate the first available will be used.
4564 The return network uuid or return None
4566 client_as_admin
= self
.connect_as_admin()
4568 if not client_as_admin
:
4569 raise vimconn
.VimConnConnectionException("Failed to connect vCD.")
4571 if network_name
is None:
4574 url_list
= [self
.url
, "/api/admin/vdc/", self
.tenant_id
]
4575 vm_list_rest_call
= "".join(url_list
)
4577 if client_as_admin
._session
:
4579 "Accept": "application/*+xml;version=" + API_VERSION
,
4580 "x-vcloud-authorization": client_as_admin
._session
.headers
[
4581 "x-vcloud-authorization"
4584 response
= self
.perform_request(
4585 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4587 provider_network
= None
4588 available_networks
= None
4589 add_vdc_rest_url
= None
4591 if response
.status_code
!= requests
.codes
.ok
:
4593 "REST API call {} failed. Return status code {}".format(
4594 vm_list_rest_call
, response
.status_code
4601 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
4602 for child
in vm_list_xmlroot
:
4603 if child
.tag
.split("}")[1] == "ProviderVdcReference":
4604 provider_network
= child
.attrib
.get("href")
4605 # application/vnd.vmware.admin.providervdc+xml
4607 if child
.tag
.split("}")[1] == "Link":
4609 child
.attrib
.get("type")
4610 == "application/vnd.vmware.vcloud.orgVdcNetwork+xml"
4611 and child
.attrib
.get("rel") == "add"
4613 add_vdc_rest_url
= child
.attrib
.get("href")
4616 "Failed parse respond for rest api call {}".format(
4620 self
.logger
.debug("Respond body {}".format(response
.text
))
4624 # find pvdc provided available network
4625 response
= self
.perform_request(
4626 req_type
="GET", url
=provider_network
, headers
=headers
4629 if response
.status_code
!= requests
.codes
.ok
:
4631 "REST API call {} failed. Return status code {}".format(
4632 vm_list_rest_call
, response
.status_code
4638 if parent_network_uuid
is None:
4640 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
4641 for child
in vm_list_xmlroot
.iter():
4642 if child
.tag
.split("}")[1] == "AvailableNetworks":
4643 for networks
in child
.iter():
4644 # application/vnd.vmware.admin.network+xml
4645 if networks
.attrib
.get("href") is not None:
4646 available_networks
= networks
.attrib
.get("href")
4652 # Configure IP profile of the network
4654 ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
4658 "subnet_address" not in ip_profile
4659 or ip_profile
["subnet_address"] is None
4661 subnet_rand
= random
.randint(0, 255)
4662 ip_base
= "192.168.{}.".format(subnet_rand
)
4663 ip_profile
["subnet_address"] = ip_base
+ "0/24"
4665 ip_base
= ip_profile
["subnet_address"].rsplit(".", 1)[0] + "."
4668 "gateway_address" not in ip_profile
4669 or ip_profile
["gateway_address"] is None
4671 ip_profile
["gateway_address"] = ip_base
+ "1"
4673 if "dhcp_count" not in ip_profile
or ip_profile
["dhcp_count"] is None:
4674 ip_profile
["dhcp_count"] = DEFAULT_IP_PROFILE
["dhcp_count"]
4677 "dhcp_enabled" not in ip_profile
4678 or ip_profile
["dhcp_enabled"] is None
4680 ip_profile
["dhcp_enabled"] = DEFAULT_IP_PROFILE
["dhcp_enabled"]
4683 "dhcp_start_address" not in ip_profile
4684 or ip_profile
["dhcp_start_address"] is None
4686 ip_profile
["dhcp_start_address"] = ip_base
+ "3"
4688 if "ip_version" not in ip_profile
or ip_profile
["ip_version"] is None:
4689 ip_profile
["ip_version"] = DEFAULT_IP_PROFILE
["ip_version"]
4691 if "dns_address" not in ip_profile
or ip_profile
["dns_address"] is None:
4692 ip_profile
["dns_address"] = ip_base
+ "2"
4694 gateway_address
= ip_profile
["gateway_address"]
4695 dhcp_count
= int(ip_profile
["dhcp_count"])
4696 subnet_address
= self
.convert_cidr_to_netmask(
4697 ip_profile
["subnet_address"]
4700 if ip_profile
["dhcp_enabled"] is True:
4701 dhcp_enabled
= "true"
4703 dhcp_enabled
= "false"
4705 dhcp_start_address
= ip_profile
["dhcp_start_address"]
4707 # derive dhcp_end_address from dhcp_start_address & dhcp_count
4708 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
4709 end_ip_int
+= dhcp_count
- 1
4710 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
4712 # ip_version = ip_profile['ip_version']
4713 dns_address
= ip_profile
["dns_address"]
4714 except KeyError as exp
:
4715 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
4717 raise vimconn
.VimConnException(
4718 "Create Network REST: Key error{}".format(exp
)
4721 # either use client provided UUID or search for a first available
4722 # if both are not defined we return none
4723 if parent_network_uuid
is not None:
4724 provider_network
= None
4725 available_networks
= None
4726 add_vdc_rest_url
= None
4727 url_list
= [self
.url
, "/api/admin/vdc/", self
.tenant_id
, "/networks"]
4728 add_vdc_rest_url
= "".join(url_list
)
4729 url_list
= [self
.url
, "/api/admin/network/", parent_network_uuid
]
4730 available_networks
= "".join(url_list
)
4732 # Creating all networks as Direct Org VDC type networks.
4733 # Unused in case of Underlay (data/ptp) network interface.
4734 fence_mode
= "isolated"
4735 is_inherited
= "false"
4736 dns_list
= dns_address
.split(";")
4740 if len(dns_list
) >= 2:
4741 dns2_text
= "\n <Dns2>{}</Dns2>\n".format(
4745 if net_type
== "isolated":
4746 fence_mode
= "isolated"
4747 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
4748 <Description>Openmano created</Description>
4752 <IsInherited>{1:s}</IsInherited>
4753 <Gateway>{2:s}</Gateway>
4754 <Netmask>{3:s}</Netmask>
4755 <Dns1>{4:s}</Dns1>{5:s}
4756 <IsEnabled>{6:s}</IsEnabled>
4759 <StartAddress>{7:s}</StartAddress>
4760 <EndAddress>{8:s}</EndAddress>
4765 <FenceMode>{9:s}</FenceMode>
4767 <IsShared>{10:s}</IsShared>
4768 </OrgVdcNetwork> """.format(
4769 escape(network_name
),
4782 fence_mode
= "bridged"
4783 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
4784 <Description>Openmano created</Description>
4788 <IsInherited>{1:s}</IsInherited>
4789 <Gateway>{2:s}</Gateway>
4790 <Netmask>{3:s}</Netmask>
4791 <Dns1>{4:s}</Dns1>{5:s}
4792 <IsEnabled>{6:s}</IsEnabled>
4795 <StartAddress>{7:s}</StartAddress>
4796 <EndAddress>{8:s}</EndAddress>
4801 <ParentNetwork href="{9:s}"/>
4802 <FenceMode>{10:s}</FenceMode>
4804 <IsShared>{11:s}</IsShared>
4805 </OrgVdcNetwork> """.format(
4806 escape(network_name
),
4820 headers
["Content-Type"] = "application/vnd.vmware.vcloud.orgVdcNetwork+xml"
4822 response
= self
.perform_request(
4823 req_type
="POST", url
=add_vdc_rest_url
, headers
=headers
, data
=data
4826 if response
.status_code
!= 201:
4828 "Create Network POST REST API call failed. "
4829 "Return status code {}, response.text: {}".format(
4830 response
.status_code
, response
.text
4834 network_task
= self
.get_task_from_response(response
.text
)
4836 "Create Network REST : Waiting for Network creation complete"
4839 result
= self
.client
.get_task_monitor().wait_for_success(
4843 if result
.get("status") == "success":
4844 return response
.text
4847 "create_network_rest task failed. Network Create response : {}".format(
4851 except Exception as exp
:
4852 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
4856 def convert_cidr_to_netmask(self
, cidr_ip
=None):
4858 Method sets convert CIDR netmask address to normal IP format
4860 cidr_ip : CIDR IP address
4862 netmask : Converted netmask
4864 if cidr_ip
is not None:
4866 _
, net_bits
= cidr_ip
.split("/")
4867 netmask
= socket
.inet_ntoa(
4868 struct
.pack(">I", (0xFFFFFFFF << (32 - int(net_bits
))) & 0xFFFFFFFF)
4877 def get_provider_rest(self
, vca
=None):
4879 Method gets provider vdc view from vcloud director
4882 network_name - is network name to be created.
4883 parent_network_uuid - is parent provider vdc network that will be used for mapping.
4884 It optional attribute. by default if no parent network indicate the first available will be used.
4887 The return xml content of respond or None
4889 url_list
= [self
.url
, "/api/admin"]
4893 "Accept": "application/*+xml;version=" + API_VERSION
,
4894 "x-vcloud-authorization": self
.client
._session
.headers
[
4895 "x-vcloud-authorization"
4898 response
= self
.perform_request(
4899 req_type
="GET", url
="".join(url_list
), headers
=headers
4902 if response
.status_code
== requests
.codes
.ok
:
4903 return response
.text
4907 def create_vdc(self
, vdc_name
=None):
4909 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
4911 if xml_content
is not None:
4913 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
4914 for child
in task_resp_xmlroot
:
4915 if child
.tag
.split("}")[1] == "Owner":
4916 vdc_id
= child
.attrib
.get("href").split("/")[-1]
4917 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get("href")
4921 self
.logger
.debug("Respond body {}".format(xml_content
))
4925 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
4927 Method create vdc in vCloud director based on VDC template.
4928 it uses pre-defined template.
4931 vdc_name - name of a new vdc.
4934 The return xml content of respond or None
4936 # pre-requesite atleast one vdc template should be available in vCD
4937 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
4938 vca
= self
.connect_as_admin()
4941 raise vimconn
.VimConnConnectionException("Failed to connect vCD")
4943 if vdc_name
is None:
4946 url_list
= [self
.url
, "/api/vdcTemplates"]
4947 vm_list_rest_call
= "".join(url_list
)
4949 "Accept": "application/*+xml;version=" + API_VERSION
,
4950 "x-vcloud-authorization": vca
._session
.headers
["x-vcloud-authorization"],
4952 response
= self
.perform_request(
4953 req_type
="GET", url
=vm_list_rest_call
, headers
=headers
4956 # container url to a template
4957 vdc_template_ref
= None
4959 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
4960 for child
in vm_list_xmlroot
:
4961 # application/vnd.vmware.admin.providervdc+xml
4962 # we need find a template from witch we instantiate VDC
4963 if child
.tag
.split("}")[1] == "VdcTemplate":
4965 child
.attrib
.get("type")
4966 == "application/vnd.vmware.admin.vdcTemplate+xml"
4968 vdc_template_ref
= child
.attrib
.get("href")
4971 "Failed parse respond for rest api call {}".format(vm_list_rest_call
)
4973 self
.logger
.debug("Respond body {}".format(response
.text
))
4977 # if we didn't found required pre defined template we return None
4978 if vdc_template_ref
is None:
4983 url_list
= [self
.url
, "/api/org/", self
.org_uuid
, "/action/instantiate"]
4984 vm_list_rest_call
= "".join(url_list
)
4985 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
4986 <Source href="{1:s}"></Source>
4987 <Description>opnemano</Description>
4988 </InstantiateVdcTemplateParams>""".format(
4989 vdc_name
, vdc_template_ref
4993 ] = "application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml"
4994 response
= self
.perform_request(
4995 req_type
="POST", url
=vm_list_rest_call
, headers
=headers
, data
=data
4997 vdc_task
= self
.get_task_from_response(response
.text
)
4998 self
.client
.get_task_monitor().wait_for_success(task
=vdc_task
)
5000 # if we all ok we respond with content otherwise by default None
5001 if response
.status_code
>= 200 and response
.status_code
< 300:
5002 return response
.text
5007 "Failed parse respond for rest api call {}".format(vm_list_rest_call
)
5009 self
.logger
.debug("Respond body {}".format(response
.text
))
5013 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
5015 Method retrieve vapp detail from vCloud director
5018 vapp_uuid - is vapp identifier.
5021 The return network uuid or return None
5026 if need_admin_access
:
5027 vca
= self
.connect_as_admin()
5032 raise vimconn
.VimConnConnectionException("Failed to connect vCD")
5033 if vapp_uuid
is None:
5036 url_list
= [self
.url
, "/api/vApp/vapp-", vapp_uuid
]
5037 get_vapp_restcall
= "".join(url_list
)
5041 "Accept": "application/*+xml;version=" + API_VERSION
,
5042 "x-vcloud-authorization": vca
._session
.headers
[
5043 "x-vcloud-authorization"
5046 response
= self
.perform_request(
5047 req_type
="GET", url
=get_vapp_restcall
, headers
=headers
5050 if response
.status_code
== 403:
5051 if need_admin_access
is False:
5052 response
= self
.retry_rest("GET", get_vapp_restcall
)
5054 if response
.status_code
!= requests
.codes
.ok
:
5056 "REST API call {} failed. Return status code {}".format(
5057 get_vapp_restcall
, response
.status_code
5061 return parsed_respond
5064 xmlroot_respond
= XmlElementTree
.fromstring(response
.text
)
5065 parsed_respond
["ovfDescriptorUploaded"] = xmlroot_respond
.attrib
[
5066 "ovfDescriptorUploaded"
5069 "vssd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData",
5070 "ovf": "http://schemas.dmtf.org/ovf/envelope/1",
5071 "vmw": "http://www.vmware.com/schema/ovf",
5072 "vm": "http://www.vmware.com/vcloud/v1.5",
5073 "rasd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
5074 "vmext": "http://www.vmware.com/vcloud/extension/v1.5",
5075 "xmlns": "http://www.vmware.com/vcloud/v1.5",
5078 created_section
= xmlroot_respond
.find("vm:DateCreated", namespaces
)
5079 if created_section
is not None:
5080 parsed_respond
["created"] = created_section
.text
5082 network_section
= xmlroot_respond
.find(
5083 "vm:NetworkConfigSection/vm:NetworkConfig", namespaces
5086 network_section
is not None
5087 and "networkName" in network_section
.attrib
5089 parsed_respond
["networkname"] = network_section
.attrib
[
5093 ipscopes_section
= xmlroot_respond
.find(
5094 "vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes",
5097 if ipscopes_section
is not None:
5098 for ipscope
in ipscopes_section
:
5099 for scope
in ipscope
:
5100 tag_key
= scope
.tag
.split("}")[1]
5101 if tag_key
== "IpRanges":
5102 ip_ranges
= scope
.getchildren()
5103 for ipblock
in ip_ranges
:
5104 for block
in ipblock
:
5106 block
.tag
.split("}")[1]
5109 parsed_respond
[tag_key
] = scope
.text
5111 # parse children section for other attrib
5112 children_section
= xmlroot_respond
.find("vm:Children/", namespaces
)
5113 if children_section
is not None:
5114 parsed_respond
["name"] = children_section
.attrib
["name"]
5115 parsed_respond
["nestedHypervisorEnabled"] = (
5116 children_section
.attrib
["nestedHypervisorEnabled"]
5117 if "nestedHypervisorEnabled" in children_section
.attrib
5120 parsed_respond
["deployed"] = children_section
.attrib
["deployed"]
5121 parsed_respond
["status"] = children_section
.attrib
["status"]
5122 parsed_respond
["vmuuid"] = children_section
.attrib
["id"].split(":")[
5125 network_adapter
= children_section
.find(
5126 "vm:NetworkConnectionSection", namespaces
5129 for adapters
in network_adapter
:
5130 adapter_key
= adapters
.tag
.split("}")[1]
5131 if adapter_key
== "PrimaryNetworkConnectionIndex":
5132 parsed_respond
["primarynetwork"] = adapters
.text
5134 if adapter_key
== "NetworkConnection":
5136 if "network" in adapters
.attrib
:
5137 vnic
["network"] = adapters
.attrib
["network"]
5138 for adapter
in adapters
:
5139 setting_key
= adapter
.tag
.split("}")[1]
5140 vnic
[setting_key
] = adapter
.text
5141 nic_list
.append(vnic
)
5143 for link
in children_section
:
5144 if link
.tag
.split("}")[1] == "Link" and "rel" in link
.attrib
:
5145 if link
.attrib
["rel"] == "screen:acquireTicket":
5146 parsed_respond
["acquireTicket"] = link
.attrib
5148 if link
.attrib
["rel"] == "screen:acquireMksTicket":
5149 parsed_respond
["acquireMksTicket"] = link
.attrib
5151 parsed_respond
["interfaces"] = nic_list
5152 vCloud_extension_section
= children_section
.find(
5153 "xmlns:VCloudExtension", namespaces
5155 if vCloud_extension_section
is not None:
5156 vm_vcenter_info
= {}
5157 vim_info
= vCloud_extension_section
.find(
5158 "vmext:VmVimInfo", namespaces
5160 vmext
= vim_info
.find("vmext:VmVimObjectRef", namespaces
)
5162 if vmext
is not None:
5163 vm_vcenter_info
["vm_moref_id"] = vmext
.find(
5164 "vmext:MoRef", namespaces
5167 parsed_respond
["vm_vcenter_info"] = vm_vcenter_info
5169 virtual_hardware_section
= children_section
.find(
5170 "ovf:VirtualHardwareSection", namespaces
5172 vm_virtual_hardware_info
= {}
5173 if virtual_hardware_section
is not None:
5174 for item
in virtual_hardware_section
.iterfind(
5175 "ovf:Item", namespaces
5178 item
.find("rasd:Description", namespaces
).text
5181 disk_size
= item
.find(
5182 "rasd:HostResource", namespaces
5183 ).attrib
["{" + namespaces
["vm"] + "}capacity"]
5184 vm_virtual_hardware_info
["disk_size"] = disk_size
5187 for link
in virtual_hardware_section
:
5189 link
.tag
.split("}")[1] == "Link"
5190 and "rel" in link
.attrib
5192 if link
.attrib
["rel"] == "edit" and link
.attrib
[
5194 ].endswith("/disks"):
5195 vm_virtual_hardware_info
[
5197 ] = link
.attrib
["href"]
5200 parsed_respond
["vm_virtual_hardware"] = vm_virtual_hardware_info
5201 except Exception as exp
:
5203 "Error occurred calling rest api for getting vApp details {}".format(
5208 return parsed_respond
5210 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
5212 Method retrieve vm disk details
5215 vapp_uuid - is vapp identifier.
5216 flavor_disk - disk size as specified in VNFD (flavor)
5219 The return network uuid or return None
5223 # Flavor disk is in GB convert it into MB
5224 flavor_disk
= int(flavor_disk
) * 1024
5225 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
5228 vm_name
= vm_details
["name"]
5229 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
5231 if vm_details
and "vm_virtual_hardware" in vm_details
:
5232 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
5233 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
5234 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
5236 if flavor_disk
> vm_disk
:
5237 status
= self
.modify_vm_disk_rest(disk_edit_href
, flavor_disk
)
5239 "Modify disk of VM {} from {} to {} MB".format(
5240 vm_name
, vm_disk
, flavor_disk
5245 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
5248 except Exception as exp
:
5249 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
5251 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
5253 Method retrieve modify vm disk size
5256 disk_href - vCD API URL to GET and PUT disk data
5257 disk_size - disk size as specified in VNFD (flavor)
5260 The return network uuid or return None
5262 if disk_href
is None or disk_size
is None:
5265 if self
.client
._session
:
5267 "Accept": "application/*+xml;version=" + API_VERSION
,
5268 "x-vcloud-authorization": self
.client
._session
.headers
[
5269 "x-vcloud-authorization"
5272 response
= self
.perform_request(
5273 req_type
="GET", url
=disk_href
, headers
=headers
5276 if response
.status_code
== 403:
5277 response
= self
.retry_rest("GET", disk_href
)
5279 if response
.status_code
!= requests
.codes
.ok
:
5281 "GET REST API call {} failed. Return status code {}".format(
5282 disk_href
, response
.status_code
5289 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
5291 prefix
: uri
for prefix
, uri
in lxmlroot_respond
.nsmap
.items() if prefix
5293 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
5295 for item
in lxmlroot_respond
.iterfind("xmlns:Item", namespaces
):
5296 if item
.find("rasd:Description", namespaces
).text
== "Hard disk":
5297 disk_item
= item
.find("rasd:HostResource", namespaces
)
5298 if disk_item
is not None:
5299 disk_item
.attrib
["{" + namespaces
["xmlns"] + "}capacity"] = str(
5304 data
= lxmlElementTree
.tostring(
5305 lxmlroot_respond
, encoding
="utf8", method
="xml", xml_declaration
=True
5308 # Send PUT request to modify disk size
5311 ] = "application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1"
5313 response
= self
.perform_request(
5314 req_type
="PUT", url
=disk_href
, headers
=headers
, data
=data
5316 if response
.status_code
== 403:
5317 add_headers
= {"Content-Type": headers
["Content-Type"]}
5318 response
= self
.retry_rest("PUT", disk_href
, add_headers
, data
)
5320 if response
.status_code
!= 202:
5322 "PUT REST API call {} failed. Return status code {}".format(
5323 disk_href
, response
.status_code
5327 modify_disk_task
= self
.get_task_from_response(response
.text
)
5328 result
= self
.client
.get_task_monitor().wait_for_success(
5329 task
=modify_disk_task
5331 if result
.get("status") == "success":
5337 except Exception as exp
:
5339 "Error occurred calling rest api for modifing disk size {}".format(exp
)
5344 def add_serial_device(self
, vapp_uuid
):
5346 Method to attach a serial device to a VM
5349 vapp_uuid - uuid of vApp/VM
5353 self
.logger
.info("Add serial devices into vApp {}".format(vapp_uuid
))
5354 _
, content
= self
.get_vcenter_content()
5355 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
5359 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
5361 "VM {} is currently on host {}".format(vm_obj
, host_obj
)
5363 if host_obj
and vm_obj
:
5364 spec
= vim
.vm
.ConfigSpec()
5365 spec
.deviceChange
= []
5366 serial_spec
= vim
.vm
.device
.VirtualDeviceSpec()
5367 serial_spec
.operation
= "add"
5368 serial_port
= vim
.vm
.device
.VirtualSerialPort()
5369 serial_port
.yieldOnPoll
= True
5370 backing
= serial_port
.URIBackingInfo()
5371 backing
.serviceURI
= "tcp://:65500"
5372 backing
.direction
= "server"
5373 serial_port
.backing
= backing
5374 serial_spec
.device
= serial_port
5375 spec
.deviceChange
.append(serial_spec
)
5376 vm_obj
.ReconfigVM_Task(spec
=spec
)
5377 self
.logger
.info("Adding serial device to VM {}".format(vm_obj
))
5378 except vmodl
.MethodFault
as error
:
5379 self
.logger
.error("Error occurred while adding PCI devices {} ", error
)
5381 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
5383 Method to attach pci devices to VM
5386 vapp_uuid - uuid of vApp/VM
5387 pci_devices - pci devices infromation as specified in VNFD (flavor)
5390 The status of add pci device task , vm object and
5391 vcenter_conect object
5395 "Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
)
5397 vcenter_conect
, content
= self
.get_vcenter_content()
5398 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
5402 no_of_pci_devices
= len(pci_devices
)
5403 if no_of_pci_devices
> 0:
5404 # Get VM and its host
5405 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
5407 "VM {} is currently on host {}".format(vm_obj
, host_obj
)
5410 if host_obj
and vm_obj
:
5411 # get PCI devies from host on which vapp is currently installed
5412 avilable_pci_devices
= self
.get_pci_devices(
5413 host_obj
, no_of_pci_devices
5416 if avilable_pci_devices
is None:
5417 # find other hosts with active pci devices
5420 avilable_pci_devices
,
5421 ) = self
.get_host_and_PCIdevices(content
, no_of_pci_devices
)
5424 new_host_obj
is not None
5425 and avilable_pci_devices
is not None
5426 and len(avilable_pci_devices
) > 0
5428 # Migrate vm to the host where PCI devices are availble
5430 "Relocate VM {} on new host {}".format(
5431 vm_obj
, new_host_obj
5435 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
5436 if task
is not None:
5437 result
= self
.wait_for_vcenter_task(
5438 task
, vcenter_conect
5441 "Migrate VM status: {}".format(result
)
5443 host_obj
= new_host_obj
5446 "Fail to migrate VM : {}".format(result
)
5448 raise vimconn
.VimConnNotFoundException(
5449 "Fail to migrate VM : {} to host {}".format(
5450 vmname_andid
, new_host_obj
5455 host_obj
is not None
5456 and avilable_pci_devices
is not None
5457 and len(avilable_pci_devices
) > 0
5459 # Add PCI devices one by one
5460 for pci_device
in avilable_pci_devices
:
5461 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
5463 status
= self
.wait_for_vcenter_task(
5464 task
, vcenter_conect
5469 "Added PCI device {} to VM {}".format(
5470 pci_device
, str(vm_obj
)
5475 "Fail to add PCI device {} to VM {}".format(
5476 pci_device
, str(vm_obj
)
5480 return True, vm_obj
, vcenter_conect
5483 "Currently there is no host with"
5484 " {} number of avaialble PCI devices required for VM {}".format(
5485 no_of_pci_devices
, vmname_andid
5489 raise vimconn
.VimConnNotFoundException(
5490 "Currently there is no host with {} "
5491 "number of avaialble PCI devices required for VM {}".format(
5492 no_of_pci_devices
, vmname_andid
5497 "No infromation about PCI devices {} ", pci_devices
5499 except vmodl
.MethodFault
as error
:
5500 self
.logger
.error("Error occurred while adding PCI devices {} ", error
)
5502 return None, vm_obj
, vcenter_conect
5504 def get_vm_obj(self
, content
, mob_id
):
5506 Method to get the vsphere VM object associated with a given morf ID
5508 vapp_uuid - uuid of vApp/VM
5509 content - vCenter content object
5510 mob_id - mob_id of VM
5519 container
= content
.viewManager
.CreateContainerView(
5520 content
.rootFolder
, [vim
.VirtualMachine
], True
5522 for vm
in container
.view
:
5523 mobID
= vm
._GetMoId
()
5527 host_obj
= vm_obj
.runtime
.host
5529 except Exception as exp
:
5530 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
5532 return host_obj
, vm_obj
5534 def get_pci_devices(self
, host
, need_devices
):
5536 Method to get the details of pci devices on given host
5538 host - vSphere host object
5539 need_devices - number of pci devices needed on host
5542 array of pci devices
5546 used_devices_ids
= []
5550 pciPassthruInfo
= host
.config
.pciPassthruInfo
5551 pciDevies
= host
.hardware
.pciDevice
5553 for pci_status
in pciPassthruInfo
:
5554 if pci_status
.passthruActive
:
5555 for device
in pciDevies
:
5556 if device
.id == pci_status
.id:
5557 all_device_ids
.append(device
.id)
5558 all_devices
.append(device
)
5560 # check if devices are in use
5561 avalible_devices
= all_devices
5563 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
5564 vm_devices
= vm
.config
.hardware
.device
5565 for device
in vm_devices
:
5566 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
5567 if device
.backing
.id in all_device_ids
:
5568 for use_device
in avalible_devices
:
5569 if use_device
.id == device
.backing
.id:
5570 avalible_devices
.remove(use_device
)
5572 used_devices_ids
.append(device
.backing
.id)
5574 "Device {} from devices {}"
5575 "is in use".format(device
.backing
.id, device
)
5577 if len(avalible_devices
) < need_devices
:
5579 "Host {} don't have {} number of active devices".format(
5584 "found only {} devices {}".format(
5585 len(avalible_devices
), avalible_devices
5591 required_devices
= avalible_devices
[:need_devices
]
5593 "Found {} PCI devices on host {} but required only {}".format(
5594 len(avalible_devices
), host
, need_devices
5598 "Retruning {} devices as {}".format(need_devices
, required_devices
)
5601 return required_devices
5602 except Exception as exp
:
5604 "Error {} occurred while finding pci devices on host: {}".format(
5611 def get_host_and_PCIdevices(self
, content
, need_devices
):
5613 Method to get the details of pci devices infromation on all hosts
5616 content - vSphere host object
5617 need_devices - number of pci devices needed on host
5620 array of pci devices and host object
5623 pci_device_objs
= None
5627 container
= content
.viewManager
.CreateContainerView(
5628 content
.rootFolder
, [vim
.HostSystem
], True
5630 for host
in container
.view
:
5631 devices
= self
.get_pci_devices(host
, need_devices
)
5635 pci_device_objs
= devices
5637 except Exception as exp
:
5639 "Error {} occurred while finding pci devices on host: {}".format(
5644 return host_obj
, pci_device_objs
5646 def relocate_vm(self
, dest_host
, vm
):
5648 Method to get the relocate VM to new host
5651 dest_host - vSphere host object
5652 vm - vSphere VM object
5660 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
5661 task
= vm
.Relocate(relocate_spec
)
5663 "Migrating {} to destination host {}".format(vm
, dest_host
)
5665 except Exception as exp
:
5667 "Error occurred while relocate VM {} to new host {}: {}".format(
5674 def wait_for_vcenter_task(self
, task
, actionName
="job", hideResult
=False):
5676 Waits and provides updates on a vSphere task
5678 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
5681 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
5682 if task
.info
.result
is not None and not hideResult
:
5684 "{} completed successfully, result: {}".format(
5685 actionName
, task
.info
.result
5689 self
.logger
.info("Task {} completed successfully.".format(actionName
))
5692 "{} did not complete successfully: {} ".format(
5693 actionName
, task
.info
.error
5697 return task
.info
.result
5699 def add_pci_to_vm(self
, host_object
, vm_object
, host_pci_dev
):
5701 Method to add pci device in given VM
5704 host_object - vSphere host object
5705 vm_object - vSphere VM object
5706 host_pci_dev - host_pci_dev must be one of the devices from the
5707 host_object.hardware.pciDevice list
5708 which is configured as a PCI passthrough device
5715 if vm_object
and host_object
and host_pci_dev
:
5717 # Add PCI device to VM
5718 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(
5721 systemid_by_pciid
= {
5722 item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
5725 if host_pci_dev
.id not in systemid_by_pciid
:
5727 "Device {} is not a passthrough device ".format(host_pci_dev
)
5731 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip("0x")
5732 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(
5735 systemId
=systemid_by_pciid
[host_pci_dev
.id],
5736 vendorId
=host_pci_dev
.vendorId
,
5737 deviceName
=host_pci_dev
.deviceName
,
5740 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
5741 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
5742 new_device_config
.operation
= "add"
5743 vmConfigSpec
= vim
.vm
.ConfigSpec()
5744 vmConfigSpec
.deviceChange
= [new_device_config
]
5745 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
5747 "Adding PCI device {} into VM {} from host {} ".format(
5748 host_pci_dev
, vm_object
, host_object
5751 except Exception as exp
:
5753 "Error occurred while adding pci devive {} to VM {}: {}".format(
5754 host_pci_dev
, vm_object
, exp
5760 def get_vm_vcenter_info(self
):
5762 Method to get details of vCenter and vm
5765 vapp_uuid - uuid of vApp or VM
5768 Moref Id of VM and deails of vCenter
5770 vm_vcenter_info
= {}
5772 if self
.vcenter_ip
is not None:
5773 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
5775 raise vimconn
.VimConnException(
5776 message
="vCenter IP is not provided."
5777 " Please provide vCenter IP while attaching datacenter "
5778 "to tenant in --config"
5781 if self
.vcenter_port
is not None:
5782 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
5784 raise vimconn
.VimConnException(
5785 message
="vCenter port is not provided."
5786 " Please provide vCenter port while attaching datacenter "
5787 "to tenant in --config"
5790 if self
.vcenter_user
is not None:
5791 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
5793 raise vimconn
.VimConnException(
5794 message
="vCenter user is not provided."
5795 " Please provide vCenter user while attaching datacenter "
5796 "to tenant in --config"
5799 if self
.vcenter_password
is not None:
5800 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
5802 raise vimconn
.VimConnException(
5803 message
="vCenter user password is not provided."
5804 " Please provide vCenter user password while attaching datacenter "
5805 "to tenant in --config"
5808 return vm_vcenter_info
5810 def get_vm_pci_details(self
, vmuuid
):
5812 Method to get VM PCI device details from vCenter
5815 vm_obj - vSphere VM object
5818 dict of PCI devives attached to VM
5821 vm_pci_devices_info
= {}
5824 _
, content
= self
.get_vcenter_content()
5825 vm_moref_id
= self
.get_vm_moref_id(vmuuid
)
5827 # Get VM and its host
5829 host_obj
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
5830 if host_obj
and vm_obj
:
5831 vm_pci_devices_info
["host_name"] = host_obj
.name
5832 vm_pci_devices_info
["host_ip"] = host_obj
.config
.network
.vnic
[
5836 for device
in vm_obj
.config
.hardware
.device
:
5837 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
5839 "devide_id": device
.backing
.id,
5840 "pciSlotNumber": device
.slotInfo
.pciSlotNumber
,
5842 vm_pci_devices_info
[
5843 device
.deviceInfo
.label
5847 "Can not connect to vCenter while getting "
5848 "PCI devices infromationn"
5851 return vm_pci_devices_info
5852 except Exception as exp
:
5854 "Error occurred while getting VM information" " for VM : {}".format(exp
)
5857 raise vimconn
.VimConnException(message
=exp
)
5859 def reserve_memory_for_all_vms(self
, vapp
, memory_mb
):
5861 Method to reserve memory for all VMs
5864 memory_mb - Memory in MB
5868 self
.logger
.info("Reserve memory for all VMs")
5870 for vms
in vapp
.get_all_vms():
5871 vm_id
= vms
.get("id").split(":")[-1]
5872 url_rest_call
= "{}/api/vApp/vm-{}/virtualHardwareSection/memory".format(
5876 "Accept": "application/*+xml;version=" + API_VERSION
,
5877 "x-vcloud-authorization": self
.client
._session
.headers
[
5878 "x-vcloud-authorization"
5881 headers
["Content-Type"] = "application/vnd.vmware.vcloud.rasdItem+xml"
5882 response
= self
.perform_request(
5883 req_type
="GET", url
=url_rest_call
, headers
=headers
5886 if response
.status_code
== 403:
5887 response
= self
.retry_rest("GET", url_rest_call
)
5889 if response
.status_code
!= 200:
5891 "REST call {} failed reason : {}"
5892 "status code : {}".format(
5893 url_rest_call
, response
.text
, response
.status_code
5896 raise vimconn
.VimConnException(
5897 "reserve_memory_for_all_vms : Failed to get " "memory"
5900 bytexml
= bytes(bytearray(response
.text
, encoding
="utf-8"))
5901 contentelem
= lxmlElementTree
.XML(bytexml
)
5903 prefix
: uri
for prefix
, uri
in contentelem
.nsmap
.items() if prefix
5905 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
5907 # Find the reservation element in the response
5908 memelem_list
= contentelem
.findall(".//rasd:Reservation", namespaces
)
5909 for memelem
in memelem_list
:
5910 memelem
.text
= str(memory_mb
)
5912 newdata
= lxmlElementTree
.tostring(contentelem
, pretty_print
=True)
5914 response
= self
.perform_request(
5915 req_type
="PUT", url
=url_rest_call
, headers
=headers
, data
=newdata
5918 if response
.status_code
== 403:
5919 add_headers
= {"Content-Type": headers
["Content-Type"]}
5920 response
= self
.retry_rest("PUT", url_rest_call
, add_headers
, newdata
)
5922 if response
.status_code
!= 202:
5924 "REST call {} failed reason : {}"
5925 "status code : {} ".format(
5926 url_rest_call
, response
.text
, response
.status_code
5929 raise vimconn
.VimConnException(
5930 "reserve_memory_for_all_vms : Failed to update "
5931 "virtual hardware memory section"
5934 mem_task
= self
.get_task_from_response(response
.text
)
5935 result
= self
.client
.get_task_monitor().wait_for_success(task
=mem_task
)
5937 if result
.get("status") == "success":
5939 "reserve_memory_for_all_vms(): VM {} succeeded ".format(vm_id
)
5943 "reserve_memory_for_all_vms(): VM {} failed ".format(vm_id
)
5946 def connect_vapp_to_org_vdc_network(self
, vapp_id
, net_name
):
5948 Configure VApp network config with org vdc network
5956 "Connecting vapp {} to org vdc network {}".format(vapp_id
, net_name
)
5959 url_rest_call
= "{}/api/vApp/vapp-{}/networkConfigSection/".format(
5964 "Accept": "application/*+xml;version=" + API_VERSION
,
5965 "x-vcloud-authorization": self
.client
._session
.headers
[
5966 "x-vcloud-authorization"
5969 response
= self
.perform_request(
5970 req_type
="GET", url
=url_rest_call
, headers
=headers
5973 if response
.status_code
== 403:
5974 response
= self
.retry_rest("GET", url_rest_call
)
5976 if response
.status_code
!= 200:
5978 "REST call {} failed reason : {}"
5979 "status code : {}".format(
5980 url_rest_call
, response
.text
, response
.status_code
5983 raise vimconn
.VimConnException(
5984 "connect_vapp_to_org_vdc_network : Failed to get "
5985 "network config section"
5988 data
= response
.text
5991 ] = "application/vnd.vmware.vcloud.networkConfigSection+xml"
5992 net_id
= self
.get_network_id_by_name(net_name
)
5994 raise vimconn
.VimConnException(
5995 "connect_vapp_to_org_vdc_network : Failed to find " "existing network"
5998 bytexml
= bytes(bytearray(data
, encoding
="utf-8"))
5999 newelem
= lxmlElementTree
.XML(bytexml
)
6000 namespaces
= {prefix
: uri
for prefix
, uri
in newelem
.nsmap
.items() if prefix
}
6001 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
6002 nwcfglist
= newelem
.findall(".//xmlns:NetworkConfig", namespaces
)
6004 # VCD 9.7 returns an incorrect parentnetwork element. Fix it before PUT operation
6005 parentnetworklist
= newelem
.findall(".//xmlns:ParentNetwork", namespaces
)
6006 if parentnetworklist
:
6007 for pn
in parentnetworklist
:
6008 if "href" not in pn
.keys():
6009 id_val
= pn
.get("id")
6010 href_val
= "{}/api/network/{}".format(self
.url
, id_val
)
6011 pn
.set("href", href_val
)
6013 newstr
= """<NetworkConfig networkName="{}">
6015 <ParentNetwork href="{}/api/network/{}"/>
6016 <FenceMode>bridged</FenceMode>
6020 net_name
, self
.url
, net_id
6022 newcfgelem
= lxmlElementTree
.fromstring(newstr
)
6024 nwcfglist
[0].addnext(newcfgelem
)
6026 newdata
= lxmlElementTree
.tostring(newelem
, pretty_print
=True)
6028 response
= self
.perform_request(
6029 req_type
="PUT", url
=url_rest_call
, headers
=headers
, data
=newdata
6032 if response
.status_code
== 403:
6033 add_headers
= {"Content-Type": headers
["Content-Type"]}
6034 response
= self
.retry_rest("PUT", url_rest_call
, add_headers
, newdata
)
6036 if response
.status_code
!= 202:
6038 "REST call {} failed reason : {}"
6039 "status code : {} ".format(
6040 url_rest_call
, response
.text
, response
.status_code
6043 raise vimconn
.VimConnException(
6044 "connect_vapp_to_org_vdc_network : Failed to update "
6045 "network config section"
6048 vapp_task
= self
.get_task_from_response(response
.text
)
6049 result
= self
.client
.get_task_monitor().wait_for_success(task
=vapp_task
)
6050 if result
.get("status") == "success":
6052 "connect_vapp_to_org_vdc_network(): Vapp {} connected to "
6053 "network {}".format(vapp_id
, net_name
)
6057 "connect_vapp_to_org_vdc_network(): Vapp {} failed to "
6058 "connect to network {}".format(vapp_id
, net_name
)
6061 def remove_primary_network_adapter_from_all_vms(self
, vapp
):
6063 Method to remove network adapter type to vm
6069 self
.logger
.info("Removing network adapter from all VMs")
6071 for vms
in vapp
.get_all_vms():
6072 vm_id
= vms
.get("id").split(":")[-1]
6074 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(
6079 "Accept": "application/*+xml;version=" + API_VERSION
,
6080 "x-vcloud-authorization": self
.client
._session
.headers
[
6081 "x-vcloud-authorization"
6084 response
= self
.perform_request(
6085 req_type
="GET", url
=url_rest_call
, headers
=headers
6088 if response
.status_code
== 403:
6089 response
= self
.retry_rest("GET", url_rest_call
)
6091 if response
.status_code
!= 200:
6093 "REST call {} failed reason : {}"
6094 "status code : {}".format(
6095 url_rest_call
, response
.text
, response
.status_code
6098 raise vimconn
.VimConnException(
6099 "remove_primary_network_adapter : Failed to get "
6100 "network connection section"
6103 data
= response
.text
6104 data
= data
.split('<Link rel="edit"')[0]
6108 ] = "application/vnd.vmware.vcloud.networkConnectionSection+xml"
6110 newdata
= """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
6111 <NetworkConnectionSection xmlns="http://www.vmware.com/vcloud/v1.5"
6112 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
6113 xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
6114 xmlns:common="http://schemas.dmtf.org/wbem/wscim/1/common"
6115 xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
6116 xmlns:vmw="http://www.vmware.com/schema/ovf"
6117 xmlns:ovfenv="http://schemas.dmtf.org/ovf/environment/1"
6118 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
6119 xmlns:ns9="http://www.vmware.com/vcloud/versions"
6120 href="{url}" type="application/vnd.vmware.vcloud.networkConnectionSection+xml"
6121 ovf:required="false">
6122 <ovf:Info>Specifies the available VM network connections</ovf:Info>
6123 <PrimaryNetworkConnectionIndex>0</PrimaryNetworkConnectionIndex>
6124 <Link rel="edit" href="{url}"
6125 type="application/vnd.vmware.vcloud.networkConnectionSection+xml"/>
6126 </NetworkConnectionSection>""".format(
6129 response
= self
.perform_request(
6130 req_type
="PUT", url
=url_rest_call
, headers
=headers
, data
=newdata
6133 if response
.status_code
== 403:
6134 add_headers
= {"Content-Type": headers
["Content-Type"]}
6135 response
= self
.retry_rest("PUT", url_rest_call
, add_headers
, newdata
)
6137 if response
.status_code
!= 202:
6139 "REST call {} failed reason : {}"
6140 "status code : {} ".format(
6141 url_rest_call
, response
.text
, response
.status_code
6144 raise vimconn
.VimConnException(
6145 "remove_primary_network_adapter : Failed to update "
6146 "network connection section"
6149 nic_task
= self
.get_task_from_response(response
.text
)
6150 result
= self
.client
.get_task_monitor().wait_for_success(task
=nic_task
)
6151 if result
.get("status") == "success":
6153 "remove_primary_network_adapter(): VM {} conneced to "
6154 "default NIC type".format(vm_id
)
6158 "remove_primary_network_adapter(): VM {} failed to "
6159 "connect NIC type".format(vm_id
)
6162 def add_network_adapter_to_vms(
6163 self
, vapp
, network_name
, primary_nic_index
, nicIndex
, net
, nic_type
=None
6166 Method to add network adapter type to vm
6168 network_name - name of network
6169 primary_nic_index - int value for primary nic index
6170 nicIndex - int value for nic index
6171 nic_type - specify model name to which add to vm
6177 "Add network adapter to VM: network_name {} nicIndex {} nic_type {}".format(
6178 network_name
, nicIndex
, nic_type
6185 if "floating_ip" in net
:
6186 floating_ip
= net
["floating_ip"]
6188 # Stub for ip_address feature
6189 if "ip_address" in net
:
6190 ip_address
= net
["ip_address"]
6192 if "mac_address" in net
:
6193 mac_address
= net
["mac_address"]
6196 allocation_mode
= "POOL"
6198 allocation_mode
= "MANUAL"
6200 allocation_mode
= "DHCP"
6203 for vms
in vapp
.get_all_vms():
6204 vm_id
= vms
.get("id").split(":")[-1]
6207 "{}/api/vApp/vm-{}/networkConnectionSection/".format(
6213 "Accept": "application/*+xml;version=" + API_VERSION
,
6214 "x-vcloud-authorization": self
.client
._session
.headers
[
6215 "x-vcloud-authorization"
6218 response
= self
.perform_request(
6219 req_type
="GET", url
=url_rest_call
, headers
=headers
6222 if response
.status_code
== 403:
6223 response
= self
.retry_rest("GET", url_rest_call
)
6225 if response
.status_code
!= 200:
6227 "REST call {} failed reason : {}"
6228 "status code : {}".format(
6229 url_rest_call
, response
.text
, response
.status_code
6232 raise vimconn
.VimConnException(
6233 "add_network_adapter_to_vms : Failed to get "
6234 "network connection section"
6237 data
= response
.text
6238 data
= data
.split('<Link rel="edit"')[0]
6239 if "<PrimaryNetworkConnectionIndex>" not in data
:
6240 self
.logger
.debug("add_network_adapter PrimaryNIC not in data")
6241 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
6242 <NetworkConnection network="{}">
6243 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
6244 <IsConnected>true</IsConnected>
6245 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
6246 </NetworkConnection>""".format(
6247 primary_nic_index
, network_name
, nicIndex
, allocation_mode
6250 # Stub for ip_address feature
6252 ip_tag
= "<IpAddress>{}</IpAddress>".format(ip_address
)
6253 item
= item
.replace(
6254 "</NetworkConnectionIndex>\n",
6255 "</NetworkConnectionIndex>\n{}\n".format(ip_tag
),
6259 mac_tag
= "<MACAddress>{}</MACAddress>".format(mac_address
)
6260 item
= item
.replace(
6262 "</IsConnected>\n{}\n".format(mac_tag
),
6265 data
= data
.replace(
6267 "</ovf:Info>\n{}\n</NetworkConnectionSection>".format(item
),
6270 self
.logger
.debug("add_network_adapter PrimaryNIC in data")
6271 new_item
= """<NetworkConnection network="{}">
6272 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
6273 <IsConnected>true</IsConnected>
6274 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
6275 </NetworkConnection>""".format(
6276 network_name
, nicIndex
, allocation_mode
6279 # Stub for ip_address feature
6281 ip_tag
= "<IpAddress>{}</IpAddress>".format(ip_address
)
6282 new_item
= new_item
.replace(
6283 "</NetworkConnectionIndex>\n",
6284 "</NetworkConnectionIndex>\n{}\n".format(ip_tag
),
6288 mac_tag
= "<MACAddress>{}</MACAddress>".format(mac_address
)
6289 new_item
= new_item
.replace(
6291 "</IsConnected>\n{}\n".format(mac_tag
),
6294 data
= data
+ new_item
+ "</NetworkConnectionSection>"
6298 ] = "application/vnd.vmware.vcloud.networkConnectionSection+xml"
6300 response
= self
.perform_request(
6301 req_type
="PUT", url
=url_rest_call
, headers
=headers
, data
=data
6304 if response
.status_code
== 403:
6305 add_headers
= {"Content-Type": headers
["Content-Type"]}
6306 response
= self
.retry_rest(
6307 "PUT", url_rest_call
, add_headers
, data
6310 if response
.status_code
!= 202:
6312 "REST call {} failed reason : {}"
6313 "status code : {} ".format(
6314 url_rest_call
, response
.text
, response
.status_code
6317 raise vimconn
.VimConnException(
6318 "add_network_adapter_to_vms : Failed to update "
6319 "network connection section"
6322 nic_task
= self
.get_task_from_response(response
.text
)
6323 result
= self
.client
.get_task_monitor().wait_for_success(
6327 if result
.get("status") == "success":
6329 "add_network_adapter_to_vms(): VM {} conneced to "
6330 "default NIC type".format(vm_id
)
6334 "add_network_adapter_to_vms(): VM {} failed to "
6335 "connect NIC type".format(vm_id
)
6338 for vms
in vapp
.get_all_vms():
6339 vm_id
= vms
.get("id").split(":")[-1]
6342 "{}/api/vApp/vm-{}/networkConnectionSection/".format(
6348 "Accept": "application/*+xml;version=" + API_VERSION
,
6349 "x-vcloud-authorization": self
.client
._session
.headers
[
6350 "x-vcloud-authorization"
6353 response
= self
.perform_request(
6354 req_type
="GET", url
=url_rest_call
, headers
=headers
6357 if response
.status_code
== 403:
6358 response
= self
.retry_rest("GET", url_rest_call
)
6360 if response
.status_code
!= 200:
6362 "REST call {} failed reason : {}"
6363 "status code : {}".format(
6364 url_rest_call
, response
.text
, response
.status_code
6367 raise vimconn
.VimConnException(
6368 "add_network_adapter_to_vms : Failed to get "
6369 "network connection section"
6371 data
= response
.text
6372 data
= data
.split('<Link rel="edit"')[0]
6373 vcd_netadapter_type
= nic_type
6375 if nic_type
in ["SR-IOV", "VF"]:
6376 vcd_netadapter_type
= "SRIOVETHERNETCARD"
6378 if "<PrimaryNetworkConnectionIndex>" not in data
:
6380 "add_network_adapter PrimaryNIC not in data nic_type {}".format(
6384 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
6385 <NetworkConnection network="{}">
6386 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
6387 <IsConnected>true</IsConnected>
6388 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
6389 <NetworkAdapterType>{}</NetworkAdapterType>
6390 </NetworkConnection>""".format(
6395 vcd_netadapter_type
,
6398 # Stub for ip_address feature
6400 ip_tag
= "<IpAddress>{}</IpAddress>".format(ip_address
)
6401 item
= item
.replace(
6402 "</NetworkConnectionIndex>\n",
6403 "</NetworkConnectionIndex>\n{}\n".format(ip_tag
),
6407 mac_tag
= "<MACAddress>{}</MACAddress>".format(mac_address
)
6408 item
= item
.replace(
6410 "</IsConnected>\n{}\n".format(mac_tag
),
6413 data
= data
.replace(
6415 "</ovf:Info>\n{}\n</NetworkConnectionSection>".format(item
),
6419 "add_network_adapter PrimaryNIC in data nic_type {}".format(
6423 new_item
= """<NetworkConnection network="{}">
6424 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
6425 <IsConnected>true</IsConnected>
6426 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
6427 <NetworkAdapterType>{}</NetworkAdapterType>
6428 </NetworkConnection>""".format(
6429 network_name
, nicIndex
, allocation_mode
, vcd_netadapter_type
6432 # Stub for ip_address feature
6434 ip_tag
= "<IpAddress>{}</IpAddress>".format(ip_address
)
6435 new_item
= new_item
.replace(
6436 "</NetworkConnectionIndex>\n",
6437 "</NetworkConnectionIndex>\n{}\n".format(ip_tag
),
6441 mac_tag
= "<MACAddress>{}</MACAddress>".format(mac_address
)
6442 new_item
= new_item
.replace(
6444 "</IsConnected>\n{}\n".format(mac_tag
),
6447 data
= data
+ new_item
+ "</NetworkConnectionSection>"
6451 ] = "application/vnd.vmware.vcloud.networkConnectionSection+xml"
6453 response
= self
.perform_request(
6454 req_type
="PUT", url
=url_rest_call
, headers
=headers
, data
=data
6457 if response
.status_code
== 403:
6458 add_headers
= {"Content-Type": headers
["Content-Type"]}
6459 response
= self
.retry_rest(
6460 "PUT", url_rest_call
, add_headers
, data
6463 if response
.status_code
!= 202:
6465 "REST call {} failed reason : {}"
6466 "status code : {}".format(
6467 url_rest_call
, response
.text
, response
.status_code
6470 raise vimconn
.VimConnException(
6471 "add_network_adapter_to_vms : Failed to update "
6472 "network connection section"
6475 nic_task
= self
.get_task_from_response(response
.text
)
6476 result
= self
.client
.get_task_monitor().wait_for_success(
6480 if result
.get("status") == "success":
6482 "add_network_adapter_to_vms(): VM {} "
6483 "conneced to NIC type {}".format(vm_id
, nic_type
)
6487 "add_network_adapter_to_vms(): VM {} "
6488 "failed to connect NIC type {}".format(vm_id
, nic_type
)
6490 except Exception as exp
:
6492 "add_network_adapter_to_vms() : exception occurred "
6493 "while adding Network adapter"
6496 raise vimconn
.VimConnException(message
=exp
)
6498 def set_numa_affinity(self
, vmuuid
, paired_threads_id
):
6500 Method to assign numa affinity in vm configuration parammeters
6503 paired_threads_id - one or more virtual processor
6509 vcenter_conect
, content
= self
.get_vcenter_content()
6510 vm_moref_id
= self
.get_vm_moref_id(vmuuid
)
6511 _
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
6514 config_spec
= vim
.vm
.ConfigSpec()
6515 config_spec
.extraConfig
= []
6516 opt
= vim
.option
.OptionValue()
6517 opt
.key
= "numa.nodeAffinity"
6518 opt
.value
= str(paired_threads_id
)
6519 config_spec
.extraConfig
.append(opt
)
6520 task
= vm_obj
.ReconfigVM_Task(config_spec
)
6523 self
.wait_for_vcenter_task(task
, vcenter_conect
)
6524 extra_config
= vm_obj
.config
.extraConfig
6527 for opts
in extra_config
:
6528 if "numa.nodeAffinity" in opts
.key
:
6531 "set_numa_affinity: Sucessfully assign numa affinity "
6532 "value {} for vm {}".format(opt
.value
, vm_obj
)
6538 self
.logger
.error("set_numa_affinity: Failed to assign numa affinity")
6539 except Exception as exp
:
6541 "set_numa_affinity : exception occurred while setting numa affinity "
6542 "for VM {} : {}".format(vm_obj
, vm_moref_id
)
6545 raise vimconn
.VimConnException(
6546 "set_numa_affinity : Error {} failed to assign numa "
6547 "affinity".format(exp
)
6550 def add_new_disk(self
, vapp_uuid
, disk_size
):
6552 Method to create an empty vm disk
6555 vapp_uuid - is vapp identifier.
6556 disk_size - size of disk to be created in GB
6564 # Disk size in GB, convert it into MB
6565 if disk_size
is not None:
6566 disk_size_mb
= int(disk_size
) * 1024
6567 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
6569 if vm_details
and "vm_virtual_hardware" in vm_details
:
6571 "Adding disk to VM: {} disk size:{}GB".format(
6572 vm_details
["name"], disk_size
6575 disk_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
6576 status
= self
.add_new_disk_rest(disk_href
, disk_size_mb
)
6577 except Exception as exp
:
6578 msg
= "Error occurred while creating new disk {}.".format(exp
)
6579 self
.rollback_newvm(vapp_uuid
, msg
)
6583 "Added new disk to VM: {} disk size:{}GB".format(
6584 vm_details
["name"], disk_size
6588 # If failed to add disk, delete VM
6589 msg
= "add_new_disk: Failed to add new disk to {}".format(
6592 self
.rollback_newvm(vapp_uuid
, msg
)
6594 def add_new_disk_rest(self
, disk_href
, disk_size_mb
):
6596 Retrives vApp Disks section & add new empty disk
6599 disk_href: Disk section href to addd disk
6600 disk_size_mb: Disk size in MB
6602 Returns: Status of add new disk task
6605 if self
.client
._session
:
6607 "Accept": "application/*+xml;version=" + API_VERSION
,
6608 "x-vcloud-authorization": self
.client
._session
.headers
[
6609 "x-vcloud-authorization"
6612 response
= self
.perform_request(
6613 req_type
="GET", url
=disk_href
, headers
=headers
6616 if response
.status_code
== 403:
6617 response
= self
.retry_rest("GET", disk_href
)
6619 if response
.status_code
!= requests
.codes
.ok
:
6621 "add_new_disk_rest: GET REST API call {} failed. Return status code {}".format(
6622 disk_href
, response
.status_code
6629 # Find but type & max of instance IDs assigned to disks
6630 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
6632 prefix
: uri
for prefix
, uri
in lxmlroot_respond
.nsmap
.items() if prefix
6634 namespaces
["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
6637 for item
in lxmlroot_respond
.iterfind("xmlns:Item", namespaces
):
6638 if item
.find("rasd:Description", namespaces
).text
== "Hard disk":
6639 inst_id
= int(item
.find("rasd:InstanceID", namespaces
).text
)
6641 if inst_id
> instance_id
:
6642 instance_id
= inst_id
6643 disk_item
= item
.find("rasd:HostResource", namespaces
)
6644 bus_subtype
= disk_item
.attrib
[
6645 "{" + namespaces
["xmlns"] + "}busSubType"
6647 bus_type
= disk_item
.attrib
[
6648 "{" + namespaces
["xmlns"] + "}busType"
6651 instance_id
= instance_id
+ 1
6652 new_item
= """<Item>
6653 <rasd:Description>Hard disk</rasd:Description>
6654 <rasd:ElementName>New disk</rasd:ElementName>
6656 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
6657 vcloud:capacity="{}"
6658 vcloud:busSubType="{}"
6659 vcloud:busType="{}"></rasd:HostResource>
6660 <rasd:InstanceID>{}</rasd:InstanceID>
6661 <rasd:ResourceType>17</rasd:ResourceType>
6663 disk_size_mb
, bus_subtype
, bus_type
, instance_id
6666 new_data
= response
.text
6667 # Add new item at the bottom
6668 new_data
= new_data
.replace(
6669 "</Item>\n</RasdItemsList>",
6670 "</Item>\n{}\n</RasdItemsList>".format(new_item
),
6673 # Send PUT request to modify virtual hardware section with new disk
6676 ] = "application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1"
6678 response
= self
.perform_request(
6679 req_type
="PUT", url
=disk_href
, data
=new_data
, headers
=headers
6682 if response
.status_code
== 403:
6683 add_headers
= {"Content-Type": headers
["Content-Type"]}
6684 response
= self
.retry_rest("PUT", disk_href
, add_headers
, new_data
)
6686 if response
.status_code
!= 202:
6688 "PUT REST API call {} failed. Return status code {}. response.text:{}".format(
6689 disk_href
, response
.status_code
, response
.text
6693 add_disk_task
= self
.get_task_from_response(response
.text
)
6694 result
= self
.client
.get_task_monitor().wait_for_success(
6698 if result
.get("status") == "success":
6702 "Add new disk REST task failed to add {} MB disk".format(
6706 except Exception as exp
:
6708 "Error occurred calling rest api for creating new disk {}".format(exp
)
6713 def add_existing_disk(
6722 Method to add existing disk to vm
6724 catalogs - List of VDC catalogs
6725 image_id - Catalog ID
6726 template_name - Name of template in catalog
6727 vapp_uuid - UUID of vApp
6732 vcenter_conect
, content
= self
.get_vcenter_content()
6733 # find moref-id of vm in image
6734 catalog_vm_info
= self
.get_vapp_template_details(
6739 if catalog_vm_info
and "vm_vcenter_info" in catalog_vm_info
:
6740 if "vm_moref_id" in catalog_vm_info
["vm_vcenter_info"]:
6741 catalog_vm_moref_id
= catalog_vm_info
["vm_vcenter_info"].get(
6745 if catalog_vm_moref_id
:
6747 "Moref_id of VM in catalog : {}".format(catalog_vm_moref_id
)
6749 _
, catalog_vm_obj
= self
.get_vm_obj(content
, catalog_vm_moref_id
)
6752 # find existing disk
6753 disk_info
= self
.find_disk(catalog_vm_obj
)
6755 exp_msg
= "No VM with image id {} found".format(image_id
)
6756 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
6758 exp_msg
= "No Image found with image ID {} ".format(image_id
)
6759 self
.rollback_newvm(vapp_uuid
, exp_msg
, exp_type
="NotFound")
6762 self
.logger
.info("Existing disk_info : {}".format(disk_info
))
6764 vm_moref_id
= self
.get_vm_moref_id(vapp_uuid
)
6765 _
, vm_obj
= self
.get_vm_obj(content
, vm_moref_id
)
6768 status
= self
.add_disk(
6769 vcenter_conect
=vcenter_conect
,
6771 disk_info
=disk_info
,
6773 vapp_uuid
=vapp_uuid
,
6778 "Disk from image id {} added to {}".format(
6779 image_id
, vm_obj
.config
.name
6783 msg
= "No disk found with image id {} to add in VM {}".format(
6784 image_id
, vm_obj
.config
.name
6786 self
.rollback_newvm(vapp_uuid
, msg
, exp_type
="NotFound")
6788 def find_disk(self
, vm_obj
):
6790 Method to find details of existing disk in VM
6792 vm_obj - vCenter object of VM
6794 disk_info : dict of disk details
6799 devices
= vm_obj
.config
.hardware
.device
6801 for device
in devices
:
6802 if type(device
) is vim
.vm
.device
.VirtualDisk
:
6805 vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo
,
6806 ) and hasattr(device
.backing
, "fileName"):
6807 disk_info
["full_path"] = device
.backing
.fileName
6808 disk_info
["datastore"] = device
.backing
.datastore
6809 disk_info
["capacityKB"] = device
.capacityInKB
6811 except Exception as exp
:
6813 "find_disk() : exception occurred while "
6814 "getting existing disk details :{}".format(exp
)
6820 self
, vcenter_conect
=None, vm
=None, size
=None, vapp_uuid
=None, disk_info
={}
6823 Method to add existing disk in VM
6825 vcenter_conect - vCenter content object
6826 vm - vCenter vm object
6827 disk_info : dict of disk details
6829 status : status of add disk task
6831 datastore
= disk_info
["datastore"] if "datastore" in disk_info
else None
6832 fullpath
= disk_info
["full_path"] if "full_path" in disk_info
else None
6833 capacityKB
= disk_info
["capacityKB"] if "capacityKB" in disk_info
else None
6834 if size
is not None:
6835 # Convert size from GB to KB
6836 sizeKB
= int(size
) * 1024 * 1024
6837 # compare size of existing disk and user given size.Assign whicherver is greater
6839 "Add Existing disk : sizeKB {} , capacityKB {}".format(
6844 if sizeKB
> capacityKB
:
6847 if datastore
and fullpath
and capacityKB
:
6849 spec
= vim
.vm
.ConfigSpec()
6850 # get all disks on a VM, set unit_number to the next available
6852 for dev
in vm
.config
.hardware
.device
:
6853 if hasattr(dev
.backing
, "fileName"):
6854 unit_number
= int(dev
.unitNumber
) + 1
6855 # unit_number 7 reserved for scsi controller
6857 if unit_number
== 7:
6860 if isinstance(dev
, vim
.vm
.device
.VirtualDisk
):
6861 # vim.vm.device.VirtualSCSIController
6862 controller_key
= dev
.controllerKey
6865 "Add Existing disk : unit number {} , controller key {}".format(
6866 unit_number
, controller_key
6871 disk_spec
= vim
.vm
.device
.VirtualDeviceSpec()
6872 disk_spec
.operation
= vim
.vm
.device
.VirtualDeviceSpec
.Operation
.add
6873 disk_spec
.device
= vim
.vm
.device
.VirtualDisk()
6874 disk_spec
.device
.backing
= (
6875 vim
.vm
.device
.VirtualDisk
.FlatVer2BackingInfo()
6877 disk_spec
.device
.backing
.thinProvisioned
= True
6878 disk_spec
.device
.backing
.diskMode
= "persistent"
6879 disk_spec
.device
.backing
.datastore
= datastore
6880 disk_spec
.device
.backing
.fileName
= fullpath
6882 disk_spec
.device
.unitNumber
= unit_number
6883 disk_spec
.device
.capacityInKB
= capacityKB
6884 disk_spec
.device
.controllerKey
= controller_key
6885 dev_changes
.append(disk_spec
)
6886 spec
.deviceChange
= dev_changes
6887 task
= vm
.ReconfigVM_Task(spec
=spec
)
6888 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
6891 except Exception as exp
:
6893 "add_disk() : exception {} occurred while adding disk "
6894 "{} to vm {}".format(exp
, fullpath
, vm
.config
.name
)
6896 self
.rollback_newvm(vapp_uuid
, exp_msg
)
6898 msg
= "add_disk() : Can not add disk to VM with disk info {} ".format(
6901 self
.rollback_newvm(vapp_uuid
, msg
)
6903 def get_vcenter_content(self
):
6905 Get the vsphere content object
6908 vm_vcenter_info
= self
.get_vm_vcenter_info()
6909 except Exception as exp
:
6911 "Error occurred while getting vCenter infromationn"
6912 " for VM : {}".format(exp
)
6915 raise vimconn
.VimConnException(message
=exp
)
6918 if hasattr(ssl
, "_create_unverified_context"):
6919 context
= ssl
._create
_unverified
_context
()
6921 vcenter_conect
= SmartConnect(
6922 host
=vm_vcenter_info
["vm_vcenter_ip"],
6923 user
=vm_vcenter_info
["vm_vcenter_user"],
6924 pwd
=vm_vcenter_info
["vm_vcenter_password"],
6925 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
6928 atexit
.register(Disconnect
, vcenter_conect
)
6929 content
= vcenter_conect
.RetrieveContent()
6931 return vcenter_conect
, content
6933 def get_vm_moref_id(self
, vapp_uuid
):
6935 Get the moref_id of given VM
6939 vm_details
= self
.get_vapp_details_rest(
6940 vapp_uuid
, need_admin_access
=True
6943 if vm_details
and "vm_vcenter_info" in vm_details
:
6944 vm_moref_id
= vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
6947 except Exception as exp
:
6949 "Error occurred while getting VM moref ID " " for VM : {}".format(exp
)
6954 def get_vapp_template_details(
6955 self
, catalogs
=None, image_id
=None, template_name
=None
6958 Method to get vApp template details
6960 catalogs - list of VDC catalogs
6961 image_id - Catalog ID to find
6962 template_name : template name in catalog
6964 parsed_respond : dict of vApp tempalte details
6966 parsed_response
= {}
6968 vca
= self
.connect_as_admin()
6970 raise vimconn
.VimConnConnectionException("Failed to connect vCD")
6973 org
, _
= self
.get_vdc_details()
6974 catalog
= self
.get_catalog_obj(image_id
, catalogs
)
6976 items
= org
.get_catalog_item(catalog
.get("name"), catalog
.get("name"))
6977 catalog_items
= [items
.attrib
]
6979 if len(catalog_items
) == 1:
6981 "Accept": "application/*+xml;version=" + API_VERSION
,
6982 "x-vcloud-authorization": vca
._session
.headers
[
6983 "x-vcloud-authorization"
6986 response
= self
.perform_request(
6988 url
=catalog_items
[0].get("href"),
6991 catalogItem
= XmlElementTree
.fromstring(response
.text
)
6994 for child
in catalogItem
6995 if child
.get("type")
6996 == "application/vnd.vmware.vcloud.vAppTemplate+xml"
6998 vapp_tempalte_href
= entity
.get("href")
6999 # get vapp details and parse moref id
7002 "vssd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData",
7003 "ovf": "http://schemas.dmtf.org/ovf/envelope/1",
7004 "vmw": "http://www.vmware.com/schema/ovf",
7005 "vm": "http://www.vmware.com/vcloud/v1.5",
7006 "rasd": "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
7007 "vmext": "http://www.vmware.com/vcloud/extension/v1.5",
7008 "xmlns": "http://www.vmware.com/vcloud/v1.5",
7012 response
= self
.perform_request(
7013 req_type
="GET", url
=vapp_tempalte_href
, headers
=headers
7016 if response
.status_code
!= requests
.codes
.ok
:
7018 "REST API call {} failed. Return status code {}".format(
7019 vapp_tempalte_href
, response
.status_code
7023 xmlroot_respond
= XmlElementTree
.fromstring(response
.text
)
7024 children_section
= xmlroot_respond
.find(
7025 "vm:Children/", namespaces
7028 if children_section
is not None:
7029 vCloud_extension_section
= children_section
.find(
7030 "xmlns:VCloudExtension", namespaces
7033 if vCloud_extension_section
is not None:
7034 vm_vcenter_info
= {}
7035 vim_info
= vCloud_extension_section
.find(
7036 "vmext:VmVimInfo", namespaces
7038 vmext
= vim_info
.find(
7039 "vmext:VmVimObjectRef", namespaces
7042 if vmext
is not None:
7043 vm_vcenter_info
["vm_moref_id"] = vmext
.find(
7044 "vmext:MoRef", namespaces
7047 parsed_response
["vm_vcenter_info"] = vm_vcenter_info
7048 except Exception as exp
:
7050 "Error occurred calling rest api for getting vApp details {}".format(
7055 return parsed_response
7057 def rollback_newvm(self
, vapp_uuid
, msg
, exp_type
="Genric"):
7059 Method to delete vApp
7061 vapp_uuid - vApp UUID
7062 msg - Error message to be logged
7063 exp_type : Exception type
7068 self
.delete_vminstance(vapp_uuid
)
7072 self
.logger
.error(msg
)
7074 if exp_type
== "Genric":
7075 raise vimconn
.VimConnException(msg
)
7076 elif exp_type
== "NotFound":
7077 raise vimconn
.VimConnNotFoundException(message
=msg
)
7079 def get_sriov_devices(self
, host
, no_of_vfs
):
7081 Method to get the details of SRIOV devices on given host
7083 host - vSphere host object
7084 no_of_vfs - number of VFs needed on host
7087 array of SRIOV devices
7092 for device
in host
.config
.pciPassthruInfo
:
7093 if isinstance(device
, vim
.host
.SriovInfo
) and device
.sriovActive
:
7094 if device
.numVirtualFunction
>= no_of_vfs
:
7095 sriovInfo
.append(device
)
7100 def reconfig_portgroup(self
, content
, dvPort_group_name
, config_info
={}):
7102 Method to reconfigure disributed virtual portgroup
7105 dvPort_group_name - name of disributed virtual portgroup
7106 content - vCenter content object
7107 config_info - disributed virtual portgroup configuration
7113 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
7116 dv_pg_spec
= vim
.dvs
.DistributedVirtualPortgroup
.ConfigSpec()
7117 dv_pg_spec
.configVersion
= dvPort_group
.config
.configVersion
7118 dv_pg_spec
.defaultPortConfig
= (
7119 vim
.dvs
.VmwareDistributedVirtualSwitch
.VmwarePortConfigPolicy()
7122 if "vlanID" in config_info
:
7123 dv_pg_spec
.defaultPortConfig
.vlan
= (
7124 vim
.dvs
.VmwareDistributedVirtualSwitch
.VlanIdSpec()
7126 dv_pg_spec
.defaultPortConfig
.vlan
.vlanId
= config_info
.get("vlanID")
7128 task
= dvPort_group
.ReconfigureDVPortgroup_Task(spec
=dv_pg_spec
)
7133 except Exception as exp
:
7135 "Error occurred while reconfiguraing disributed virtaul port group {}"
7136 " : {}".format(dvPort_group_name
, exp
)
7141 def get_dvport_group(self
, dvPort_group_name
):
7143 Method to get disributed virtual portgroup
7146 network_name - name of network/portgroup
7151 _
, content
= self
.get_vcenter_content()
7155 container
= content
.viewManager
.CreateContainerView(
7156 content
.rootFolder
, [vim
.dvs
.DistributedVirtualPortgroup
], True
7159 for item
in container
.view
:
7160 if item
.key
== dvPort_group_name
:
7165 except vmodl
.MethodFault
as exp
:
7167 "Caught vmodl fault {} for disributed virtual port group {}".format(
7168 exp
, dvPort_group_name
7174 def get_vlanID_from_dvs_portgr(self
, dvPort_group_name
):
7176 Method to get disributed virtual portgroup vlanID
7179 network_name - name of network/portgroup
7187 dvPort_group
= self
.get_dvport_group(dvPort_group_name
)
7190 vlanId
= dvPort_group
.config
.defaultPortConfig
.vlan
.vlanId
7191 except vmodl
.MethodFault
as exp
:
7193 "Caught vmodl fault {} for disributed virtaul port group {}".format(
7194 exp
, dvPort_group_name
7200 def insert_media_to_vm(self
, vapp
, image_id
):
7202 Method to insert media CD-ROM (ISO image) from catalog to vm.
7203 vapp - vapp object to get vm id
7204 Image_id - image id for cdrom to be inerted to vm
7206 # create connection object
7207 vca
= self
.connect()
7209 # fetching catalog details
7210 rest_url
= "{}/api/catalog/{}".format(self
.url
, image_id
)
7214 "Accept": "application/*+xml;version=" + API_VERSION
,
7215 "x-vcloud-authorization": vca
._session
.headers
[
7216 "x-vcloud-authorization"
7219 response
= self
.perform_request(
7220 req_type
="GET", url
=rest_url
, headers
=headers
7223 if response
.status_code
!= 200:
7225 "REST call {} failed reason : {}"
7226 "status code : {}".format(
7227 rest_url
, response
.text
, response
.status_code
7231 raise vimconn
.VimConnException(
7232 "insert_media_to_vm(): Failed to get " "catalog details"
7235 # searching iso name and id
7236 iso_name
, media_id
= self
.get_media_details(vca
, response
.text
)
7238 if iso_name
and media_id
:
7239 data
= """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
7240 <ns6:MediaInsertOrEjectParams
7241 xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1"
7242 xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
7243 xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common"
7244 xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
7245 xmlns:ns6="http://www.vmware.com/vcloud/v1.5"
7246 xmlns:ns7="http://www.vmware.com/schema/ovf"
7247 xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1"
7248 xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
7250 type="application/vnd.vmware.vcloud.media+xml"
7252 id="urn:vcloud:media:{}"
7253 href="https://{}/api/media/{}"/>
7254 </ns6:MediaInsertOrEjectParams>""".format(
7255 iso_name
, media_id
, self
.url
, media_id
7258 for vms
in vapp
.get_all_vms():
7259 vm_id
= vms
.get("id").split(":")[-1]
7263 ] = "application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml"
7264 rest_url
= "{}/api/vApp/vm-{}/media/action/insertMedia".format(
7268 response
= self
.perform_request(
7269 req_type
="POST", url
=rest_url
, data
=data
, headers
=headers
7272 if response
.status_code
!= 202:
7274 "insert_media_to_vm() : Failed to insert CD-ROM to vm. Reason {}. "
7275 "Status code {}".format(response
.text
, response
.status_code
)
7277 self
.logger
.error(error_msg
)
7279 raise vimconn
.VimConnException(error_msg
)
7281 task
= self
.get_task_from_response(response
.text
)
7282 result
= self
.client
.get_task_monitor().wait_for_success(
7286 if result
.get("status") == "success":
7288 "insert_media_to_vm(): Sucessfully inserted media ISO"
7289 " image to vm {}".format(vm_id
)
7291 except Exception as exp
:
7293 "insert_media_to_vm() : exception occurred "
7294 "while inserting media CD-ROM"
7297 raise vimconn
.VimConnException(message
=exp
)
7299 def get_media_details(self
, vca
, content
):
7301 Method to get catalog item details
7302 vca - connection object
7303 content - Catalog details
7304 Return - Media name, media id
7306 cataloghref_list
= []
7309 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
7311 for child
in vm_list_xmlroot
.iter():
7312 if "CatalogItem" in child
.tag
:
7313 cataloghref_list
.append(child
.attrib
.get("href"))
7315 if cataloghref_list
is not None:
7316 for href
in cataloghref_list
:
7319 "Accept": "application/*+xml;version=" + API_VERSION
,
7320 "x-vcloud-authorization": vca
._session
.headers
[
7321 "x-vcloud-authorization"
7324 response
= self
.perform_request(
7325 req_type
="GET", url
=href
, headers
=headers
7328 if response
.status_code
!= 200:
7330 "REST call {} failed reason : {}"
7331 "status code : {}".format(
7332 href
, response
.text
, response
.status_code
7336 raise vimconn
.VimConnException(
7337 "get_media_details : Failed to get "
7338 "catalogitem details"
7341 list_xmlroot
= XmlElementTree
.fromstring(response
.text
)
7343 for child
in list_xmlroot
.iter():
7344 if "Entity" in child
.tag
:
7345 if "media" in child
.attrib
.get("href"):
7346 name
= child
.attrib
.get("name")
7348 child
.attrib
.get("href").split("/").pop()
7351 return name
, media_id
7353 self
.logger
.debug("Media name and id not found")
7356 except Exception as exp
:
7358 "get_media_details : exception occurred " "getting media details"
7361 raise vimconn
.VimConnException(message
=exp
)
7363 def retry_rest(self
, method
, url
, add_headers
=None, data
=None):
7364 """Method to get Token & retry respective REST request
7366 api - REST API - Can be one of 'GET' or 'PUT' or 'POST'
7367 url - request url to be used
7368 add_headers - Additional headers (optional)
7369 data - Request payload data to be passed in request
7371 response - Response of request
7378 if self
.client
._session
:
7380 "Accept": "application/*+xml;version=" + API_VERSION
,
7381 "x-vcloud-authorization": self
.client
._session
.headers
[
7382 "x-vcloud-authorization"
7387 headers
.update(add_headers
)
7390 response
= self
.perform_request(req_type
="GET", url
=url
, headers
=headers
)
7391 elif method
== "PUT":
7392 response
= self
.perform_request(
7393 req_type
="PUT", url
=url
, headers
=headers
, data
=data
7395 elif method
== "POST":
7396 response
= self
.perform_request(
7397 req_type
="POST", url
=url
, headers
=headers
, data
=data
7399 elif method
== "DELETE":
7400 response
= self
.perform_request(req_type
="DELETE", url
=url
, headers
=headers
)
7404 def get_token(self
):
7405 """Generate a new token if expired
7408 The return client object that letter can be used to connect to vCloud director as admin for VDC
7410 self
.client
= self
.connect()
7412 def get_vdc_details(self
):
7413 """Get VDC details using pyVcloud Lib
7415 Returns org and vdc object
7420 org
= Org(self
.client
, resource
=self
.client
.get_org())
7421 vdc
= org
.get_vdc(self
.tenant_name
)
7422 except Exception as e
:
7423 # pyvcloud not giving a specific exception, Refresh nevertheless
7424 self
.logger
.debug("Received exception {}, refreshing token ".format(str(e
)))
7426 # Retry once, if failed by refreshing token
7429 org
= Org(self
.client
, resource
=self
.client
.get_org())
7430 vdc
= org
.get_vdc(self
.tenant_name
)
7434 def perform_request(self
, req_type
, url
, headers
=None, data
=None):
7435 """Perform the POST/PUT/GET/DELETE request."""
7436 # Log REST request details
7437 self
.log_request(req_type
, url
=url
, headers
=headers
, data
=data
)
7438 # perform request and return its result
7440 if req_type
== "GET":
7441 response
= requests
.get(url
=url
, headers
=headers
, verify
=False)
7442 elif req_type
== "PUT":
7443 response
= requests
.put(url
=url
, headers
=headers
, data
=data
, verify
=False)
7444 elif req_type
== "POST":
7445 response
= requests
.post(url
=url
, headers
=headers
, data
=data
, verify
=False)
7446 elif req_type
== "DELETE":
7447 response
= requests
.delete(url
=url
, headers
=headers
, verify
=False)
7449 # Log the REST response
7450 self
.log_response(response
)
7454 def log_request(self
, req_type
, url
=None, headers
=None, data
=None):
7455 """Logs REST request details"""
7457 if req_type
is not None:
7458 self
.logger
.debug("Request type: {}".format(req_type
))
7461 self
.logger
.debug("Request url: {}".format(url
))
7463 if headers
is not None:
7464 for header
in headers
:
7466 "Request header: {}: {}".format(header
, headers
[header
])
7469 if data
is not None:
7470 self
.logger
.debug("Request data: {}".format(data
))
7472 def log_response(self
, response
):
7473 """Logs REST response details"""
7475 self
.logger
.debug("Response status code: {} ".format(response
.status_code
))
7477 def get_task_from_response(self
, content
):
7479 content - API response.text(response.text)
7482 xmlroot
= XmlElementTree
.fromstring(content
)
7484 if xmlroot
.tag
.split("}")[1] == "Task":
7488 if ele
.tag
.split("}")[1] == "Tasks":
7494 def power_on_vapp(self
, vapp_id
, vapp_name
):
7497 vapp_name - vAapp name
7498 return - Task object
7501 "Accept": "application/*+xml;version=" + API_VERSION
,
7502 "x-vcloud-authorization": self
.client
._session
.headers
[
7503 "x-vcloud-authorization"
7507 poweron_href
= "{}/api/vApp/vapp-{}/power/action/powerOn".format(
7510 response
= self
.perform_request(
7511 req_type
="POST", url
=poweron_href
, headers
=headers
7514 if response
.status_code
!= 202:
7516 "REST call {} failed reason : {}"
7517 "status code : {} ".format(
7518 poweron_href
, response
.text
, response
.status_code
7522 raise vimconn
.VimConnException(
7523 "power_on_vapp() : Failed to power on " "vApp {}".format(vapp_name
)
7526 poweron_task
= self
.get_task_from_response(response
.text
)
7530 def migrate_instance(self
, vm_id
, compute_host
=None):
7534 vm_id: ID of an instance
7535 compute_host: Host to migrate the vdu to
7537 # TODO: Add support for migration
7538 raise vimconn
.VimConnNotImplemented("Should have implemented this")
7540 def resize_instance(self
, vm_id
, flavor_id
=None):
7544 vm_id: ID of an instance
7545 flavor_id: flavor_id to resize the vdu to
7547 # TODO: Add support for resize
7548 raise vimconn
.VimConnNotImplemented("Should have implemented this")