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
))
562 vdcid
= vdc
.get_id().split(":")[3]
563 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")
614 vdc
= vca
.get_vdc(self
.tenant_name
)
615 vdc_id
= vdc
.get_id().split(":")[3]
617 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
848 for catalog
in vca
.get_catalogs():
849 if catalog_name
!= catalog
.name
:
851 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
852 link
.get_rel() == 'add', catalog
.get_Link())
853 assert len(link
) == 1
855 <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>
856 """ % (escape(catalog_name
), escape(description
))
857 headers
= vca
.vcloud_session
.get_vcloud_headers()
858 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
859 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
860 if response
.status_code
== requests
.codes
.created
:
861 catalogItem
= XmlElementTree
.fromstring(response
.content
)
862 entity
= [child
for child
in catalogItem
if
863 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
864 href
= entity
.get('href')
866 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
867 verify
=vca
.verify
, logger
=self
.logger
)
869 if response
.status_code
== requests
.codes
.ok
:
870 media
= mediaType
.parseString(response
.content
, True)
871 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
872 media
.get_Files().get_File()[0].get_Link())[0]
873 headers
= vca
.vcloud_session
.get_vcloud_headers()
874 headers
['Content-Type'] = 'Content-Type text/xml'
875 response
= Http
.put(link
.get_href(),
876 data
=open(media_file_name
, 'rb'),
878 verify
=vca
.verify
, logger
=self
.logger
)
879 if response
.status_code
!= requests
.codes
.ok
:
881 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
885 # TODO fix this with aync block
888 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
890 # uploading VMDK file
891 # check status of OVF upload and upload remaining files.
892 response
= Http
.get(template
,
893 headers
=vca
.vcloud_session
.get_vcloud_headers(),
897 if response
.status_code
== requests
.codes
.ok
:
898 media
= mediaType
.parseString(response
.content
, True)
899 number_of_files
= len(media
.get_Files().get_File())
900 for index
in xrange(0, number_of_files
):
901 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
902 media
.get_Files().get_File()[index
].get_Link())
903 for link
in links_list
:
904 # we skip ovf since it already uploaded.
905 if 'ovf' in link
.get_href():
907 # The OVF file and VMDK must be in a same directory
908 head
, tail
= os
.path
.split(media_file_name
)
909 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
910 if not os
.path
.isfile(file_vmdk
):
912 statinfo
= os
.stat(file_vmdk
)
913 if statinfo
.st_size
== 0:
915 hrefvmdk
= link
.get_href()
918 print("Uploading file: {}".format(file_vmdk
))
920 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
922 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
924 bytes_transferred
= 0
925 f
= open(file_vmdk
, 'rb')
926 while bytes_transferred
< statinfo
.st_size
:
927 my_bytes
= f
.read(chunk_bytes
)
928 if len(my_bytes
) <= chunk_bytes
:
929 headers
= vca
.vcloud_session
.get_vcloud_headers()
930 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
931 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
932 headers
['Content-Length'] = str(len(my_bytes
))
933 response
= Http
.put(hrefvmdk
,
939 if response
.status_code
== requests
.codes
.ok
:
940 bytes_transferred
+= len(my_bytes
)
942 progress_bar
.update(bytes_transferred
)
945 'file upload failed with error: [%s] %s' % (response
.status_code
,
952 progress_bar
.finish()
956 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
957 format(catalog_name
, media_file_name
))
959 except Exception as exp
:
960 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
961 .format(catalog_name
,media_file_name
, exp
))
962 raise vimconn
.vimconnException(
963 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
964 .format(catalog_name
,media_file_name
, exp
))
966 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
969 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
970 """Upload media file"""
971 # TODO add named parameters for readability
973 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
974 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
976 def validate_uuid4(self
, uuid_string
=None):
977 """ Method validate correct format of UUID.
979 Return: true if string represent valid uuid
982 val
= uuid
.UUID(uuid_string
, version
=4)
987 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
988 """ Method check catalog and return catalog ID in UUID format.
991 catalog_name: catalog name as string
992 catalogs: list of catalogs.
994 Return: catalogs uuid
997 for catalog
in catalogs
:
998 if catalog
.name
== catalog_name
:
999 catalog_id
= catalog
.get_id().split(":")
1000 return catalog_id
[3]
1003 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1004 """ Method check catalog and return catalog name lookup done by catalog UUID.
1007 catalog_name: catalog name as string
1008 catalogs: list of catalogs.
1010 Return: catalogs name or None
1013 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1016 for catalog
in catalogs
:
1017 catalog_id
= catalog
.get_id().split(":")[3]
1018 if catalog_id
== catalog_uuid
:
1022 def get_image_id_from_path(self
, path
=None, progress
=False):
1023 """ Method upload OVF image to vCloud director.
1025 Each OVF image represented as single catalog entry in vcloud director.
1026 The method check for existing catalog entry. The check done by file name without file extension.
1028 if given catalog name already present method will respond with existing catalog uuid otherwise
1029 it will create new catalog entry and upload OVF file to newly created catalog.
1031 If method can't create catalog entry or upload a file it will throw exception.
1033 Method accept boolean flag progress that will output progress bar. It useful method
1034 for standalone upload use case. In case to test large file upload.
1037 path: - valid path to OVF file.
1038 progress - boolean progress bar show progress bar.
1040 Return: if image uploaded correct method will provide image catalog UUID.
1042 vca
= self
.connect()
1044 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1047 raise vimconn
.vimconnException("Image path can't be None.")
1049 if not os
.path
.isfile(path
):
1050 raise vimconn
.vimconnException("Can't read file. File not found.")
1052 if not os
.access(path
, os
.R_OK
):
1053 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1055 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1057 dirpath
, filename
= os
.path
.split(path
)
1058 flname
, file_extension
= os
.path
.splitext(path
)
1059 if file_extension
!= '.ovf':
1060 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1061 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1063 catalog_name
= os
.path
.splitext(filename
)[0]
1064 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1065 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1066 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1069 catalogs
= vca
.get_catalogs()
1070 except Exception as exp
:
1071 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1072 raise vimconn
.vimconnException("Failed get catalogs() with Exception {} ".format(exp
))
1074 if len(catalogs
) == 0:
1075 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1076 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1078 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1079 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1080 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1082 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1083 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1085 for catalog
in catalogs
:
1086 # search for existing catalog if we find same name we return ID
1087 # TODO optimize this
1088 if catalog
.name
== catalog_md5_name
:
1089 self
.logger
.debug("Found existing catalog entry for {} "
1090 "catalog id {}".format(catalog_name
,
1091 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1092 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1094 # if we didn't find existing catalog we create a new one and upload image.
1095 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1096 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1098 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1100 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1101 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1103 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1105 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1107 def get_image_list(self
, filter_dict
={}):
1108 '''Obtain tenant images from VIM
1112 checksum: image checksum
1113 location: image path
1114 Returns the image list of dictionaries:
1115 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1118 vca
= self
.connect()
1120 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1123 catalogs
= vca
.get_catalogs()
1124 if len(catalogs
) == 0:
1127 for catalog
in catalogs
:
1128 catalog_uuid
= catalog
.get_id().split(":")[3]
1131 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1133 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1135 filtered_dict
["name"] = name
1136 filtered_dict
["id"] = catalog_uuid
1137 image_list
.append(filtered_dict
)
1139 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1141 except Exception as exp
:
1142 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1144 def get_vappid(self
, vdc
=None, vapp_name
=None):
1145 """ Method takes vdc object and vApp name and returns vapp uuid or None
1148 vdc: The VDC object.
1149 vapp_name: is application vappp name identifier
1152 The return vApp name otherwise None
1154 if vdc
is None or vapp_name
is None:
1156 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1158 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1159 vdc
.ResourceEntities
.ResourceEntity
)
1161 return refs
[0].href
.split("vapp")[1][1:]
1162 except Exception as e
:
1163 self
.logger
.exception(e
)
1167 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1168 """ Method Method returns True or False if vapp deployed in vCloud director
1171 vca: Connector to VCA
1172 vdc: The VDC object.
1173 vappid: vappid is application identifier
1176 The return True if vApp deployed
1181 refs
= filter(lambda ref
:
1182 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1183 vdc
.ResourceEntities
.ResourceEntity
)
1185 vappid
= ref
.href
.split("vapp")[1][1:]
1186 # find vapp with respected vapp uuid
1187 if vappid
== vapp_uuid
:
1189 except Exception as e
:
1190 self
.logger
.exception(e
)
1194 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1195 """Method returns vApp name from vCD and lookup done by vapp_id.
1198 vca: Connector to VCA
1199 vdc: The VDC object.
1200 vapp_uuid: vappid is application identifier
1203 The return vApp name otherwise None
1207 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1208 vdc
.ResourceEntities
.ResourceEntity
)
1210 # we care only about UUID the rest doesn't matter
1211 vappid
= ref
.href
.split("vapp")[1][1:]
1212 if vappid
== vapp_uuid
:
1213 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1215 tree
= XmlElementTree
.fromstring(response
.content
)
1216 return tree
.attrib
['name']
1217 except Exception as e
:
1218 self
.logger
.exception(e
)
1222 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1223 cloud_config
=None, disk_list
=None):
1224 """Adds a VM instance to VIM
1226 start: indicates if VM must start or boot in pause mode. Ignored
1227 image_id,flavor_id: image and flavor uuid
1228 net_list: list of interfaces, each one is a dictionary with:
1230 net_id: network uuid to connect
1231 vpci: virtual vcpi to assign
1232 model: interface model, virtio, e2000, ...
1234 use: 'data', 'bridge', 'mgmt'
1235 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1236 vim_id: filled/added by this function
1237 cloud_config: can be a text script to be passed directly to cloud-init,
1238 or an object to inject users and ssh keys with format:
1239 key-pairs: [] list of keys to install to the default user
1240 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1241 #TODO ip, security groups
1242 Returns >=0, the instance identifier
1246 self
.logger
.info("Creating new instance for entry {}".format(name
))
1247 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
1248 description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1249 vca
= self
.connect()
1251 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1253 #new vm name = vmname + tenant_id + uuid
1254 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1255 vmname_andid
= ''.join(new_vm_name
)
1257 # if vm already deployed we return existing uuid
1258 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1259 # if vapp_uuid is not None:
1262 # we check for presence of VDC, Catalog entry and Flavor.
1263 vdc
= vca
.get_vdc(self
.tenant_name
)
1265 raise vimconn
.vimconnNotFoundException(
1266 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1267 catalogs
= vca
.get_catalogs()
1268 if catalogs
is None:
1269 raise vimconn
.vimconnNotFoundException(
1270 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1272 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1273 if catalog_hash_name
:
1274 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1276 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1277 "(Failed retrieve catalog information {})".format(name
, image_id
))
1280 # Set vCPU and Memory based on flavor.
1285 pci_devices_info
= []
1286 if flavor_id
is not None:
1287 if flavor_id
not in vimconnector
.flavorlist
:
1288 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1289 "Failed retrieve flavor information "
1290 "flavor id {}".format(name
, flavor_id
))
1293 flavor
= vimconnector
.flavorlist
[flavor_id
]
1294 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1295 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1296 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1297 extended
= flavor
.get("extended", None)
1299 numas
=extended
.get("numas", None)
1302 for interface
in numa
.get("interfaces",() ):
1303 if interface
["dedicated"].strip()=="yes":
1304 pci_devices_info
.append(interface
)
1305 except Exception as exp
:
1306 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1308 # image upload creates template name as catalog name space Template.
1309 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1314 # client must provide at least one entry in net_list if not we report error
1315 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1316 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1318 primary_netname
= None
1319 network_mode
= 'bridged'
1320 if net_list
is not None and len(net_list
) > 0:
1321 for net
in net_list
:
1322 if 'use' in net
and net
['use'] == 'mgmt':
1324 if primary_net
is None:
1325 primary_net
= net_list
[0]
1328 primary_net_id
= primary_net
['net_id']
1329 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1330 if 'name' in network_dict
:
1331 primary_netname
= network_dict
['name']
1334 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1336 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1338 # use: 'data', 'bridge', 'mgmt'
1339 # create vApp. Set vcpu and ram based on flavor id.
1341 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1342 self
.get_catalogbyid(image_id
, catalogs
),
1343 network_name
=None, # None while creating vapp
1344 network_mode
=network_mode
,
1345 vm_name
=vmname_andid
,
1346 vm_cpus
=vm_cpus
, # can be None if flavor is None
1347 vm_memory
=vm_memory
) # can be None if flavor is None
1349 if vapptask
is None or vapptask
is False:
1350 raise vimconn
.vimconnUnexpectedResponse(
1351 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1352 if type(vapptask
) is VappTask
:
1353 vca
.block_until_completed(vapptask
)
1355 except Exception as exp
:
1356 raise vimconn
.vimconnUnexpectedResponse(
1357 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid
, exp
))
1359 # we should have now vapp in undeployed state.
1361 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1362 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1363 except Exception as exp
:
1364 raise vimconn
.vimconnUnexpectedResponse(
1365 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1366 .format(vmname_andid
, exp
))
1369 raise vimconn
.vimconnUnexpectedResponse(
1370 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1373 #Add PCI passthrough configrations
1374 PCI_devices_status
= False
1377 if len(pci_devices_info
) > 0:
1378 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1380 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1383 if PCI_devices_status
:
1384 self
.logger
.info("Added PCI devives {} to VM {}".format(
1389 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1395 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1396 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1398 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1400 # add NICs & connect to networks in netlist
1402 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1404 primary_nic_index
= 0
1405 for net
in net_list
:
1406 # openmano uses network id in UUID format.
1407 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1408 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1409 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1411 if 'net_id' not in net
:
1414 interface_net_id
= net
['net_id']
1415 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1416 interface_network_mode
= net
['use']
1418 if interface_network_mode
== 'mgmt':
1419 primary_nic_index
= nicIndex
1421 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1422 - DHCP (The IP address is obtained from a DHCP service.)
1423 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1424 - NONE (No IP addressing mode specified.)"""
1426 if primary_netname
is not None:
1427 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1429 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1430 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1431 if type(task
) is GenericTask
:
1432 vca
.block_until_completed(task
)
1433 # connect network to VM - with all DHCP by default
1434 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1435 task
= vapp
.connect_vms(nets
[0].name
,
1436 connection_index
=nicIndex
,
1437 connections_primary_index
=primary_nic_index
,
1438 ip_allocation_mode
='DHCP')
1439 if type(task
) is GenericTask
:
1440 vca
.block_until_completed(task
)
1443 # deploy and power on vm
1444 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1445 deploytask
= vapp
.deploy(powerOn
=False)
1446 if type(deploytask
) is GenericTask
:
1447 vca
.block_until_completed(deploytask
)
1449 # If VM has PCI devices reserve memory for VM
1450 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1451 memReserve
= vm_obj
.config
.hardware
.memoryMB
1452 spec
= vim
.vm
.ConfigSpec()
1453 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1454 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1456 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1457 self
.logger
.info("Reserved memmoery {} MB for "\
1458 "VM VM status: {}".format(str(memReserve
),result
))
1460 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1461 str(memReserve
),str(vm_obj
)))
1463 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1464 poweron_task
= vapp
.poweron()
1465 if type(poweron_task
) is GenericTask
:
1466 vca
.block_until_completed(poweron_task
)
1468 except Exception as exp
:
1469 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1470 self
.logger
.debug("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1471 raise vimconn
.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1473 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1476 while wait_time
<= MAX_WAIT_TIME
:
1478 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1479 except Exception as exp
:
1480 raise vimconn
.vimconnUnexpectedResponse(
1481 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1482 .format(vmname_andid
, exp
))
1484 if vapp
and vapp
.me
.deployed
:
1485 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1488 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1489 time
.sleep(INTERVAL_TIME
)
1491 wait_time
+=INTERVAL_TIME
1493 if vapp_uuid
is not None:
1496 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1500 ## based on current discussion
1504 # created: '2016-09-08T11:51:58'
1505 # description: simple-instance.linux1.1
1506 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1507 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1508 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1513 def get_vminstance(self
, vim_vm_uuid
=None):
1514 """Returns the VM instance information from VIM"""
1516 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1517 vca
= self
.connect()
1519 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1521 vdc
= vca
.get_vdc(self
.tenant_name
)
1523 raise vimconn
.vimconnConnectionException(
1524 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1526 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1527 if not vm_info_dict
:
1528 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1529 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1531 status_key
= vm_info_dict
['status']
1534 vm_dict
= {'created': vm_info_dict
['created'],
1535 'description': vm_info_dict
['name'],
1536 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1537 'hostId': vm_info_dict
['vmuuid'],
1539 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1541 if 'interfaces' in vm_info_dict
:
1542 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1544 vm_dict
['interfaces'] = []
1546 vm_dict
= {'created': '',
1548 'status': vcdStatusCode2manoFormat
[int(-1)],
1549 'hostId': vm_info_dict
['vmuuid'],
1550 'error_msg': "Inconsistency state",
1551 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1555 def delete_vminstance(self
, vm__vim_uuid
):
1556 """Method poweroff and remove VM instance from vcloud director network.
1559 vm__vim_uuid: VM UUID
1562 Returns the instance identifier
1565 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1566 vca
= self
.connect()
1568 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1570 vdc
= vca
.get_vdc(self
.tenant_name
)
1572 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1574 raise vimconn
.vimconnException(
1575 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1578 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1579 if vapp_name
is None:
1580 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1581 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1583 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1585 # Delete vApp and wait for status change if task executed and vApp is None.
1586 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1589 if vapp
.me
.deployed
:
1590 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1594 while wait_time
<= MAX_WAIT_TIME
:
1595 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1597 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1598 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1600 power_off_task
= vapp
.poweroff()
1601 if type(power_off_task
) is GenericTask
:
1602 result
= vca
.block_until_completed(power_off_task
)
1607 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1608 time
.sleep(INTERVAL_TIME
)
1610 wait_time
+=INTERVAL_TIME
1612 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1614 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1617 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1620 while wait_time
<= MAX_WAIT_TIME
:
1621 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1623 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1624 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1625 undeploy_task
= vapp
.undeploy(action
='powerOff')
1627 if type(undeploy_task
) is GenericTask
:
1628 result
= vca
.block_until_completed(undeploy_task
)
1633 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1634 time
.sleep(INTERVAL_TIME
)
1636 wait_time
+=INTERVAL_TIME
1639 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1642 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1643 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1645 if vapp
is not None:
1649 while wait_time
<= MAX_WAIT_TIME
:
1650 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1652 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1653 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1655 delete_task
= vapp
.delete()
1657 if type(delete_task
) is GenericTask
:
1658 vca
.block_until_completed(delete_task
)
1659 result
= vca
.block_until_completed(delete_task
)
1663 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1664 time
.sleep(INTERVAL_TIME
)
1666 wait_time
+=INTERVAL_TIME
1669 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1672 self
.logger
.debug(traceback
.format_exc())
1673 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1675 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1676 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1679 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1681 def refresh_vms_status(self
, vm_list
):
1682 """Get the status of the virtual machines and their interfaces/ports
1683 Params: the list of VM identifiers
1684 Returns a dictionary with:
1685 vm_id: #VIM id of this Virtual Machine
1686 status: #Mandatory. Text with one of:
1687 # DELETED (not found at vim)
1688 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1689 # OTHER (Vim reported other status not understood)
1690 # ERROR (VIM indicates an ERROR status)
1691 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1692 # CREATING (on building process), ERROR
1693 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1695 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1696 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1698 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1699 mac_address: #Text format XX:XX:XX:XX:XX:XX
1700 vim_net_id: #network id where this interface is connected
1701 vim_interface_id: #interface/port VIM id
1702 ip_address: #null, or text with IPv4, IPv6 address
1705 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1708 rheaders
= {'Content-Type': 'application/xml'}
1709 iso_edges
= ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
1712 for edge
in iso_edges
:
1713 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1714 self
.logger
.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url
))
1716 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1717 auth
= (self
.nsx_user
, self
.nsx_password
),
1718 verify
= False, headers
= rheaders
)
1720 if resp
.status_code
== requests
.codes
.ok
:
1721 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1722 for child
in dhcp_leases
:
1723 if child
.tag
== 'dhcpLeaseInfo':
1724 dhcpLeaseInfo
= child
1725 for leaseInfo
in dhcpLeaseInfo
:
1726 for elem
in leaseInfo
:
1727 if (elem
.tag
)=='macAddress':
1728 mac_addr
= elem
.text
1729 if (elem
.tag
)=='ipAddress':
1731 if (mac_addr
) is not None:
1732 mac_ip_addr
[mac_addr
]= ip_addr
1733 self
.logger
.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr
))
1735 self
.logger
.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp
.content
))
1737 self
.logger
.debug("Error in response from NSX Manager {}".format(KeyError.message
))
1738 self
.logger
.debug(traceback
.format_exc())
1740 vca
= self
.connect()
1742 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1744 vdc
= vca
.get_vdc(self
.tenant_name
)
1746 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1749 for vmuuid
in vm_list
:
1750 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1751 if vmname
is not None:
1754 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1755 vm_info
= the_vapp
.get_vms_details()
1756 vm_status
= vm_info
[0]['status']
1757 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1758 vm_info
[0].update(vm_pci_details
)
1760 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1761 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1762 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1765 vm_app_networks
= the_vapp
.get_vms_network_info()
1766 for vapp_network
in vm_app_networks
:
1767 for vm_network
in vapp_network
:
1768 if vm_network
['name'] == vmname
:
1769 #Assign IP Address based on MAC Address in NSX DHCP lease info
1770 for mac_adres
,ip_adres
in mac_ip_addr
.iteritems():
1771 if mac_adres
== vm_network
['mac']:
1772 vm_network
['ip']=ip_adres
1773 interface
= {"mac_address": vm_network
['mac'],
1774 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1775 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1776 'ip_address': vm_network
['ip']}
1777 # interface['vim_info'] = yaml.safe_dump(vm_network)
1778 vm_dict
["interfaces"].append(interface
)
1779 # add a vm to vm dict
1780 vms_dict
.setdefault(vmuuid
, vm_dict
)
1781 except Exception as exp
:
1782 self
.logger
.debug("Error in response {}".format(exp
))
1783 self
.logger
.debug(traceback
.format_exc())
1787 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1788 """Send and action over a VM instance from VIM
1789 Returns the vm_id if the action was successfully sent to the VIM"""
1791 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1792 if vm__vim_uuid
is None or action_dict
is None:
1793 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1795 vca
= self
.connect()
1797 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1799 vdc
= vca
.get_vdc(self
.tenant_name
)
1801 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1803 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1804 if vapp_name
is None:
1805 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1806 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1808 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1811 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1812 # TODO fix all status
1813 if "start" in action_dict
:
1814 vm_info
= the_vapp
.get_vms_details()
1815 vm_status
= vm_info
[0]['status']
1816 self
.logger
.info("Power on vApp: vm_status:{} {}".format(type(vm_status
),vm_status
))
1817 if vm_status
== "Suspended" or vm_status
== "Powered off":
1818 power_on_task
= the_vapp
.poweron()
1819 if power_on_task
is not None and type(power_on_task
) is GenericTask
:
1820 result
= vca
.block_until_completed(power_on_task
)
1822 self
.logger
.info("action_vminstance: Powered on vApp: {}".format(vapp_name
))
1824 self
.logger
.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name
))
1826 self
.logger
.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name
))
1827 elif "rebuild" in action_dict
:
1828 self
.logger
.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name
))
1829 power_on_task
= the_vapp
.deploy(powerOn
=True)
1830 if type(power_on_task
) is GenericTask
:
1831 result
= vca
.block_until_completed(power_on_task
)
1833 self
.logger
.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name
))
1835 self
.logger
.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name
))
1837 self
.logger
.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name
))
1838 elif "pause" in action_dict
:
1841 elif "resume" in action_dict
:
1844 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1845 power_off_task
= the_vapp
.undeploy(action
='powerOff')
1846 if type(power_off_task
) is GenericTask
:
1847 result
= vca
.block_until_completed(power_off_task
)
1849 self
.logger
.info("action_vminstance: Powered off vApp: {}".format(vapp_name
))
1851 self
.logger
.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name
))
1853 self
.logger
.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name
))
1854 elif "forceOff" in action_dict
:
1856 elif "terminate" in action_dict
:
1858 # elif "createImage" in action_dict:
1859 # server.create_image()
1862 except Exception as exp
:
1863 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
1864 raise vimconn
.vimconnException("action_vminstance: Failed with Exception {}".format(exp
))
1866 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1868 Get a console for the virtual machine
1870 vm_id: uuid of the VM
1871 console_type, can be:
1872 "novnc" (by default), "xvpvnc" for VNC types,
1873 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1874 Returns dict with the console parameters:
1875 protocol: ssh, ftp, http, https, ...
1876 server: usually ip address
1877 port: the http, ssh, ... port
1878 suffix: extra text, e.g. the http path and query string
1880 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1882 # NOT USED METHODS in current version
1884 def host_vim2gui(self
, host
, server_dict
):
1885 """Transform host dictionary from VIM format to GUI format,
1886 and append to the server_dict
1888 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1890 def get_hosts_info(self
):
1891 """Get the information of deployed hosts
1892 Returns the hosts content"""
1893 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1895 def get_hosts(self
, vim_tenant
):
1896 """Get the hosts and deployed instances
1897 Returns the hosts content"""
1898 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1900 def get_processor_rankings(self
):
1901 """Get the processor rankings in the VIM database"""
1902 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1904 def new_host(self
, host_data
):
1905 """Adds a new host to VIM"""
1906 '''Returns status code of the VIM response'''
1907 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1909 def new_external_port(self
, port_data
):
1910 """Adds a external port to VIM"""
1911 '''Returns the port identifier'''
1912 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1914 def new_external_network(self
, net_name
, net_type
):
1915 """Adds a external network to VIM (shared)"""
1916 '''Returns the network identifier'''
1917 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1919 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1920 """Connects a external port to a network"""
1921 '''Returns status code of the VIM response'''
1922 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1924 def new_vminstancefromJSON(self
, vm_data
):
1925 """Adds a VM instance to VIM"""
1926 '''Returns the instance identifier'''
1927 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1929 def get_network_name_by_id(self
, network_uuid
=None):
1930 """Method gets vcloud director network named based on supplied uuid.
1933 network_uuid: network_id
1936 The return network name.
1939 vca
= self
.connect()
1941 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1943 if not network_uuid
:
1947 org_dict
= self
.get_org(self
.org_uuid
)
1948 if 'networks' in org_dict
:
1949 org_network_dict
= org_dict
['networks']
1950 for net_uuid
in org_network_dict
:
1951 if net_uuid
== network_uuid
:
1952 return org_network_dict
[net_uuid
]
1954 self
.logger
.debug("Exception in get_network_name_by_id")
1955 self
.logger
.debug(traceback
.format_exc())
1959 def get_network_id_by_name(self
, network_name
=None):
1960 """Method gets vcloud director network uuid based on supplied name.
1963 network_name: network_name
1965 The return network uuid.
1966 network_uuid: network_id
1969 vca
= self
.connect()
1971 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1973 if not network_name
:
1974 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1978 org_dict
= self
.get_org(self
.org_uuid
)
1979 if org_dict
and 'networks' in org_dict
:
1980 org_network_dict
= org_dict
['networks']
1981 for net_uuid
,net_name
in org_network_dict
.iteritems():
1982 if net_name
== network_name
:
1985 except KeyError as exp
:
1986 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1990 def list_org_action(self
):
1992 Method leverages vCloud director and query for available organization for particular user
1995 vca - is active VCA connection.
1996 vdc_name - is a vdc name that will be used to query vms action
1999 The return XML respond
2002 vca
= self
.connect()
2004 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2006 url_list
= [vca
.host
, '/api/org']
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_action(self
, org_uuid
=None):
2021 Method leverages vCloud director and retrieve available object fdr organization.
2024 vca - is active VCA connection.
2025 vdc_name - is a vdc name that will be used to query vms action
2028 The return XML respond
2031 vca
= self
.connect()
2033 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2035 if org_uuid
is None:
2038 url_list
= [vca
.host
, '/api/org/', org_uuid
]
2039 vm_list_rest_call
= ''.join(url_list
)
2041 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2042 response
= Http
.get(url
=vm_list_rest_call
,
2043 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2046 if response
.status_code
== requests
.codes
.ok
:
2047 return response
.content
2051 def get_org(self
, org_uuid
=None):
2053 Method retrieves available organization in vCloud Director
2056 org_uuid - is a organization uuid.
2059 The return dictionary with following key
2060 "network" - for network list under the org
2061 "catalogs" - for network list under the org
2062 "vdcs" - for vdc list under org
2066 vca
= self
.connect()
2068 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2070 if org_uuid
is None:
2073 content
= self
.get_org_action(org_uuid
=org_uuid
)
2078 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2079 for child
in vm_list_xmlroot
:
2080 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2081 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2082 org_dict
['vdcs'] = vdc_list
2083 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2084 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2085 org_dict
['networks'] = network_list
2086 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2087 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2088 org_dict
['catalogs'] = catalog_list
2094 def get_org_list(self
):
2096 Method retrieves available organization in vCloud Director
2099 vca - is active VCA connection.
2102 The return dictionary and key for each entry VDC UUID
2106 vca
= self
.connect()
2108 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2110 content
= self
.list_org_action()
2112 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2113 for vm_xml
in vm_list_xmlroot
:
2114 if vm_xml
.tag
.split("}")[1] == 'Org':
2115 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2116 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2122 def vms_view_action(self
, vdc_name
=None):
2123 """ Method leverages vCloud director vms query call
2126 vca - is active VCA connection.
2127 vdc_name - is a vdc name that will be used to query vms action
2130 The return XML respond
2132 vca
= self
.connect()
2133 if vdc_name
is None:
2136 url_list
= [vca
.host
, '/api/vms/query']
2137 vm_list_rest_call
= ''.join(url_list
)
2139 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2140 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2141 vca
.vcloud_session
.organization
.Link
)
2143 response
= Http
.get(url
=vm_list_rest_call
,
2144 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2147 if response
.status_code
== requests
.codes
.ok
:
2148 return response
.content
2152 def get_vapp_list(self
, vdc_name
=None):
2154 Method retrieves vApp list deployed vCloud director and returns a dictionary
2155 contains a list of all vapp deployed for queried VDC.
2156 The key for a dictionary is vApp UUID
2160 vca - is active VCA connection.
2161 vdc_name - is a vdc name that will be used to query vms action
2164 The return dictionary and key for each entry vapp UUID
2168 if vdc_name
is None:
2171 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2173 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2174 for vm_xml
in vm_list_xmlroot
:
2175 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2176 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2177 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2178 if 'vappTemplate-' in rawuuid
[0]:
2179 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2180 # vm and use raw UUID as key
2181 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2187 def get_vm_list(self
, vdc_name
=None):
2189 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2190 contains a list of all VM's deployed for queried VDC.
2191 The key for a dictionary is VM UUID
2195 vca - is active VCA connection.
2196 vdc_name - is a vdc name that will be used to query vms action
2199 The return dictionary and key for each entry vapp UUID
2203 if vdc_name
is None:
2206 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2208 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2209 for vm_xml
in vm_list_xmlroot
:
2210 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2211 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2212 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2213 if 'vm-' in rawuuid
[0]:
2214 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2215 # vm and use raw UUID as key
2216 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2222 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2224 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2225 contains a list of all VM's deployed for queried VDC.
2226 The key for a dictionary is VM UUID
2230 vca - is active VCA connection.
2231 vdc_name - is a vdc name that will be used to query vms action
2234 The return dictionary and key for each entry vapp UUID
2237 vca
= self
.connect()
2239 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2241 if vdc_name
is None:
2244 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2246 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2247 for vm_xml
in vm_list_xmlroot
:
2248 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2249 # lookup done by UUID
2251 if vapp_name
in vm_xml
.attrib
['container']:
2252 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2253 if 'vm-' in rawuuid
[0]:
2254 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2256 # lookup done by Name
2258 if vapp_name
in vm_xml
.attrib
['name']:
2259 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2260 if 'vm-' in rawuuid
[0]:
2261 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2268 def get_network_action(self
, network_uuid
=None):
2270 Method leverages vCloud director and query network based on network uuid
2273 vca - is active VCA connection.
2274 network_uuid - is a network uuid
2277 The return XML respond
2280 vca
= self
.connect()
2282 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2284 if network_uuid
is None:
2287 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2288 vm_list_rest_call
= ''.join(url_list
)
2290 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2291 response
= Http
.get(url
=vm_list_rest_call
,
2292 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2295 if response
.status_code
== requests
.codes
.ok
:
2296 return response
.content
2300 def get_vcd_network(self
, network_uuid
=None):
2302 Method retrieves available network from vCloud Director
2305 network_uuid - is VCD network UUID
2307 Each element serialized as key : value pair
2309 Following keys available for access. network_configuration['Gateway'}
2313 <IsInherited>true</IsInherited>
2314 <Gateway>172.16.252.100</Gateway>
2315 <Netmask>255.255.255.0</Netmask>
2316 <Dns1>172.16.254.201</Dns1>
2317 <Dns2>172.16.254.202</Dns2>
2318 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2319 <IsEnabled>true</IsEnabled>
2322 <StartAddress>172.16.252.1</StartAddress>
2323 <EndAddress>172.16.252.99</EndAddress>
2328 <FenceMode>bridged</FenceMode>
2331 The return dictionary and key for each entry vapp UUID
2334 network_configuration
= {}
2335 if network_uuid
is None:
2339 content
= self
.get_network_action(network_uuid
=network_uuid
)
2340 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2342 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2343 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2344 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2346 for child
in vm_list_xmlroot
:
2347 if child
.tag
.split("}")[1] == 'IsShared':
2348 network_configuration
['isShared'] = child
.text
.strip()
2349 if child
.tag
.split("}")[1] == 'Configuration':
2350 for configuration
in child
.iter():
2351 tagKey
= configuration
.tag
.split("}")[1].strip()
2353 network_configuration
[tagKey
] = configuration
.text
.strip()
2354 return network_configuration
2355 except Exception as exp
:
2356 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
2357 raise vimconn
.vimconnException("get_vcd_network: Failed with Exception {}".format(exp
))
2359 return network_configuration
2361 def delete_network_action(self
, network_uuid
=None):
2363 Method delete given network from vCloud director
2366 network_uuid - is a network uuid that client wish to delete
2369 The return None or XML respond or false
2372 vca
= self
.connect_as_admin()
2374 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2375 if network_uuid
is None:
2378 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2379 vm_list_rest_call
= ''.join(url_list
)
2381 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2382 response
= Http
.delete(url
=vm_list_rest_call
,
2383 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2387 if response
.status_code
== 202:
2392 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2393 ip_profile
=None, isshared
='true'):
2395 Method create network in vCloud director
2398 network_name - is network name to be created.
2399 net_type - can be 'bridge','data','ptp','mgmt'.
2400 ip_profile is a dict containing the IP parameters of the network
2401 isshared - is a boolean
2402 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2403 It optional attribute. by default if no parent network indicate the first available will be used.
2406 The return network uuid or return None
2409 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2410 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2411 ip_profile
=ip_profile
,
2413 parent_network_uuid
=parent_network_uuid
,
2416 self
.logger
.debug("Failed create network {}.".format(network_name
))
2420 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2421 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2422 if len(vcd_uuid
) == 4:
2423 self
.logger
.info("Created new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2426 self
.logger
.debug("Failed create network {}".format(network_name
))
2429 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2430 ip_profile
=None, isshared
='true'):
2432 Method create network in vCloud director
2435 network_name - is network name to be created.
2436 net_type - can be 'bridge','data','ptp','mgmt'.
2437 ip_profile is a dict containing the IP parameters of the network
2438 isshared - is a boolean
2439 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2440 It optional attribute. by default if no parent network indicate the first available will be used.
2443 The return network uuid or return None
2446 vca
= self
.connect_as_admin()
2448 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2449 if network_name
is None:
2452 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2453 vm_list_rest_call
= ''.join(url_list
)
2454 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2455 response
= Http
.get(url
=vm_list_rest_call
,
2456 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2460 provider_network
= None
2461 available_networks
= None
2462 add_vdc_rest_url
= None
2464 if response
.status_code
!= requests
.codes
.ok
:
2465 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2466 response
.status_code
))
2470 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2471 for child
in vm_list_xmlroot
:
2472 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2473 provider_network
= child
.attrib
.get('href')
2474 # application/vnd.vmware.admin.providervdc+xml
2475 if child
.tag
.split("}")[1] == 'Link':
2476 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2477 and child
.attrib
.get('rel') == 'add':
2478 add_vdc_rest_url
= child
.attrib
.get('href')
2480 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2481 self
.logger
.debug("Respond body {}".format(response
.content
))
2484 # find pvdc provided available network
2485 response
= Http
.get(url
=provider_network
,
2486 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2489 if response
.status_code
!= requests
.codes
.ok
:
2490 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2491 response
.status_code
))
2494 # available_networks.split("/")[-1]
2496 if parent_network_uuid
is None:
2498 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2499 for child
in vm_list_xmlroot
.iter():
2500 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2501 for networks
in child
.iter():
2502 # application/vnd.vmware.admin.network+xml
2503 if networks
.attrib
.get('href') is not None:
2504 available_networks
= networks
.attrib
.get('href')
2510 #Configure IP profile of the network
2511 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2513 if 'gateway_address' not in ip_profile
or ip_profile
['gateway_address'] is None:
2514 ip_profile
['gateway_address']=DEFAULT_IP_PROFILE
['gateway_address']
2515 if 'dhcp_count' not in ip_profile
or ip_profile
['dhcp_count'] is None:
2516 ip_profile
['dhcp_count']=DEFAULT_IP_PROFILE
['dhcp_count']
2517 if 'subnet_address' not in ip_profile
or ip_profile
['subnet_address'] is None:
2518 ip_profile
['subnet_address']=DEFAULT_IP_PROFILE
['subnet_address']
2519 if 'dhcp_enabled' not in ip_profile
or ip_profile
['dhcp_enabled'] is None:
2520 ip_profile
['dhcp_enabled']=DEFAULT_IP_PROFILE
['dhcp_enabled']
2521 if 'dhcp_start_address' not in ip_profile
or ip_profile
['dhcp_start_address'] is None:
2522 ip_profile
['dhcp_start_address']=DEFAULT_IP_PROFILE
['dhcp_start_address']
2523 if 'ip_version' not in ip_profile
or ip_profile
['ip_version'] is None:
2524 ip_profile
['ip_version']=DEFAULT_IP_PROFILE
['ip_version']
2525 if 'dns_address' not in ip_profile
or ip_profile
['dns_address'] is None:
2526 ip_profile
['dns_address']=DEFAULT_IP_PROFILE
['dns_address']
2528 gateway_address
=ip_profile
['gateway_address']
2529 dhcp_count
=int(ip_profile
['dhcp_count'])
2530 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2532 if ip_profile
['dhcp_enabled']==True:
2535 dhcp_enabled
='false'
2536 dhcp_start_address
=ip_profile
['dhcp_start_address']
2538 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2539 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2540 end_ip_int
+= dhcp_count
- 1
2541 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2543 ip_version
=ip_profile
['ip_version']
2544 dns_address
=ip_profile
['dns_address']
2545 except KeyError as exp
:
2546 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
2547 raise vimconn
.vimconnException("Create Network REST: Key error{}".format(exp
))
2549 # either use client provided UUID or search for a first available
2550 # if both are not defined we return none
2551 if parent_network_uuid
is not None:
2552 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2553 add_vdc_rest_url
= ''.join(url_list
)
2556 fence_mode
="isolated"
2558 is_inherited
='false'
2559 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2560 <Description>Openmano created</Description>
2564 <IsInherited>{1:s}</IsInherited>
2565 <Gateway>{2:s}</Gateway>
2566 <Netmask>{3:s}</Netmask>
2568 <IsEnabled>{5:s}</IsEnabled>
2571 <StartAddress>{6:s}</StartAddress>
2572 <EndAddress>{7:s}</EndAddress>
2577 <FenceMode>{8:s}</FenceMode>
2579 <IsShared>{9:s}</IsShared>
2580 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2581 subnet_address
, dns_address
, dhcp_enabled
,
2582 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2585 fence_mode
="bridged"
2586 is_inherited
='false'
2587 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2588 <Description>Openmano created</Description>
2592 <IsInherited>{1:s}</IsInherited>
2593 <Gateway>{2:s}</Gateway>
2594 <Netmask>{3:s}</Netmask>
2596 <IsEnabled>{5:s}</IsEnabled>
2599 <StartAddress>{6:s}</StartAddress>
2600 <EndAddress>{7:s}</EndAddress>
2605 <ParentNetwork href="{8:s}"/>
2606 <FenceMode>{9:s}</FenceMode>
2608 <IsShared>{10:s}</IsShared>
2609 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2610 subnet_address
, dns_address
, dhcp_enabled
,
2611 dhcp_start_address
, dhcp_end_address
, available_networks
,
2612 fence_mode
, isshared
)
2614 headers
= vca
.vcloud_session
.get_vcloud_headers()
2615 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2617 response
= Http
.post(url
=add_vdc_rest_url
,
2623 if response
.status_code
!= 201:
2624 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2625 .format(response
.status_code
,response
.content
))
2627 network
= networkType
.parseString(response
.content
, True)
2628 create_nw_task
= network
.get_Tasks().get_Task()[0]
2630 # if we all ok we respond with content after network creation completes
2631 # otherwise by default return None
2632 if create_nw_task
is not None:
2633 self
.logger
.debug("Create Network REST : Waiting for Network creation complete")
2634 status
= vca
.block_until_completed(create_nw_task
)
2636 return response
.content
2638 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2639 .format(response
.content
))
2640 except Exception as exp
:
2641 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2645 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2647 Method sets convert CIDR netmask address to normal IP format
2649 cidr_ip : CIDR IP address
2651 netmask : Converted netmask
2653 if cidr_ip
is not None:
2655 network
, net_bits
= cidr_ip
.split('/')
2656 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2662 def get_provider_rest(self
, vca
=None):
2664 Method gets provider vdc view from vcloud director
2667 network_name - is network name to be created.
2668 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2669 It optional attribute. by default if no parent network indicate the first available will be used.
2672 The return xml content of respond or None
2675 url_list
= [vca
.host
, '/api/admin']
2676 response
= Http
.get(url
=''.join(url_list
),
2677 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2681 if response
.status_code
== requests
.codes
.ok
:
2682 return response
.content
2685 def create_vdc(self
, vdc_name
=None):
2689 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2690 if xml_content
is not None:
2692 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2693 for child
in task_resp_xmlroot
:
2694 if child
.tag
.split("}")[1] == 'Owner':
2695 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2696 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2699 self
.logger
.debug("Respond body {}".format(xml_content
))
2703 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2705 Method create vdc in vCloud director based on VDC template.
2706 it uses pre-defined template that must be named openmano
2709 vdc_name - name of a new vdc.
2712 The return xml content of respond or None
2715 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2716 vca
= self
.connect()
2718 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2719 if vdc_name
is None:
2722 url_list
= [vca
.host
, '/api/vdcTemplates']
2723 vm_list_rest_call
= ''.join(url_list
)
2724 response
= Http
.get(url
=vm_list_rest_call
,
2725 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2729 # container url to a template
2730 vdc_template_ref
= None
2732 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2733 for child
in vm_list_xmlroot
:
2734 # application/vnd.vmware.admin.providervdc+xml
2735 # we need find a template from witch we instantiate VDC
2736 if child
.tag
.split("}")[1] == 'VdcTemplate':
2737 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2738 'name') == 'openmano':
2739 vdc_template_ref
= child
.attrib
.get('href')
2741 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2742 self
.logger
.debug("Respond body {}".format(response
.content
))
2745 # if we didn't found required pre defined template we return None
2746 if vdc_template_ref
is None:
2751 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2752 vm_list_rest_call
= ''.join(url_list
)
2753 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2754 <Source href="{1:s}"></Source>
2755 <Description>opnemano</Description>
2756 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2757 headers
= vca
.vcloud_session
.get_vcloud_headers()
2758 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2759 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2761 # if we all ok we respond with content otherwise by default None
2762 if response
.status_code
>= 200 and response
.status_code
< 300:
2763 return response
.content
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
))
2771 def create_vdc_rest(self
, vdc_name
=None):
2773 Method create network in vCloud director
2776 network_name - is network name to be created.
2777 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2778 It optional attribute. by default if no parent network indicate the first available will be used.
2781 The return network uuid or return None
2784 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2786 vca
= self
.connect_as_admin()
2788 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2789 if vdc_name
is None:
2792 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2793 vm_list_rest_call
= ''.join(url_list
)
2794 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2795 response
= Http
.get(url
=vm_list_rest_call
,
2796 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2800 provider_vdc_ref
= None
2801 add_vdc_rest_url
= None
2802 available_networks
= None
2804 if response
.status_code
!= requests
.codes
.ok
:
2805 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2806 response
.status_code
))
2810 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2811 for child
in vm_list_xmlroot
:
2812 # application/vnd.vmware.admin.providervdc+xml
2813 if child
.tag
.split("}")[1] == 'Link':
2814 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2815 and child
.attrib
.get('rel') == 'add':
2816 add_vdc_rest_url
= child
.attrib
.get('href')
2818 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2819 self
.logger
.debug("Respond body {}".format(response
.content
))
2822 response
= self
.get_provider_rest(vca
=vca
)
2824 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2825 for child
in vm_list_xmlroot
:
2826 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2827 for sub_child
in child
:
2828 provider_vdc_ref
= sub_child
.attrib
.get('href')
2830 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2831 self
.logger
.debug("Respond body {}".format(response
))
2834 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2835 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2836 <AllocationModel>ReservationPool</AllocationModel>
2837 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2838 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2839 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2840 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2841 <ProviderVdcReference
2842 name="Main Provider"
2844 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2848 headers
= vca
.vcloud_session
.get_vcloud_headers()
2849 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2850 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2853 # if we all ok we respond with content otherwise by default None
2854 if response
.status_code
== 201:
2855 return response
.content
2858 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2860 Method retrieve vapp detail from vCloud director
2863 vapp_uuid - is vapp identifier.
2866 The return network uuid or return None
2872 if need_admin_access
:
2873 vca
= self
.connect_as_admin()
2875 vca
= self
.connect()
2878 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2879 if vapp_uuid
is None:
2882 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2883 get_vapp_restcall
= ''.join(url_list
)
2885 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2886 response
= Http
.get(url
=get_vapp_restcall
,
2887 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2891 if response
.status_code
!= requests
.codes
.ok
:
2892 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2893 response
.status_code
))
2894 return parsed_respond
2897 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2898 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2900 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2901 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2902 'vmw': 'http://www.vmware.com/schema/ovf',
2903 'vm': 'http://www.vmware.com/vcloud/v1.5',
2904 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2905 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2906 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2909 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2910 if created_section
is not None:
2911 parsed_respond
['created'] = created_section
.text
2913 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2914 if network_section
is not None and 'networkName' in network_section
.attrib
:
2915 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2917 ipscopes_section
= \
2918 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2920 if ipscopes_section
is not None:
2921 for ipscope
in ipscopes_section
:
2922 for scope
in ipscope
:
2923 tag_key
= scope
.tag
.split("}")[1]
2924 if tag_key
== 'IpRanges':
2925 ip_ranges
= scope
.getchildren()
2926 for ipblock
in ip_ranges
:
2927 for block
in ipblock
:
2928 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2930 parsed_respond
[tag_key
] = scope
.text
2932 # parse children section for other attrib
2933 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2934 if children_section
is not None:
2935 parsed_respond
['name'] = children_section
.attrib
['name']
2936 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2937 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2938 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2939 parsed_respond
['status'] = children_section
.attrib
['status']
2940 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2941 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2943 for adapters
in network_adapter
:
2944 adapter_key
= adapters
.tag
.split("}")[1]
2945 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2946 parsed_respond
['primarynetwork'] = adapters
.text
2947 if adapter_key
== 'NetworkConnection':
2949 if 'network' in adapters
.attrib
:
2950 vnic
['network'] = adapters
.attrib
['network']
2951 for adapter
in adapters
:
2952 setting_key
= adapter
.tag
.split("}")[1]
2953 vnic
[setting_key
] = adapter
.text
2954 nic_list
.append(vnic
)
2956 for link
in children_section
:
2957 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2958 if link
.attrib
['rel'] == 'screen:acquireTicket':
2959 parsed_respond
['acquireTicket'] = link
.attrib
2960 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2961 parsed_respond
['acquireMksTicket'] = link
.attrib
2963 parsed_respond
['interfaces'] = nic_list
2964 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2965 if vCloud_extension_section
is not None:
2966 vm_vcenter_info
= {}
2967 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2968 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2969 if vmext
is not None:
2970 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2971 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2973 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2974 vm_virtual_hardware_info
= {}
2975 if virtual_hardware_section
is not None:
2976 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2977 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2978 disk_size
= item
.find("rasd:HostResource" ,namespaces
2979 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2981 vm_virtual_hardware_info
["disk_size"]= disk_size
2984 for link
in virtual_hardware_section
:
2985 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2986 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2987 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2990 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2991 except Exception as exp
:
2992 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2993 return parsed_respond
2995 def acuire_console(self
, vm_uuid
=None):
2997 vca
= self
.connect()
2999 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3003 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
3004 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
3005 console_dict
= vm_dict
['acquireTicket']
3006 console_rest_call
= console_dict
['href']
3008 response
= Http
.post(url
=console_rest_call
,
3009 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3013 if response
.status_code
== requests
.codes
.ok
:
3014 return response
.content
3018 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
3020 Method retrieve vm disk details
3023 vapp_uuid - is vapp identifier.
3024 flavor_disk - disk size as specified in VNFD (flavor)
3027 The return network uuid or return None
3031 #Flavor disk is in GB convert it into MB
3032 flavor_disk
= int(flavor_disk
) * 1024
3033 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
3035 vm_name
= vm_details
["name"]
3036 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
3038 if vm_details
and "vm_virtual_hardware" in vm_details
:
3039 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
3040 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
3042 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
3044 if flavor_disk
> vm_disk
:
3045 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
3046 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
3047 vm_disk
, flavor_disk
))
3050 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
3053 except Exception as exp
:
3054 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
3057 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
3059 Method retrieve modify vm disk size
3062 disk_href - vCD API URL to GET and PUT disk data
3063 disk_size - disk size as specified in VNFD (flavor)
3066 The return network uuid or return None
3068 vca
= self
.connect()
3070 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3071 if disk_href
is None or disk_size
is None:
3074 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3075 response
= Http
.get(url
=disk_href
,
3076 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3080 if response
.status_code
!= requests
.codes
.ok
:
3081 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3082 response
.status_code
))
3085 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3086 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3087 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3089 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3090 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3091 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3092 if disk_item
is not None:
3093 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3096 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3097 xml_declaration
=True)
3099 #Send PUT request to modify disk size
3100 headers
= vca
.vcloud_session
.get_vcloud_headers()
3101 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3103 response
= Http
.put(url
=disk_href
,
3106 verify
=vca
.verify
, logger
=self
.logger
)
3108 if response
.status_code
!= 202:
3109 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3110 response
.status_code
))
3112 modify_disk_task
= taskType
.parseString(response
.content
, True)
3113 if type(modify_disk_task
) is GenericTask
:
3114 status
= vca
.block_until_completed(modify_disk_task
)
3119 except Exception as exp
:
3120 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3123 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3125 Method to attach pci devices to VM
3128 vapp_uuid - uuid of vApp/VM
3129 pci_devices - pci devices infromation as specified in VNFD (flavor)
3132 The status of add pci device task , vm object and
3133 vcenter_conect object
3136 vcenter_conect
= None
3137 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3139 vm_vcenter_info
= self
.get_vm_vcenter_info(vapp_uuid
)
3140 except Exception as exp
:
3141 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3142 " for VM : {}".format(exp
))
3143 raise vimconn
.vimconnException(message
=exp
)
3145 if vm_vcenter_info
["vm_moref_id"]:
3147 if hasattr(ssl
, '_create_unverified_context'):
3148 context
= ssl
._create
_unverified
_context
()
3150 no_of_pci_devices
= len(pci_devices
)
3151 if no_of_pci_devices
> 0:
3152 vcenter_conect
= SmartConnect(
3153 host
=vm_vcenter_info
["vm_vcenter_ip"],
3154 user
=vm_vcenter_info
["vm_vcenter_user"],
3155 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3156 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3158 atexit
.register(Disconnect
, vcenter_conect
)
3159 content
= vcenter_conect
.RetrieveContent()
3161 #Get VM and its host
3162 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3163 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3164 if host_obj
and vm_obj
:
3165 #get PCI devies from host on which vapp is currently installed
3166 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3168 if avilable_pci_devices
is None:
3169 #find other hosts with active pci devices
3170 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3175 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3176 #Migrate vm to the host where PCI devices are availble
3177 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3178 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3179 if task
is not None:
3180 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3181 self
.logger
.info("Migrate VM status: {}".format(result
))
3182 host_obj
= new_host_obj
3184 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3185 raise vimconn
.vimconnNotFoundException(
3186 "Fail to migrate VM : {} to host {}".format(
3191 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3192 #Add PCI devices one by one
3193 for pci_device
in avilable_pci_devices
:
3194 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3196 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3198 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3200 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3201 return True, vm_obj
, vcenter_conect
3203 self
.logger
.error("Currently there is no host with"\
3204 " {} number of avaialble PCI devices required for VM {}".format(
3208 raise vimconn
.vimconnNotFoundException(
3209 "Currently there is no host with {} "\
3210 "number of avaialble PCI devices required for VM {}".format(
3214 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3216 except vmodl
.MethodFault
as error
:
3217 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3218 return None, vm_obj
, vcenter_conect
3220 def get_vm_obj(self
, content
, mob_id
):
3222 Method to get the vsphere VM object associated with a given morf ID
3224 vapp_uuid - uuid of vApp/VM
3225 content - vCenter content object
3226 mob_id - mob_id of VM
3234 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3235 [vim
.VirtualMachine
], True
3237 for vm
in container
.view
:
3238 mobID
= vm
._GetMoId
()
3241 host_obj
= vm_obj
.runtime
.host
3243 except Exception as exp
:
3244 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3245 return host_obj
, vm_obj
3247 def get_pci_devices(self
, host
, need_devices
):
3249 Method to get the details of pci devices on given host
3251 host - vSphere host object
3252 need_devices - number of pci devices needed on host
3255 array of pci devices
3259 used_devices_ids
= []
3263 pciPassthruInfo
= host
.config
.pciPassthruInfo
3264 pciDevies
= host
.hardware
.pciDevice
3266 for pci_status
in pciPassthruInfo
:
3267 if pci_status
.passthruActive
:
3268 for device
in pciDevies
:
3269 if device
.id == pci_status
.id:
3270 all_device_ids
.append(device
.id)
3271 all_devices
.append(device
)
3273 #check if devices are in use
3274 avalible_devices
= all_devices
3276 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3277 vm_devices
= vm
.config
.hardware
.device
3278 for device
in vm_devices
:
3279 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3280 if device
.backing
.id in all_device_ids
:
3281 for use_device
in avalible_devices
:
3282 if use_device
.id == device
.backing
.id:
3283 avalible_devices
.remove(use_device
)
3284 used_devices_ids
.append(device
.backing
.id)
3285 self
.logger
.debug("Device {} from devices {}"\
3286 "is in use".format(device
.backing
.id,
3289 if len(avalible_devices
) < need_devices
:
3290 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3292 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3296 required_devices
= avalible_devices
[:need_devices
]
3297 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3298 len(avalible_devices
),
3301 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3303 return required_devices
3305 except Exception as exp
:
3306 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3310 def get_host_and_PCIdevices(self
, content
, need_devices
):
3312 Method to get the details of pci devices infromation on all hosts
3315 content - vSphere host object
3316 need_devices - number of pci devices needed on host
3319 array of pci devices and host object
3322 pci_device_objs
= None
3325 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3326 [vim
.HostSystem
], True)
3327 for host
in container
.view
:
3328 devices
= self
.get_pci_devices(host
, need_devices
)
3331 pci_device_objs
= devices
3333 except Exception as exp
:
3334 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3336 return host_obj
,pci_device_objs
3338 def relocate_vm(self
, dest_host
, vm
) :
3340 Method to get the relocate VM to new host
3343 dest_host - vSphere host object
3344 vm - vSphere VM object
3351 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3352 task
= vm
.Relocate(relocate_spec
)
3353 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3354 except Exception as exp
:
3355 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3356 dest_host
, vm
, exp
))
3359 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3361 Waits and provides updates on a vSphere task
3363 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3366 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3367 if task
.info
.result
is not None and not hideResult
:
3368 self
.logger
.info('{} completed successfully, result: {}'.format(
3372 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3374 self
.logger
.error('{} did not complete successfully: {} '.format(
3379 return task
.info
.result
3381 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3383 Method to add pci device in given VM
3386 host_object - vSphere host object
3387 vm_object - vSphere VM object
3388 host_pci_dev - host_pci_dev must be one of the devices from the
3389 host_object.hardware.pciDevice list
3390 which is configured as a PCI passthrough device
3396 if vm_object
and host_object
and host_pci_dev
:
3398 #Add PCI device to VM
3399 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3400 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3402 if host_pci_dev
.id not in systemid_by_pciid
:
3403 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3406 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3407 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3409 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3410 vendorId
=host_pci_dev
.vendorId
,
3411 deviceName
=host_pci_dev
.deviceName
)
3413 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3415 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3416 new_device_config
.operation
= "add"
3417 vmConfigSpec
= vim
.vm
.ConfigSpec()
3418 vmConfigSpec
.deviceChange
= [new_device_config
]
3420 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3421 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3422 host_pci_dev
, vm_object
, host_object
)
3424 except Exception as exp
:
3425 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3431 def get_vm_vcenter_info(self
, vapp_uuid
):
3433 Method to get details of vCenter and vm
3436 vapp_uuid - uuid of vApp or VM
3439 Moref Id of VM and deails of vCenter
3441 vm_vcenter_info
= {}
3443 if self
.vcenter_ip
is not None:
3444 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
3446 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
3447 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3448 if self
.vcenter_port
is not None:
3449 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
3451 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
3452 " Please provide vCenter port while attaching datacenter to tenant in --config")
3453 if self
.vcenter_user
is not None:
3454 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
3456 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
3457 " Please provide vCenter user while attaching datacenter to tenant in --config")
3459 if self
.vcenter_password
is not None:
3460 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
3462 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
3463 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3465 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3466 if vm_details
and "vm_vcenter_info" in vm_details
:
3467 vm_vcenter_info
["vm_moref_id"] = vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
3469 return vm_vcenter_info
3471 except Exception as exp
:
3472 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3473 " for VM : {}".format(exp
))
3476 def get_vm_pci_details(self
, vmuuid
):
3478 Method to get VM PCI device details from vCenter
3481 vm_obj - vSphere VM object
3484 dict of PCI devives attached to VM
3487 vm_pci_devices_info
= {}
3489 vm_vcenter_info
= self
.get_vm_vcenter_info(vmuuid
)
3490 if vm_vcenter_info
["vm_moref_id"]:
3492 if hasattr(ssl
, '_create_unverified_context'):
3493 context
= ssl
._create
_unverified
_context
()
3494 vcenter_conect
= SmartConnect(host
=vm_vcenter_info
["vm_vcenter_ip"],
3495 user
=vm_vcenter_info
["vm_vcenter_user"],
3496 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3497 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3500 atexit
.register(Disconnect
, vcenter_conect
)
3501 content
= vcenter_conect
.RetrieveContent()
3503 #Get VM and its host
3505 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3506 if host_obj
and vm_obj
:
3507 vm_pci_devices_info
["host_name"]= host_obj
.name
3508 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
3509 for device
in vm_obj
.config
.hardware
.device
:
3510 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3511 device_details
={'devide_id':device
.backing
.id,
3512 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
3514 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3516 self
.logger
.error("Can not connect to vCenter while getting "\
3517 "PCI devices infromationn")
3518 return vm_pci_devices_info
3519 except Exception as exp
:
3520 self
.logger
.error("Error occurred while getting VM infromationn"\
3521 " for VM : {}".format(exp
))
3522 raise vimconn
.vimconnException(message
=exp
)