1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
28 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
38 from pyVmomi
import vim
, vmodl
39 from pyVim
.connect
import SmartConnect
, Disconnect
41 from xml
.etree
import ElementTree
as XmlElementTree
42 from lxml
import etree
as lxmlElementTree
45 from pyvcloud
import Http
46 from pyvcloud
.vcloudair
import VCA
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
48 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
49 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
50 from xml
.sax
.saxutils
import escape
52 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
53 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
54 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
55 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
67 # global variable for vcd connector type
68 STANDALONE
= 'standalone'
70 # key for flavor dicts
71 FLAVOR_RAM_KEY
= 'ram'
72 FLAVOR_VCPUS_KEY
= 'vcpus'
73 FLAVOR_DISK_KEY
= 'disk'
74 DEFAULT_IP_PROFILE
= {'gateway_address':"192.168.1.1",
76 'subnet_address':"192.168.1.0/24",
78 'dhcp_start_address':"192.168.1.3",
80 'dns_address':"192.168.1.2"
82 # global variable for wait time
88 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
89 __date__
= "$12-Jan-2017 11:09:29$"
92 # -1: "Could not be created",
98 # 5: "Waiting for user input",
100 # 7: "Unrecognized state",
102 # 9: "Inconsistent state",
103 # 10: "Children do not all have the same status",
104 # 11: "Upload initiated, OVF descriptor pending",
105 # 12: "Upload initiated, copying contents",
106 # 13: "Upload initiated , disk contents pending",
107 # 14: "Upload has been quarantined",
108 # 15: "Upload quarantine period has expired"
110 # mapping vCD status to MANO
111 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
120 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
121 'ERROR': 'ERROR', 'DELETED': 'DELETED'
124 # dict used to store flavor in memory
128 class vimconnector(vimconn
.vimconnector
):
129 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
130 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}, persistent_info
={}):
132 Constructor create vmware connector to vCloud director.
134 By default construct doesn't validate connection state. So client can create object with None arguments.
135 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
137 a) It initialize organization UUID
138 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
141 uuid - is organization uuid.
142 name - is organization name that must be presented in vCloud director.
143 tenant_id - is VDC uuid it must be presented in vCloud director
144 tenant_name - is VDC name.
145 url - is hostname or ip address of vCloud director
146 url_admin - same as above.
147 user - is user that administrator for organization. Caller must make sure that
148 username has right privileges.
150 password - is password for a user.
152 VMware connector also requires PVDC administrative privileges and separate account.
153 This variables must be passed via config argument dict contains keys
155 dict['admin_username']
156 dict['admin_password']
162 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
163 url_admin
, user
, passwd
, log_level
, config
)
165 self
.logger
= logging
.getLogger('openmano.vim.vmware')
166 self
.logger
.setLevel(10)
167 self
.persistent_info
= persistent_info
172 self
.url_admin
= url_admin
173 self
.tenant_id
= tenant_id
174 self
.tenant_name
= tenant_name
178 self
.admin_password
= None
179 self
.admin_user
= None
181 self
.nsx_manager
= None
183 self
.nsx_password
= None
185 if tenant_name
is not None:
186 orgnameandtenant
= tenant_name
.split(":")
187 if len(orgnameandtenant
) == 2:
188 self
.tenant_name
= orgnameandtenant
[1]
189 self
.org_name
= orgnameandtenant
[0]
191 self
.tenant_name
= tenant_name
192 if "orgname" in config
:
193 self
.org_name
= config
['orgname']
196 self
.logger
.setLevel(getattr(logging
, log_level
))
199 self
.admin_user
= config
['admin_username']
200 self
.admin_password
= config
['admin_password']
202 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
205 self
.nsx_manager
= config
['nsx_manager']
206 self
.nsx_user
= config
['nsx_user']
207 self
.nsx_password
= config
['nsx_password']
209 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
215 raise vimconn
.vimconnException('url param can not be NoneType')
217 if not self
.url_admin
: # try to use normal url
218 self
.url_admin
= self
.url
220 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
221 self
.tenant_id
, self
.tenant_name
))
222 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
223 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
225 # initialize organization
226 if self
.user
is not None and self
.passwd
is not None and self
.url
:
227 self
.init_organization()
229 def __getitem__(self
, index
):
232 if index
== 'tenant_id':
233 return self
.tenant_id
234 if index
== 'tenant_name':
235 return self
.tenant_name
238 elif index
== 'org_name':
240 elif index
== 'org_uuid':
242 elif index
== 'user':
244 elif index
== 'passwd':
248 elif index
== 'url_admin':
249 return self
.url_admin
250 elif index
== "config":
253 raise KeyError("Invalid key '%s'" % str(index
))
255 def __setitem__(self
, index
, value
):
258 if index
== 'tenant_id':
259 self
.tenant_id
= value
260 if index
== 'tenant_name':
261 self
.tenant_name
= value
264 elif index
== 'org_name':
265 self
.org_name
= value
266 elif index
== 'org_uuid':
267 self
.org_uuid
= value
268 elif index
== 'user':
270 elif index
== 'passwd':
274 elif index
== 'url_admin':
275 self
.url_admin
= value
277 raise KeyError("Invalid key '%s'" % str(index
))
279 def connect_as_admin(self
):
280 """ Method connect as pvdc admin user to vCloud director.
281 There are certain action that can be done only by provider vdc admin user.
282 Organization creation / provider network creation etc.
285 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
288 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
290 vca_admin
= VCA(host
=self
.url
,
291 username
=self
.admin_user
,
292 service_type
=STANDALONE
,
296 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
298 raise vimconn
.vimconnConnectionException(
299 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
300 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
303 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
308 """ Method connect as normal user to vCloud director.
311 The return vca object that letter can be used to connect to vCloud director as admin for VDC
315 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
318 vca
= VCA(host
=self
.url
,
320 service_type
=STANDALONE
,
325 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
327 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
328 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
331 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
334 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
335 "{} as user: {}".format(self
.org_name
, self
.user
))
339 def init_organization(self
):
340 """ Method initialize organization UUID and VDC parameters.
342 At bare minimum client must provide organization name that present in vCloud director and VDC.
344 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
345 The Org - UUID will be initialized at the run time if data center present in vCloud director.
348 The return vca object that letter can be used to connect to vcloud direct as admin
351 if self
.org_uuid
is None:
352 org_dict
= self
.get_org_list()
354 # we set org UUID at the init phase but we can do it only when we have valid credential.
355 if org_dict
[org
] == self
.org_name
:
357 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
360 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
362 # if well good we require for org details
363 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
365 # we have two case if we want to initialize VDC ID or VDC name at run time
366 # tenant_name provided but no tenant id
367 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
368 vdcs_dict
= org_details_dict
['vdcs']
369 for vdc
in vdcs_dict
:
370 if vdcs_dict
[vdc
] == self
.tenant_name
:
372 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
376 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
377 # case two we have tenant_id but we don't have tenant name so we find and set it.
378 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
379 vdcs_dict
= org_details_dict
['vdcs']
380 for vdc
in vdcs_dict
:
381 if vdc
== self
.tenant_id
:
382 self
.tenant_name
= vdcs_dict
[vdc
]
383 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
387 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
388 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
390 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
391 self
.logger
.debug(traceback
.format_exc())
394 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
395 """ Method adds a new tenant to VIM with this name.
396 This action requires access to create VDC action in vCloud director.
399 tenant_name is tenant_name to be created.
400 tenant_description not used for this call
403 returns the tenant identifier in UUID format.
404 If action is failed method will throw vimconn.vimconnException method
406 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
407 if vdc_task
is not None:
408 vdc_uuid
, value
= vdc_task
.popitem()
409 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
412 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
414 def delete_tenant(self
, tenant_id
=None):
415 """Delete a tenant from VIM"""
416 'Returns the tenant identifier'
417 raise vimconn
.vimconnNotImplemented("Should have implemented this")
419 def get_tenant_list(self
, filter_dict
={}):
420 """Obtain tenants of VIM
421 filter_dict can contain the following keys:
422 name: filter by tenant name
423 id: filter by tenant uuid/id
425 Returns the tenant list of dictionaries:
426 [{'name':'<name>, 'id':'<id>, ...}, ...]
429 org_dict
= self
.get_org(self
.org_uuid
)
430 vdcs_dict
= org_dict
['vdcs']
435 entry
= {'name': vdcs_dict
[k
], 'id': k
}
436 # if caller didn't specify dictionary we return all tenants.
437 if filter_dict
is not None and filter_dict
:
438 filtered_entry
= entry
.copy()
439 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
440 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
441 if filter_dict
== entry
:
442 vdclist
.append(filtered_entry
)
444 vdclist
.append(entry
)
446 self
.logger
.debug("Error in get_tenant_list()")
447 self
.logger
.debug(traceback
.format_exc())
448 raise vimconn
.vimconnException("Incorrect state. {}")
452 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
453 """Adds a tenant network to VIM
455 net_type can be 'bridge','data'.'ptp'.
456 ip_profile is a dict containing the IP parameters of the network
458 Returns the network identifier"""
460 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
461 .format(net_name
, net_type
, ip_profile
, shared
))
467 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
468 ip_profile
=ip_profile
, isshared
=isshared
)
469 if network_uuid
is not None:
472 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
474 def get_vcd_network_list(self
):
475 """ Method available organization for a logged in tenant
478 The return vca object that letter can be used to connect to vcloud direct as admin
481 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
484 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
486 if not self
.tenant_name
:
487 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
489 vdc
= vca
.get_vdc(self
.tenant_name
)
491 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
493 vdc_uuid
= vdc
.get_id().split(":")[3]
494 networks
= vca
.get_networks(vdc
.get_name())
497 for network
in networks
:
499 netid
= network
.get_id().split(":")
503 filter_dict
["name"] = network
.get_name()
504 filter_dict
["id"] = netid
[3]
505 filter_dict
["shared"] = network
.get_IsShared()
506 filter_dict
["tenant_id"] = vdc_uuid
507 if network
.get_status() == 1:
508 filter_dict
["admin_state_up"] = True
510 filter_dict
["admin_state_up"] = False
511 filter_dict
["status"] = "ACTIVE"
512 filter_dict
["type"] = "bridge"
513 network_list
.append(filter_dict
)
514 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
516 self
.logger
.debug("Error in get_vcd_network_list")
517 self
.logger
.debug(traceback
.format_exc())
520 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
523 def get_network_list(self
, filter_dict
={}):
524 """Obtain tenant networks of VIM
526 name: network name OR/AND
527 id: network uuid OR/AND
528 shared: boolean OR/AND
529 tenant_id: tenant OR/AND
530 admin_state_up: boolean
533 [{key : value , key : value}]
535 Returns the network list of dictionaries:
536 [{<the fields at Filter_dict plus some VIM specific>}, ...]
540 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
543 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
545 if not self
.tenant_name
:
546 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
548 vdc
= vca
.get_vdc(self
.tenant_name
)
550 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
552 vdcid
= vdc
.get_id().split(":")[3]
553 networks
= vca
.get_networks(vdc
.get_name())
557 for network
in networks
:
559 net_uuid
= network
.get_id().split(":")
560 if len(net_uuid
) != 4:
563 net_uuid
= net_uuid
[3]
565 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
568 filter_entry
["name"] = network
.get_name()
569 filter_entry
["id"] = net_uuid
570 filter_entry
["shared"] = network
.get_IsShared()
571 filter_entry
["tenant_id"] = vdcid
572 if network
.get_status() == 1:
573 filter_entry
["admin_state_up"] = True
575 filter_entry
["admin_state_up"] = False
576 filter_entry
["status"] = "ACTIVE"
577 filter_entry
["type"] = "bridge"
578 filtered_entry
= filter_entry
.copy()
580 if filter_dict
is not None and filter_dict
:
581 # we remove all the key : value we don't care and match only
583 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
584 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
585 if filter_dict
== filter_entry
:
586 network_list
.append(filtered_entry
)
588 network_list
.append(filtered_entry
)
590 self
.logger
.debug("Error in get_vcd_network_list")
591 self
.logger
.debug(traceback
.format_exc())
593 self
.logger
.debug("Returning {}".format(network_list
))
596 def get_network(self
, net_id
):
597 """Method obtains network details of net_id VIM network
598 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
602 raise vimconn
.vimconnConnectionException("self.connect() is failed")
604 vdc
= vca
.get_vdc(self
.tenant_name
)
605 vdc_id
= vdc
.get_id().split(":")[3]
607 networks
= vca
.get_networks(vdc
.get_name())
611 for network
in networks
:
612 vdc_network_id
= network
.get_id().split(":")
613 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
614 filter_dict
["name"] = network
.get_name()
615 filter_dict
["id"] = vdc_network_id
[3]
616 filter_dict
["shared"] = network
.get_IsShared()
617 filter_dict
["tenant_id"] = vdc_id
618 if network
.get_status() == 1:
619 filter_dict
["admin_state_up"] = True
621 filter_dict
["admin_state_up"] = False
622 filter_dict
["status"] = "ACTIVE"
623 filter_dict
["type"] = "bridge"
624 self
.logger
.debug("Returning {}".format(filter_dict
))
627 self
.logger
.debug("Error in get_network")
628 self
.logger
.debug(traceback
.format_exc())
632 def delete_network(self
, net_id
):
634 Method Deletes a tenant network from VIM, provide the network id.
636 Returns the network identifier or raise an exception
641 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
643 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
644 if vcd_network
is not None and vcd_network
:
645 if self
.delete_network_action(network_uuid
=net_id
):
648 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
650 def refresh_nets_status(self
, net_list
):
651 """Get the status of the networks
652 Params: the list of network identifiers
653 Returns a dictionary with:
654 net_id: #VIM id of this network
655 status: #Mandatory. Text with one of:
656 # DELETED (not found at vim)
657 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
658 # OTHER (Vim reported other status not understood)
659 # ERROR (VIM indicates an ERROR status)
660 # ACTIVE, INACTIVE, DOWN (admin down),
661 # BUILD (on building process)
663 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
664 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
670 raise vimconn
.vimconnConnectionException("self.connect() is failed")
676 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
677 if vcd_network
is not None and vcd_network
:
678 if vcd_network
['status'] == '1':
684 errormsg
= 'Network not found.'
686 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
687 'vim_info': yaml
.safe_dump(vcd_network
)}
689 self
.logger
.debug("Error in refresh_nets_status")
690 self
.logger
.debug(traceback
.format_exc())
694 def get_flavor(self
, flavor_id
):
695 """Obtain flavor details from the VIM
696 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
698 if flavor_id
not in flavorlist
:
699 raise vimconn
.vimconnNotFoundException("Flavor not found.")
700 return flavorlist
[flavor_id
]
702 def new_flavor(self
, flavor_data
):
703 """Adds a tenant flavor to VIM
704 flavor_data contains a dictionary with information, keys:
706 ram: memory (cloud type) in MBytes
707 vpcus: cpus (cloud type)
708 extended: EPA parameters
709 - numas: #items requested in same NUMA
710 memory: number of 1G huge pages memory
711 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
712 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
713 - name: interface name
714 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
715 bandwidth: X Gbps; requested guarantee bandwidth
716 vpci: requested virtual PCI address
720 Returns the flavor identifier"""
722 # generate a new uuid put to internal dict and return it.
723 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
724 new_flavor
=flavor_data
725 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
726 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
727 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
729 extended_flv
= flavor_data
.get("extended")
731 numas
=extended_flv
.get("numas")
734 #overwrite ram and vcpus
735 ram
= numa
['memory']*1024
736 if 'paired-threads' in numa
:
737 cpu
= numa
['paired-threads']*2
738 elif 'cores' in numa
:
740 elif 'threads' in numa
:
741 cpu
= numa
['threads']
743 new_flavor
[FLAVOR_RAM_KEY
] = ram
744 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
745 new_flavor
[FLAVOR_DISK_KEY
] = disk
746 # generate a new uuid put to internal dict and return it.
747 flavor_id
= uuid
.uuid4()
748 flavorlist
[str(flavor_id
)] = new_flavor
749 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
751 return str(flavor_id
)
753 def delete_flavor(self
, flavor_id
):
754 """Deletes a tenant flavor from VIM identify by its id
756 Returns the used id or raise an exception
758 if flavor_id
not in flavorlist
:
759 raise vimconn
.vimconnNotFoundException("Flavor not found.")
761 flavorlist
.pop(flavor_id
, None)
764 def new_image(self
, image_dict
):
766 Adds a tenant image to VIM
768 200, image-id if the image is created
769 <0, message if there is an error
772 return self
.get_image_id_from_path(image_dict
['location'])
774 def delete_image(self
, image_id
):
781 raise vimconn
.vimconnNotImplemented("Should have implemented this")
783 def catalog_exists(self
, catalog_name
, catalogs
):
790 for catalog
in catalogs
:
791 if catalog
.name
== catalog_name
:
795 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
796 """ Create new catalog entry in vCloud director.
799 vca: vCloud director.
800 catalog_name catalog that client wish to create. Note no validation done for a name.
801 Client must make sure that provide valid string representation.
803 Return (bool) True if catalog created.
807 task
= vca
.create_catalog(catalog_name
, catalog_name
)
808 result
= vca
.block_until_completed(task
)
811 catalogs
= vca
.get_catalogs()
814 return self
.catalog_exists(catalog_name
, catalogs
)
816 # noinspection PyIncorrectDocstring
817 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
818 description
='', progress
=False, chunk_bytes
=128 * 1024):
820 Uploads a OVF file to a vCloud catalog
827 :param catalog_name: (str): The name of the catalog to upload the media.
828 :param media_file_name: (str): The name of the local media file to upload.
829 :return: (bool) True if the media file was successfully uploaded, false otherwise.
831 os
.path
.isfile(media_file_name
)
832 statinfo
= os
.stat(media_file_name
)
834 # find a catalog entry where we upload OVF.
835 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
837 # if VCD can parse OVF we upload VMDK file
838 for catalog
in vca
.get_catalogs():
839 if catalog_name
!= catalog
.name
:
841 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
842 link
.get_rel() == 'add', catalog
.get_Link())
843 assert len(link
) == 1
845 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
846 """ % (escape(catalog_name
), escape(description
))
847 headers
= vca
.vcloud_session
.get_vcloud_headers()
848 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
849 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
850 if response
.status_code
== requests
.codes
.created
:
851 catalogItem
= XmlElementTree
.fromstring(response
.content
)
852 entity
= [child
for child
in catalogItem
if
853 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
854 href
= entity
.get('href')
856 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
857 verify
=vca
.verify
, logger
=self
.logger
)
859 if response
.status_code
== requests
.codes
.ok
:
860 media
= mediaType
.parseString(response
.content
, True)
861 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
862 media
.get_Files().get_File()[0].get_Link())[0]
863 headers
= vca
.vcloud_session
.get_vcloud_headers()
864 headers
['Content-Type'] = 'Content-Type text/xml'
865 response
= Http
.put(link
.get_href(),
866 data
=open(media_file_name
, 'rb'),
868 verify
=vca
.verify
, logger
=self
.logger
)
869 if response
.status_code
!= requests
.codes
.ok
:
871 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
875 # TODO fix this with aync block
878 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
880 # uploading VMDK file
881 # check status of OVF upload and upload remaining files.
882 response
= Http
.get(template
,
883 headers
=vca
.vcloud_session
.get_vcloud_headers(),
887 if response
.status_code
== requests
.codes
.ok
:
888 media
= mediaType
.parseString(response
.content
, True)
889 number_of_files
= len(media
.get_Files().get_File())
890 for index
in xrange(0, number_of_files
):
891 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
892 media
.get_Files().get_File()[index
].get_Link())
893 for link
in links_list
:
894 # we skip ovf since it already uploaded.
895 if 'ovf' in link
.get_href():
897 # The OVF file and VMDK must be in a same directory
898 head
, tail
= os
.path
.split(media_file_name
)
899 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
900 if not os
.path
.isfile(file_vmdk
):
902 statinfo
= os
.stat(file_vmdk
)
903 if statinfo
.st_size
== 0:
905 hrefvmdk
= link
.get_href()
908 print("Uploading file: {}".format(file_vmdk
))
910 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
912 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
914 bytes_transferred
= 0
915 f
= open(file_vmdk
, 'rb')
916 while bytes_transferred
< statinfo
.st_size
:
917 my_bytes
= f
.read(chunk_bytes
)
918 if len(my_bytes
) <= chunk_bytes
:
919 headers
= vca
.vcloud_session
.get_vcloud_headers()
920 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
921 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
922 headers
['Content-Length'] = str(len(my_bytes
))
923 response
= Http
.put(hrefvmdk
,
929 if response
.status_code
== requests
.codes
.ok
:
930 bytes_transferred
+= len(my_bytes
)
932 progress_bar
.update(bytes_transferred
)
935 'file upload failed with error: [%s] %s' % (response
.status_code
,
942 progress_bar
.finish()
946 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
947 format(catalog_name
, media_file_name
))
950 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
953 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
954 """Upload media file"""
955 # TODO add named parameters for readability
957 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
958 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
960 def validate_uuid4(self
, uuid_string
=None):
961 """ Method validate correct format of UUID.
963 Return: true if string represent valid uuid
966 val
= uuid
.UUID(uuid_string
, version
=4)
971 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
972 """ Method check catalog and return catalog ID in UUID format.
975 catalog_name: catalog name as string
976 catalogs: list of catalogs.
978 Return: catalogs uuid
981 for catalog
in catalogs
:
982 if catalog
.name
== catalog_name
:
983 catalog_id
= catalog
.get_id().split(":")
987 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
988 """ Method check catalog and return catalog name lookup done by catalog UUID.
991 catalog_name: catalog name as string
992 catalogs: list of catalogs.
994 Return: catalogs name or None
997 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1000 for catalog
in catalogs
:
1001 catalog_id
= catalog
.get_id().split(":")[3]
1002 if catalog_id
== catalog_uuid
:
1006 def get_image_id_from_path(self
, path
=None, progress
=False):
1007 """ Method upload OVF image to vCloud director.
1009 Each OVF image represented as single catalog entry in vcloud director.
1010 The method check for existing catalog entry. The check done by file name without file extension.
1012 if given catalog name already present method will respond with existing catalog uuid otherwise
1013 it will create new catalog entry and upload OVF file to newly created catalog.
1015 If method can't create catalog entry or upload a file it will throw exception.
1017 Method accept boolean flag progress that will output progress bar. It useful method
1018 for standalone upload use case. In case to test large file upload.
1021 path: - valid path to OVF file.
1022 progress - boolean progress bar show progress bar.
1024 Return: if image uploaded correct method will provide image catalog UUID.
1026 vca
= self
.connect()
1028 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1031 raise vimconn
.vimconnException("Image path can't be None.")
1033 if not os
.path
.isfile(path
):
1034 raise vimconn
.vimconnException("Can't read file. File not found.")
1036 if not os
.access(path
, os
.R_OK
):
1037 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1039 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1041 dirpath
, filename
= os
.path
.split(path
)
1042 flname
, file_extension
= os
.path
.splitext(path
)
1043 if file_extension
!= '.ovf':
1044 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1045 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1047 catalog_name
= os
.path
.splitext(filename
)[0]
1048 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1049 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1050 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1052 catalogs
= vca
.get_catalogs()
1053 if len(catalogs
) == 0:
1054 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1055 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1057 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1058 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1059 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1061 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1062 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1064 for catalog
in catalogs
:
1065 # search for existing catalog if we find same name we return ID
1066 # TODO optimize this
1067 if catalog
.name
== catalog_md5_name
:
1068 self
.logger
.debug("Found existing catalog entry for {} "
1069 "catalog id {}".format(catalog_name
,
1070 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1071 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1073 # if we didn't find existing catalog we create a new one and upload image.
1074 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1075 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1077 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1079 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1080 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1082 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1084 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1086 def get_image_list(self
, filter_dict
={}):
1087 '''Obtain tenant images from VIM
1091 checksum: image checksum
1092 location: image path
1093 Returns the image list of dictionaries:
1094 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1097 vca
= self
.connect()
1099 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1102 catalogs
= vca
.get_catalogs()
1103 if len(catalogs
) == 0:
1106 for catalog
in catalogs
:
1107 catalog_uuid
= catalog
.get_id().split(":")[3]
1110 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1112 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1114 filtered_dict
["name"] = name
1115 filtered_dict
["id"] = catalog_uuid
1116 image_list
.append(filtered_dict
)
1118 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1120 except Exception as exp
:
1121 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1123 def get_vappid(self
, vdc
=None, vapp_name
=None):
1124 """ Method takes vdc object and vApp name and returns vapp uuid or None
1127 vdc: The VDC object.
1128 vapp_name: is application vappp name identifier
1131 The return vApp name otherwise None
1133 if vdc
is None or vapp_name
is None:
1135 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1137 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1138 vdc
.ResourceEntities
.ResourceEntity
)
1140 return refs
[0].href
.split("vapp")[1][1:]
1141 except Exception as e
:
1142 self
.logger
.exception(e
)
1146 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1147 """ Method Method returns True or False if vapp deployed in vCloud director
1150 vca: Connector to VCA
1151 vdc: The VDC object.
1152 vappid: vappid is application identifier
1155 The return True if vApp deployed
1160 refs
= filter(lambda ref
:
1161 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1162 vdc
.ResourceEntities
.ResourceEntity
)
1164 vappid
= ref
.href
.split("vapp")[1][1:]
1165 # find vapp with respected vapp uuid
1166 if vappid
== vapp_uuid
:
1168 except Exception as e
:
1169 self
.logger
.exception(e
)
1173 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1174 """Method returns vApp name from vCD and lookup done by vapp_id.
1177 vca: Connector to VCA
1178 vdc: The VDC object.
1179 vapp_uuid: vappid is application identifier
1182 The return vApp name otherwise None
1186 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1187 vdc
.ResourceEntities
.ResourceEntity
)
1189 # we care only about UUID the rest doesn't matter
1190 vappid
= ref
.href
.split("vapp")[1][1:]
1191 if vappid
== vapp_uuid
:
1192 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1194 tree
= XmlElementTree
.fromstring(response
.content
)
1195 return tree
.attrib
['name']
1196 except Exception as e
:
1197 self
.logger
.exception(e
)
1201 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1202 cloud_config
=None, disk_list
=None):
1203 """Adds a VM instance to VIM
1205 start: indicates if VM must start or boot in pause mode. Ignored
1206 image_id,flavor_id: image and flavor uuid
1207 net_list: list of interfaces, each one is a dictionary with:
1209 net_id: network uuid to connect
1210 vpci: virtual vcpi to assign
1211 model: interface model, virtio, e2000, ...
1213 use: 'data', 'bridge', 'mgmt'
1214 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1215 vim_id: filled/added by this function
1216 cloud_config: can be a text script to be passed directly to cloud-init,
1217 or an object to inject users and ssh keys with format:
1218 key-pairs: [] list of keys to install to the default user
1219 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1220 #TODO ip, security groups
1221 Returns >=0, the instance identifier
1225 self
.logger
.info("Creating new instance for entry {}".format(name
))
1226 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1227 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1228 vca
= self
.connect()
1230 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1232 #new vm name = vmname + tenant_id + uuid
1233 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1234 vmname_andid
= ''.join(new_vm_name
)
1236 # if vm already deployed we return existing uuid
1237 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1238 # if vapp_uuid is not None:
1241 # we check for presence of VDC, Catalog entry and Flavor.
1242 vdc
= vca
.get_vdc(self
.tenant_name
)
1244 raise vimconn
.vimconnNotFoundException(
1245 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1246 catalogs
= vca
.get_catalogs()
1247 if catalogs
is None:
1248 raise vimconn
.vimconnNotFoundException(
1249 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1251 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1252 if catalog_hash_name
:
1253 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1255 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1256 "(Failed retrieve catalog information {})".format(name
, image_id
))
1259 # Set vCPU and Memory based on flavor.
1264 pci_devices_info
= []
1265 if flavor_id
is not None:
1266 if flavor_id
not in flavorlist
:
1267 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1268 "Failed retrieve flavor information "
1269 "flavor id {}".format(name
, flavor_id
))
1272 flavor
= flavorlist
[flavor_id
]
1273 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1274 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1275 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1276 extended
= flavor
.get("extended", None)
1278 numas
=extended
.get("numas", None)
1281 for interface
in numa
.get("interfaces",() ):
1282 if interface
["dedicated"].strip()=="yes":
1283 pci_devices_info
.append(interface
)
1285 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1287 # image upload creates template name as catalog name space Template.
1288 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1293 # client must provide at least one entry in net_list if not we report error
1294 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1295 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1297 primary_netname
= None
1298 network_mode
= 'bridged'
1299 if net_list
is not None and len(net_list
) > 0:
1300 for net
in net_list
:
1301 if 'use' in net
and net
['use'] == 'mgmt':
1303 if primary_net
is None:
1304 primary_net
= net_list
[0]
1307 primary_net_id
= primary_net
['net_id']
1308 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1309 if 'name' in network_dict
:
1310 primary_netname
= network_dict
['name']
1313 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1315 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1317 # use: 'data', 'bridge', 'mgmt'
1318 # create vApp. Set vcpu and ram based on flavor id.
1319 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1320 self
.get_catalogbyid(image_id
, catalogs
),
1321 network_name
=None, # None while creating vapp
1322 network_mode
=network_mode
,
1323 vm_name
=vmname_andid
,
1324 vm_cpus
=vm_cpus
, # can be None if flavor is None
1325 vm_memory
=vm_memory
) # can be None if flavor is None
1327 if vapptask
is None or vapptask
is False:
1328 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1329 if type(vapptask
) is VappTask
:
1330 vca
.block_until_completed(vapptask
)
1332 # we should have now vapp in undeployed state.
1333 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1334 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1336 raise vimconn
.vimconnUnexpectedResponse(
1337 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1340 #Add PCI passthrough configrations
1341 PCI_devices_status
= False
1344 if len(pci_devices_info
) > 0:
1345 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1347 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1350 if PCI_devices_status
:
1351 self
.logger
.info("Added PCI devives {} to VM {}".format(
1356 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1362 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1363 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1365 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1367 # add NICs & connect to networks in netlist
1369 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1371 primary_nic_index
= 0
1372 for net
in net_list
:
1373 # openmano uses network id in UUID format.
1374 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1375 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1376 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1378 if 'net_id' not in net
:
1381 interface_net_id
= net
['net_id']
1382 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1383 interface_network_mode
= net
['use']
1385 if interface_network_mode
== 'mgmt':
1386 primary_nic_index
= nicIndex
1388 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1389 - DHCP (The IP address is obtained from a DHCP service.)
1390 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1391 - NONE (No IP addressing mode specified.)"""
1393 if primary_netname
is not None:
1394 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1396 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1397 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1398 if type(task
) is GenericTask
:
1399 vca
.block_until_completed(task
)
1400 # connect network to VM - with all DHCP by default
1401 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1402 task
= vapp
.connect_vms(nets
[0].name
,
1403 connection_index
=nicIndex
,
1404 connections_primary_index
=primary_nic_index
,
1405 ip_allocation_mode
='DHCP')
1406 if type(task
) is GenericTask
:
1407 vca
.block_until_completed(task
)
1410 # it might be a case if specific mandatory entry in dict is empty
1411 self
.logger
.debug("Key error {}".format(KeyError.message
))
1412 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1414 # deploy and power on vm
1415 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1416 deploytask
= vapp
.deploy(powerOn
=False)
1417 if type(deploytask
) is GenericTask
:
1418 vca
.block_until_completed(deploytask
)
1420 # If VM has PCI devices reserve memory for VM
1421 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1422 memReserve
= vm_obj
.config
.hardware
.memoryMB
1423 spec
= vim
.vm
.ConfigSpec()
1424 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1425 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1427 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1428 self
.logger
.info("Reserved memmoery {} MB for "\
1429 "VM VM status: {}".format(str(memReserve
),result
))
1431 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1432 str(memReserve
),str(vm_obj
)))
1434 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1435 poweron_task
= vapp
.poweron()
1436 if type(poweron_task
) is GenericTask
:
1437 vca
.block_until_completed(poweron_task
)
1439 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1442 while wait_time
<= MAX_WAIT_TIME
:
1443 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1444 if vapp
and vapp
.me
.deployed
:
1445 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1448 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1449 time
.sleep(INTERVAL_TIME
)
1451 wait_time
+=INTERVAL_TIME
1453 if vapp_uuid
is not None:
1456 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1460 ## based on current discussion
1464 # created: '2016-09-08T11:51:58'
1465 # description: simple-instance.linux1.1
1466 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1467 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1468 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1473 def get_vminstance(self
, vim_vm_uuid
=None):
1474 """Returns the VM instance information from VIM"""
1476 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1477 vca
= self
.connect()
1479 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1481 vdc
= vca
.get_vdc(self
.tenant_name
)
1483 raise vimconn
.vimconnConnectionException(
1484 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1486 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1487 if not vm_info_dict
:
1488 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1489 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1491 status_key
= vm_info_dict
['status']
1494 vm_dict
= {'created': vm_info_dict
['created'],
1495 'description': vm_info_dict
['name'],
1496 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1497 'hostId': vm_info_dict
['vmuuid'],
1499 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1501 if 'interfaces' in vm_info_dict
:
1502 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1504 vm_dict
['interfaces'] = []
1506 vm_dict
= {'created': '',
1508 'status': vcdStatusCode2manoFormat
[int(-1)],
1509 'hostId': vm_info_dict
['vmuuid'],
1510 'error_msg': "Inconsistency state",
1511 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1515 def delete_vminstance(self
, vm__vim_uuid
):
1516 """Method poweroff and remove VM instance from vcloud director network.
1519 vm__vim_uuid: VM UUID
1522 Returns the instance identifier
1525 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1526 vca
= self
.connect()
1528 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1530 vdc
= vca
.get_vdc(self
.tenant_name
)
1532 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1534 raise vimconn
.vimconnException(
1535 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1538 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1539 if vapp_name
is None:
1540 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1541 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1543 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1545 # Delete vApp and wait for status change if task executed and vApp is None.
1546 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1549 if vapp
.me
.deployed
:
1550 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1554 while wait_time
<= MAX_WAIT_TIME
:
1555 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1557 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1558 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1560 power_off_task
= vapp
.poweroff()
1561 if type(power_off_task
) is GenericTask
:
1562 result
= vca
.block_until_completed(power_off_task
)
1567 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1568 time
.sleep(INTERVAL_TIME
)
1570 wait_time
+=INTERVAL_TIME
1572 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1574 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1577 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1580 while wait_time
<= MAX_WAIT_TIME
:
1581 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1583 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1584 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1585 undeploy_task
= vapp
.undeploy(action
='powerOff')
1587 if type(undeploy_task
) is GenericTask
:
1588 result
= vca
.block_until_completed(undeploy_task
)
1593 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1594 time
.sleep(INTERVAL_TIME
)
1596 wait_time
+=INTERVAL_TIME
1599 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1602 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1603 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1605 if vapp
is not None:
1609 while wait_time
<= MAX_WAIT_TIME
:
1610 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1612 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1613 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1615 delete_task
= vapp
.delete()
1617 if type(delete_task
) is GenericTask
:
1618 vca
.block_until_completed(delete_task
)
1619 result
= vca
.block_until_completed(delete_task
)
1623 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1624 time
.sleep(INTERVAL_TIME
)
1626 wait_time
+=INTERVAL_TIME
1629 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1632 self
.logger
.debug(traceback
.format_exc())
1633 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1635 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1636 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1639 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1641 def refresh_vms_status(self
, vm_list
):
1642 """Get the status of the virtual machines and their interfaces/ports
1643 Params: the list of VM identifiers
1644 Returns a dictionary with:
1645 vm_id: #VIM id of this Virtual Machine
1646 status: #Mandatory. Text with one of:
1647 # DELETED (not found at vim)
1648 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1649 # OTHER (Vim reported other status not understood)
1650 # ERROR (VIM indicates an ERROR status)
1651 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1652 # CREATING (on building process), ERROR
1653 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1655 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1656 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1658 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1659 mac_address: #Text format XX:XX:XX:XX:XX:XX
1660 vim_net_id: #network id where this interface is connected
1661 vim_interface_id: #interface/port VIM id
1662 ip_address: #null, or text with IPv4, IPv6 address
1665 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1668 rheaders
= {'Content-Type': 'application/xml'}
1669 iso_edges
= ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
1672 for edge
in iso_edges
:
1673 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1674 self
.logger
.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url
))
1676 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1677 auth
= (self
.nsx_user
, self
.nsx_password
),
1678 verify
= False, headers
= rheaders
)
1680 if resp
.status_code
== requests
.codes
.ok
:
1681 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1682 for child
in dhcp_leases
:
1683 if child
.tag
== 'dhcpLeaseInfo':
1684 dhcpLeaseInfo
= child
1685 for leaseInfo
in dhcpLeaseInfo
:
1686 for elem
in leaseInfo
:
1687 if (elem
.tag
)=='macAddress':
1688 mac_addr
= elem
.text
1689 if (elem
.tag
)=='ipAddress':
1691 if (mac_addr
) is not None:
1692 mac_ip_addr
[mac_addr
]= ip_addr
1693 self
.logger
.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr
))
1695 self
.logger
.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp
.content
))
1697 self
.logger
.debug("Error in response from NSX Manager {}".format(KeyError.message
))
1698 self
.logger
.debug(traceback
.format_exc())
1700 vca
= self
.connect()
1702 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1704 vdc
= vca
.get_vdc(self
.tenant_name
)
1706 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1709 for vmuuid
in vm_list
:
1710 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1711 if vmname
is not None:
1713 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1714 vm_info
= the_vapp
.get_vms_details()
1715 vm_status
= vm_info
[0]['status']
1716 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1717 vm_info
[0].update(vm_pci_details
)
1719 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1720 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1721 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1725 vm_app_networks
= the_vapp
.get_vms_network_info()
1726 for vapp_network
in vm_app_networks
:
1727 for vm_network
in vapp_network
:
1728 if vm_network
['name'] == vmname
:
1729 #Assign IP Address based on MAC Address in NSX DHCP lease info
1730 for mac_adres
,ip_adres
in mac_ip_addr
.iteritems():
1731 if mac_adres
== vm_network
['mac']:
1732 vm_network
['ip']=ip_adres
1733 interface
= {"mac_address": vm_network
['mac'],
1734 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1735 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1736 'ip_address': vm_network
['ip']}
1737 # interface['vim_info'] = yaml.safe_dump(vm_network)
1738 vm_dict
["interfaces"].append(interface
)
1739 # add a vm to vm dict
1740 vms_dict
.setdefault(vmuuid
, vm_dict
)
1742 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1743 self
.logger
.debug(traceback
.format_exc())
1747 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1748 """Send and action over a VM instance from VIM
1749 Returns the vm_id if the action was successfully sent to the VIM"""
1751 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1752 if vm__vim_uuid
is None or action_dict
is None:
1753 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1755 vca
= self
.connect()
1757 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1759 vdc
= vca
.get_vdc(self
.tenant_name
)
1761 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1763 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1764 if vapp_name
is None:
1765 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1766 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1768 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1771 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1772 # TODO fix all status
1773 if "start" in action_dict
:
1774 vm_info
= the_vapp
.get_vms_details()
1775 vm_status
= vm_info
[0]['status']
1776 self
.logger
.info("Power on vApp: vm_status:{} {}".format(type(vm_status
),vm_status
))
1777 if vm_status
== "Suspended" or vm_status
== "Powered off":
1778 power_on_task
= the_vapp
.poweron()
1779 if power_on_task
is not None and type(power_on_task
) is GenericTask
:
1780 result
= vca
.block_until_completed(power_on_task
)
1782 self
.logger
.info("action_vminstance: Powered on vApp: {}".format(vapp_name
))
1784 self
.logger
.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name
))
1786 self
.logger
.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name
))
1787 elif "rebuild" in action_dict
:
1788 self
.logger
.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name
))
1789 power_on_task
= the_vapp
.deploy(powerOn
=True)
1790 if type(power_on_task
) is GenericTask
:
1791 result
= vca
.block_until_completed(power_on_task
)
1793 self
.logger
.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name
))
1795 self
.logger
.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name
))
1797 self
.logger
.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name
))
1798 elif "pause" in action_dict
:
1801 elif "resume" in action_dict
:
1804 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1805 power_off_task
= the_vapp
.undeploy(action
='powerOff')
1806 if type(power_off_task
) is GenericTask
:
1807 result
= vca
.block_until_completed(power_off_task
)
1809 self
.logger
.info("action_vminstance: Powered off vApp: {}".format(vapp_name
))
1811 self
.logger
.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name
))
1813 self
.logger
.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name
))
1814 elif "forceOff" in action_dict
:
1816 elif "terminate" in action_dict
:
1818 # elif "createImage" in action_dict:
1819 # server.create_image()
1825 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1827 Get a console for the virtual machine
1829 vm_id: uuid of the VM
1830 console_type, can be:
1831 "novnc" (by default), "xvpvnc" for VNC types,
1832 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1833 Returns dict with the console parameters:
1834 protocol: ssh, ftp, http, https, ...
1835 server: usually ip address
1836 port: the http, ssh, ... port
1837 suffix: extra text, e.g. the http path and query string
1839 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1841 # NOT USED METHODS in current version
1843 def host_vim2gui(self
, host
, server_dict
):
1844 """Transform host dictionary from VIM format to GUI format,
1845 and append to the server_dict
1847 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1849 def get_hosts_info(self
):
1850 """Get the information of deployed hosts
1851 Returns the hosts content"""
1852 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1854 def get_hosts(self
, vim_tenant
):
1855 """Get the hosts and deployed instances
1856 Returns the hosts content"""
1857 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1859 def get_processor_rankings(self
):
1860 """Get the processor rankings in the VIM database"""
1861 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1863 def new_host(self
, host_data
):
1864 """Adds a new host to VIM"""
1865 '''Returns status code of the VIM response'''
1866 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1868 def new_external_port(self
, port_data
):
1869 """Adds a external port to VIM"""
1870 '''Returns the port identifier'''
1871 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1873 def new_external_network(self
, net_name
, net_type
):
1874 """Adds a external network to VIM (shared)"""
1875 '''Returns the network identifier'''
1876 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1878 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1879 """Connects a external port to a network"""
1880 '''Returns status code of the VIM response'''
1881 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1883 def new_vminstancefromJSON(self
, vm_data
):
1884 """Adds a VM instance to VIM"""
1885 '''Returns the instance identifier'''
1886 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1888 def get_network_name_by_id(self
, network_uuid
=None):
1889 """Method gets vcloud director network named based on supplied uuid.
1892 network_uuid: network_id
1895 The return network name.
1898 vca
= self
.connect()
1900 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1902 if not network_uuid
:
1906 org_dict
= self
.get_org(self
.org_uuid
)
1907 if 'networks' in org_dict
:
1908 org_network_dict
= org_dict
['networks']
1909 for net_uuid
in org_network_dict
:
1910 if net_uuid
== network_uuid
:
1911 return org_network_dict
[net_uuid
]
1913 self
.logger
.debug("Exception in get_network_name_by_id")
1914 self
.logger
.debug(traceback
.format_exc())
1918 def get_network_id_by_name(self
, network_name
=None):
1919 """Method gets vcloud director network uuid based on supplied name.
1922 network_name: network_name
1924 The return network uuid.
1925 network_uuid: network_id
1928 vca
= self
.connect()
1930 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1932 if not network_name
:
1933 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1937 org_dict
= self
.get_org(self
.org_uuid
)
1938 if org_dict
and 'networks' in org_dict
:
1939 org_network_dict
= org_dict
['networks']
1940 for net_uuid
,net_name
in org_network_dict
.iteritems():
1941 if net_name
== network_name
:
1944 except KeyError as exp
:
1945 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1949 def list_org_action(self
):
1951 Method leverages vCloud director and query for available organization for particular user
1954 vca - is active VCA connection.
1955 vdc_name - is a vdc name that will be used to query vms action
1958 The return XML respond
1961 vca
= self
.connect()
1963 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1965 url_list
= [vca
.host
, '/api/org']
1966 vm_list_rest_call
= ''.join(url_list
)
1968 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1969 response
= Http
.get(url
=vm_list_rest_call
,
1970 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1973 if response
.status_code
== requests
.codes
.ok
:
1974 return response
.content
1978 def get_org_action(self
, org_uuid
=None):
1980 Method leverages vCloud director and retrieve available object fdr organization.
1983 vca - is active VCA connection.
1984 vdc_name - is a vdc name that will be used to query vms action
1987 The return XML respond
1990 vca
= self
.connect()
1992 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1994 if org_uuid
is None:
1997 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1998 vm_list_rest_call
= ''.join(url_list
)
2000 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2001 response
= Http
.get(url
=vm_list_rest_call
,
2002 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2005 if response
.status_code
== requests
.codes
.ok
:
2006 return response
.content
2010 def get_org(self
, org_uuid
=None):
2012 Method retrieves available organization in vCloud Director
2015 org_uuid - is a organization uuid.
2018 The return dictionary with following key
2019 "network" - for network list under the org
2020 "catalogs" - for network list under the org
2021 "vdcs" - for vdc list under org
2025 vca
= self
.connect()
2027 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2029 if org_uuid
is None:
2032 content
= self
.get_org_action(org_uuid
=org_uuid
)
2037 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2038 for child
in vm_list_xmlroot
:
2039 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2040 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2041 org_dict
['vdcs'] = vdc_list
2042 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2043 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2044 org_dict
['networks'] = network_list
2045 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2046 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2047 org_dict
['catalogs'] = catalog_list
2053 def get_org_list(self
):
2055 Method retrieves available organization in vCloud Director
2058 vca - is active VCA connection.
2061 The return dictionary and key for each entry VDC UUID
2065 vca
= self
.connect()
2067 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2069 content
= self
.list_org_action()
2071 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2072 for vm_xml
in vm_list_xmlroot
:
2073 if vm_xml
.tag
.split("}")[1] == 'Org':
2074 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2075 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2081 def vms_view_action(self
, vdc_name
=None):
2082 """ Method leverages vCloud director vms query call
2085 vca - is active VCA connection.
2086 vdc_name - is a vdc name that will be used to query vms action
2089 The return XML respond
2091 vca
= self
.connect()
2092 if vdc_name
is None:
2095 url_list
= [vca
.host
, '/api/vms/query']
2096 vm_list_rest_call
= ''.join(url_list
)
2098 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2099 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2100 vca
.vcloud_session
.organization
.Link
)
2102 response
= Http
.get(url
=vm_list_rest_call
,
2103 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2106 if response
.status_code
== requests
.codes
.ok
:
2107 return response
.content
2111 def get_vapp_list(self
, vdc_name
=None):
2113 Method retrieves vApp list deployed vCloud director and returns a dictionary
2114 contains a list of all vapp deployed for queried VDC.
2115 The key for a dictionary is vApp UUID
2119 vca - is active VCA connection.
2120 vdc_name - is a vdc name that will be used to query vms action
2123 The return dictionary and key for each entry vapp UUID
2127 if vdc_name
is None:
2130 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2132 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2133 for vm_xml
in vm_list_xmlroot
:
2134 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2135 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2136 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2137 if 'vappTemplate-' in rawuuid
[0]:
2138 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2139 # vm and use raw UUID as key
2140 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2146 def get_vm_list(self
, vdc_name
=None):
2148 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2149 contains a list of all VM's deployed for queried VDC.
2150 The key for a dictionary is VM UUID
2154 vca - is active VCA connection.
2155 vdc_name - is a vdc name that will be used to query vms action
2158 The return dictionary and key for each entry vapp UUID
2162 if vdc_name
is None:
2165 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2167 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2168 for vm_xml
in vm_list_xmlroot
:
2169 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2170 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2171 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2172 if 'vm-' in rawuuid
[0]:
2173 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2174 # vm and use raw UUID as key
2175 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2181 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2183 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2184 contains a list of all VM's deployed for queried VDC.
2185 The key for a dictionary is VM UUID
2189 vca - is active VCA connection.
2190 vdc_name - is a vdc name that will be used to query vms action
2193 The return dictionary and key for each entry vapp UUID
2196 vca
= self
.connect()
2198 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2200 if vdc_name
is None:
2203 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2205 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2206 for vm_xml
in vm_list_xmlroot
:
2207 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2208 # lookup done by UUID
2210 if vapp_name
in vm_xml
.attrib
['container']:
2211 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2212 if 'vm-' in rawuuid
[0]:
2213 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2215 # lookup done by Name
2217 if vapp_name
in vm_xml
.attrib
['name']:
2218 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2219 if 'vm-' in rawuuid
[0]:
2220 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2227 def get_network_action(self
, network_uuid
=None):
2229 Method leverages vCloud director and query network based on network uuid
2232 vca - is active VCA connection.
2233 network_uuid - is a network uuid
2236 The return XML respond
2239 vca
= self
.connect()
2241 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2243 if network_uuid
is None:
2246 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2247 vm_list_rest_call
= ''.join(url_list
)
2249 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2250 response
= Http
.get(url
=vm_list_rest_call
,
2251 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2254 if response
.status_code
== requests
.codes
.ok
:
2255 return response
.content
2259 def get_vcd_network(self
, network_uuid
=None):
2261 Method retrieves available network from vCloud Director
2264 network_uuid - is VCD network UUID
2266 Each element serialized as key : value pair
2268 Following keys available for access. network_configuration['Gateway'}
2272 <IsInherited>true</IsInherited>
2273 <Gateway>172.16.252.100</Gateway>
2274 <Netmask>255.255.255.0</Netmask>
2275 <Dns1>172.16.254.201</Dns1>
2276 <Dns2>172.16.254.202</Dns2>
2277 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2278 <IsEnabled>true</IsEnabled>
2281 <StartAddress>172.16.252.1</StartAddress>
2282 <EndAddress>172.16.252.99</EndAddress>
2287 <FenceMode>bridged</FenceMode>
2290 The return dictionary and key for each entry vapp UUID
2293 network_configuration
= {}
2294 if network_uuid
is None:
2297 content
= self
.get_network_action(network_uuid
=network_uuid
)
2299 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2301 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2302 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2303 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2305 for child
in vm_list_xmlroot
:
2306 if child
.tag
.split("}")[1] == 'IsShared':
2307 network_configuration
['isShared'] = child
.text
.strip()
2308 if child
.tag
.split("}")[1] == 'Configuration':
2309 for configuration
in child
.iter():
2310 tagKey
= configuration
.tag
.split("}")[1].strip()
2312 network_configuration
[tagKey
] = configuration
.text
.strip()
2313 return network_configuration
2317 return network_configuration
2319 def delete_network_action(self
, network_uuid
=None):
2321 Method delete given network from vCloud director
2324 network_uuid - is a network uuid that client wish to delete
2327 The return None or XML respond or false
2330 vca
= self
.connect_as_admin()
2332 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2333 if network_uuid
is None:
2336 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2337 vm_list_rest_call
= ''.join(url_list
)
2339 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2340 response
= Http
.delete(url
=vm_list_rest_call
,
2341 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2345 if response
.status_code
== 202:
2350 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2351 ip_profile
=None, isshared
='true'):
2353 Method create network in vCloud director
2356 network_name - is network name to be created.
2357 net_type - can be 'bridge','data','ptp','mgmt'.
2358 ip_profile is a dict containing the IP parameters of the network
2359 isshared - is a boolean
2360 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2361 It optional attribute. by default if no parent network indicate the first available will be used.
2364 The return network uuid or return None
2367 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2368 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2369 ip_profile
=ip_profile
,
2371 parent_network_uuid
=parent_network_uuid
,
2374 self
.logger
.debug("Failed create network {}.".format(network_name
))
2378 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2379 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2380 if len(vcd_uuid
) == 4:
2381 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2384 self
.logger
.debug("Failed create network {}".format(network_name
))
2387 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2388 ip_profile
=None, isshared
='true'):
2390 Method create network in vCloud director
2393 network_name - is network name to be created.
2394 net_type - can be 'bridge','data','ptp','mgmt'.
2395 ip_profile is a dict containing the IP parameters of the network
2396 isshared - is a boolean
2397 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2398 It optional attribute. by default if no parent network indicate the first available will be used.
2401 The return network uuid or return None
2404 vca
= self
.connect_as_admin()
2406 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2407 if network_name
is None:
2410 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2411 vm_list_rest_call
= ''.join(url_list
)
2412 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2413 response
= Http
.get(url
=vm_list_rest_call
,
2414 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2418 provider_network
= None
2419 available_networks
= None
2420 add_vdc_rest_url
= None
2422 if response
.status_code
!= requests
.codes
.ok
:
2423 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2424 response
.status_code
))
2428 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2429 for child
in vm_list_xmlroot
:
2430 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2431 provider_network
= child
.attrib
.get('href')
2432 # application/vnd.vmware.admin.providervdc+xml
2433 if child
.tag
.split("}")[1] == 'Link':
2434 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2435 and child
.attrib
.get('rel') == 'add':
2436 add_vdc_rest_url
= child
.attrib
.get('href')
2438 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2439 self
.logger
.debug("Respond body {}".format(response
.content
))
2442 # find pvdc provided available network
2443 response
= Http
.get(url
=provider_network
,
2444 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2447 if response
.status_code
!= requests
.codes
.ok
:
2448 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2449 response
.status_code
))
2452 # available_networks.split("/")[-1]
2454 if parent_network_uuid
is None:
2456 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2457 for child
in vm_list_xmlroot
.iter():
2458 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2459 for networks
in child
.iter():
2460 # application/vnd.vmware.admin.network+xml
2461 if networks
.attrib
.get('href') is not None:
2462 available_networks
= networks
.attrib
.get('href')
2467 #Configure IP profile of the network
2468 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2470 gateway_address
=ip_profile
['gateway_address']
2471 dhcp_count
=int(ip_profile
['dhcp_count'])
2472 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2474 if ip_profile
['dhcp_enabled']==True:
2477 dhcp_enabled
='false'
2478 dhcp_start_address
=ip_profile
['dhcp_start_address']
2480 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2481 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2482 end_ip_int
+= dhcp_count
- 1
2483 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2485 ip_version
=ip_profile
['ip_version']
2486 dns_address
=ip_profile
['dns_address']
2488 # either use client provided UUID or search for a first available
2489 # if both are not defined we return none
2490 if parent_network_uuid
is not None:
2491 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2492 add_vdc_rest_url
= ''.join(url_list
)
2495 fence_mode
="isolated"
2497 is_inherited
='false'
2498 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2499 <Description>Openmano created</Description>
2503 <IsInherited>{1:s}</IsInherited>
2504 <Gateway>{2:s}</Gateway>
2505 <Netmask>{3:s}</Netmask>
2507 <IsEnabled>{5:s}</IsEnabled>
2510 <StartAddress>{6:s}</StartAddress>
2511 <EndAddress>{7:s}</EndAddress>
2516 <FenceMode>{8:s}</FenceMode>
2518 <IsShared>{9:s}</IsShared>
2519 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2520 subnet_address
, dns_address
, dhcp_enabled
,
2521 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2524 fence_mode
="bridged"
2525 is_inherited
='false'
2526 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2527 <Description>Openmano created</Description>
2531 <IsInherited>{1:s}</IsInherited>
2532 <Gateway>{2:s}</Gateway>
2533 <Netmask>{3:s}</Netmask>
2535 <IsEnabled>{5:s}</IsEnabled>
2538 <StartAddress>{6:s}</StartAddress>
2539 <EndAddress>{7:s}</EndAddress>
2544 <ParentNetwork href="{8:s}"/>
2545 <FenceMode>{9:s}</FenceMode>
2547 <IsShared>{10:s}</IsShared>
2548 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2549 subnet_address
, dns_address
, dhcp_enabled
,
2550 dhcp_start_address
, dhcp_end_address
, available_networks
,
2551 fence_mode
, isshared
)
2553 headers
= vca
.vcloud_session
.get_vcloud_headers()
2554 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2556 response
= Http
.post(url
=add_vdc_rest_url
,
2562 if response
.status_code
!= 201:
2563 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2564 .format(response
.status_code
))
2566 network
= networkType
.parseString(response
.content
, True)
2567 create_nw_task
= network
.get_Tasks().get_Task()[0]
2569 # if we all ok we respond with content after network creation completes
2570 # otherwise by default return None
2571 if create_nw_task
is not None:
2572 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2573 status
= vca
.block_until_completed(create_nw_task
)
2575 return response
.content
2577 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2578 .format(response
.content
))
2579 except Exception as exp
:
2580 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2584 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2586 Method sets convert CIDR netmask address to normal IP format
2588 cidr_ip : CIDR IP address
2590 netmask : Converted netmask
2592 if cidr_ip
is not None:
2594 network
, net_bits
= cidr_ip
.split('/')
2595 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2601 def get_provider_rest(self
, vca
=None):
2603 Method gets provider vdc view from vcloud director
2606 network_name - is network name to be created.
2607 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2608 It optional attribute. by default if no parent network indicate the first available will be used.
2611 The return xml content of respond or None
2614 url_list
= [vca
.host
, '/api/admin']
2615 response
= Http
.get(url
=''.join(url_list
),
2616 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2620 if response
.status_code
== requests
.codes
.ok
:
2621 return response
.content
2624 def create_vdc(self
, vdc_name
=None):
2628 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2629 if xml_content
is not None:
2631 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2632 for child
in task_resp_xmlroot
:
2633 if child
.tag
.split("}")[1] == 'Owner':
2634 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2635 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2638 self
.logger
.debug("Respond body {}".format(xml_content
))
2642 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2644 Method create vdc in vCloud director based on VDC template.
2645 it uses pre-defined template that must be named openmano
2648 vdc_name - name of a new vdc.
2651 The return xml content of respond or None
2654 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2655 vca
= self
.connect()
2657 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2658 if vdc_name
is None:
2661 url_list
= [vca
.host
, '/api/vdcTemplates']
2662 vm_list_rest_call
= ''.join(url_list
)
2663 response
= Http
.get(url
=vm_list_rest_call
,
2664 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2668 # container url to a template
2669 vdc_template_ref
= None
2671 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2672 for child
in vm_list_xmlroot
:
2673 # application/vnd.vmware.admin.providervdc+xml
2674 # we need find a template from witch we instantiate VDC
2675 if child
.tag
.split("}")[1] == 'VdcTemplate':
2676 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2677 'name') == 'openmano':
2678 vdc_template_ref
= child
.attrib
.get('href')
2680 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2681 self
.logger
.debug("Respond body {}".format(response
.content
))
2684 # if we didn't found required pre defined template we return None
2685 if vdc_template_ref
is None:
2690 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2691 vm_list_rest_call
= ''.join(url_list
)
2692 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2693 <Source href="{1:s}"></Source>
2694 <Description>opnemano</Description>
2695 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2696 headers
= vca
.vcloud_session
.get_vcloud_headers()
2697 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2698 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2700 # if we all ok we respond with content otherwise by default None
2701 if response
.status_code
>= 200 and response
.status_code
< 300:
2702 return response
.content
2705 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2706 self
.logger
.debug("Respond body {}".format(response
.content
))
2710 def create_vdc_rest(self
, vdc_name
=None):
2712 Method create network in vCloud director
2715 network_name - is network name to be created.
2716 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2717 It optional attribute. by default if no parent network indicate the first available will be used.
2720 The return network uuid or return None
2723 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2725 vca
= self
.connect_as_admin()
2727 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2728 if vdc_name
is None:
2731 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2732 vm_list_rest_call
= ''.join(url_list
)
2733 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2734 response
= Http
.get(url
=vm_list_rest_call
,
2735 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2739 provider_vdc_ref
= None
2740 add_vdc_rest_url
= None
2741 available_networks
= None
2743 if response
.status_code
!= requests
.codes
.ok
:
2744 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2745 response
.status_code
))
2749 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2750 for child
in vm_list_xmlroot
:
2751 # application/vnd.vmware.admin.providervdc+xml
2752 if child
.tag
.split("}")[1] == 'Link':
2753 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2754 and child
.attrib
.get('rel') == 'add':
2755 add_vdc_rest_url
= child
.attrib
.get('href')
2757 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2758 self
.logger
.debug("Respond body {}".format(response
.content
))
2761 response
= self
.get_provider_rest(vca
=vca
)
2763 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2764 for child
in vm_list_xmlroot
:
2765 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2766 for sub_child
in child
:
2767 provider_vdc_ref
= sub_child
.attrib
.get('href')
2769 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2770 self
.logger
.debug("Respond body {}".format(response
))
2773 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2774 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2775 <AllocationModel>ReservationPool</AllocationModel>
2776 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2777 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2778 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2779 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2780 <ProviderVdcReference
2781 name="Main Provider"
2783 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2787 headers
= vca
.vcloud_session
.get_vcloud_headers()
2788 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2789 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2792 # if we all ok we respond with content otherwise by default None
2793 if response
.status_code
== 201:
2794 return response
.content
2797 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2799 Method retrieve vapp detail from vCloud director
2802 vapp_uuid - is vapp identifier.
2805 The return network uuid or return None
2811 if need_admin_access
:
2812 vca
= self
.connect_as_admin()
2814 vca
= self
.connect()
2817 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2818 if vapp_uuid
is None:
2821 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2822 get_vapp_restcall
= ''.join(url_list
)
2824 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2825 response
= Http
.get(url
=get_vapp_restcall
,
2826 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2830 if response
.status_code
!= requests
.codes
.ok
:
2831 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2832 response
.status_code
))
2833 return parsed_respond
2836 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2837 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2839 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2840 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2841 'vmw': 'http://www.vmware.com/schema/ovf',
2842 'vm': 'http://www.vmware.com/vcloud/v1.5',
2843 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2844 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2845 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2848 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2849 if created_section
is not None:
2850 parsed_respond
['created'] = created_section
.text
2852 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2853 if network_section
is not None and 'networkName' in network_section
.attrib
:
2854 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2856 ipscopes_section
= \
2857 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2859 if ipscopes_section
is not None:
2860 for ipscope
in ipscopes_section
:
2861 for scope
in ipscope
:
2862 tag_key
= scope
.tag
.split("}")[1]
2863 if tag_key
== 'IpRanges':
2864 ip_ranges
= scope
.getchildren()
2865 for ipblock
in ip_ranges
:
2866 for block
in ipblock
:
2867 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2869 parsed_respond
[tag_key
] = scope
.text
2871 # parse children section for other attrib
2872 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2873 if children_section
is not None:
2874 parsed_respond
['name'] = children_section
.attrib
['name']
2875 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2876 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2877 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2878 parsed_respond
['status'] = children_section
.attrib
['status']
2879 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2880 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2882 for adapters
in network_adapter
:
2883 adapter_key
= adapters
.tag
.split("}")[1]
2884 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2885 parsed_respond
['primarynetwork'] = adapters
.text
2886 if adapter_key
== 'NetworkConnection':
2888 if 'network' in adapters
.attrib
:
2889 vnic
['network'] = adapters
.attrib
['network']
2890 for adapter
in adapters
:
2891 setting_key
= adapter
.tag
.split("}")[1]
2892 vnic
[setting_key
] = adapter
.text
2893 nic_list
.append(vnic
)
2895 for link
in children_section
:
2896 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2897 if link
.attrib
['rel'] == 'screen:acquireTicket':
2898 parsed_respond
['acquireTicket'] = link
.attrib
2899 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2900 parsed_respond
['acquireMksTicket'] = link
.attrib
2902 parsed_respond
['interfaces'] = nic_list
2903 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2904 if vCloud_extension_section
is not None:
2905 vm_vcenter_info
= {}
2906 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2907 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2908 if vmext
is not None:
2909 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2910 vm_vcenter_info
["vim_server_href"] = vmext
.find('vmext:VimServerRef', namespaces
).attrib
['href']
2911 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2913 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2914 vm_virtual_hardware_info
= {}
2915 if virtual_hardware_section
is not None:
2916 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2917 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2918 disk_size
= item
.find("rasd:HostResource" ,namespaces
2919 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2921 vm_virtual_hardware_info
["disk_size"]= disk_size
2924 for link
in virtual_hardware_section
:
2925 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2926 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2927 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2930 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2931 except Exception as exp
:
2932 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2933 return parsed_respond
2935 def acuire_console(self
, vm_uuid
=None):
2937 vca
= self
.connect()
2939 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2943 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2944 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2945 console_dict
= vm_dict
['acquireTicket']
2946 console_rest_call
= console_dict
['href']
2948 response
= Http
.post(url
=console_rest_call
,
2949 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2953 if response
.status_code
== requests
.codes
.ok
:
2954 return response
.content
2958 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2960 Method retrieve vm disk details
2963 vapp_uuid - is vapp identifier.
2964 flavor_disk - disk size as specified in VNFD (flavor)
2967 The return network uuid or return None
2971 #Flavor disk is in GB convert it into MB
2972 flavor_disk
= int(flavor_disk
) * 1024
2973 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2975 vm_name
= vm_details
["name"]
2976 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2978 if vm_details
and "vm_virtual_hardware" in vm_details
:
2979 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2980 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2982 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2984 if flavor_disk
> vm_disk
:
2985 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2986 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2987 vm_disk
, flavor_disk
))
2990 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
2993 except Exception as exp
:
2994 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
2997 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
2999 Method retrieve modify vm disk size
3002 disk_href - vCD API URL to GET and PUT disk data
3003 disk_size - disk size as specified in VNFD (flavor)
3006 The return network uuid or return None
3008 vca
= self
.connect()
3010 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3011 if disk_href
is None or disk_size
is None:
3014 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3015 response
= Http
.get(url
=disk_href
,
3016 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3020 if response
.status_code
!= requests
.codes
.ok
:
3021 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3022 response
.status_code
))
3025 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3026 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3027 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3029 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3030 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3031 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3032 if disk_item
is not None:
3033 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3036 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3037 xml_declaration
=True)
3039 #Send PUT request to modify disk size
3040 headers
= vca
.vcloud_session
.get_vcloud_headers()
3041 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3043 response
= Http
.put(url
=disk_href
,
3046 verify
=vca
.verify
, logger
=self
.logger
)
3048 if response
.status_code
!= 202:
3049 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3050 response
.status_code
))
3052 modify_disk_task
= taskType
.parseString(response
.content
, True)
3053 if type(modify_disk_task
) is GenericTask
:
3054 status
= vca
.block_until_completed(modify_disk_task
)
3059 except Exception as exp
:
3060 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3063 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3065 Method to attach pci devices to VM
3068 vapp_uuid - uuid of vApp/VM
3069 pci_devices - pci devices infromation as specified in VNFD (flavor)
3072 The status of add pci device task , vm object and
3073 vcenter_conect object
3076 vcenter_conect
= None
3077 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3078 #Assuming password of vCenter user is same as password of vCloud user
3079 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vapp_uuid
)
3080 self
.logger
.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
3081 "vm_vcenter_port{}".format(
3082 vm_moref_id
, vm_vcenter_host
,
3083 vm_vcenter_username
, vm_vcenter_port
))
3084 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3086 if hasattr(ssl
, '_create_unverified_context'):
3087 context
= ssl
._create
_unverified
_context
()
3089 no_of_pci_devices
= len(pci_devices
)
3090 if no_of_pci_devices
> 0:
3091 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3092 pwd
=self
.passwd
, port
=int(vm_vcenter_port
) ,
3094 atexit
.register(Disconnect
, vcenter_conect
)
3095 content
= vcenter_conect
.RetrieveContent()
3097 #Get VM and its host
3098 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3099 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3100 if host_obj
and vm_obj
:
3101 #get PCI devies from host on which vapp is currently installed
3102 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3104 if avilable_pci_devices
is None:
3105 #find other hosts with active pci devices
3106 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3111 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3112 #Migrate vm to the host where PCI devices are availble
3113 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3114 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3115 if task
is not None:
3116 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3117 self
.logger
.info("Migrate VM status: {}".format(result
))
3118 host_obj
= new_host_obj
3120 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3121 raise vimconn
.vimconnNotFoundException(
3122 "Fail to migrate VM : {} to host {}".format(
3127 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3128 #Add PCI devices one by one
3129 for pci_device
in avilable_pci_devices
:
3130 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3132 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3134 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3136 self
.logger
.info("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3137 return True, vm_obj
, vcenter_conect
3139 self
.logger
.error("Currently there is no host with"\
3140 " {} number of avaialble PCI devices required for VM {}".format(
3144 raise vimconn
.vimconnNotFoundException(
3145 "Currently there is no host with {} "\
3146 "number of avaialble PCI devices required for VM {}".format(
3150 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3152 except vmodl
.MethodFault
as error
:
3153 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3154 return None, vm_obj
, vcenter_conect
3156 def get_vm_obj(self
, content
, mob_id
):
3158 Method to get the vsphere VM object associated with a given morf ID
3160 vapp_uuid - uuid of vApp/VM
3161 content - vCenter content object
3162 mob_id - mob_id of VM
3170 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3171 [vim
.VirtualMachine
], True
3173 for vm
in container
.view
:
3174 mobID
= vm
._GetMoId
()
3177 host_obj
= vm_obj
.runtime
.host
3179 except Exception as exp
:
3180 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3181 return host_obj
, vm_obj
3183 def get_pci_devices(self
, host
, need_devices
):
3185 Method to get the details of pci devices on given host
3187 host - vSphere host object
3188 need_devices - number of pci devices needed on host
3191 array of pci devices
3195 used_devices_ids
= []
3199 pciPassthruInfo
= host
.config
.pciPassthruInfo
3200 pciDevies
= host
.hardware
.pciDevice
3202 for pci_status
in pciPassthruInfo
:
3203 if pci_status
.passthruActive
:
3204 for device
in pciDevies
:
3205 if device
.id == pci_status
.id:
3206 all_device_ids
.append(device
.id)
3207 all_devices
.append(device
)
3209 #check if devices are in use
3210 avalible_devices
= all_devices
3212 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3213 vm_devices
= vm
.config
.hardware
.device
3214 for device
in vm_devices
:
3215 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3216 if device
.backing
.id in all_device_ids
:
3217 for use_device
in avalible_devices
:
3218 if use_device
.id == device
.backing
.id:
3219 avalible_devices
.remove(use_device
)
3220 used_devices_ids
.append(device
.backing
.id)
3221 self
.logger
.debug("Device {} from devices {}"\
3222 "is in use".format(device
.backing
.id,
3225 if len(avalible_devices
) < need_devices
:
3226 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3228 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3232 required_devices
= avalible_devices
[:need_devices
]
3233 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3234 len(avalible_devices
),
3237 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3239 return required_devices
3241 except Exception as exp
:
3242 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3246 def get_host_and_PCIdevices(self
, content
, need_devices
):
3248 Method to get the details of pci devices infromation on all hosts
3251 content - vSphere host object
3252 need_devices - number of pci devices needed on host
3255 array of pci devices and host object
3258 pci_device_objs
= None
3261 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3262 [vim
.HostSystem
], True)
3263 for host
in container
.view
:
3264 devices
= self
.get_pci_devices(host
, need_devices
)
3267 pci_device_objs
= devices
3269 except Exception as exp
:
3270 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3272 return host_obj
,pci_device_objs
3274 def relocate_vm(self
, dest_host
, vm
) :
3276 Method to get the relocate VM to new host
3279 dest_host - vSphere host object
3280 vm - vSphere VM object
3287 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3288 task
= vm
.Relocate(relocate_spec
)
3289 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3290 except Exception as exp
:
3291 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3292 dest_host
, vm
, exp
))
3295 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3297 Waits and provides updates on a vSphere task
3299 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3302 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3303 if task
.info
.result
is not None and not hideResult
:
3304 self
.logger
.info('{} completed successfully, result: {}'.format(
3308 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3310 self
.logger
.error('{} did not complete successfully: {} '.format(
3315 return task
.info
.result
3317 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3319 Method to add pci device in given VM
3322 host_object - vSphere host object
3323 vm_object - vSphere VM object
3324 host_pci_dev - host_pci_dev must be one of the devices from the
3325 host_object.hardware.pciDevice list
3326 which is configured as a PCI passthrough device
3332 if vm_object
and host_object
and host_pci_dev
:
3334 #Add PCI device to VM
3335 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3336 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3338 if host_pci_dev
.id not in systemid_by_pciid
:
3339 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3342 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3343 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3345 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3346 vendorId
=host_pci_dev
.vendorId
,
3347 deviceName
=host_pci_dev
.deviceName
)
3349 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3351 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3352 new_device_config
.operation
= "add"
3353 vmConfigSpec
= vim
.vm
.ConfigSpec()
3354 vmConfigSpec
.deviceChange
= [new_device_config
]
3356 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3357 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3358 host_pci_dev
, vm_object
, host_object
)
3360 except Exception as exp
:
3361 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3367 def get_vcenter_info_rest(self
, vapp_uuid
):
3369 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3370 Method to get details of vCenter
3373 vapp_uuid - uuid of vApp or VM
3376 Moref Id of VM and deails of vCenter
3380 vm_vcenter_username
= None
3381 vm_vcenter_port
= None
3383 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3384 if vm_details
and "vm_vcenter_info" in vm_details
:
3385 vm_moref_id
= vm_details
["vm_vcenter_info"]["vm_moref_id"]
3386 vim_server_href
= vm_details
["vm_vcenter_info"]["vim_server_href"]
3389 vca
= self
.connect_as_admin()
3391 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3392 if vim_server_href
is None:
3393 self
.logger
.error("No url to get vcenter details")
3395 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3396 response
= Http
.get(url
=vim_server_href
,
3397 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3401 if response
.status_code
!= requests
.codes
.ok
:
3402 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href
,
3403 response
.status_code
))
3405 namespaces
={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3406 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3408 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3409 vm_vcenter_username
= xmlroot_respond
.find('vmext:Username', namespaces
).text
3410 vcenter_url
= xmlroot_respond
.find('vmext:Url', namespaces
).text
3411 vm_vcenter_port
= vcenter_url
.split(":")[2]
3412 vm_vcenter
= vcenter_url
.split(":")[1].split("//")[1]
3414 except Exception as exp
:
3415 self
.logger
.info("Error occurred calling rest api for vcenter information {}".format(exp
))
3417 return vm_moref_id
, vm_vcenter
, vm_vcenter_username
, vm_vcenter_port
3420 def get_vm_pci_details(self
, vmuuid
):
3422 Method to get VM PCI device details from vCenter
3425 vm_obj - vSphere VM object
3428 dict of PCI devives attached to VM
3431 vm_pci_devices_info
= {}
3433 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3434 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3436 if hasattr(ssl
, '_create_unverified_context'):
3437 context
= ssl
._create
_unverified
_context
()
3438 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3439 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3441 atexit
.register(Disconnect
, vcenter_conect
)
3442 content
= vcenter_conect
.RetrieveContent()
3444 #Get VM and its host
3445 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3446 for device
in vm_obj
.config
.hardware
.device
:
3447 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3448 device_details
={'devide_id':device
.backing
.id,
3449 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
3451 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3452 except Exception as exp
:
3453 self
.logger
.info("Error occurred while getting PCI devices infromationn"\
3454 " for VM {} : {}".format(vm_obj
,exp
))
3455 return vm_pci_devices_info