1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
28 from progressbar
import Percentage
, Bar
, ETA
, FileTransferSpeed
, ProgressBar
38 from pyVmomi
import vim
, vmodl
39 from pyVim
.connect
import SmartConnect
, Disconnect
41 from xml
.etree
import ElementTree
as XmlElementTree
42 from lxml
import etree
as lxmlElementTree
45 from pyvcloud
import Http
46 from pyvcloud
.vcloudair
import VCA
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
48 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
49 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
50 from xml
.sax
.saxutils
import escape
52 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
53 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
54 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
55 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
67 # global variable for vcd connector type
68 STANDALONE
= 'standalone'
70 # key for flavor dicts
71 FLAVOR_RAM_KEY
= 'ram'
72 FLAVOR_VCPUS_KEY
= 'vcpus'
73 FLAVOR_DISK_KEY
= 'disk'
74 DEFAULT_IP_PROFILE
= {'gateway_address':"192.168.1.1",
76 'subnet_address':"192.168.1.0/24",
78 'dhcp_start_address':"192.168.1.3",
80 'dns_address':"192.168.1.2"
82 # global variable for wait time
88 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
89 __date__
= "$12-Jan-2017 11:09:29$"
92 # -1: "Could not be created",
98 # 5: "Waiting for user input",
100 # 7: "Unrecognized state",
102 # 9: "Inconsistent state",
103 # 10: "Children do not all have the same status",
104 # 11: "Upload initiated, OVF descriptor pending",
105 # 12: "Upload initiated, copying contents",
106 # 13: "Upload initiated , disk contents pending",
107 # 14: "Upload has been quarantined",
108 # 15: "Upload quarantine period has expired"
110 # mapping vCD status to MANO
111 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
120 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
121 'ERROR': 'ERROR', 'DELETED': 'DELETED'
124 # dict used to store flavor in memory
128 class vimconnector(vimconn
.vimconnector
):
129 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
130 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
132 Constructor create vmware connector to vCloud director.
134 By default construct doesn't validate connection state. So client can create object with None arguments.
135 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
137 a) It initialize organization UUID
138 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
141 uuid - is organization uuid.
142 name - is organization name that must be presented in vCloud director.
143 tenant_id - is VDC uuid it must be presented in vCloud director
144 tenant_name - is VDC name.
145 url - is hostname or ip address of vCloud director
146 url_admin - same as above.
147 user - is user that administrator for organization. Caller must make sure that
148 username has right privileges.
150 password - is password for a user.
152 VMware connector also requires PVDC administrative privileges and separate account.
153 This variables must be passed via config argument dict contains keys
155 dict['admin_username']
156 dict['admin_password']
162 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
163 url_admin
, user
, passwd
, log_level
, config
)
165 self
.logger
= logging
.getLogger('openmano.vim.vmware')
166 self
.logger
.setLevel(10)
171 self
.url_admin
= url_admin
172 self
.tenant_id
= tenant_id
173 self
.tenant_name
= tenant_name
177 self
.admin_password
= None
178 self
.admin_user
= None
181 if tenant_name
is not None:
182 orgnameandtenant
= tenant_name
.split(":")
183 if len(orgnameandtenant
) == 2:
184 self
.tenant_name
= orgnameandtenant
[1]
185 self
.org_name
= orgnameandtenant
[0]
187 self
.tenant_name
= tenant_name
188 if "orgname" in config
:
189 self
.org_name
= config
['orgname']
192 self
.logger
.setLevel(getattr(logging
, log_level
))
195 self
.admin_user
= config
['admin_username']
196 self
.admin_password
= config
['admin_password']
198 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
204 raise vimconn
.vimconnException('url param can not be NoneType')
206 if not self
.url_admin
: # try to use normal url
207 self
.url_admin
= self
.url
209 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
210 self
.tenant_id
, self
.tenant_name
))
211 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
212 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
214 # initialize organization
215 if self
.user
is not None and self
.passwd
is not None and self
.url
:
216 self
.init_organization()
218 def __getitem__(self
, index
):
221 if index
== 'tenant_id':
222 return self
.tenant_id
223 if index
== 'tenant_name':
224 return self
.tenant_name
227 elif index
== 'org_name':
229 elif index
== 'org_uuid':
231 elif index
== 'user':
233 elif index
== 'passwd':
237 elif index
== 'url_admin':
238 return self
.url_admin
239 elif index
== "config":
242 raise KeyError("Invalid key '%s'" % str(index
))
244 def __setitem__(self
, index
, value
):
247 if index
== 'tenant_id':
248 self
.tenant_id
= value
249 if index
== 'tenant_name':
250 self
.tenant_name
= value
253 elif index
== 'org_name':
254 self
.org_name
= value
255 elif index
== 'org_uuid':
256 self
.org_uuid
= value
257 elif index
== 'user':
259 elif index
== 'passwd':
263 elif index
== 'url_admin':
264 self
.url_admin
= value
266 raise KeyError("Invalid key '%s'" % str(index
))
268 def connect_as_admin(self
):
269 """ Method connect as pvdc admin user to vCloud director.
270 There are certain action that can be done only by provider vdc admin user.
271 Organization creation / provider network creation etc.
274 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
277 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
279 vca_admin
= VCA(host
=self
.url
,
280 username
=self
.admin_user
,
281 service_type
=STANDALONE
,
285 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
287 raise vimconn
.vimconnConnectionException(
288 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
289 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
292 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
297 """ Method connect as normal user to vCloud director.
300 The return vca object that letter can be used to connect to vCloud director as admin for VDC
304 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
307 vca
= VCA(host
=self
.url
,
309 service_type
=STANDALONE
,
314 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
316 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
317 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
320 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
323 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
324 "{} as user: {}".format(self
.org_name
, self
.user
))
328 def init_organization(self
):
329 """ Method initialize organization UUID and VDC parameters.
331 At bare minimum client must provide organization name that present in vCloud director and VDC.
333 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
334 The Org - UUID will be initialized at the run time if data center present in vCloud director.
337 The return vca object that letter can be used to connect to vcloud direct as admin
340 if self
.org_uuid
is None:
341 org_dict
= self
.get_org_list()
343 # we set org UUID at the init phase but we can do it only when we have valid credential.
344 if org_dict
[org
] == self
.org_name
:
346 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
349 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
351 # if well good we require for org details
352 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
354 # we have two case if we want to initialize VDC ID or VDC name at run time
355 # tenant_name provided but no tenant id
356 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
357 vdcs_dict
= org_details_dict
['vdcs']
358 for vdc
in vdcs_dict
:
359 if vdcs_dict
[vdc
] == self
.tenant_name
:
361 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
365 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
366 # case two we have tenant_id but we don't have tenant name so we find and set it.
367 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
368 vdcs_dict
= org_details_dict
['vdcs']
369 for vdc
in vdcs_dict
:
370 if vdc
== self
.tenant_id
:
371 self
.tenant_name
= vdcs_dict
[vdc
]
372 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
376 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
377 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
379 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
380 self
.logger
.debug(traceback
.format_exc())
383 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
384 """ Method adds a new tenant to VIM with this name.
385 This action requires access to create VDC action in vCloud director.
388 tenant_name is tenant_name to be created.
389 tenant_description not used for this call
392 returns the tenant identifier in UUID format.
393 If action is failed method will throw vimconn.vimconnException method
395 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
396 if vdc_task
is not None:
397 vdc_uuid
, value
= vdc_task
.popitem()
398 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
401 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
403 def delete_tenant(self
, tenant_id
=None):
404 """Delete a tenant from VIM"""
405 'Returns the tenant identifier'
406 raise vimconn
.vimconnNotImplemented("Should have implemented this")
408 def get_tenant_list(self
, filter_dict
={}):
409 """Obtain tenants of VIM
410 filter_dict can contain the following keys:
411 name: filter by tenant name
412 id: filter by tenant uuid/id
414 Returns the tenant list of dictionaries:
415 [{'name':'<name>, 'id':'<id>, ...}, ...]
418 org_dict
= self
.get_org(self
.org_uuid
)
419 vdcs_dict
= org_dict
['vdcs']
424 entry
= {'name': vdcs_dict
[k
], 'id': k
}
425 # if caller didn't specify dictionary we return all tenants.
426 if filter_dict
is not None and filter_dict
:
427 filtered_entry
= entry
.copy()
428 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
429 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
430 if filter_dict
== entry
:
431 vdclist
.append(filtered_entry
)
433 vdclist
.append(entry
)
435 self
.logger
.debug("Error in get_tenant_list()")
436 self
.logger
.debug(traceback
.format_exc())
437 raise vimconn
.vimconnException("Incorrect state. {}")
441 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
442 """Adds a tenant network to VIM
444 net_type can be 'bridge','data'.'ptp'.
445 ip_profile is a dict containing the IP parameters of the network
447 Returns the network identifier"""
449 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
450 .format(net_name
, net_type
, ip_profile
, shared
))
456 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
457 ip_profile
=ip_profile
, isshared
=isshared
)
458 if network_uuid
is not None:
461 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
463 def get_vcd_network_list(self
):
464 """ Method available organization for a logged in tenant
467 The return vca object that letter can be used to connect to vcloud direct as admin
470 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
473 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
475 if not self
.tenant_name
:
476 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
478 vdc
= vca
.get_vdc(self
.tenant_name
)
480 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
482 vdc_uuid
= vdc
.get_id().split(":")[3]
483 networks
= vca
.get_networks(vdc
.get_name())
486 for network
in networks
:
488 netid
= network
.get_id().split(":")
492 filter_dict
["name"] = network
.get_name()
493 filter_dict
["id"] = netid
[3]
494 filter_dict
["shared"] = network
.get_IsShared()
495 filter_dict
["tenant_id"] = vdc_uuid
496 if network
.get_status() == 1:
497 filter_dict
["admin_state_up"] = True
499 filter_dict
["admin_state_up"] = False
500 filter_dict
["status"] = "ACTIVE"
501 filter_dict
["type"] = "bridge"
502 network_list
.append(filter_dict
)
503 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
505 self
.logger
.debug("Error in get_vcd_network_list")
506 self
.logger
.debug(traceback
.format_exc())
509 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
512 def get_network_list(self
, filter_dict
={}):
513 """Obtain tenant networks of VIM
515 name: network name OR/AND
516 id: network uuid OR/AND
517 shared: boolean OR/AND
518 tenant_id: tenant OR/AND
519 admin_state_up: boolean
522 [{key : value , key : value}]
524 Returns the network list of dictionaries:
525 [{<the fields at Filter_dict plus some VIM specific>}, ...]
529 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
532 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
534 if not self
.tenant_name
:
535 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
537 vdc
= vca
.get_vdc(self
.tenant_name
)
539 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
541 vdcid
= vdc
.get_id().split(":")[3]
542 networks
= vca
.get_networks(vdc
.get_name())
546 for network
in networks
:
548 net_uuid
= network
.get_id().split(":")
549 if len(net_uuid
) != 4:
552 net_uuid
= net_uuid
[3]
554 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
557 filter_entry
["name"] = network
.get_name()
558 filter_entry
["id"] = net_uuid
559 filter_entry
["shared"] = network
.get_IsShared()
560 filter_entry
["tenant_id"] = vdcid
561 if network
.get_status() == 1:
562 filter_entry
["admin_state_up"] = True
564 filter_entry
["admin_state_up"] = False
565 filter_entry
["status"] = "ACTIVE"
566 filter_entry
["type"] = "bridge"
567 filtered_entry
= filter_entry
.copy()
569 if filter_dict
is not None and filter_dict
:
570 # we remove all the key : value we don't care and match only
572 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
573 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
574 if filter_dict
== filter_entry
:
575 network_list
.append(filtered_entry
)
577 network_list
.append(filtered_entry
)
579 self
.logger
.debug("Error in get_vcd_network_list")
580 self
.logger
.debug(traceback
.format_exc())
582 self
.logger
.debug("Returning {}".format(network_list
))
585 def get_network(self
, net_id
):
586 """Method obtains network details of net_id VIM network
587 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
591 raise vimconn
.vimconnConnectionException("self.connect() is failed")
593 vdc
= vca
.get_vdc(self
.tenant_name
)
594 vdc_id
= vdc
.get_id().split(":")[3]
596 networks
= vca
.get_networks(vdc
.get_name())
600 for network
in networks
:
601 vdc_network_id
= network
.get_id().split(":")
602 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
603 filter_dict
["name"] = network
.get_name()
604 filter_dict
["id"] = vdc_network_id
[3]
605 filter_dict
["shared"] = network
.get_IsShared()
606 filter_dict
["tenant_id"] = vdc_id
607 if network
.get_status() == 1:
608 filter_dict
["admin_state_up"] = True
610 filter_dict
["admin_state_up"] = False
611 filter_dict
["status"] = "ACTIVE"
612 filter_dict
["type"] = "bridge"
613 self
.logger
.debug("Returning {}".format(filter_dict
))
616 self
.logger
.debug("Error in get_network")
617 self
.logger
.debug(traceback
.format_exc())
621 def delete_network(self
, net_id
):
623 Method Deletes a tenant network from VIM, provide the network id.
625 Returns the network identifier or raise an exception
630 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
632 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
633 if vcd_network
is not None and vcd_network
:
634 if self
.delete_network_action(network_uuid
=net_id
):
637 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
639 def refresh_nets_status(self
, net_list
):
640 """Get the status of the networks
641 Params: the list of network identifiers
642 Returns a dictionary with:
643 net_id: #VIM id of this network
644 status: #Mandatory. Text with one of:
645 # DELETED (not found at vim)
646 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
647 # OTHER (Vim reported other status not understood)
648 # ERROR (VIM indicates an ERROR status)
649 # ACTIVE, INACTIVE, DOWN (admin down),
650 # BUILD (on building process)
652 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
653 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
659 raise vimconn
.vimconnConnectionException("self.connect() is failed")
665 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
666 if vcd_network
is not None and vcd_network
:
667 if vcd_network
['status'] == '1':
673 errormsg
= 'Network not found.'
675 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
676 'vim_info': yaml
.safe_dump(vcd_network
)}
678 self
.logger
.debug("Error in refresh_nets_status")
679 self
.logger
.debug(traceback
.format_exc())
683 def get_flavor(self
, flavor_id
):
684 """Obtain flavor details from the VIM
685 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
687 if flavor_id
not in flavorlist
:
688 raise vimconn
.vimconnNotFoundException("Flavor not found.")
689 return flavorlist
[flavor_id
]
691 def new_flavor(self
, flavor_data
):
692 """Adds a tenant flavor to VIM
693 flavor_data contains a dictionary with information, keys:
695 ram: memory (cloud type) in MBytes
696 vpcus: cpus (cloud type)
697 extended: EPA parameters
698 - numas: #items requested in same NUMA
699 memory: number of 1G huge pages memory
700 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
701 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
702 - name: interface name
703 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
704 bandwidth: X Gbps; requested guarantee bandwidth
705 vpci: requested virtual PCI address
709 Returns the flavor identifier"""
711 # generate a new uuid put to internal dict and return it.
712 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
713 new_flavor
=flavor_data
714 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
715 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
716 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
718 extended_flv
= flavor_data
.get("extended")
720 numas
=extended_flv
.get("numas")
723 #overwrite ram and vcpus
724 ram
= numa
['memory']*1024
725 if 'paired-threads' in numa
:
726 cpu
= numa
['paired-threads']*2
727 elif 'cores' in numa
:
729 elif 'threads' in numa
:
730 cpu
= numa
['threads']
732 new_flavor
[FLAVOR_RAM_KEY
] = ram
733 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
734 new_flavor
[FLAVOR_DISK_KEY
] = disk
735 # generate a new uuid put to internal dict and return it.
736 flavor_id
= uuid
.uuid4()
737 flavorlist
[str(flavor_id
)] = new_flavor
738 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
740 return str(flavor_id
)
742 def delete_flavor(self
, flavor_id
):
743 """Deletes a tenant flavor from VIM identify by its id
745 Returns the used id or raise an exception
747 if flavor_id
not in flavorlist
:
748 raise vimconn
.vimconnNotFoundException("Flavor not found.")
750 flavorlist
.pop(flavor_id
, None)
753 def new_image(self
, image_dict
):
755 Adds a tenant image to VIM
757 200, image-id if the image is created
758 <0, message if there is an error
761 return self
.get_image_id_from_path(image_dict
['location'])
763 def delete_image(self
, image_id
):
770 raise vimconn
.vimconnNotImplemented("Should have implemented this")
772 def catalog_exists(self
, catalog_name
, catalogs
):
779 for catalog
in catalogs
:
780 if catalog
.name
== catalog_name
:
784 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
785 """ Create new catalog entry in vCloud director.
788 vca: vCloud director.
789 catalog_name catalog that client wish to create. Note no validation done for a name.
790 Client must make sure that provide valid string representation.
792 Return (bool) True if catalog created.
796 task
= vca
.create_catalog(catalog_name
, catalog_name
)
797 result
= vca
.block_until_completed(task
)
800 catalogs
= vca
.get_catalogs()
803 return self
.catalog_exists(catalog_name
, catalogs
)
805 # noinspection PyIncorrectDocstring
806 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
807 description
='', progress
=False, chunk_bytes
=128 * 1024):
809 Uploads a OVF file to a vCloud catalog
816 :param catalog_name: (str): The name of the catalog to upload the media.
817 :param media_file_name: (str): The name of the local media file to upload.
818 :return: (bool) True if the media file was successfully uploaded, false otherwise.
820 os
.path
.isfile(media_file_name
)
821 statinfo
= os
.stat(media_file_name
)
823 # find a catalog entry where we upload OVF.
824 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
826 # if VCD can parse OVF we upload VMDK file
827 for catalog
in vca
.get_catalogs():
828 if catalog_name
!= catalog
.name
:
830 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
831 link
.get_rel() == 'add', catalog
.get_Link())
832 assert len(link
) == 1
834 <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>
835 """ % (escape(catalog_name
), escape(description
))
836 headers
= vca
.vcloud_session
.get_vcloud_headers()
837 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
838 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
839 if response
.status_code
== requests
.codes
.created
:
840 catalogItem
= XmlElementTree
.fromstring(response
.content
)
841 entity
= [child
for child
in catalogItem
if
842 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
843 href
= entity
.get('href')
845 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
846 verify
=vca
.verify
, logger
=self
.logger
)
848 if response
.status_code
== requests
.codes
.ok
:
849 media
= mediaType
.parseString(response
.content
, True)
850 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
851 media
.get_Files().get_File()[0].get_Link())[0]
852 headers
= vca
.vcloud_session
.get_vcloud_headers()
853 headers
['Content-Type'] = 'Content-Type text/xml'
854 response
= Http
.put(link
.get_href(),
855 data
=open(media_file_name
, 'rb'),
857 verify
=vca
.verify
, logger
=self
.logger
)
858 if response
.status_code
!= requests
.codes
.ok
:
860 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
864 # TODO fix this with aync block
867 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
869 # uploading VMDK file
870 # check status of OVF upload and upload remaining files.
871 response
= Http
.get(template
,
872 headers
=vca
.vcloud_session
.get_vcloud_headers(),
876 if response
.status_code
== requests
.codes
.ok
:
877 media
= mediaType
.parseString(response
.content
, True)
878 number_of_files
= len(media
.get_Files().get_File())
879 for index
in xrange(0, number_of_files
):
880 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
881 media
.get_Files().get_File()[index
].get_Link())
882 for link
in links_list
:
883 # we skip ovf since it already uploaded.
884 if 'ovf' in link
.get_href():
886 # The OVF file and VMDK must be in a same directory
887 head
, tail
= os
.path
.split(media_file_name
)
888 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
889 if not os
.path
.isfile(file_vmdk
):
891 statinfo
= os
.stat(file_vmdk
)
892 if statinfo
.st_size
== 0:
894 hrefvmdk
= link
.get_href()
897 print("Uploading file: {}".format(file_vmdk
))
899 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
901 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
903 bytes_transferred
= 0
904 f
= open(file_vmdk
, 'rb')
905 while bytes_transferred
< statinfo
.st_size
:
906 my_bytes
= f
.read(chunk_bytes
)
907 if len(my_bytes
) <= chunk_bytes
:
908 headers
= vca
.vcloud_session
.get_vcloud_headers()
909 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
910 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
911 headers
['Content-Length'] = str(len(my_bytes
))
912 response
= Http
.put(hrefvmdk
,
918 if response
.status_code
== requests
.codes
.ok
:
919 bytes_transferred
+= len(my_bytes
)
921 progress_bar
.update(bytes_transferred
)
924 'file upload failed with error: [%s] %s' % (response
.status_code
,
931 progress_bar
.finish()
935 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
936 format(catalog_name
, media_file_name
))
939 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
942 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
943 """Upload media file"""
944 # TODO add named parameters for readability
946 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
947 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
949 def validate_uuid4(self
, uuid_string
=None):
950 """ Method validate correct format of UUID.
952 Return: true if string represent valid uuid
955 val
= uuid
.UUID(uuid_string
, version
=4)
960 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
961 """ Method check catalog and return catalog ID in UUID format.
964 catalog_name: catalog name as string
965 catalogs: list of catalogs.
967 Return: catalogs uuid
970 for catalog
in catalogs
:
971 if catalog
.name
== catalog_name
:
972 catalog_id
= catalog
.get_id().split(":")
976 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
977 """ Method check catalog and return catalog name lookup done by catalog UUID.
980 catalog_name: catalog name as string
981 catalogs: list of catalogs.
983 Return: catalogs name or None
986 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
989 for catalog
in catalogs
:
990 catalog_id
= catalog
.get_id().split(":")[3]
991 if catalog_id
== catalog_uuid
:
995 def get_image_id_from_path(self
, path
=None, progress
=False):
996 """ Method upload OVF image to vCloud director.
998 Each OVF image represented as single catalog entry in vcloud director.
999 The method check for existing catalog entry. The check done by file name without file extension.
1001 if given catalog name already present method will respond with existing catalog uuid otherwise
1002 it will create new catalog entry and upload OVF file to newly created catalog.
1004 If method can't create catalog entry or upload a file it will throw exception.
1006 Method accept boolean flag progress that will output progress bar. It useful method
1007 for standalone upload use case. In case to test large file upload.
1010 path: - valid path to OVF file.
1011 progress - boolean progress bar show progress bar.
1013 Return: if image uploaded correct method will provide image catalog UUID.
1015 vca
= self
.connect()
1017 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1020 raise vimconn
.vimconnException("Image path can't be None.")
1022 if not os
.path
.isfile(path
):
1023 raise vimconn
.vimconnException("Can't read file. File not found.")
1025 if not os
.access(path
, os
.R_OK
):
1026 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1028 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1030 dirpath
, filename
= os
.path
.split(path
)
1031 flname
, file_extension
= os
.path
.splitext(path
)
1032 if file_extension
!= '.ovf':
1033 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1034 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1036 catalog_name
= os
.path
.splitext(filename
)[0]
1037 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1038 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1039 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1041 catalogs
= vca
.get_catalogs()
1042 if len(catalogs
) == 0:
1043 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1044 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1046 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1047 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1048 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1050 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1051 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1053 for catalog
in catalogs
:
1054 # search for existing catalog if we find same name we return ID
1055 # TODO optimize this
1056 if catalog
.name
== catalog_md5_name
:
1057 self
.logger
.debug("Found existing catalog entry for {} "
1058 "catalog id {}".format(catalog_name
,
1059 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1060 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1062 # if we didn't find existing catalog we create a new one and upload image.
1063 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1064 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1066 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1068 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1069 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1071 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1073 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1075 def get_vappid(self
, vdc
=None, vapp_name
=None):
1076 """ Method takes vdc object and vApp name and returns vapp uuid or None
1079 vdc: The VDC object.
1080 vapp_name: is application vappp name identifier
1083 The return vApp name otherwise None
1085 if vdc
is None or vapp_name
is None:
1087 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1089 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1090 vdc
.ResourceEntities
.ResourceEntity
)
1092 return refs
[0].href
.split("vapp")[1][1:]
1093 except Exception as e
:
1094 self
.logger
.exception(e
)
1098 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1099 """ Method Method returns True or False if vapp deployed in vCloud director
1102 vca: Connector to VCA
1103 vdc: The VDC object.
1104 vappid: vappid is application identifier
1107 The return True if vApp deployed
1112 refs
= filter(lambda ref
:
1113 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1114 vdc
.ResourceEntities
.ResourceEntity
)
1116 vappid
= ref
.href
.split("vapp")[1][1:]
1117 # find vapp with respected vapp uuid
1118 if vappid
== vapp_uuid
:
1120 except Exception as e
:
1121 self
.logger
.exception(e
)
1125 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1126 """Method returns vApp name from vCD and lookup done by vapp_id.
1129 vca: Connector to VCA
1130 vdc: The VDC object.
1131 vapp_uuid: vappid is application identifier
1134 The return vApp name otherwise None
1138 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1139 vdc
.ResourceEntities
.ResourceEntity
)
1141 # we care only about UUID the rest doesn't matter
1142 vappid
= ref
.href
.split("vapp")[1][1:]
1143 if vappid
== vapp_uuid
:
1144 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1146 tree
= XmlElementTree
.fromstring(response
.content
)
1147 return tree
.attrib
['name']
1148 except Exception as e
:
1149 self
.logger
.exception(e
)
1153 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1154 cloud_config
=None, disk_list
=None):
1155 """Adds a VM instance to VIM
1157 start: indicates if VM must start or boot in pause mode. Ignored
1158 image_id,flavor_id: image and flavor uuid
1159 net_list: list of interfaces, each one is a dictionary with:
1161 net_id: network uuid to connect
1162 vpci: virtual vcpi to assign
1163 model: interface model, virtio, e2000, ...
1165 use: 'data', 'bridge', 'mgmt'
1166 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1167 vim_id: filled/added by this function
1168 cloud_config: can be a text script to be passed directly to cloud-init,
1169 or an object to inject users and ssh keys with format:
1170 key-pairs: [] list of keys to install to the default user
1171 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1172 #TODO ip, security groups
1173 Returns >=0, the instance identifier
1177 self
.logger
.info("Creating new instance for entry {}".format(name
))
1178 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1179 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1180 vca
= self
.connect()
1182 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1184 #new vm name = vmname + tenant_id + uuid
1185 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1186 vmname_andid
= ''.join(new_vm_name
)
1188 # if vm already deployed we return existing uuid
1189 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1190 # if vapp_uuid is not None:
1193 # we check for presence of VDC, Catalog entry and Flavor.
1194 vdc
= vca
.get_vdc(self
.tenant_name
)
1196 raise vimconn
.vimconnNotFoundException(
1197 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1198 catalogs
= vca
.get_catalogs()
1199 if catalogs
is None:
1200 raise vimconn
.vimconnNotFoundException(
1201 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1203 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1204 if catalog_hash_name
:
1205 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1207 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1208 "(Failed retrieve catalog information {})".format(name
, image_id
))
1211 # Set vCPU and Memory based on flavor.
1216 pci_devices_info
= []
1217 if flavor_id
is not None:
1218 if flavor_id
not in flavorlist
:
1219 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1220 "Failed retrieve flavor information "
1221 "flavor id {}".format(name
, flavor_id
))
1224 flavor
= flavorlist
[flavor_id
]
1225 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1226 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1227 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1228 extended
= flavor
.get("extended", None)
1230 numas
=extended
.get("numas", None)
1233 for interface
in numa
.get("interfaces",() ):
1234 if interface
["dedicated"].strip()=="yes":
1235 pci_devices_info
.append(interface
)
1237 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1239 # image upload creates template name as catalog name space Template.
1240 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1245 # client must provide at least one entry in net_list if not we report error
1246 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1247 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1249 primary_netname
= None
1250 network_mode
= 'bridged'
1251 if net_list
is not None and len(net_list
) > 0:
1252 for net
in net_list
:
1253 if 'use' in net
and net
['use'] == 'mgmt':
1255 if primary_net
is None:
1256 primary_net
= net_list
[0]
1259 primary_net_id
= primary_net
['net_id']
1260 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1261 if 'name' in network_dict
:
1262 primary_netname
= network_dict
['name']
1265 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1267 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1269 # use: 'data', 'bridge', 'mgmt'
1270 # create vApp. Set vcpu and ram based on flavor id.
1271 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1272 self
.get_catalogbyid(image_id
, catalogs
),
1273 network_name
=None, # None while creating vapp
1274 network_mode
=network_mode
,
1275 vm_name
=vmname_andid
,
1276 vm_cpus
=vm_cpus
, # can be None if flavor is None
1277 vm_memory
=vm_memory
) # can be None if flavor is None
1279 if vapptask
is None or vapptask
is False:
1280 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1281 if type(vapptask
) is VappTask
:
1282 vca
.block_until_completed(vapptask
)
1284 # we should have now vapp in undeployed state.
1285 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1286 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1288 raise vimconn
.vimconnUnexpectedResponse(
1289 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1292 #Add PCI passthrough configrations
1293 PCI_devices_status
= False
1296 if len(pci_devices_info
) > 0:
1297 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1299 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1302 if PCI_devices_status
:
1303 self
.logger
.info("Added PCI devives {} to VM {}".format(
1308 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1314 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1315 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1317 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1319 # add NICs & connect to networks in netlist
1321 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1323 primary_nic_index
= 0
1324 for net
in net_list
:
1325 # openmano uses network id in UUID format.
1326 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1327 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1328 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1330 if 'net_id' not in net
:
1333 interface_net_id
= net
['net_id']
1334 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1335 interface_network_mode
= net
['use']
1337 if interface_network_mode
== 'mgmt':
1338 primary_nic_index
= nicIndex
1340 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1341 - DHCP (The IP address is obtained from a DHCP service.)
1342 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1343 - NONE (No IP addressing mode specified.)"""
1345 if primary_netname
is not None:
1346 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1348 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1349 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1350 if type(task
) is GenericTask
:
1351 vca
.block_until_completed(task
)
1352 # connect network to VM - with all DHCP by default
1353 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1354 task
= vapp
.connect_vms(nets
[0].name
,
1355 connection_index
=nicIndex
,
1356 connections_primary_index
=primary_nic_index
,
1357 ip_allocation_mode
='DHCP')
1358 if type(task
) is GenericTask
:
1359 vca
.block_until_completed(task
)
1362 # it might be a case if specific mandatory entry in dict is empty
1363 self
.logger
.debug("Key error {}".format(KeyError.message
))
1364 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1366 # deploy and power on vm
1367 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1368 deploytask
= vapp
.deploy(powerOn
=False)
1369 if type(deploytask
) is GenericTask
:
1370 vca
.block_until_completed(deploytask
)
1372 # If VM has PCI devices reserve memory for VM
1373 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1374 memReserve
= vm_obj
.config
.hardware
.memoryMB
1375 spec
= vim
.vm
.ConfigSpec()
1376 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1377 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1379 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1380 self
.logger
.info("Reserved memmoery {} MB for "\
1381 "VM VM status: {}".format(str(memReserve
),result
))
1383 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1384 str(memReserve
),str(vm_obj
)))
1386 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1387 poweron_task
= vapp
.poweron()
1388 if type(poweron_task
) is GenericTask
:
1389 vca
.block_until_completed(poweron_task
)
1391 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1394 while wait_time
<= MAX_WAIT_TIME
:
1395 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1396 if vapp
and vapp
.me
.deployed
:
1397 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1400 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1401 time
.sleep(INTERVAL_TIME
)
1403 wait_time
+=INTERVAL_TIME
1405 if vapp_uuid
is not None:
1408 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1412 ## based on current discussion
1416 # created: '2016-09-08T11:51:58'
1417 # description: simple-instance.linux1.1
1418 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1419 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1420 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1425 def get_vminstance(self
, vim_vm_uuid
=None):
1426 """Returns the VM instance information from VIM"""
1428 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1429 vca
= self
.connect()
1431 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1433 vdc
= vca
.get_vdc(self
.tenant_name
)
1435 raise vimconn
.vimconnConnectionException(
1436 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1438 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1439 if not vm_info_dict
:
1440 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1441 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1443 status_key
= vm_info_dict
['status']
1446 vm_dict
= {'created': vm_info_dict
['created'],
1447 'description': vm_info_dict
['name'],
1448 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1449 'hostId': vm_info_dict
['vmuuid'],
1451 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1453 if 'interfaces' in vm_info_dict
:
1454 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1456 vm_dict
['interfaces'] = []
1458 vm_dict
= {'created': '',
1460 'status': vcdStatusCode2manoFormat
[int(-1)],
1461 'hostId': vm_info_dict
['vmuuid'],
1462 'error_msg': "Inconsistency state",
1463 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1467 def delete_vminstance(self
, vm__vim_uuid
):
1468 """Method poweroff and remove VM instance from vcloud director network.
1471 vm__vim_uuid: VM UUID
1474 Returns the instance identifier
1477 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1478 vca
= self
.connect()
1480 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1482 vdc
= vca
.get_vdc(self
.tenant_name
)
1484 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1486 raise vimconn
.vimconnException(
1487 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1490 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1491 if vapp_name
is None:
1492 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1493 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1495 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1497 # Delete vApp and wait for status change if task executed and vApp is None.
1498 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1501 if vapp
.me
.deployed
:
1502 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1506 while wait_time
<= MAX_WAIT_TIME
:
1507 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1509 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1510 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1512 power_off_task
= vapp
.poweroff()
1513 if type(power_off_task
) is GenericTask
:
1514 result
= vca
.block_until_completed(power_off_task
)
1519 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1520 time
.sleep(INTERVAL_TIME
)
1522 wait_time
+=INTERVAL_TIME
1524 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1526 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1529 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1532 while wait_time
<= MAX_WAIT_TIME
:
1533 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1535 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1536 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1537 undeploy_task
= vapp
.undeploy(action
='powerOff')
1539 if type(undeploy_task
) is GenericTask
:
1540 result
= vca
.block_until_completed(undeploy_task
)
1545 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1546 time
.sleep(INTERVAL_TIME
)
1548 wait_time
+=INTERVAL_TIME
1551 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1554 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1555 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1557 if vapp
is not None:
1561 while wait_time
<= MAX_WAIT_TIME
:
1562 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1564 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1565 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1567 delete_task
= vapp
.delete()
1569 if type(delete_task
) is GenericTask
:
1570 vca
.block_until_completed(delete_task
)
1571 result
= vca
.block_until_completed(delete_task
)
1575 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1576 time
.sleep(INTERVAL_TIME
)
1578 wait_time
+=INTERVAL_TIME
1581 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1584 self
.logger
.debug(traceback
.format_exc())
1585 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1587 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1588 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1591 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1593 def refresh_vms_status(self
, vm_list
):
1594 """Get the status of the virtual machines and their interfaces/ports
1595 Params: the list of VM identifiers
1596 Returns a dictionary with:
1597 vm_id: #VIM id of this Virtual Machine
1598 status: #Mandatory. Text with one of:
1599 # DELETED (not found at vim)
1600 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1601 # OTHER (Vim reported other status not understood)
1602 # ERROR (VIM indicates an ERROR status)
1603 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1604 # CREATING (on building process), ERROR
1605 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1607 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1608 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1610 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1611 mac_address: #Text format XX:XX:XX:XX:XX:XX
1612 vim_net_id: #network id where this interface is connected
1613 vim_interface_id: #interface/port VIM id
1614 ip_address: #null, or text with IPv4, IPv6 address
1617 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1618 vca
= self
.connect()
1620 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1622 vdc
= vca
.get_vdc(self
.tenant_name
)
1624 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1627 for vmuuid
in vm_list
:
1628 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1629 if vmname
is not None:
1631 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1632 vm_info
= the_vapp
.get_vms_details()
1633 vm_status
= vm_info
[0]['status']
1634 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1635 vm_info
[0].update(vm_pci_details
)
1637 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1638 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1639 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1643 vm_app_networks
= the_vapp
.get_vms_network_info()
1644 for vapp_network
in vm_app_networks
:
1645 for vm_network
in vapp_network
:
1646 if vm_network
['name'] == vmname
:
1647 interface
= {"mac_address": vm_network
['mac'],
1648 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1649 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1650 'ip_address': vm_network
['ip']}
1651 # interface['vim_info'] = yaml.safe_dump(vm_network)
1652 vm_dict
["interfaces"].append(interface
)
1653 # add a vm to vm dict
1654 vms_dict
.setdefault(vmuuid
, vm_dict
)
1656 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1657 self
.logger
.debug(traceback
.format_exc())
1661 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1662 """Send and action over a VM instance from VIM
1663 Returns the vm_id if the action was successfully sent to the VIM"""
1665 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1666 if vm__vim_uuid
is None or action_dict
is None:
1667 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1669 vca
= self
.connect()
1671 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1673 vdc
= vca
.get_vdc(self
.tenant_name
)
1675 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1677 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1678 if vapp_name
is None:
1679 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1680 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1682 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1685 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1686 # TODO fix all status
1687 if "start" in action_dict
:
1688 if action_dict
["start"] == "rebuild":
1689 the_vapp
.deploy(powerOn
=True)
1691 vm_info
= the_vapp
.get_vms_details()
1692 vm_status
= vm_info
[0]['status']
1693 if vm_status
== "Suspended":
1695 elif vm_status
.status
== "Powered off":
1697 elif "pause" in action_dict
:
1700 elif "resume" in action_dict
:
1703 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1705 elif "forceOff" in action_dict
:
1707 elif "terminate" in action_dict
:
1709 # elif "createImage" in action_dict:
1710 # server.create_image()
1716 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1718 Get a console for the virtual machine
1720 vm_id: uuid of the VM
1721 console_type, can be:
1722 "novnc" (by default), "xvpvnc" for VNC types,
1723 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1724 Returns dict with the console parameters:
1725 protocol: ssh, ftp, http, https, ...
1726 server: usually ip address
1727 port: the http, ssh, ... port
1728 suffix: extra text, e.g. the http path and query string
1730 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1732 # NOT USED METHODS in current version
1734 def host_vim2gui(self
, host
, server_dict
):
1735 """Transform host dictionary from VIM format to GUI format,
1736 and append to the server_dict
1738 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1740 def get_hosts_info(self
):
1741 """Get the information of deployed hosts
1742 Returns the hosts content"""
1743 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1745 def get_hosts(self
, vim_tenant
):
1746 """Get the hosts and deployed instances
1747 Returns the hosts content"""
1748 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1750 def get_processor_rankings(self
):
1751 """Get the processor rankings in the VIM database"""
1752 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1754 def new_host(self
, host_data
):
1755 """Adds a new host to VIM"""
1756 '''Returns status code of the VIM response'''
1757 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1759 def new_external_port(self
, port_data
):
1760 """Adds a external port to VIM"""
1761 '''Returns the port identifier'''
1762 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1764 def new_external_network(self
, net_name
, net_type
):
1765 """Adds a external network to VIM (shared)"""
1766 '''Returns the network identifier'''
1767 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1769 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1770 """Connects a external port to a network"""
1771 '''Returns status code of the VIM response'''
1772 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1774 def new_vminstancefromJSON(self
, vm_data
):
1775 """Adds a VM instance to VIM"""
1776 '''Returns the instance identifier'''
1777 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1779 def get_network_name_by_id(self
, network_uuid
=None):
1780 """Method gets vcloud director network named based on supplied uuid.
1783 network_uuid: network_id
1786 The return network name.
1789 vca
= self
.connect()
1791 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1793 if not network_uuid
:
1797 org_dict
= self
.get_org(self
.org_uuid
)
1798 if 'networks' in org_dict
:
1799 org_network_dict
= org_dict
['networks']
1800 for net_uuid
in org_network_dict
:
1801 if net_uuid
== network_uuid
:
1802 return org_network_dict
[net_uuid
]
1804 self
.logger
.debug("Exception in get_network_name_by_id")
1805 self
.logger
.debug(traceback
.format_exc())
1809 def get_network_id_by_name(self
, network_name
=None):
1810 """Method gets vcloud director network uuid based on supplied name.
1813 network_name: network_name
1815 The return network uuid.
1816 network_uuid: network_id
1819 vca
= self
.connect()
1821 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1823 if not network_name
:
1824 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1828 org_dict
= self
.get_org(self
.org_uuid
)
1829 if org_dict
and 'networks' in org_dict
:
1830 org_network_dict
= org_dict
['networks']
1831 for net_uuid
,net_name
in org_network_dict
.iteritems():
1832 if net_name
== network_name
:
1835 except KeyError as exp
:
1836 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1840 def list_org_action(self
):
1842 Method leverages vCloud director and query for available organization for particular user
1845 vca - is active VCA connection.
1846 vdc_name - is a vdc name that will be used to query vms action
1849 The return XML respond
1852 vca
= self
.connect()
1854 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1856 url_list
= [vca
.host
, '/api/org']
1857 vm_list_rest_call
= ''.join(url_list
)
1859 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1860 response
= Http
.get(url
=vm_list_rest_call
,
1861 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1864 if response
.status_code
== requests
.codes
.ok
:
1865 return response
.content
1869 def get_org_action(self
, org_uuid
=None):
1871 Method leverages vCloud director and retrieve available object fdr organization.
1874 vca - is active VCA connection.
1875 vdc_name - is a vdc name that will be used to query vms action
1878 The return XML respond
1881 vca
= self
.connect()
1883 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1885 if org_uuid
is None:
1888 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1889 vm_list_rest_call
= ''.join(url_list
)
1891 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1892 response
= Http
.get(url
=vm_list_rest_call
,
1893 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1896 if response
.status_code
== requests
.codes
.ok
:
1897 return response
.content
1901 def get_org(self
, org_uuid
=None):
1903 Method retrieves available organization in vCloud Director
1906 org_uuid - is a organization uuid.
1909 The return dictionary with following key
1910 "network" - for network list under the org
1911 "catalogs" - for network list under the org
1912 "vdcs" - for vdc list under org
1916 vca
= self
.connect()
1918 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1920 if org_uuid
is None:
1923 content
= self
.get_org_action(org_uuid
=org_uuid
)
1928 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1929 for child
in vm_list_xmlroot
:
1930 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1931 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1932 org_dict
['vdcs'] = vdc_list
1933 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1934 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1935 org_dict
['networks'] = network_list
1936 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1937 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1938 org_dict
['catalogs'] = catalog_list
1944 def get_org_list(self
):
1946 Method retrieves available organization in vCloud Director
1949 vca - is active VCA connection.
1952 The return dictionary and key for each entry VDC UUID
1956 vca
= self
.connect()
1958 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1960 content
= self
.list_org_action()
1962 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1963 for vm_xml
in vm_list_xmlroot
:
1964 if vm_xml
.tag
.split("}")[1] == 'Org':
1965 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1966 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1972 def vms_view_action(self
, vdc_name
=None):
1973 """ Method leverages vCloud director vms query call
1976 vca - is active VCA connection.
1977 vdc_name - is a vdc name that will be used to query vms action
1980 The return XML respond
1982 vca
= self
.connect()
1983 if vdc_name
is None:
1986 url_list
= [vca
.host
, '/api/vms/query']
1987 vm_list_rest_call
= ''.join(url_list
)
1989 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1990 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1991 vca
.vcloud_session
.organization
.Link
)
1993 response
= Http
.get(url
=vm_list_rest_call
,
1994 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1997 if response
.status_code
== requests
.codes
.ok
:
1998 return response
.content
2002 def get_vapp_list(self
, vdc_name
=None):
2004 Method retrieves vApp list deployed vCloud director and returns a dictionary
2005 contains a list of all vapp deployed for queried VDC.
2006 The key for a dictionary is vApp UUID
2010 vca - is active VCA connection.
2011 vdc_name - is a vdc name that will be used to query vms action
2014 The return dictionary and key for each entry vapp UUID
2018 if vdc_name
is None:
2021 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2023 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2024 for vm_xml
in vm_list_xmlroot
:
2025 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2026 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2027 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2028 if 'vappTemplate-' in rawuuid
[0]:
2029 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2030 # vm and use raw UUID as key
2031 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2037 def get_vm_list(self
, vdc_name
=None):
2039 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2040 contains a list of all VM's deployed for queried VDC.
2041 The key for a dictionary is VM UUID
2045 vca - is active VCA connection.
2046 vdc_name - is a vdc name that will be used to query vms action
2049 The return dictionary and key for each entry vapp UUID
2053 if vdc_name
is None:
2056 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2058 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2059 for vm_xml
in vm_list_xmlroot
:
2060 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2061 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2062 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2063 if 'vm-' in rawuuid
[0]:
2064 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2065 # vm and use raw UUID as key
2066 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2072 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2074 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2075 contains a list of all VM's deployed for queried VDC.
2076 The key for a dictionary is VM UUID
2080 vca - is active VCA connection.
2081 vdc_name - is a vdc name that will be used to query vms action
2084 The return dictionary and key for each entry vapp UUID
2087 vca
= self
.connect()
2089 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2091 if vdc_name
is None:
2094 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2096 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2097 for vm_xml
in vm_list_xmlroot
:
2098 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2099 # lookup done by UUID
2101 if vapp_name
in vm_xml
.attrib
['container']:
2102 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2103 if 'vm-' in rawuuid
[0]:
2104 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2106 # lookup done by Name
2108 if vapp_name
in vm_xml
.attrib
['name']:
2109 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2110 if 'vm-' in rawuuid
[0]:
2111 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2118 def get_network_action(self
, network_uuid
=None):
2120 Method leverages vCloud director and query network based on network uuid
2123 vca - is active VCA connection.
2124 network_uuid - is a network uuid
2127 The return XML respond
2130 vca
= self
.connect()
2132 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2134 if network_uuid
is None:
2137 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2138 vm_list_rest_call
= ''.join(url_list
)
2140 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2141 response
= Http
.get(url
=vm_list_rest_call
,
2142 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2145 if response
.status_code
== requests
.codes
.ok
:
2146 return response
.content
2150 def get_vcd_network(self
, network_uuid
=None):
2152 Method retrieves available network from vCloud Director
2155 network_uuid - is VCD network UUID
2157 Each element serialized as key : value pair
2159 Following keys available for access. network_configuration['Gateway'}
2163 <IsInherited>true</IsInherited>
2164 <Gateway>172.16.252.100</Gateway>
2165 <Netmask>255.255.255.0</Netmask>
2166 <Dns1>172.16.254.201</Dns1>
2167 <Dns2>172.16.254.202</Dns2>
2168 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2169 <IsEnabled>true</IsEnabled>
2172 <StartAddress>172.16.252.1</StartAddress>
2173 <EndAddress>172.16.252.99</EndAddress>
2178 <FenceMode>bridged</FenceMode>
2181 The return dictionary and key for each entry vapp UUID
2184 network_configuration
= {}
2185 if network_uuid
is None:
2188 content
= self
.get_network_action(network_uuid
=network_uuid
)
2190 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2192 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2193 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2194 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2196 for child
in vm_list_xmlroot
:
2197 if child
.tag
.split("}")[1] == 'IsShared':
2198 network_configuration
['isShared'] = child
.text
.strip()
2199 if child
.tag
.split("}")[1] == 'Configuration':
2200 for configuration
in child
.iter():
2201 tagKey
= configuration
.tag
.split("}")[1].strip()
2203 network_configuration
[tagKey
] = configuration
.text
.strip()
2204 return network_configuration
2208 return network_configuration
2210 def delete_network_action(self
, network_uuid
=None):
2212 Method delete given network from vCloud director
2215 network_uuid - is a network uuid that client wish to delete
2218 The return None or XML respond or false
2221 vca
= self
.connect_as_admin()
2223 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2224 if network_uuid
is None:
2227 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2228 vm_list_rest_call
= ''.join(url_list
)
2230 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2231 response
= Http
.delete(url
=vm_list_rest_call
,
2232 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2236 if response
.status_code
== 202:
2241 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2242 ip_profile
=None, isshared
='true'):
2244 Method create network in vCloud director
2247 network_name - is network name to be created.
2248 net_type - can be 'bridge','data','ptp','mgmt'.
2249 ip_profile is a dict containing the IP parameters of the network
2250 isshared - is a boolean
2251 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2252 It optional attribute. by default if no parent network indicate the first available will be used.
2255 The return network uuid or return None
2258 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2259 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2260 ip_profile
=ip_profile
,
2262 parent_network_uuid
=parent_network_uuid
,
2265 self
.logger
.debug("Failed create network {}.".format(network_name
))
2269 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2270 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2271 if len(vcd_uuid
) == 4:
2272 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2275 self
.logger
.debug("Failed create network {}".format(network_name
))
2278 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2279 ip_profile
=None, isshared
='true'):
2281 Method create network in vCloud director
2284 network_name - is network name to be created.
2285 net_type - can be 'bridge','data','ptp','mgmt'.
2286 ip_profile is a dict containing the IP parameters of the network
2287 isshared - is a boolean
2288 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2289 It optional attribute. by default if no parent network indicate the first available will be used.
2292 The return network uuid or return None
2295 vca
= self
.connect_as_admin()
2297 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2298 if network_name
is None:
2301 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2302 vm_list_rest_call
= ''.join(url_list
)
2303 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2304 response
= Http
.get(url
=vm_list_rest_call
,
2305 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2309 provider_network
= None
2310 available_networks
= None
2311 add_vdc_rest_url
= None
2313 if response
.status_code
!= requests
.codes
.ok
:
2314 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2315 response
.status_code
))
2319 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2320 for child
in vm_list_xmlroot
:
2321 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2322 provider_network
= child
.attrib
.get('href')
2323 # application/vnd.vmware.admin.providervdc+xml
2324 if child
.tag
.split("}")[1] == 'Link':
2325 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2326 and child
.attrib
.get('rel') == 'add':
2327 add_vdc_rest_url
= child
.attrib
.get('href')
2329 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2330 self
.logger
.debug("Respond body {}".format(response
.content
))
2333 # find pvdc provided available network
2334 response
= Http
.get(url
=provider_network
,
2335 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2338 if response
.status_code
!= requests
.codes
.ok
:
2339 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2340 response
.status_code
))
2343 # available_networks.split("/")[-1]
2345 if parent_network_uuid
is None:
2347 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2348 for child
in vm_list_xmlroot
.iter():
2349 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2350 for networks
in child
.iter():
2351 # application/vnd.vmware.admin.network+xml
2352 if networks
.attrib
.get('href') is not None:
2353 available_networks
= networks
.attrib
.get('href')
2358 #Configure IP profile of the network
2359 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2361 gateway_address
=ip_profile
['gateway_address']
2362 dhcp_count
=int(ip_profile
['dhcp_count'])
2363 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2365 if ip_profile
['dhcp_enabled']==True:
2368 dhcp_enabled
='false'
2369 dhcp_start_address
=ip_profile
['dhcp_start_address']
2371 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2372 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2373 end_ip_int
+= dhcp_count
- 1
2374 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2376 ip_version
=ip_profile
['ip_version']
2377 dns_address
=ip_profile
['dns_address']
2379 # either use client provided UUID or search for a first available
2380 # if both are not defined we return none
2381 if parent_network_uuid
is not None:
2382 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2383 add_vdc_rest_url
= ''.join(url_list
)
2386 fence_mode
="isolated"
2388 is_inherited
='false'
2389 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2390 <Description>Openmano created</Description>
2394 <IsInherited>{1:s}</IsInherited>
2395 <Gateway>{2:s}</Gateway>
2396 <Netmask>{3:s}</Netmask>
2398 <IsEnabled>{5:s}</IsEnabled>
2401 <StartAddress>{6:s}</StartAddress>
2402 <EndAddress>{7:s}</EndAddress>
2407 <FenceMode>{8:s}</FenceMode>
2409 <IsShared>{9:s}</IsShared>
2410 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2411 subnet_address
, dns_address
, dhcp_enabled
,
2412 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2415 fence_mode
="bridged"
2416 is_inherited
='false'
2417 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2418 <Description>Openmano created</Description>
2422 <IsInherited>{1:s}</IsInherited>
2423 <Gateway>{2:s}</Gateway>
2424 <Netmask>{3:s}</Netmask>
2426 <IsEnabled>{5:s}</IsEnabled>
2429 <StartAddress>{6:s}</StartAddress>
2430 <EndAddress>{7:s}</EndAddress>
2435 <ParentNetwork href="{8:s}"/>
2436 <FenceMode>{9:s}</FenceMode>
2438 <IsShared>{10:s}</IsShared>
2439 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2440 subnet_address
, dns_address
, dhcp_enabled
,
2441 dhcp_start_address
, dhcp_end_address
, available_networks
,
2442 fence_mode
, isshared
)
2444 headers
= vca
.vcloud_session
.get_vcloud_headers()
2445 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2447 response
= Http
.post(url
=add_vdc_rest_url
,
2453 if response
.status_code
!= 201:
2454 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2455 .format(response
.status_code
))
2457 network
= networkType
.parseString(response
.content
, True)
2458 create_nw_task
= network
.get_Tasks().get_Task()[0]
2460 # if we all ok we respond with content after network creation completes
2461 # otherwise by default return None
2462 if create_nw_task
is not None:
2463 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2464 status
= vca
.block_until_completed(create_nw_task
)
2466 return response
.content
2468 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2469 .format(response
.content
))
2470 except Exception as exp
:
2471 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2475 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2477 Method sets convert CIDR netmask address to normal IP format
2479 cidr_ip : CIDR IP address
2481 netmask : Converted netmask
2483 if cidr_ip
is not None:
2485 network
, net_bits
= cidr_ip
.split('/')
2486 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2492 def get_provider_rest(self
, vca
=None):
2494 Method gets provider vdc view from vcloud director
2497 network_name - is network name to be created.
2498 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2499 It optional attribute. by default if no parent network indicate the first available will be used.
2502 The return xml content of respond or None
2505 url_list
= [vca
.host
, '/api/admin']
2506 response
= Http
.get(url
=''.join(url_list
),
2507 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2511 if response
.status_code
== requests
.codes
.ok
:
2512 return response
.content
2515 def create_vdc(self
, vdc_name
=None):
2519 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2520 if xml_content
is not None:
2522 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2523 for child
in task_resp_xmlroot
:
2524 if child
.tag
.split("}")[1] == 'Owner':
2525 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2526 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2529 self
.logger
.debug("Respond body {}".format(xml_content
))
2533 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2535 Method create vdc in vCloud director based on VDC template.
2536 it uses pre-defined template that must be named openmano
2539 vdc_name - name of a new vdc.
2542 The return xml content of respond or None
2545 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2546 vca
= self
.connect()
2548 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2549 if vdc_name
is None:
2552 url_list
= [vca
.host
, '/api/vdcTemplates']
2553 vm_list_rest_call
= ''.join(url_list
)
2554 response
= Http
.get(url
=vm_list_rest_call
,
2555 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2559 # container url to a template
2560 vdc_template_ref
= None
2562 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2563 for child
in vm_list_xmlroot
:
2564 # application/vnd.vmware.admin.providervdc+xml
2565 # we need find a template from witch we instantiate VDC
2566 if child
.tag
.split("}")[1] == 'VdcTemplate':
2567 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2568 'name') == 'openmano':
2569 vdc_template_ref
= child
.attrib
.get('href')
2571 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2572 self
.logger
.debug("Respond body {}".format(response
.content
))
2575 # if we didn't found required pre defined template we return None
2576 if vdc_template_ref
is None:
2581 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2582 vm_list_rest_call
= ''.join(url_list
)
2583 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2584 <Source href="{1:s}"></Source>
2585 <Description>opnemano</Description>
2586 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2587 headers
= vca
.vcloud_session
.get_vcloud_headers()
2588 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2589 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2591 # if we all ok we respond with content otherwise by default None
2592 if response
.status_code
>= 200 and response
.status_code
< 300:
2593 return response
.content
2596 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2597 self
.logger
.debug("Respond body {}".format(response
.content
))
2601 def create_vdc_rest(self
, vdc_name
=None):
2603 Method create network in vCloud director
2606 network_name - is network name to be created.
2607 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2608 It optional attribute. by default if no parent network indicate the first available will be used.
2611 The return network uuid or return None
2614 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2616 vca
= self
.connect_as_admin()
2618 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2619 if vdc_name
is None:
2622 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2623 vm_list_rest_call
= ''.join(url_list
)
2624 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2625 response
= Http
.get(url
=vm_list_rest_call
,
2626 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2630 provider_vdc_ref
= None
2631 add_vdc_rest_url
= None
2632 available_networks
= None
2634 if response
.status_code
!= requests
.codes
.ok
:
2635 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2636 response
.status_code
))
2640 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2641 for child
in vm_list_xmlroot
:
2642 # application/vnd.vmware.admin.providervdc+xml
2643 if child
.tag
.split("}")[1] == 'Link':
2644 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2645 and child
.attrib
.get('rel') == 'add':
2646 add_vdc_rest_url
= child
.attrib
.get('href')
2648 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2649 self
.logger
.debug("Respond body {}".format(response
.content
))
2652 response
= self
.get_provider_rest(vca
=vca
)
2654 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2655 for child
in vm_list_xmlroot
:
2656 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2657 for sub_child
in child
:
2658 provider_vdc_ref
= sub_child
.attrib
.get('href')
2660 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2661 self
.logger
.debug("Respond body {}".format(response
))
2664 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2665 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2666 <AllocationModel>ReservationPool</AllocationModel>
2667 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2668 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2669 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2670 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2671 <ProviderVdcReference
2672 name="Main Provider"
2674 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2678 headers
= vca
.vcloud_session
.get_vcloud_headers()
2679 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2680 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2683 # if we all ok we respond with content otherwise by default None
2684 if response
.status_code
== 201:
2685 return response
.content
2688 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2690 Method retrieve vapp detail from vCloud director
2693 vapp_uuid - is vapp identifier.
2696 The return network uuid or return None
2702 if need_admin_access
:
2703 vca
= self
.connect_as_admin()
2705 vca
= self
.connect()
2708 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2709 if vapp_uuid
is None:
2712 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2713 get_vapp_restcall
= ''.join(url_list
)
2715 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2716 response
= Http
.get(url
=get_vapp_restcall
,
2717 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2721 if response
.status_code
!= requests
.codes
.ok
:
2722 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2723 response
.status_code
))
2724 return parsed_respond
2727 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2728 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2730 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2731 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2732 'vmw': 'http://www.vmware.com/schema/ovf',
2733 'vm': 'http://www.vmware.com/vcloud/v1.5',
2734 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2735 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2736 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2739 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2740 if created_section
is not None:
2741 parsed_respond
['created'] = created_section
.text
2743 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2744 if network_section
is not None and 'networkName' in network_section
.attrib
:
2745 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2747 ipscopes_section
= \
2748 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2750 if ipscopes_section
is not None:
2751 for ipscope
in ipscopes_section
:
2752 for scope
in ipscope
:
2753 tag_key
= scope
.tag
.split("}")[1]
2754 if tag_key
== 'IpRanges':
2755 ip_ranges
= scope
.getchildren()
2756 for ipblock
in ip_ranges
:
2757 for block
in ipblock
:
2758 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2760 parsed_respond
[tag_key
] = scope
.text
2762 # parse children section for other attrib
2763 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2764 if children_section
is not None:
2765 parsed_respond
['name'] = children_section
.attrib
['name']
2766 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2767 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2768 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2769 parsed_respond
['status'] = children_section
.attrib
['status']
2770 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2771 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2773 for adapters
in network_adapter
:
2774 adapter_key
= adapters
.tag
.split("}")[1]
2775 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2776 parsed_respond
['primarynetwork'] = adapters
.text
2777 if adapter_key
== 'NetworkConnection':
2779 if 'network' in adapters
.attrib
:
2780 vnic
['network'] = adapters
.attrib
['network']
2781 for adapter
in adapters
:
2782 setting_key
= adapter
.tag
.split("}")[1]
2783 vnic
[setting_key
] = adapter
.text
2784 nic_list
.append(vnic
)
2786 for link
in children_section
:
2787 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2788 if link
.attrib
['rel'] == 'screen:acquireTicket':
2789 parsed_respond
['acquireTicket'] = link
.attrib
2790 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2791 parsed_respond
['acquireMksTicket'] = link
.attrib
2793 parsed_respond
['interfaces'] = nic_list
2794 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2795 if vCloud_extension_section
is not None:
2796 vm_vcenter_info
= {}
2797 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2798 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2799 if vmext
is not None:
2800 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2801 vm_vcenter_info
["vim_server_href"] = vmext
.find('vmext:VimServerRef', namespaces
).attrib
['href']
2802 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2804 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2805 vm_virtual_hardware_info
= {}
2806 if virtual_hardware_section
is not None:
2807 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2808 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2809 disk_size
= item
.find("rasd:HostResource" ,namespaces
2810 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2812 vm_virtual_hardware_info
["disk_size"]= disk_size
2815 for link
in virtual_hardware_section
:
2816 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2817 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2818 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2821 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2822 except Exception as exp
:
2823 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2824 return parsed_respond
2826 def acuire_console(self
, vm_uuid
=None):
2828 vca
= self
.connect()
2830 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2834 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2835 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2836 console_dict
= vm_dict
['acquireTicket']
2837 console_rest_call
= console_dict
['href']
2839 response
= Http
.post(url
=console_rest_call
,
2840 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2844 if response
.status_code
== requests
.codes
.ok
:
2845 return response
.content
2849 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2851 Method retrieve vm disk details
2854 vapp_uuid - is vapp identifier.
2855 flavor_disk - disk size as specified in VNFD (flavor)
2858 The return network uuid or return None
2862 #Flavor disk is in GB convert it into MB
2863 flavor_disk
= int(flavor_disk
) * 1024
2864 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2866 vm_name
= vm_details
["name"]
2867 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2869 if vm_details
and "vm_virtual_hardware" in vm_details
:
2870 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2871 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2873 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2875 if flavor_disk
> vm_disk
:
2876 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2877 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2878 vm_disk
, flavor_disk
))
2881 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
2884 except Exception as exp
:
2885 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
2888 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
2890 Method retrieve modify vm disk size
2893 disk_href - vCD API URL to GET and PUT disk data
2894 disk_size - disk size as specified in VNFD (flavor)
2897 The return network uuid or return None
2899 vca
= self
.connect()
2901 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2902 if disk_href
is None or disk_size
is None:
2905 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2906 response
= Http
.get(url
=disk_href
,
2907 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2911 if response
.status_code
!= requests
.codes
.ok
:
2912 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
2913 response
.status_code
))
2916 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
2917 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
2918 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2920 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
2921 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2922 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
2923 if disk_item
is not None:
2924 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
2927 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
2928 xml_declaration
=True)
2930 #Send PUT request to modify disk size
2931 headers
= vca
.vcloud_session
.get_vcloud_headers()
2932 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2934 response
= Http
.put(url
=disk_href
,
2937 verify
=vca
.verify
, logger
=self
.logger
)
2939 if response
.status_code
!= 202:
2940 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
2941 response
.status_code
))
2943 modify_disk_task
= taskType
.parseString(response
.content
, True)
2944 if type(modify_disk_task
) is GenericTask
:
2945 status
= vca
.block_until_completed(modify_disk_task
)
2950 except Exception as exp
:
2951 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
2954 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
2956 Method to attach pci devices to VM
2959 vapp_uuid - uuid of vApp/VM
2960 pci_devices - pci devices infromation as specified in VNFD (flavor)
2963 The status of add pci device task , vm object and
2964 vcenter_conect object
2967 vcenter_conect
= None
2968 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
2969 #Assuming password of vCenter user is same as password of vCloud user
2970 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vapp_uuid
)
2971 self
.logger
.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
2972 "vm_vcenter_port{}".format(
2973 vm_moref_id
, vm_vcenter_host
,
2974 vm_vcenter_username
, vm_vcenter_port
))
2975 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
2977 if hasattr(ssl
, '_create_unverified_context'):
2978 context
= ssl
._create
_unverified
_context
()
2980 no_of_pci_devices
= len(pci_devices
)
2981 if no_of_pci_devices
> 0:
2982 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
2983 pwd
=self
.passwd
, port
=int(vm_vcenter_port
) ,
2985 atexit
.register(Disconnect
, vcenter_conect
)
2986 content
= vcenter_conect
.RetrieveContent()
2988 #Get VM and its host
2989 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
2990 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
2991 if host_obj
and vm_obj
:
2992 #get PCI devies from host on which vapp is currently installed
2993 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
2995 if avilable_pci_devices
is None:
2996 #find other hosts with active pci devices
2997 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3002 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3003 #Migrate vm to the host where PCI devices are availble
3004 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3005 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3006 if task
is not None:
3007 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3008 self
.logger
.info("Migrate VM status: {}".format(result
))
3009 host_obj
= new_host_obj
3011 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3012 raise vimconn
.vimconnNotFoundException(
3013 "Fail to migrate VM : {} to host {}".format(
3018 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3019 #Add PCI devices one by one
3020 for pci_device
in avilable_pci_devices
:
3021 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3023 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3025 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3027 self
.logger
.info("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3028 return True, vm_obj
, vcenter_conect
3030 self
.logger
.error("Currently there is no host with"\
3031 " {} number of avaialble PCI devices required for VM {}".format(
3035 raise vimconn
.vimconnNotFoundException(
3036 "Currently there is no host with {} "\
3037 "number of avaialble PCI devices required for VM {}".format(
3041 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3043 except vmodl
.MethodFault
as error
:
3044 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3045 return None, vm_obj
, vcenter_conect
3047 def get_vm_obj(self
, content
, mob_id
):
3049 Method to get the vsphere VM object associated with a given morf ID
3051 vapp_uuid - uuid of vApp/VM
3052 content - vCenter content object
3053 mob_id - mob_id of VM
3061 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3062 [vim
.VirtualMachine
], True
3064 for vm
in container
.view
:
3065 mobID
= vm
._GetMoId
()
3068 host_obj
= vm_obj
.runtime
.host
3070 except Exception as exp
:
3071 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3072 return host_obj
, vm_obj
3074 def get_pci_devices(self
, host
, need_devices
):
3076 Method to get the details of pci devices on given host
3078 host - vSphere host object
3079 need_devices - number of pci devices needed on host
3082 array of pci devices
3086 used_devices_ids
= []
3090 pciPassthruInfo
= host
.config
.pciPassthruInfo
3091 pciDevies
= host
.hardware
.pciDevice
3093 for pci_status
in pciPassthruInfo
:
3094 if pci_status
.passthruActive
:
3095 for device
in pciDevies
:
3096 if device
.id == pci_status
.id:
3097 all_device_ids
.append(device
.id)
3098 all_devices
.append(device
)
3100 #check if devices are in use
3101 avalible_devices
= all_devices
3103 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3104 vm_devices
= vm
.config
.hardware
.device
3105 for device
in vm_devices
:
3106 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3107 if device
.backing
.id in all_device_ids
:
3108 for use_device
in avalible_devices
:
3109 if use_device
.id == device
.backing
.id:
3110 avalible_devices
.remove(use_device
)
3111 used_devices_ids
.append(device
.backing
.id)
3112 self
.logger
.debug("Device {} from devices {}"\
3113 "is in use".format(device
.backing
.id,
3116 if len(avalible_devices
) < need_devices
:
3117 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3119 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3123 required_devices
= avalible_devices
[:need_devices
]
3124 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3125 len(avalible_devices
),
3128 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3130 return required_devices
3132 except Exception as exp
:
3133 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3137 def get_host_and_PCIdevices(self
, content
, need_devices
):
3139 Method to get the details of pci devices infromation on all hosts
3142 content - vSphere host object
3143 need_devices - number of pci devices needed on host
3146 array of pci devices and host object
3149 pci_device_objs
= None
3152 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3153 [vim
.HostSystem
], True)
3154 for host
in container
.view
:
3155 devices
= self
.get_pci_devices(host
, need_devices
)
3158 pci_device_objs
= devices
3160 except Exception as exp
:
3161 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3163 return host_obj
,pci_device_objs
3165 def relocate_vm(self
, dest_host
, vm
) :
3167 Method to get the relocate VM to new host
3170 dest_host - vSphere host object
3171 vm - vSphere VM object
3178 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3179 task
= vm
.Relocate(relocate_spec
)
3180 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3181 except Exception as exp
:
3182 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3183 dest_host
, vm
, exp
))
3186 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3188 Waits and provides updates on a vSphere task
3190 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3193 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3194 if task
.info
.result
is not None and not hideResult
:
3195 self
.logger
.info('{} completed successfully, result: {}'.format(
3199 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3201 self
.logger
.error('{} did not complete successfully: {} '.format(
3206 return task
.info
.result
3208 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3210 Method to add pci device in given VM
3213 host_object - vSphere host object
3214 vm_object - vSphere VM object
3215 host_pci_dev - host_pci_dev must be one of the devices from the
3216 host_object.hardware.pciDevice list
3217 which is configured as a PCI passthrough device
3223 if vm_object
and host_object
and host_pci_dev
:
3225 #Add PCI device to VM
3226 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3227 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3229 if host_pci_dev
.id not in systemid_by_pciid
:
3230 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3233 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3234 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3236 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3237 vendorId
=host_pci_dev
.vendorId
,
3238 deviceName
=host_pci_dev
.deviceName
)
3240 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3242 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3243 new_device_config
.operation
= "add"
3244 vmConfigSpec
= vim
.vm
.ConfigSpec()
3245 vmConfigSpec
.deviceChange
= [new_device_config
]
3247 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3248 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3249 host_pci_dev
, vm_object
, host_object
)
3251 except Exception as exp
:
3252 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3258 def get_vcenter_info_rest(self
, vapp_uuid
):
3260 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3261 Method to get details of vCenter
3264 vapp_uuid - uuid of vApp or VM
3267 Moref Id of VM and deails of vCenter
3271 vm_vcenter_username
= None
3272 vm_vcenter_port
= None
3274 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3275 if vm_details
and "vm_vcenter_info" in vm_details
:
3276 vm_moref_id
= vm_details
["vm_vcenter_info"]["vm_moref_id"]
3277 vim_server_href
= vm_details
["vm_vcenter_info"]["vim_server_href"]
3280 vca
= self
.connect_as_admin()
3282 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3283 if vim_server_href
is None:
3284 self
.logger
.error("No url to get vcenter details")
3286 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3287 response
= Http
.get(url
=vim_server_href
,
3288 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3292 if response
.status_code
!= requests
.codes
.ok
:
3293 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href
,
3294 response
.status_code
))
3296 namespaces
={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3297 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3299 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3300 vm_vcenter_username
= xmlroot_respond
.find('vmext:Username', namespaces
).text
3301 vcenter_url
= xmlroot_respond
.find('vmext:Url', namespaces
).text
3302 vm_vcenter_port
= vcenter_url
.split(":")[2]
3303 vm_vcenter
= vcenter_url
.split(":")[1].split("//")[1]
3305 except Exception as exp
:
3306 self
.logger
.info("Error occurred calling rest api for vcenter information {}".format(exp
))
3308 return vm_moref_id
, vm_vcenter
, vm_vcenter_username
, vm_vcenter_port
3311 def get_vm_pci_details(self
, vmuuid
):
3313 Method to get VM PCI device details from vCenter
3316 vm_obj - vSphere VM object
3319 dict of PCI devives attached to VM
3322 vm_pci_devices_info
= {}
3324 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3325 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3327 if hasattr(ssl
, '_create_unverified_context'):
3328 context
= ssl
._create
_unverified
_context
()
3329 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3330 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3332 atexit
.register(Disconnect
, vcenter_conect
)
3333 content
= vcenter_conect
.RetrieveContent()
3335 #Get VM and its host
3336 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3337 for device
in vm_obj
.config
.hardware
.device
:
3338 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3339 device_details
={'devide_id':device
.backing
.id,
3340 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
3342 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3343 except Exception as exp
:
3344 self
.logger
.info("Error occurred while getting PCI devices infromationn"\
3345 " for VM {} : {}".format(vm_obj
,exp
))
3346 return vm_pci_devices_info