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 class vimconnector(vimconn
.vimconnector
):
125 # dict used to store flavor in memory
128 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
129 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}, persistent_info
={}):
131 Constructor create vmware connector to vCloud director.
133 By default construct doesn't validate connection state. So client can create object with None arguments.
134 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
136 a) It initialize organization UUID
137 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
140 uuid - is organization uuid.
141 name - is organization name that must be presented in vCloud director.
142 tenant_id - is VDC uuid it must be presented in vCloud director
143 tenant_name - is VDC name.
144 url - is hostname or ip address of vCloud director
145 url_admin - same as above.
146 user - is user that administrator for organization. Caller must make sure that
147 username has right privileges.
149 password - is password for a user.
151 VMware connector also requires PVDC administrative privileges and separate account.
152 This variables must be passed via config argument dict contains keys
154 dict['admin_username']
155 dict['admin_password']
156 config - Provide NSX and vCenter information
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
184 self
.vcenter_ip
= None
185 self
.vcenter_port
= None
186 self
.vcenter_user
= None
187 self
.vcenter_password
= None
189 if tenant_name
is not None:
190 orgnameandtenant
= tenant_name
.split(":")
191 if len(orgnameandtenant
) == 2:
192 self
.tenant_name
= orgnameandtenant
[1]
193 self
.org_name
= orgnameandtenant
[0]
195 self
.tenant_name
= tenant_name
196 if "orgname" in config
:
197 self
.org_name
= config
['orgname']
200 self
.logger
.setLevel(getattr(logging
, log_level
))
203 self
.admin_user
= config
['admin_username']
204 self
.admin_password
= config
['admin_password']
206 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
209 self
.nsx_manager
= config
['nsx_manager']
210 self
.nsx_user
= config
['nsx_user']
211 self
.nsx_password
= config
['nsx_password']
213 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
215 self
.vcenter_ip
= config
.get("vcenter_ip", None)
216 self
.vcenter_port
= config
.get("vcenter_port", None)
217 self
.vcenter_user
= config
.get("vcenter_user", None)
218 self
.vcenter_password
= config
.get("vcenter_password", None)
224 raise vimconn
.vimconnException('url param can not be NoneType')
226 if not self
.url_admin
: # try to use normal url
227 self
.url_admin
= self
.url
229 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
230 self
.tenant_id
, self
.tenant_name
))
231 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
232 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
234 # initialize organization
235 if self
.user
is not None and self
.passwd
is not None and self
.url
:
236 self
.init_organization()
238 def __getitem__(self
, index
):
241 if index
== 'tenant_id':
242 return self
.tenant_id
243 if index
== 'tenant_name':
244 return self
.tenant_name
247 elif index
== 'org_name':
249 elif index
== 'org_uuid':
251 elif index
== 'user':
253 elif index
== 'passwd':
257 elif index
== 'url_admin':
258 return self
.url_admin
259 elif index
== "config":
262 raise KeyError("Invalid key '%s'" % str(index
))
264 def __setitem__(self
, index
, value
):
267 if index
== 'tenant_id':
268 self
.tenant_id
= value
269 if index
== 'tenant_name':
270 self
.tenant_name
= value
273 elif index
== 'org_name':
274 self
.org_name
= value
275 elif index
== 'org_uuid':
276 self
.org_uuid
= value
277 elif index
== 'user':
279 elif index
== 'passwd':
283 elif index
== 'url_admin':
284 self
.url_admin
= value
286 raise KeyError("Invalid key '%s'" % str(index
))
288 def connect_as_admin(self
):
289 """ Method connect as pvdc admin user to vCloud director.
290 There are certain action that can be done only by provider vdc admin user.
291 Organization creation / provider network creation etc.
294 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
297 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
299 vca_admin
= VCA(host
=self
.url
,
300 username
=self
.admin_user
,
301 service_type
=STANDALONE
,
305 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
307 raise vimconn
.vimconnConnectionException(
308 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
309 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
312 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
317 """ Method connect as normal user to vCloud director.
320 The return vca object that letter can be used to connect to vCloud director as admin for VDC
324 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
327 vca
= VCA(host
=self
.url
,
329 service_type
=STANDALONE
,
334 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
336 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
337 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
340 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
343 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
344 "{} as user: {}".format(self
.org_name
, self
.user
))
348 def init_organization(self
):
349 """ Method initialize organization UUID and VDC parameters.
351 At bare minimum client must provide organization name that present in vCloud director and VDC.
353 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
354 The Org - UUID will be initialized at the run time if data center present in vCloud director.
357 The return vca object that letter can be used to connect to vcloud direct as admin
360 if self
.org_uuid
is None:
361 org_dict
= self
.get_org_list()
363 # we set org UUID at the init phase but we can do it only when we have valid credential.
364 if org_dict
[org
] == self
.org_name
:
366 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
369 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
371 # if well good we require for org details
372 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
374 # we have two case if we want to initialize VDC ID or VDC name at run time
375 # tenant_name provided but no tenant id
376 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
377 vdcs_dict
= org_details_dict
['vdcs']
378 for vdc
in vdcs_dict
:
379 if vdcs_dict
[vdc
] == self
.tenant_name
:
381 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
385 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
386 # case two we have tenant_id but we don't have tenant name so we find and set it.
387 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
388 vdcs_dict
= org_details_dict
['vdcs']
389 for vdc
in vdcs_dict
:
390 if vdc
== self
.tenant_id
:
391 self
.tenant_name
= vdcs_dict
[vdc
]
392 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
396 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
397 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
399 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
400 self
.logger
.debug(traceback
.format_exc())
403 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
404 """ Method adds a new tenant to VIM with this name.
405 This action requires access to create VDC action in vCloud director.
408 tenant_name is tenant_name to be created.
409 tenant_description not used for this call
412 returns the tenant identifier in UUID format.
413 If action is failed method will throw vimconn.vimconnException method
415 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
416 if vdc_task
is not None:
417 vdc_uuid
, value
= vdc_task
.popitem()
418 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
421 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
423 def delete_tenant(self
, tenant_id
=None):
424 """Delete a tenant from VIM"""
425 'Returns the tenant identifier'
426 raise vimconn
.vimconnNotImplemented("Should have implemented this")
428 def get_tenant_list(self
, filter_dict
={}):
429 """Obtain tenants of VIM
430 filter_dict can contain the following keys:
431 name: filter by tenant name
432 id: filter by tenant uuid/id
434 Returns the tenant list of dictionaries:
435 [{'name':'<name>, 'id':'<id>, ...}, ...]
438 org_dict
= self
.get_org(self
.org_uuid
)
439 vdcs_dict
= org_dict
['vdcs']
444 entry
= {'name': vdcs_dict
[k
], 'id': k
}
445 # if caller didn't specify dictionary we return all tenants.
446 if filter_dict
is not None and filter_dict
:
447 filtered_entry
= entry
.copy()
448 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
449 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
450 if filter_dict
== entry
:
451 vdclist
.append(filtered_entry
)
453 vdclist
.append(entry
)
455 self
.logger
.debug("Error in get_tenant_list()")
456 self
.logger
.debug(traceback
.format_exc())
457 raise vimconn
.vimconnException("Incorrect state. {}")
461 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
462 """Adds a tenant network to VIM
464 net_type can be 'bridge','data'.'ptp'.
465 ip_profile is a dict containing the IP parameters of the network
467 Returns the network identifier"""
469 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
470 .format(net_name
, net_type
, ip_profile
, shared
))
476 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
477 ip_profile
=ip_profile
, isshared
=isshared
)
478 if network_uuid
is not None:
481 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
483 def get_vcd_network_list(self
):
484 """ Method available organization for a logged in tenant
487 The return vca object that letter can be used to connect to vcloud direct as admin
490 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
493 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
495 if not self
.tenant_name
:
496 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
498 vdc
= vca
.get_vdc(self
.tenant_name
)
500 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
502 vdc_uuid
= vdc
.get_id().split(":")[3]
503 networks
= vca
.get_networks(vdc
.get_name())
506 for network
in networks
:
508 netid
= network
.get_id().split(":")
512 filter_dict
["name"] = network
.get_name()
513 filter_dict
["id"] = netid
[3]
514 filter_dict
["shared"] = network
.get_IsShared()
515 filter_dict
["tenant_id"] = vdc_uuid
516 if network
.get_status() == 1:
517 filter_dict
["admin_state_up"] = True
519 filter_dict
["admin_state_up"] = False
520 filter_dict
["status"] = "ACTIVE"
521 filter_dict
["type"] = "bridge"
522 network_list
.append(filter_dict
)
523 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
525 self
.logger
.debug("Error in get_vcd_network_list")
526 self
.logger
.debug(traceback
.format_exc())
529 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
532 def get_network_list(self
, filter_dict
={}):
533 """Obtain tenant networks of VIM
535 name: network name OR/AND
536 id: network uuid OR/AND
537 shared: boolean OR/AND
538 tenant_id: tenant OR/AND
539 admin_state_up: boolean
542 [{key : value , key : value}]
544 Returns the network list of dictionaries:
545 [{<the fields at Filter_dict plus some VIM specific>}, ...]
549 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
552 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
554 if not self
.tenant_name
:
555 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
557 vdc
= vca
.get_vdc(self
.tenant_name
)
559 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
561 vdcid
= vdc
.get_id().split(":")[3]
562 networks
= vca
.get_networks(vdc
.get_name())
566 for network
in networks
:
568 net_uuid
= network
.get_id().split(":")
569 if len(net_uuid
) != 4:
572 net_uuid
= net_uuid
[3]
574 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
577 filter_entry
["name"] = network
.get_name()
578 filter_entry
["id"] = net_uuid
579 filter_entry
["shared"] = network
.get_IsShared()
580 filter_entry
["tenant_id"] = vdcid
581 if network
.get_status() == 1:
582 filter_entry
["admin_state_up"] = True
584 filter_entry
["admin_state_up"] = False
585 filter_entry
["status"] = "ACTIVE"
586 filter_entry
["type"] = "bridge"
587 filtered_entry
= filter_entry
.copy()
589 if filter_dict
is not None and filter_dict
:
590 # we remove all the key : value we don't care and match only
592 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
593 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
594 if filter_dict
== filter_entry
:
595 network_list
.append(filtered_entry
)
597 network_list
.append(filtered_entry
)
599 self
.logger
.debug("Error in get_vcd_network_list")
600 self
.logger
.debug(traceback
.format_exc())
602 self
.logger
.debug("Returning {}".format(network_list
))
605 def get_network(self
, net_id
):
606 """Method obtains network details of net_id VIM network
607 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
611 raise vimconn
.vimconnConnectionException("self.connect() is failed")
613 vdc
= vca
.get_vdc(self
.tenant_name
)
614 vdc_id
= vdc
.get_id().split(":")[3]
616 networks
= vca
.get_networks(vdc
.get_name())
620 for network
in networks
:
621 vdc_network_id
= network
.get_id().split(":")
622 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
623 filter_dict
["name"] = network
.get_name()
624 filter_dict
["id"] = vdc_network_id
[3]
625 filter_dict
["shared"] = network
.get_IsShared()
626 filter_dict
["tenant_id"] = vdc_id
627 if network
.get_status() == 1:
628 filter_dict
["admin_state_up"] = True
630 filter_dict
["admin_state_up"] = False
631 filter_dict
["status"] = "ACTIVE"
632 filter_dict
["type"] = "bridge"
633 self
.logger
.debug("Returning {}".format(filter_dict
))
636 self
.logger
.debug("Error in get_network")
637 self
.logger
.debug(traceback
.format_exc())
641 def delete_network(self
, net_id
):
643 Method Deletes a tenant network from VIM, provide the network id.
645 Returns the network identifier or raise an exception
650 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
652 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
653 if vcd_network
is not None and vcd_network
:
654 if self
.delete_network_action(network_uuid
=net_id
):
657 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
659 def refresh_nets_status(self
, net_list
):
660 """Get the status of the networks
661 Params: the list of network identifiers
662 Returns a dictionary with:
663 net_id: #VIM id of this network
664 status: #Mandatory. Text with one of:
665 # DELETED (not found at vim)
666 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
667 # OTHER (Vim reported other status not understood)
668 # ERROR (VIM indicates an ERROR status)
669 # ACTIVE, INACTIVE, DOWN (admin down),
670 # BUILD (on building process)
672 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
673 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
679 raise vimconn
.vimconnConnectionException("self.connect() is failed")
685 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
686 if vcd_network
is not None and vcd_network
:
687 if vcd_network
['status'] == '1':
693 errormsg
= 'Network not found.'
695 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
696 'vim_info': yaml
.safe_dump(vcd_network
)}
698 self
.logger
.debug("Error in refresh_nets_status")
699 self
.logger
.debug(traceback
.format_exc())
703 def get_flavor(self
, flavor_id
):
704 """Obtain flavor details from the VIM
705 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
707 if flavor_id
not in vimconnector
.flavorlist
:
708 raise vimconn
.vimconnNotFoundException("Flavor not found.")
709 return vimconnector
.flavorlist
[flavor_id
]
711 def new_flavor(self
, flavor_data
):
712 """Adds a tenant flavor to VIM
713 flavor_data contains a dictionary with information, keys:
715 ram: memory (cloud type) in MBytes
716 vpcus: cpus (cloud type)
717 extended: EPA parameters
718 - numas: #items requested in same NUMA
719 memory: number of 1G huge pages memory
720 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
721 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
722 - name: interface name
723 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
724 bandwidth: X Gbps; requested guarantee bandwidth
725 vpci: requested virtual PCI address
729 Returns the flavor identifier"""
731 # generate a new uuid put to internal dict and return it.
732 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
733 new_flavor
=flavor_data
734 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
735 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
736 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
738 extended_flv
= flavor_data
.get("extended")
740 numas
=extended_flv
.get("numas")
743 #overwrite ram and vcpus
744 ram
= numa
['memory']*1024
745 if 'paired-threads' in numa
:
746 cpu
= numa
['paired-threads']*2
747 elif 'cores' in numa
:
749 elif 'threads' in numa
:
750 cpu
= numa
['threads']
752 new_flavor
[FLAVOR_RAM_KEY
] = ram
753 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
754 new_flavor
[FLAVOR_DISK_KEY
] = disk
755 # generate a new uuid put to internal dict and return it.
756 flavor_id
= uuid
.uuid4()
757 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
758 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
760 return str(flavor_id
)
762 def delete_flavor(self
, flavor_id
):
763 """Deletes a tenant flavor from VIM identify by its id
765 Returns the used id or raise an exception
767 if flavor_id
not in vimconnector
.flavorlist
:
768 raise vimconn
.vimconnNotFoundException("Flavor not found.")
770 vimconnector
.flavorlist
.pop(flavor_id
, None)
773 def new_image(self
, image_dict
):
775 Adds a tenant image to VIM
777 200, image-id if the image is created
778 <0, message if there is an error
781 return self
.get_image_id_from_path(image_dict
['location'])
783 def delete_image(self
, image_id
):
790 raise vimconn
.vimconnNotImplemented("Should have implemented this")
792 def catalog_exists(self
, catalog_name
, catalogs
):
799 for catalog
in catalogs
:
800 if catalog
.name
== catalog_name
:
804 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
805 """ Create new catalog entry in vCloud director.
808 vca: vCloud director.
809 catalog_name catalog that client wish to create. Note no validation done for a name.
810 Client must make sure that provide valid string representation.
812 Return (bool) True if catalog created.
816 task
= vca
.create_catalog(catalog_name
, catalog_name
)
817 result
= vca
.block_until_completed(task
)
820 catalogs
= vca
.get_catalogs()
823 return self
.catalog_exists(catalog_name
, catalogs
)
825 # noinspection PyIncorrectDocstring
826 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
827 description
='', progress
=False, chunk_bytes
=128 * 1024):
829 Uploads a OVF file to a vCloud catalog
836 :param catalog_name: (str): The name of the catalog to upload the media.
837 :param media_file_name: (str): The name of the local media file to upload.
838 :return: (bool) True if the media file was successfully uploaded, false otherwise.
840 os
.path
.isfile(media_file_name
)
841 statinfo
= os
.stat(media_file_name
)
843 # find a catalog entry where we upload OVF.
844 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
846 # if VCD can parse OVF we upload VMDK file
847 for catalog
in vca
.get_catalogs():
848 if catalog_name
!= catalog
.name
:
850 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
851 link
.get_rel() == 'add', catalog
.get_Link())
852 assert len(link
) == 1
854 <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>
855 """ % (escape(catalog_name
), escape(description
))
856 headers
= vca
.vcloud_session
.get_vcloud_headers()
857 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
858 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
859 if response
.status_code
== requests
.codes
.created
:
860 catalogItem
= XmlElementTree
.fromstring(response
.content
)
861 entity
= [child
for child
in catalogItem
if
862 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
863 href
= entity
.get('href')
865 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
866 verify
=vca
.verify
, logger
=self
.logger
)
868 if response
.status_code
== requests
.codes
.ok
:
869 media
= mediaType
.parseString(response
.content
, True)
870 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
871 media
.get_Files().get_File()[0].get_Link())[0]
872 headers
= vca
.vcloud_session
.get_vcloud_headers()
873 headers
['Content-Type'] = 'Content-Type text/xml'
874 response
= Http
.put(link
.get_href(),
875 data
=open(media_file_name
, 'rb'),
877 verify
=vca
.verify
, logger
=self
.logger
)
878 if response
.status_code
!= requests
.codes
.ok
:
880 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
884 # TODO fix this with aync block
887 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
889 # uploading VMDK file
890 # check status of OVF upload and upload remaining files.
891 response
= Http
.get(template
,
892 headers
=vca
.vcloud_session
.get_vcloud_headers(),
896 if response
.status_code
== requests
.codes
.ok
:
897 media
= mediaType
.parseString(response
.content
, True)
898 number_of_files
= len(media
.get_Files().get_File())
899 for index
in xrange(0, number_of_files
):
900 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
901 media
.get_Files().get_File()[index
].get_Link())
902 for link
in links_list
:
903 # we skip ovf since it already uploaded.
904 if 'ovf' in link
.get_href():
906 # The OVF file and VMDK must be in a same directory
907 head
, tail
= os
.path
.split(media_file_name
)
908 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
909 if not os
.path
.isfile(file_vmdk
):
911 statinfo
= os
.stat(file_vmdk
)
912 if statinfo
.st_size
== 0:
914 hrefvmdk
= link
.get_href()
917 print("Uploading file: {}".format(file_vmdk
))
919 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
921 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
923 bytes_transferred
= 0
924 f
= open(file_vmdk
, 'rb')
925 while bytes_transferred
< statinfo
.st_size
:
926 my_bytes
= f
.read(chunk_bytes
)
927 if len(my_bytes
) <= chunk_bytes
:
928 headers
= vca
.vcloud_session
.get_vcloud_headers()
929 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
930 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
931 headers
['Content-Length'] = str(len(my_bytes
))
932 response
= Http
.put(hrefvmdk
,
938 if response
.status_code
== requests
.codes
.ok
:
939 bytes_transferred
+= len(my_bytes
)
941 progress_bar
.update(bytes_transferred
)
944 'file upload failed with error: [%s] %s' % (response
.status_code
,
951 progress_bar
.finish()
955 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
956 format(catalog_name
, media_file_name
))
959 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
962 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
963 """Upload media file"""
964 # TODO add named parameters for readability
966 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
967 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
969 def validate_uuid4(self
, uuid_string
=None):
970 """ Method validate correct format of UUID.
972 Return: true if string represent valid uuid
975 val
= uuid
.UUID(uuid_string
, version
=4)
980 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
981 """ Method check catalog and return catalog ID in UUID format.
984 catalog_name: catalog name as string
985 catalogs: list of catalogs.
987 Return: catalogs uuid
990 for catalog
in catalogs
:
991 if catalog
.name
== catalog_name
:
992 catalog_id
= catalog
.get_id().split(":")
996 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
997 """ Method check catalog and return catalog name lookup done by catalog UUID.
1000 catalog_name: catalog name as string
1001 catalogs: list of catalogs.
1003 Return: catalogs name or None
1006 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1009 for catalog
in catalogs
:
1010 catalog_id
= catalog
.get_id().split(":")[3]
1011 if catalog_id
== catalog_uuid
:
1015 def get_image_id_from_path(self
, path
=None, progress
=False):
1016 """ Method upload OVF image to vCloud director.
1018 Each OVF image represented as single catalog entry in vcloud director.
1019 The method check for existing catalog entry. The check done by file name without file extension.
1021 if given catalog name already present method will respond with existing catalog uuid otherwise
1022 it will create new catalog entry and upload OVF file to newly created catalog.
1024 If method can't create catalog entry or upload a file it will throw exception.
1026 Method accept boolean flag progress that will output progress bar. It useful method
1027 for standalone upload use case. In case to test large file upload.
1030 path: - valid path to OVF file.
1031 progress - boolean progress bar show progress bar.
1033 Return: if image uploaded correct method will provide image catalog UUID.
1035 vca
= self
.connect()
1037 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1040 raise vimconn
.vimconnException("Image path can't be None.")
1042 if not os
.path
.isfile(path
):
1043 raise vimconn
.vimconnException("Can't read file. File not found.")
1045 if not os
.access(path
, os
.R_OK
):
1046 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1048 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1050 dirpath
, filename
= os
.path
.split(path
)
1051 flname
, file_extension
= os
.path
.splitext(path
)
1052 if file_extension
!= '.ovf':
1053 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1054 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1056 catalog_name
= os
.path
.splitext(filename
)[0]
1057 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1058 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1059 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1061 catalogs
= vca
.get_catalogs()
1062 if len(catalogs
) == 0:
1063 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1064 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1066 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1067 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1068 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1070 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1071 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1073 for catalog
in catalogs
:
1074 # search for existing catalog if we find same name we return ID
1075 # TODO optimize this
1076 if catalog
.name
== catalog_md5_name
:
1077 self
.logger
.debug("Found existing catalog entry for {} "
1078 "catalog id {}".format(catalog_name
,
1079 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1080 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1082 # if we didn't find existing catalog we create a new one and upload image.
1083 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1084 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1086 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1088 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1089 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1091 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1093 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1095 def get_image_list(self
, filter_dict
={}):
1096 '''Obtain tenant images from VIM
1100 checksum: image checksum
1101 location: image path
1102 Returns the image list of dictionaries:
1103 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1106 vca
= self
.connect()
1108 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1111 catalogs
= vca
.get_catalogs()
1112 if len(catalogs
) == 0:
1115 for catalog
in catalogs
:
1116 catalog_uuid
= catalog
.get_id().split(":")[3]
1119 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1121 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1123 filtered_dict
["name"] = name
1124 filtered_dict
["id"] = catalog_uuid
1125 image_list
.append(filtered_dict
)
1127 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1129 except Exception as exp
:
1130 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1132 def get_vappid(self
, vdc
=None, vapp_name
=None):
1133 """ Method takes vdc object and vApp name and returns vapp uuid or None
1136 vdc: The VDC object.
1137 vapp_name: is application vappp name identifier
1140 The return vApp name otherwise None
1142 if vdc
is None or vapp_name
is None:
1144 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1146 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1147 vdc
.ResourceEntities
.ResourceEntity
)
1149 return refs
[0].href
.split("vapp")[1][1:]
1150 except Exception as e
:
1151 self
.logger
.exception(e
)
1155 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1156 """ Method Method returns True or False if vapp deployed in vCloud director
1159 vca: Connector to VCA
1160 vdc: The VDC object.
1161 vappid: vappid is application identifier
1164 The return True if vApp deployed
1169 refs
= filter(lambda ref
:
1170 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1171 vdc
.ResourceEntities
.ResourceEntity
)
1173 vappid
= ref
.href
.split("vapp")[1][1:]
1174 # find vapp with respected vapp uuid
1175 if vappid
== vapp_uuid
:
1177 except Exception as e
:
1178 self
.logger
.exception(e
)
1182 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1183 """Method returns vApp name from vCD and lookup done by vapp_id.
1186 vca: Connector to VCA
1187 vdc: The VDC object.
1188 vapp_uuid: vappid is application identifier
1191 The return vApp name otherwise None
1195 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1196 vdc
.ResourceEntities
.ResourceEntity
)
1198 # we care only about UUID the rest doesn't matter
1199 vappid
= ref
.href
.split("vapp")[1][1:]
1200 if vappid
== vapp_uuid
:
1201 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1203 tree
= XmlElementTree
.fromstring(response
.content
)
1204 return tree
.attrib
['name']
1205 except Exception as e
:
1206 self
.logger
.exception(e
)
1210 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1211 cloud_config
=None, disk_list
=None):
1212 """Adds a VM instance to VIM
1214 start: indicates if VM must start or boot in pause mode. Ignored
1215 image_id,flavor_id: image and flavor uuid
1216 net_list: list of interfaces, each one is a dictionary with:
1218 net_id: network uuid to connect
1219 vpci: virtual vcpi to assign
1220 model: interface model, virtio, e2000, ...
1222 use: 'data', 'bridge', 'mgmt'
1223 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1224 vim_id: filled/added by this function
1225 cloud_config: can be a text script to be passed directly to cloud-init,
1226 or an object to inject users and ssh keys with format:
1227 key-pairs: [] list of keys to install to the default user
1228 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1229 #TODO ip, security groups
1230 Returns >=0, the instance identifier
1234 self
.logger
.info("Creating new instance for entry {}".format(name
))
1235 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
1236 description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1237 vca
= self
.connect()
1239 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1241 #new vm name = vmname + tenant_id + uuid
1242 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1243 vmname_andid
= ''.join(new_vm_name
)
1245 # if vm already deployed we return existing uuid
1246 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1247 # if vapp_uuid is not None:
1250 # we check for presence of VDC, Catalog entry and Flavor.
1251 vdc
= vca
.get_vdc(self
.tenant_name
)
1253 raise vimconn
.vimconnNotFoundException(
1254 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1255 catalogs
= vca
.get_catalogs()
1256 if catalogs
is None:
1257 raise vimconn
.vimconnNotFoundException(
1258 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1260 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1261 if catalog_hash_name
:
1262 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1264 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1265 "(Failed retrieve catalog information {})".format(name
, image_id
))
1268 # Set vCPU and Memory based on flavor.
1273 pci_devices_info
= []
1274 if flavor_id
is not None:
1275 if flavor_id
not in vimconnector
.flavorlist
:
1276 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1277 "Failed retrieve flavor information "
1278 "flavor id {}".format(name
, flavor_id
))
1281 flavor
= vimconnector
.flavorlist
[flavor_id
]
1282 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1283 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1284 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1285 extended
= flavor
.get("extended", None)
1287 numas
=extended
.get("numas", None)
1290 for interface
in numa
.get("interfaces",() ):
1291 if interface
["dedicated"].strip()=="yes":
1292 pci_devices_info
.append(interface
)
1293 except Exception as exp
:
1294 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1296 # image upload creates template name as catalog name space Template.
1297 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1302 # client must provide at least one entry in net_list if not we report error
1303 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1304 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1306 primary_netname
= None
1307 network_mode
= 'bridged'
1308 if net_list
is not None and len(net_list
) > 0:
1309 for net
in net_list
:
1310 if 'use' in net
and net
['use'] == 'mgmt':
1312 if primary_net
is None:
1313 primary_net
= net_list
[0]
1316 primary_net_id
= primary_net
['net_id']
1317 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1318 if 'name' in network_dict
:
1319 primary_netname
= network_dict
['name']
1322 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1324 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1326 # use: 'data', 'bridge', 'mgmt'
1327 # create vApp. Set vcpu and ram based on flavor id.
1328 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1329 self
.get_catalogbyid(image_id
, catalogs
),
1330 network_name
=None, # None while creating vapp
1331 network_mode
=network_mode
,
1332 vm_name
=vmname_andid
,
1333 vm_cpus
=vm_cpus
, # can be None if flavor is None
1334 vm_memory
=vm_memory
) # can be None if flavor is None
1336 if vapptask
is None or vapptask
is False:
1337 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1338 if type(vapptask
) is VappTask
:
1339 vca
.block_until_completed(vapptask
)
1341 # we should have now vapp in undeployed state.
1342 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1343 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1345 raise vimconn
.vimconnUnexpectedResponse(
1346 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1349 #Add PCI passthrough configrations
1350 PCI_devices_status
= False
1353 if len(pci_devices_info
) > 0:
1354 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1356 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1359 if PCI_devices_status
:
1360 self
.logger
.info("Added PCI devives {} to VM {}".format(
1365 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1371 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1372 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1374 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1376 # add NICs & connect to networks in netlist
1378 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1380 primary_nic_index
= 0
1381 for net
in net_list
:
1382 # openmano uses network id in UUID format.
1383 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1384 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1385 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1387 if 'net_id' not in net
:
1390 interface_net_id
= net
['net_id']
1391 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1392 interface_network_mode
= net
['use']
1394 if interface_network_mode
== 'mgmt':
1395 primary_nic_index
= nicIndex
1397 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1398 - DHCP (The IP address is obtained from a DHCP service.)
1399 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1400 - NONE (No IP addressing mode specified.)"""
1402 if primary_netname
is not None:
1403 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1405 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1406 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1407 if type(task
) is GenericTask
:
1408 vca
.block_until_completed(task
)
1409 # connect network to VM - with all DHCP by default
1410 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1411 task
= vapp
.connect_vms(nets
[0].name
,
1412 connection_index
=nicIndex
,
1413 connections_primary_index
=primary_nic_index
,
1414 ip_allocation_mode
='DHCP')
1415 if type(task
) is GenericTask
:
1416 vca
.block_until_completed(task
)
1419 # it might be a case if specific mandatory entry in dict is empty
1420 self
.logger
.debug("Key error {}".format(KeyError.message
))
1421 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1423 # deploy and power on vm
1424 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1425 deploytask
= vapp
.deploy(powerOn
=False)
1426 if type(deploytask
) is GenericTask
:
1427 vca
.block_until_completed(deploytask
)
1429 # If VM has PCI devices reserve memory for VM
1430 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1431 memReserve
= vm_obj
.config
.hardware
.memoryMB
1432 spec
= vim
.vm
.ConfigSpec()
1433 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1434 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1436 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1437 self
.logger
.info("Reserved memmoery {} MB for "\
1438 "VM VM status: {}".format(str(memReserve
),result
))
1440 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1441 str(memReserve
),str(vm_obj
)))
1443 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1444 poweron_task
= vapp
.poweron()
1445 if type(poweron_task
) is GenericTask
:
1446 vca
.block_until_completed(poweron_task
)
1448 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1451 while wait_time
<= MAX_WAIT_TIME
:
1452 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1453 if vapp
and vapp
.me
.deployed
:
1454 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1457 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1458 time
.sleep(INTERVAL_TIME
)
1460 wait_time
+=INTERVAL_TIME
1462 if vapp_uuid
is not None:
1465 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1469 ## based on current discussion
1473 # created: '2016-09-08T11:51:58'
1474 # description: simple-instance.linux1.1
1475 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1476 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1477 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1482 def get_vminstance(self
, vim_vm_uuid
=None):
1483 """Returns the VM instance information from VIM"""
1485 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1486 vca
= self
.connect()
1488 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1490 vdc
= vca
.get_vdc(self
.tenant_name
)
1492 raise vimconn
.vimconnConnectionException(
1493 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1495 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1496 if not vm_info_dict
:
1497 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1498 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1500 status_key
= vm_info_dict
['status']
1503 vm_dict
= {'created': vm_info_dict
['created'],
1504 'description': vm_info_dict
['name'],
1505 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1506 'hostId': vm_info_dict
['vmuuid'],
1508 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1510 if 'interfaces' in vm_info_dict
:
1511 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1513 vm_dict
['interfaces'] = []
1515 vm_dict
= {'created': '',
1517 'status': vcdStatusCode2manoFormat
[int(-1)],
1518 'hostId': vm_info_dict
['vmuuid'],
1519 'error_msg': "Inconsistency state",
1520 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1524 def delete_vminstance(self
, vm__vim_uuid
):
1525 """Method poweroff and remove VM instance from vcloud director network.
1528 vm__vim_uuid: VM UUID
1531 Returns the instance identifier
1534 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1535 vca
= self
.connect()
1537 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1539 vdc
= vca
.get_vdc(self
.tenant_name
)
1541 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1543 raise vimconn
.vimconnException(
1544 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1547 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1548 if vapp_name
is None:
1549 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1550 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1552 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1554 # Delete vApp and wait for status change if task executed and vApp is None.
1555 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1558 if vapp
.me
.deployed
:
1559 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1563 while wait_time
<= MAX_WAIT_TIME
:
1564 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1566 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1567 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1569 power_off_task
= vapp
.poweroff()
1570 if type(power_off_task
) is GenericTask
:
1571 result
= vca
.block_until_completed(power_off_task
)
1576 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1577 time
.sleep(INTERVAL_TIME
)
1579 wait_time
+=INTERVAL_TIME
1581 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1583 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1586 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1589 while wait_time
<= MAX_WAIT_TIME
:
1590 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1592 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1593 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1594 undeploy_task
= vapp
.undeploy(action
='powerOff')
1596 if type(undeploy_task
) is GenericTask
:
1597 result
= vca
.block_until_completed(undeploy_task
)
1602 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1603 time
.sleep(INTERVAL_TIME
)
1605 wait_time
+=INTERVAL_TIME
1608 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1611 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1612 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1614 if vapp
is not None:
1618 while wait_time
<= MAX_WAIT_TIME
:
1619 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1621 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1622 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1624 delete_task
= vapp
.delete()
1626 if type(delete_task
) is GenericTask
:
1627 vca
.block_until_completed(delete_task
)
1628 result
= vca
.block_until_completed(delete_task
)
1632 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1633 time
.sleep(INTERVAL_TIME
)
1635 wait_time
+=INTERVAL_TIME
1638 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1641 self
.logger
.debug(traceback
.format_exc())
1642 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1644 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1645 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1648 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1650 def refresh_vms_status(self
, vm_list
):
1651 """Get the status of the virtual machines and their interfaces/ports
1652 Params: the list of VM identifiers
1653 Returns a dictionary with:
1654 vm_id: #VIM id of this Virtual Machine
1655 status: #Mandatory. Text with one of:
1656 # DELETED (not found at vim)
1657 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1658 # OTHER (Vim reported other status not understood)
1659 # ERROR (VIM indicates an ERROR status)
1660 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1661 # CREATING (on building process), ERROR
1662 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1664 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1665 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1667 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1668 mac_address: #Text format XX:XX:XX:XX:XX:XX
1669 vim_net_id: #network id where this interface is connected
1670 vim_interface_id: #interface/port VIM id
1671 ip_address: #null, or text with IPv4, IPv6 address
1674 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1677 rheaders
= {'Content-Type': 'application/xml'}
1678 iso_edges
= ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
1681 for edge
in iso_edges
:
1682 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1683 self
.logger
.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url
))
1685 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1686 auth
= (self
.nsx_user
, self
.nsx_password
),
1687 verify
= False, headers
= rheaders
)
1689 if resp
.status_code
== requests
.codes
.ok
:
1690 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1691 for child
in dhcp_leases
:
1692 if child
.tag
== 'dhcpLeaseInfo':
1693 dhcpLeaseInfo
= child
1694 for leaseInfo
in dhcpLeaseInfo
:
1695 for elem
in leaseInfo
:
1696 if (elem
.tag
)=='macAddress':
1697 mac_addr
= elem
.text
1698 if (elem
.tag
)=='ipAddress':
1700 if (mac_addr
) is not None:
1701 mac_ip_addr
[mac_addr
]= ip_addr
1702 self
.logger
.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr
))
1704 self
.logger
.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp
.content
))
1706 self
.logger
.debug("Error in response from NSX Manager {}".format(KeyError.message
))
1707 self
.logger
.debug(traceback
.format_exc())
1709 vca
= self
.connect()
1711 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1713 vdc
= vca
.get_vdc(self
.tenant_name
)
1715 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1718 for vmuuid
in vm_list
:
1719 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1720 if vmname
is not None:
1722 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1723 vm_info
= the_vapp
.get_vms_details()
1724 vm_status
= vm_info
[0]['status']
1725 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1726 vm_info
[0].update(vm_pci_details
)
1728 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1729 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1730 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1734 vm_app_networks
= the_vapp
.get_vms_network_info()
1735 for vapp_network
in vm_app_networks
:
1736 for vm_network
in vapp_network
:
1737 if vm_network
['name'] == vmname
:
1738 #Assign IP Address based on MAC Address in NSX DHCP lease info
1739 for mac_adres
,ip_adres
in mac_ip_addr
.iteritems():
1740 if mac_adres
== vm_network
['mac']:
1741 vm_network
['ip']=ip_adres
1742 interface
= {"mac_address": vm_network
['mac'],
1743 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1744 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1745 'ip_address': vm_network
['ip']}
1746 # interface['vim_info'] = yaml.safe_dump(vm_network)
1747 vm_dict
["interfaces"].append(interface
)
1748 # add a vm to vm dict
1749 vms_dict
.setdefault(vmuuid
, vm_dict
)
1751 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1752 self
.logger
.debug(traceback
.format_exc())
1756 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1757 """Send and action over a VM instance from VIM
1758 Returns the vm_id if the action was successfully sent to the VIM"""
1760 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1761 if vm__vim_uuid
is None or action_dict
is None:
1762 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1764 vca
= self
.connect()
1766 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1768 vdc
= vca
.get_vdc(self
.tenant_name
)
1770 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1772 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1773 if vapp_name
is None:
1774 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1775 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1777 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1780 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1781 # TODO fix all status
1782 if "start" in action_dict
:
1783 vm_info
= the_vapp
.get_vms_details()
1784 vm_status
= vm_info
[0]['status']
1785 self
.logger
.info("Power on vApp: vm_status:{} {}".format(type(vm_status
),vm_status
))
1786 if vm_status
== "Suspended" or vm_status
== "Powered off":
1787 power_on_task
= the_vapp
.poweron()
1788 if power_on_task
is not None and type(power_on_task
) is GenericTask
:
1789 result
= vca
.block_until_completed(power_on_task
)
1791 self
.logger
.info("action_vminstance: Powered on vApp: {}".format(vapp_name
))
1793 self
.logger
.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name
))
1795 self
.logger
.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name
))
1796 elif "rebuild" in action_dict
:
1797 self
.logger
.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name
))
1798 power_on_task
= the_vapp
.deploy(powerOn
=True)
1799 if type(power_on_task
) is GenericTask
:
1800 result
= vca
.block_until_completed(power_on_task
)
1802 self
.logger
.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name
))
1804 self
.logger
.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name
))
1806 self
.logger
.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name
))
1807 elif "pause" in action_dict
:
1810 elif "resume" in action_dict
:
1813 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1814 power_off_task
= the_vapp
.undeploy(action
='powerOff')
1815 if type(power_off_task
) is GenericTask
:
1816 result
= vca
.block_until_completed(power_off_task
)
1818 self
.logger
.info("action_vminstance: Powered off vApp: {}".format(vapp_name
))
1820 self
.logger
.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name
))
1822 self
.logger
.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name
))
1823 elif "forceOff" in action_dict
:
1825 elif "terminate" in action_dict
:
1827 # elif "createImage" in action_dict:
1828 # server.create_image()
1834 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1836 Get a console for the virtual machine
1838 vm_id: uuid of the VM
1839 console_type, can be:
1840 "novnc" (by default), "xvpvnc" for VNC types,
1841 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1842 Returns dict with the console parameters:
1843 protocol: ssh, ftp, http, https, ...
1844 server: usually ip address
1845 port: the http, ssh, ... port
1846 suffix: extra text, e.g. the http path and query string
1848 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1850 # NOT USED METHODS in current version
1852 def host_vim2gui(self
, host
, server_dict
):
1853 """Transform host dictionary from VIM format to GUI format,
1854 and append to the server_dict
1856 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1858 def get_hosts_info(self
):
1859 """Get the information of deployed hosts
1860 Returns the hosts content"""
1861 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1863 def get_hosts(self
, vim_tenant
):
1864 """Get the hosts and deployed instances
1865 Returns the hosts content"""
1866 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1868 def get_processor_rankings(self
):
1869 """Get the processor rankings in the VIM database"""
1870 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1872 def new_host(self
, host_data
):
1873 """Adds a new host to VIM"""
1874 '''Returns status code of the VIM response'''
1875 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1877 def new_external_port(self
, port_data
):
1878 """Adds a external port to VIM"""
1879 '''Returns the port identifier'''
1880 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1882 def new_external_network(self
, net_name
, net_type
):
1883 """Adds a external network to VIM (shared)"""
1884 '''Returns the network identifier'''
1885 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1887 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1888 """Connects a external port to a network"""
1889 '''Returns status code of the VIM response'''
1890 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1892 def new_vminstancefromJSON(self
, vm_data
):
1893 """Adds a VM instance to VIM"""
1894 '''Returns the instance identifier'''
1895 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1897 def get_network_name_by_id(self
, network_uuid
=None):
1898 """Method gets vcloud director network named based on supplied uuid.
1901 network_uuid: network_id
1904 The return network name.
1907 vca
= self
.connect()
1909 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1911 if not network_uuid
:
1915 org_dict
= self
.get_org(self
.org_uuid
)
1916 if 'networks' in org_dict
:
1917 org_network_dict
= org_dict
['networks']
1918 for net_uuid
in org_network_dict
:
1919 if net_uuid
== network_uuid
:
1920 return org_network_dict
[net_uuid
]
1922 self
.logger
.debug("Exception in get_network_name_by_id")
1923 self
.logger
.debug(traceback
.format_exc())
1927 def get_network_id_by_name(self
, network_name
=None):
1928 """Method gets vcloud director network uuid based on supplied name.
1931 network_name: network_name
1933 The return network uuid.
1934 network_uuid: network_id
1937 vca
= self
.connect()
1939 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1941 if not network_name
:
1942 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1946 org_dict
= self
.get_org(self
.org_uuid
)
1947 if org_dict
and 'networks' in org_dict
:
1948 org_network_dict
= org_dict
['networks']
1949 for net_uuid
,net_name
in org_network_dict
.iteritems():
1950 if net_name
== network_name
:
1953 except KeyError as exp
:
1954 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1958 def list_org_action(self
):
1960 Method leverages vCloud director and query for available organization for particular user
1963 vca - is active VCA connection.
1964 vdc_name - is a vdc name that will be used to query vms action
1967 The return XML respond
1970 vca
= self
.connect()
1972 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1974 url_list
= [vca
.host
, '/api/org']
1975 vm_list_rest_call
= ''.join(url_list
)
1977 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1978 response
= Http
.get(url
=vm_list_rest_call
,
1979 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1982 if response
.status_code
== requests
.codes
.ok
:
1983 return response
.content
1987 def get_org_action(self
, org_uuid
=None):
1989 Method leverages vCloud director and retrieve available object fdr organization.
1992 vca - is active VCA connection.
1993 vdc_name - is a vdc name that will be used to query vms action
1996 The return XML respond
1999 vca
= self
.connect()
2001 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2003 if org_uuid
is None:
2006 url_list
= [vca
.host
, '/api/org/', org_uuid
]
2007 vm_list_rest_call
= ''.join(url_list
)
2009 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2010 response
= Http
.get(url
=vm_list_rest_call
,
2011 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2014 if response
.status_code
== requests
.codes
.ok
:
2015 return response
.content
2019 def get_org(self
, org_uuid
=None):
2021 Method retrieves available organization in vCloud Director
2024 org_uuid - is a organization uuid.
2027 The return dictionary with following key
2028 "network" - for network list under the org
2029 "catalogs" - for network list under the org
2030 "vdcs" - for vdc list under org
2034 vca
= self
.connect()
2036 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2038 if org_uuid
is None:
2041 content
= self
.get_org_action(org_uuid
=org_uuid
)
2046 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2047 for child
in vm_list_xmlroot
:
2048 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2049 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2050 org_dict
['vdcs'] = vdc_list
2051 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2052 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2053 org_dict
['networks'] = network_list
2054 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2055 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2056 org_dict
['catalogs'] = catalog_list
2062 def get_org_list(self
):
2064 Method retrieves available organization in vCloud Director
2067 vca - is active VCA connection.
2070 The return dictionary and key for each entry VDC UUID
2074 vca
= self
.connect()
2076 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2078 content
= self
.list_org_action()
2080 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2081 for vm_xml
in vm_list_xmlroot
:
2082 if vm_xml
.tag
.split("}")[1] == 'Org':
2083 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2084 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2090 def vms_view_action(self
, vdc_name
=None):
2091 """ Method leverages vCloud director vms query call
2094 vca - is active VCA connection.
2095 vdc_name - is a vdc name that will be used to query vms action
2098 The return XML respond
2100 vca
= self
.connect()
2101 if vdc_name
is None:
2104 url_list
= [vca
.host
, '/api/vms/query']
2105 vm_list_rest_call
= ''.join(url_list
)
2107 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2108 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2109 vca
.vcloud_session
.organization
.Link
)
2111 response
= Http
.get(url
=vm_list_rest_call
,
2112 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2115 if response
.status_code
== requests
.codes
.ok
:
2116 return response
.content
2120 def get_vapp_list(self
, vdc_name
=None):
2122 Method retrieves vApp list deployed vCloud director and returns a dictionary
2123 contains a list of all vapp deployed for queried VDC.
2124 The key for a dictionary is vApp UUID
2128 vca - is active VCA connection.
2129 vdc_name - is a vdc name that will be used to query vms action
2132 The return dictionary and key for each entry vapp UUID
2136 if vdc_name
is None:
2139 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2141 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2142 for vm_xml
in vm_list_xmlroot
:
2143 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2144 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2145 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2146 if 'vappTemplate-' in rawuuid
[0]:
2147 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2148 # vm and use raw UUID as key
2149 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2155 def get_vm_list(self
, vdc_name
=None):
2157 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2158 contains a list of all VM's deployed for queried VDC.
2159 The key for a dictionary is VM UUID
2163 vca - is active VCA connection.
2164 vdc_name - is a vdc name that will be used to query vms action
2167 The return dictionary and key for each entry vapp UUID
2171 if vdc_name
is None:
2174 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2176 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2177 for vm_xml
in vm_list_xmlroot
:
2178 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2179 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2180 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2181 if 'vm-' in rawuuid
[0]:
2182 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2183 # vm and use raw UUID as key
2184 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2190 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2192 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2193 contains a list of all VM's deployed for queried VDC.
2194 The key for a dictionary is VM UUID
2198 vca - is active VCA connection.
2199 vdc_name - is a vdc name that will be used to query vms action
2202 The return dictionary and key for each entry vapp UUID
2205 vca
= self
.connect()
2207 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2209 if vdc_name
is None:
2212 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2214 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2215 for vm_xml
in vm_list_xmlroot
:
2216 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2217 # lookup done by UUID
2219 if vapp_name
in vm_xml
.attrib
['container']:
2220 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2221 if 'vm-' in rawuuid
[0]:
2222 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2224 # lookup done by Name
2226 if vapp_name
in vm_xml
.attrib
['name']:
2227 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2228 if 'vm-' in rawuuid
[0]:
2229 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2236 def get_network_action(self
, network_uuid
=None):
2238 Method leverages vCloud director and query network based on network uuid
2241 vca - is active VCA connection.
2242 network_uuid - is a network uuid
2245 The return XML respond
2248 vca
= self
.connect()
2250 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2252 if network_uuid
is None:
2255 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2256 vm_list_rest_call
= ''.join(url_list
)
2258 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2259 response
= Http
.get(url
=vm_list_rest_call
,
2260 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2263 if response
.status_code
== requests
.codes
.ok
:
2264 return response
.content
2268 def get_vcd_network(self
, network_uuid
=None):
2270 Method retrieves available network from vCloud Director
2273 network_uuid - is VCD network UUID
2275 Each element serialized as key : value pair
2277 Following keys available for access. network_configuration['Gateway'}
2281 <IsInherited>true</IsInherited>
2282 <Gateway>172.16.252.100</Gateway>
2283 <Netmask>255.255.255.0</Netmask>
2284 <Dns1>172.16.254.201</Dns1>
2285 <Dns2>172.16.254.202</Dns2>
2286 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2287 <IsEnabled>true</IsEnabled>
2290 <StartAddress>172.16.252.1</StartAddress>
2291 <EndAddress>172.16.252.99</EndAddress>
2296 <FenceMode>bridged</FenceMode>
2299 The return dictionary and key for each entry vapp UUID
2302 network_configuration
= {}
2303 if network_uuid
is None:
2306 content
= self
.get_network_action(network_uuid
=network_uuid
)
2308 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2310 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2311 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2312 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2314 for child
in vm_list_xmlroot
:
2315 if child
.tag
.split("}")[1] == 'IsShared':
2316 network_configuration
['isShared'] = child
.text
.strip()
2317 if child
.tag
.split("}")[1] == 'Configuration':
2318 for configuration
in child
.iter():
2319 tagKey
= configuration
.tag
.split("}")[1].strip()
2321 network_configuration
[tagKey
] = configuration
.text
.strip()
2322 return network_configuration
2326 return network_configuration
2328 def delete_network_action(self
, network_uuid
=None):
2330 Method delete given network from vCloud director
2333 network_uuid - is a network uuid that client wish to delete
2336 The return None or XML respond or false
2339 vca
= self
.connect_as_admin()
2341 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2342 if network_uuid
is None:
2345 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2346 vm_list_rest_call
= ''.join(url_list
)
2348 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2349 response
= Http
.delete(url
=vm_list_rest_call
,
2350 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2354 if response
.status_code
== 202:
2359 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2360 ip_profile
=None, isshared
='true'):
2362 Method create network in vCloud director
2365 network_name - is network name to be created.
2366 net_type - can be 'bridge','data','ptp','mgmt'.
2367 ip_profile is a dict containing the IP parameters of the network
2368 isshared - is a boolean
2369 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2370 It optional attribute. by default if no parent network indicate the first available will be used.
2373 The return network uuid or return None
2376 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2377 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2378 ip_profile
=ip_profile
,
2380 parent_network_uuid
=parent_network_uuid
,
2383 self
.logger
.debug("Failed create network {}.".format(network_name
))
2387 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2388 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2389 if len(vcd_uuid
) == 4:
2390 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2393 self
.logger
.debug("Failed create network {}".format(network_name
))
2396 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2397 ip_profile
=None, isshared
='true'):
2399 Method create network in vCloud director
2402 network_name - is network name to be created.
2403 net_type - can be 'bridge','data','ptp','mgmt'.
2404 ip_profile is a dict containing the IP parameters of the network
2405 isshared - is a boolean
2406 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2407 It optional attribute. by default if no parent network indicate the first available will be used.
2410 The return network uuid or return None
2413 vca
= self
.connect_as_admin()
2415 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2416 if network_name
is None:
2419 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2420 vm_list_rest_call
= ''.join(url_list
)
2421 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2422 response
= Http
.get(url
=vm_list_rest_call
,
2423 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2427 provider_network
= None
2428 available_networks
= None
2429 add_vdc_rest_url
= None
2431 if response
.status_code
!= requests
.codes
.ok
:
2432 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2433 response
.status_code
))
2437 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2438 for child
in vm_list_xmlroot
:
2439 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2440 provider_network
= child
.attrib
.get('href')
2441 # application/vnd.vmware.admin.providervdc+xml
2442 if child
.tag
.split("}")[1] == 'Link':
2443 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2444 and child
.attrib
.get('rel') == 'add':
2445 add_vdc_rest_url
= child
.attrib
.get('href')
2447 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2448 self
.logger
.debug("Respond body {}".format(response
.content
))
2451 # find pvdc provided available network
2452 response
= Http
.get(url
=provider_network
,
2453 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2456 if response
.status_code
!= requests
.codes
.ok
:
2457 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2458 response
.status_code
))
2461 # available_networks.split("/")[-1]
2463 if parent_network_uuid
is None:
2465 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2466 for child
in vm_list_xmlroot
.iter():
2467 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2468 for networks
in child
.iter():
2469 # application/vnd.vmware.admin.network+xml
2470 if networks
.attrib
.get('href') is not None:
2471 available_networks
= networks
.attrib
.get('href')
2476 #Configure IP profile of the network
2477 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2479 gateway_address
=ip_profile
['gateway_address']
2480 dhcp_count
=int(ip_profile
['dhcp_count'])
2481 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2483 if ip_profile
['dhcp_enabled']==True:
2486 dhcp_enabled
='false'
2487 dhcp_start_address
=ip_profile
['dhcp_start_address']
2489 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2490 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2491 end_ip_int
+= dhcp_count
- 1
2492 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2494 ip_version
=ip_profile
['ip_version']
2495 dns_address
=ip_profile
['dns_address']
2497 # either use client provided UUID or search for a first available
2498 # if both are not defined we return none
2499 if parent_network_uuid
is not None:
2500 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2501 add_vdc_rest_url
= ''.join(url_list
)
2504 fence_mode
="isolated"
2506 is_inherited
='false'
2507 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2508 <Description>Openmano created</Description>
2512 <IsInherited>{1:s}</IsInherited>
2513 <Gateway>{2:s}</Gateway>
2514 <Netmask>{3:s}</Netmask>
2516 <IsEnabled>{5:s}</IsEnabled>
2519 <StartAddress>{6:s}</StartAddress>
2520 <EndAddress>{7:s}</EndAddress>
2525 <FenceMode>{8:s}</FenceMode>
2527 <IsShared>{9:s}</IsShared>
2528 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2529 subnet_address
, dns_address
, dhcp_enabled
,
2530 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2533 fence_mode
="bridged"
2534 is_inherited
='false'
2535 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2536 <Description>Openmano created</Description>
2540 <IsInherited>{1:s}</IsInherited>
2541 <Gateway>{2:s}</Gateway>
2542 <Netmask>{3:s}</Netmask>
2544 <IsEnabled>{5:s}</IsEnabled>
2547 <StartAddress>{6:s}</StartAddress>
2548 <EndAddress>{7:s}</EndAddress>
2553 <ParentNetwork href="{8:s}"/>
2554 <FenceMode>{9:s}</FenceMode>
2556 <IsShared>{10:s}</IsShared>
2557 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2558 subnet_address
, dns_address
, dhcp_enabled
,
2559 dhcp_start_address
, dhcp_end_address
, available_networks
,
2560 fence_mode
, isshared
)
2562 headers
= vca
.vcloud_session
.get_vcloud_headers()
2563 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2565 response
= Http
.post(url
=add_vdc_rest_url
,
2571 if response
.status_code
!= 201:
2572 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2573 .format(response
.status_code
))
2575 network
= networkType
.parseString(response
.content
, True)
2576 create_nw_task
= network
.get_Tasks().get_Task()[0]
2578 # if we all ok we respond with content after network creation completes
2579 # otherwise by default return None
2580 if create_nw_task
is not None:
2581 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2582 status
= vca
.block_until_completed(create_nw_task
)
2584 return response
.content
2586 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2587 .format(response
.content
))
2588 except Exception as exp
:
2589 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2593 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2595 Method sets convert CIDR netmask address to normal IP format
2597 cidr_ip : CIDR IP address
2599 netmask : Converted netmask
2601 if cidr_ip
is not None:
2603 network
, net_bits
= cidr_ip
.split('/')
2604 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2610 def get_provider_rest(self
, vca
=None):
2612 Method gets provider vdc view from vcloud director
2615 network_name - is network name to be created.
2616 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2617 It optional attribute. by default if no parent network indicate the first available will be used.
2620 The return xml content of respond or None
2623 url_list
= [vca
.host
, '/api/admin']
2624 response
= Http
.get(url
=''.join(url_list
),
2625 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2629 if response
.status_code
== requests
.codes
.ok
:
2630 return response
.content
2633 def create_vdc(self
, vdc_name
=None):
2637 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2638 if xml_content
is not None:
2640 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2641 for child
in task_resp_xmlroot
:
2642 if child
.tag
.split("}")[1] == 'Owner':
2643 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2644 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2647 self
.logger
.debug("Respond body {}".format(xml_content
))
2651 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2653 Method create vdc in vCloud director based on VDC template.
2654 it uses pre-defined template that must be named openmano
2657 vdc_name - name of a new vdc.
2660 The return xml content of respond or None
2663 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2664 vca
= self
.connect()
2666 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2667 if vdc_name
is None:
2670 url_list
= [vca
.host
, '/api/vdcTemplates']
2671 vm_list_rest_call
= ''.join(url_list
)
2672 response
= Http
.get(url
=vm_list_rest_call
,
2673 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2677 # container url to a template
2678 vdc_template_ref
= None
2680 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2681 for child
in vm_list_xmlroot
:
2682 # application/vnd.vmware.admin.providervdc+xml
2683 # we need find a template from witch we instantiate VDC
2684 if child
.tag
.split("}")[1] == 'VdcTemplate':
2685 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2686 'name') == 'openmano':
2687 vdc_template_ref
= child
.attrib
.get('href')
2689 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2690 self
.logger
.debug("Respond body {}".format(response
.content
))
2693 # if we didn't found required pre defined template we return None
2694 if vdc_template_ref
is None:
2699 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2700 vm_list_rest_call
= ''.join(url_list
)
2701 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2702 <Source href="{1:s}"></Source>
2703 <Description>opnemano</Description>
2704 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2705 headers
= vca
.vcloud_session
.get_vcloud_headers()
2706 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2707 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2709 # if we all ok we respond with content otherwise by default None
2710 if response
.status_code
>= 200 and response
.status_code
< 300:
2711 return response
.content
2714 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2715 self
.logger
.debug("Respond body {}".format(response
.content
))
2719 def create_vdc_rest(self
, vdc_name
=None):
2721 Method create network in vCloud director
2724 network_name - is network name to be created.
2725 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2726 It optional attribute. by default if no parent network indicate the first available will be used.
2729 The return network uuid or return None
2732 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2734 vca
= self
.connect_as_admin()
2736 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2737 if vdc_name
is None:
2740 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2741 vm_list_rest_call
= ''.join(url_list
)
2742 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2743 response
= Http
.get(url
=vm_list_rest_call
,
2744 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2748 provider_vdc_ref
= None
2749 add_vdc_rest_url
= None
2750 available_networks
= None
2752 if response
.status_code
!= requests
.codes
.ok
:
2753 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2754 response
.status_code
))
2758 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2759 for child
in vm_list_xmlroot
:
2760 # application/vnd.vmware.admin.providervdc+xml
2761 if child
.tag
.split("}")[1] == 'Link':
2762 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2763 and child
.attrib
.get('rel') == 'add':
2764 add_vdc_rest_url
= child
.attrib
.get('href')
2766 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2767 self
.logger
.debug("Respond body {}".format(response
.content
))
2770 response
= self
.get_provider_rest(vca
=vca
)
2772 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2773 for child
in vm_list_xmlroot
:
2774 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2775 for sub_child
in child
:
2776 provider_vdc_ref
= sub_child
.attrib
.get('href')
2778 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2779 self
.logger
.debug("Respond body {}".format(response
))
2782 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2783 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2784 <AllocationModel>ReservationPool</AllocationModel>
2785 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2786 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2787 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2788 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2789 <ProviderVdcReference
2790 name="Main Provider"
2792 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2796 headers
= vca
.vcloud_session
.get_vcloud_headers()
2797 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2798 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2801 # if we all ok we respond with content otherwise by default None
2802 if response
.status_code
== 201:
2803 return response
.content
2806 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2808 Method retrieve vapp detail from vCloud director
2811 vapp_uuid - is vapp identifier.
2814 The return network uuid or return None
2820 if need_admin_access
:
2821 vca
= self
.connect_as_admin()
2823 vca
= self
.connect()
2826 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2827 if vapp_uuid
is None:
2830 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2831 get_vapp_restcall
= ''.join(url_list
)
2833 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2834 response
= Http
.get(url
=get_vapp_restcall
,
2835 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2839 if response
.status_code
!= requests
.codes
.ok
:
2840 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2841 response
.status_code
))
2842 return parsed_respond
2845 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2846 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2848 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2849 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2850 'vmw': 'http://www.vmware.com/schema/ovf',
2851 'vm': 'http://www.vmware.com/vcloud/v1.5',
2852 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2853 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2854 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2857 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2858 if created_section
is not None:
2859 parsed_respond
['created'] = created_section
.text
2861 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2862 if network_section
is not None and 'networkName' in network_section
.attrib
:
2863 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2865 ipscopes_section
= \
2866 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2868 if ipscopes_section
is not None:
2869 for ipscope
in ipscopes_section
:
2870 for scope
in ipscope
:
2871 tag_key
= scope
.tag
.split("}")[1]
2872 if tag_key
== 'IpRanges':
2873 ip_ranges
= scope
.getchildren()
2874 for ipblock
in ip_ranges
:
2875 for block
in ipblock
:
2876 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2878 parsed_respond
[tag_key
] = scope
.text
2880 # parse children section for other attrib
2881 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2882 if children_section
is not None:
2883 parsed_respond
['name'] = children_section
.attrib
['name']
2884 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2885 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2886 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2887 parsed_respond
['status'] = children_section
.attrib
['status']
2888 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2889 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2891 for adapters
in network_adapter
:
2892 adapter_key
= adapters
.tag
.split("}")[1]
2893 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2894 parsed_respond
['primarynetwork'] = adapters
.text
2895 if adapter_key
== 'NetworkConnection':
2897 if 'network' in adapters
.attrib
:
2898 vnic
['network'] = adapters
.attrib
['network']
2899 for adapter
in adapters
:
2900 setting_key
= adapter
.tag
.split("}")[1]
2901 vnic
[setting_key
] = adapter
.text
2902 nic_list
.append(vnic
)
2904 for link
in children_section
:
2905 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2906 if link
.attrib
['rel'] == 'screen:acquireTicket':
2907 parsed_respond
['acquireTicket'] = link
.attrib
2908 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2909 parsed_respond
['acquireMksTicket'] = link
.attrib
2911 parsed_respond
['interfaces'] = nic_list
2912 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2913 if vCloud_extension_section
is not None:
2914 vm_vcenter_info
= {}
2915 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2916 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2917 if vmext
is not None:
2918 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2919 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2921 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2922 vm_virtual_hardware_info
= {}
2923 if virtual_hardware_section
is not None:
2924 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2925 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2926 disk_size
= item
.find("rasd:HostResource" ,namespaces
2927 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2929 vm_virtual_hardware_info
["disk_size"]= disk_size
2932 for link
in virtual_hardware_section
:
2933 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2934 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2935 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2938 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2939 except Exception as exp
:
2940 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2941 return parsed_respond
2943 def acuire_console(self
, vm_uuid
=None):
2945 vca
= self
.connect()
2947 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2951 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2952 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2953 console_dict
= vm_dict
['acquireTicket']
2954 console_rest_call
= console_dict
['href']
2956 response
= Http
.post(url
=console_rest_call
,
2957 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2961 if response
.status_code
== requests
.codes
.ok
:
2962 return response
.content
2966 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2968 Method retrieve vm disk details
2971 vapp_uuid - is vapp identifier.
2972 flavor_disk - disk size as specified in VNFD (flavor)
2975 The return network uuid or return None
2979 #Flavor disk is in GB convert it into MB
2980 flavor_disk
= int(flavor_disk
) * 1024
2981 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2983 vm_name
= vm_details
["name"]
2984 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2986 if vm_details
and "vm_virtual_hardware" in vm_details
:
2987 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2988 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2990 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2992 if flavor_disk
> vm_disk
:
2993 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2994 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2995 vm_disk
, flavor_disk
))
2998 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
3001 except Exception as exp
:
3002 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
3005 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
3007 Method retrieve modify vm disk size
3010 disk_href - vCD API URL to GET and PUT disk data
3011 disk_size - disk size as specified in VNFD (flavor)
3014 The return network uuid or return None
3016 vca
= self
.connect()
3018 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3019 if disk_href
is None or disk_size
is None:
3022 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3023 response
= Http
.get(url
=disk_href
,
3024 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3028 if response
.status_code
!= requests
.codes
.ok
:
3029 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3030 response
.status_code
))
3033 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3034 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3035 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3037 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3038 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3039 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3040 if disk_item
is not None:
3041 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3044 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3045 xml_declaration
=True)
3047 #Send PUT request to modify disk size
3048 headers
= vca
.vcloud_session
.get_vcloud_headers()
3049 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3051 response
= Http
.put(url
=disk_href
,
3054 verify
=vca
.verify
, logger
=self
.logger
)
3056 if response
.status_code
!= 202:
3057 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3058 response
.status_code
))
3060 modify_disk_task
= taskType
.parseString(response
.content
, True)
3061 if type(modify_disk_task
) is GenericTask
:
3062 status
= vca
.block_until_completed(modify_disk_task
)
3067 except Exception as exp
:
3068 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3071 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3073 Method to attach pci devices to VM
3076 vapp_uuid - uuid of vApp/VM
3077 pci_devices - pci devices infromation as specified in VNFD (flavor)
3080 The status of add pci device task , vm object and
3081 vcenter_conect object
3084 vcenter_conect
= None
3085 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3087 vm_vcenter_info
= self
.get_vm_vcenter_info(vapp_uuid
)
3088 except Exception as exp
:
3089 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3090 " for VM : {}".format(exp
))
3091 raise vimconn
.vimconnException(message
=exp
)
3093 if vm_vcenter_info
["vm_moref_id"]:
3095 if hasattr(ssl
, '_create_unverified_context'):
3096 context
= ssl
._create
_unverified
_context
()
3098 no_of_pci_devices
= len(pci_devices
)
3099 if no_of_pci_devices
> 0:
3100 vcenter_conect
= SmartConnect(
3101 host
=vm_vcenter_info
["vm_vcenter_ip"],
3102 user
=vm_vcenter_info
["vm_vcenter_user"],
3103 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3104 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3106 atexit
.register(Disconnect
, vcenter_conect
)
3107 content
= vcenter_conect
.RetrieveContent()
3109 #Get VM and its host
3110 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3111 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3112 if host_obj
and vm_obj
:
3113 #get PCI devies from host on which vapp is currently installed
3114 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3116 if avilable_pci_devices
is None:
3117 #find other hosts with active pci devices
3118 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3123 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3124 #Migrate vm to the host where PCI devices are availble
3125 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3126 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3127 if task
is not None:
3128 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3129 self
.logger
.info("Migrate VM status: {}".format(result
))
3130 host_obj
= new_host_obj
3132 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3133 raise vimconn
.vimconnNotFoundException(
3134 "Fail to migrate VM : {} to host {}".format(
3139 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3140 #Add PCI devices one by one
3141 for pci_device
in avilable_pci_devices
:
3142 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3144 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3146 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3148 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3149 return True, vm_obj
, vcenter_conect
3151 self
.logger
.error("Currently there is no host with"\
3152 " {} number of avaialble PCI devices required for VM {}".format(
3156 raise vimconn
.vimconnNotFoundException(
3157 "Currently there is no host with {} "\
3158 "number of avaialble PCI devices required for VM {}".format(
3162 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3164 except vmodl
.MethodFault
as error
:
3165 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3166 return None, vm_obj
, vcenter_conect
3168 def get_vm_obj(self
, content
, mob_id
):
3170 Method to get the vsphere VM object associated with a given morf ID
3172 vapp_uuid - uuid of vApp/VM
3173 content - vCenter content object
3174 mob_id - mob_id of VM
3182 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3183 [vim
.VirtualMachine
], True
3185 for vm
in container
.view
:
3186 mobID
= vm
._GetMoId
()
3189 host_obj
= vm_obj
.runtime
.host
3191 except Exception as exp
:
3192 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3193 return host_obj
, vm_obj
3195 def get_pci_devices(self
, host
, need_devices
):
3197 Method to get the details of pci devices on given host
3199 host - vSphere host object
3200 need_devices - number of pci devices needed on host
3203 array of pci devices
3207 used_devices_ids
= []
3211 pciPassthruInfo
= host
.config
.pciPassthruInfo
3212 pciDevies
= host
.hardware
.pciDevice
3214 for pci_status
in pciPassthruInfo
:
3215 if pci_status
.passthruActive
:
3216 for device
in pciDevies
:
3217 if device
.id == pci_status
.id:
3218 all_device_ids
.append(device
.id)
3219 all_devices
.append(device
)
3221 #check if devices are in use
3222 avalible_devices
= all_devices
3224 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3225 vm_devices
= vm
.config
.hardware
.device
3226 for device
in vm_devices
:
3227 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3228 if device
.backing
.id in all_device_ids
:
3229 for use_device
in avalible_devices
:
3230 if use_device
.id == device
.backing
.id:
3231 avalible_devices
.remove(use_device
)
3232 used_devices_ids
.append(device
.backing
.id)
3233 self
.logger
.debug("Device {} from devices {}"\
3234 "is in use".format(device
.backing
.id,
3237 if len(avalible_devices
) < need_devices
:
3238 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3240 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3244 required_devices
= avalible_devices
[:need_devices
]
3245 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3246 len(avalible_devices
),
3249 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3251 return required_devices
3253 except Exception as exp
:
3254 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3258 def get_host_and_PCIdevices(self
, content
, need_devices
):
3260 Method to get the details of pci devices infromation on all hosts
3263 content - vSphere host object
3264 need_devices - number of pci devices needed on host
3267 array of pci devices and host object
3270 pci_device_objs
= None
3273 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3274 [vim
.HostSystem
], True)
3275 for host
in container
.view
:
3276 devices
= self
.get_pci_devices(host
, need_devices
)
3279 pci_device_objs
= devices
3281 except Exception as exp
:
3282 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3284 return host_obj
,pci_device_objs
3286 def relocate_vm(self
, dest_host
, vm
) :
3288 Method to get the relocate VM to new host
3291 dest_host - vSphere host object
3292 vm - vSphere VM object
3299 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3300 task
= vm
.Relocate(relocate_spec
)
3301 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3302 except Exception as exp
:
3303 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3304 dest_host
, vm
, exp
))
3307 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3309 Waits and provides updates on a vSphere task
3311 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3314 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3315 if task
.info
.result
is not None and not hideResult
:
3316 self
.logger
.info('{} completed successfully, result: {}'.format(
3320 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3322 self
.logger
.error('{} did not complete successfully: {} '.format(
3327 return task
.info
.result
3329 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3331 Method to add pci device in given VM
3334 host_object - vSphere host object
3335 vm_object - vSphere VM object
3336 host_pci_dev - host_pci_dev must be one of the devices from the
3337 host_object.hardware.pciDevice list
3338 which is configured as a PCI passthrough device
3344 if vm_object
and host_object
and host_pci_dev
:
3346 #Add PCI device to VM
3347 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3348 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3350 if host_pci_dev
.id not in systemid_by_pciid
:
3351 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3354 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3355 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3357 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3358 vendorId
=host_pci_dev
.vendorId
,
3359 deviceName
=host_pci_dev
.deviceName
)
3361 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3363 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3364 new_device_config
.operation
= "add"
3365 vmConfigSpec
= vim
.vm
.ConfigSpec()
3366 vmConfigSpec
.deviceChange
= [new_device_config
]
3368 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3369 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3370 host_pci_dev
, vm_object
, host_object
)
3372 except Exception as exp
:
3373 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3379 def get_vm_vcenter_info(self
, vapp_uuid
):
3381 Method to get details of vCenter and vm
3384 vapp_uuid - uuid of vApp or VM
3387 Moref Id of VM and deails of vCenter
3389 vm_vcenter_info
= {}
3391 if self
.vcenter_ip
is not None:
3392 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
3394 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
3395 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3396 if self
.vcenter_port
is not None:
3397 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
3399 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
3400 " Please provide vCenter port while attaching datacenter to tenant in --config")
3401 if self
.vcenter_user
is not None:
3402 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
3404 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
3405 " Please provide vCenter user while attaching datacenter to tenant in --config")
3407 if self
.vcenter_password
is not None:
3408 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
3410 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
3411 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3413 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3414 if vm_details
and "vm_vcenter_info" in vm_details
:
3415 vm_vcenter_info
["vm_moref_id"] = vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
3417 return vm_vcenter_info
3419 except Exception as exp
:
3420 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3421 " for VM : {}".format(exp
))
3424 def get_vm_pci_details(self
, vmuuid
):
3426 Method to get VM PCI device details from vCenter
3429 vm_obj - vSphere VM object
3432 dict of PCI devives attached to VM
3435 vm_pci_devices_info
= {}
3437 vm_vcenter_info
= self
.get_vm_vcenter_info(vmuuid
)
3438 if vm_vcenter_info
["vm_moref_id"]:
3440 if hasattr(ssl
, '_create_unverified_context'):
3441 context
= ssl
._create
_unverified
_context
()
3442 vcenter_conect
= SmartConnect(host
=vm_vcenter_info
["vm_vcenter_ip"],
3443 user
=vm_vcenter_info
["vm_vcenter_user"],
3444 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3445 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3448 atexit
.register(Disconnect
, vcenter_conect
)
3449 content
= vcenter_conect
.RetrieveContent()
3451 #Get VM and its host
3453 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3454 if host_obj
and vm_obj
:
3455 vm_pci_devices_info
["host_name"]= host_obj
.name
3456 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
3457 for device
in vm_obj
.config
.hardware
.device
:
3458 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3459 device_details
={'devide_id':device
.backing
.id,
3460 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
3462 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3464 self
.logger
.error("Can not connect to vCenter while getting "\
3465 "PCI devices infromationn")
3466 return vm_pci_devices_info
3467 except Exception as exp
:
3468 self
.logger
.error("Error occurred while getting VM infromationn"\
3469 " for VM : {}".format(exp
))
3470 raise vimconn
.vimconnException(message
=exp
)