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_image_list(self
, filter_dict
={}):
1076 '''Obtain tenant images from VIM
1080 checksum: image checksum
1081 location: image path
1082 Returns the image list of dictionaries:
1083 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1086 vca
= self
.connect()
1088 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1091 catalogs
= vca
.get_catalogs()
1092 if len(catalogs
) == 0:
1095 for catalog
in catalogs
:
1096 catalog_uuid
= catalog
.get_id().split(":")[3]
1099 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1101 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1103 filtered_dict
["name"] = name
1104 filtered_dict
["id"] = catalog_uuid
1105 image_list
.append(filtered_dict
)
1107 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1109 except Exception as exp
:
1110 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1112 def get_vappid(self
, vdc
=None, vapp_name
=None):
1113 """ Method takes vdc object and vApp name and returns vapp uuid or None
1116 vdc: The VDC object.
1117 vapp_name: is application vappp name identifier
1120 The return vApp name otherwise None
1122 if vdc
is None or vapp_name
is None:
1124 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1126 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1127 vdc
.ResourceEntities
.ResourceEntity
)
1129 return refs
[0].href
.split("vapp")[1][1:]
1130 except Exception as e
:
1131 self
.logger
.exception(e
)
1135 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1136 """ Method Method returns True or False if vapp deployed in vCloud director
1139 vca: Connector to VCA
1140 vdc: The VDC object.
1141 vappid: vappid is application identifier
1144 The return True if vApp deployed
1149 refs
= filter(lambda ref
:
1150 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1151 vdc
.ResourceEntities
.ResourceEntity
)
1153 vappid
= ref
.href
.split("vapp")[1][1:]
1154 # find vapp with respected vapp uuid
1155 if vappid
== vapp_uuid
:
1157 except Exception as e
:
1158 self
.logger
.exception(e
)
1162 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1163 """Method returns vApp name from vCD and lookup done by vapp_id.
1166 vca: Connector to VCA
1167 vdc: The VDC object.
1168 vapp_uuid: vappid is application identifier
1171 The return vApp name otherwise None
1175 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1176 vdc
.ResourceEntities
.ResourceEntity
)
1178 # we care only about UUID the rest doesn't matter
1179 vappid
= ref
.href
.split("vapp")[1][1:]
1180 if vappid
== vapp_uuid
:
1181 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1183 tree
= XmlElementTree
.fromstring(response
.content
)
1184 return tree
.attrib
['name']
1185 except Exception as e
:
1186 self
.logger
.exception(e
)
1190 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1191 cloud_config
=None, disk_list
=None):
1192 """Adds a VM instance to VIM
1194 start: indicates if VM must start or boot in pause mode. Ignored
1195 image_id,flavor_id: image and flavor uuid
1196 net_list: list of interfaces, each one is a dictionary with:
1198 net_id: network uuid to connect
1199 vpci: virtual vcpi to assign
1200 model: interface model, virtio, e2000, ...
1202 use: 'data', 'bridge', 'mgmt'
1203 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1204 vim_id: filled/added by this function
1205 cloud_config: can be a text script to be passed directly to cloud-init,
1206 or an object to inject users and ssh keys with format:
1207 key-pairs: [] list of keys to install to the default user
1208 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1209 #TODO ip, security groups
1210 Returns >=0, the instance identifier
1214 self
.logger
.info("Creating new instance for entry {}".format(name
))
1215 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1216 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1217 vca
= self
.connect()
1219 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1221 #new vm name = vmname + tenant_id + uuid
1222 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1223 vmname_andid
= ''.join(new_vm_name
)
1225 # if vm already deployed we return existing uuid
1226 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1227 # if vapp_uuid is not None:
1230 # we check for presence of VDC, Catalog entry and Flavor.
1231 vdc
= vca
.get_vdc(self
.tenant_name
)
1233 raise vimconn
.vimconnNotFoundException(
1234 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1235 catalogs
= vca
.get_catalogs()
1236 if catalogs
is None:
1237 raise vimconn
.vimconnNotFoundException(
1238 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1240 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1241 if catalog_hash_name
:
1242 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1244 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1245 "(Failed retrieve catalog information {})".format(name
, image_id
))
1248 # Set vCPU and Memory based on flavor.
1253 pci_devices_info
= []
1254 if flavor_id
is not None:
1255 if flavor_id
not in flavorlist
:
1256 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1257 "Failed retrieve flavor information "
1258 "flavor id {}".format(name
, flavor_id
))
1261 flavor
= flavorlist
[flavor_id
]
1262 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1263 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1264 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1265 extended
= flavor
.get("extended", None)
1267 numas
=extended
.get("numas", None)
1270 for interface
in numa
.get("interfaces",() ):
1271 if interface
["dedicated"].strip()=="yes":
1272 pci_devices_info
.append(interface
)
1274 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1276 # image upload creates template name as catalog name space Template.
1277 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1282 # client must provide at least one entry in net_list if not we report error
1283 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1284 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1286 primary_netname
= None
1287 network_mode
= 'bridged'
1288 if net_list
is not None and len(net_list
) > 0:
1289 for net
in net_list
:
1290 if 'use' in net
and net
['use'] == 'mgmt':
1292 if primary_net
is None:
1293 primary_net
= net_list
[0]
1296 primary_net_id
= primary_net
['net_id']
1297 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1298 if 'name' in network_dict
:
1299 primary_netname
= network_dict
['name']
1302 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1304 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1306 # use: 'data', 'bridge', 'mgmt'
1307 # create vApp. Set vcpu and ram based on flavor id.
1308 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1309 self
.get_catalogbyid(image_id
, catalogs
),
1310 network_name
=None, # None while creating vapp
1311 network_mode
=network_mode
,
1312 vm_name
=vmname_andid
,
1313 vm_cpus
=vm_cpus
, # can be None if flavor is None
1314 vm_memory
=vm_memory
) # can be None if flavor is None
1316 if vapptask
is None or vapptask
is False:
1317 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1318 if type(vapptask
) is VappTask
:
1319 vca
.block_until_completed(vapptask
)
1321 # we should have now vapp in undeployed state.
1322 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1323 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1325 raise vimconn
.vimconnUnexpectedResponse(
1326 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1329 #Add PCI passthrough configrations
1330 PCI_devices_status
= False
1333 if len(pci_devices_info
) > 0:
1334 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1336 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1339 if PCI_devices_status
:
1340 self
.logger
.info("Added PCI devives {} to VM {}".format(
1345 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1351 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1352 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1354 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1356 # add NICs & connect to networks in netlist
1358 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1360 primary_nic_index
= 0
1361 for net
in net_list
:
1362 # openmano uses network id in UUID format.
1363 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1364 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1365 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1367 if 'net_id' not in net
:
1370 interface_net_id
= net
['net_id']
1371 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1372 interface_network_mode
= net
['use']
1374 if interface_network_mode
== 'mgmt':
1375 primary_nic_index
= nicIndex
1377 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1378 - DHCP (The IP address is obtained from a DHCP service.)
1379 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1380 - NONE (No IP addressing mode specified.)"""
1382 if primary_netname
is not None:
1383 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1385 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1386 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1387 if type(task
) is GenericTask
:
1388 vca
.block_until_completed(task
)
1389 # connect network to VM - with all DHCP by default
1390 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1391 task
= vapp
.connect_vms(nets
[0].name
,
1392 connection_index
=nicIndex
,
1393 connections_primary_index
=primary_nic_index
,
1394 ip_allocation_mode
='DHCP')
1395 if type(task
) is GenericTask
:
1396 vca
.block_until_completed(task
)
1399 # it might be a case if specific mandatory entry in dict is empty
1400 self
.logger
.debug("Key error {}".format(KeyError.message
))
1401 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1403 # deploy and power on vm
1404 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1405 deploytask
= vapp
.deploy(powerOn
=False)
1406 if type(deploytask
) is GenericTask
:
1407 vca
.block_until_completed(deploytask
)
1409 # If VM has PCI devices reserve memory for VM
1410 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1411 memReserve
= vm_obj
.config
.hardware
.memoryMB
1412 spec
= vim
.vm
.ConfigSpec()
1413 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1414 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1416 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1417 self
.logger
.info("Reserved memmoery {} MB for "\
1418 "VM VM status: {}".format(str(memReserve
),result
))
1420 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1421 str(memReserve
),str(vm_obj
)))
1423 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1424 poweron_task
= vapp
.poweron()
1425 if type(poweron_task
) is GenericTask
:
1426 vca
.block_until_completed(poweron_task
)
1428 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1431 while wait_time
<= MAX_WAIT_TIME
:
1432 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1433 if vapp
and vapp
.me
.deployed
:
1434 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1437 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1438 time
.sleep(INTERVAL_TIME
)
1440 wait_time
+=INTERVAL_TIME
1442 if vapp_uuid
is not None:
1445 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1449 ## based on current discussion
1453 # created: '2016-09-08T11:51:58'
1454 # description: simple-instance.linux1.1
1455 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1456 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1457 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1462 def get_vminstance(self
, vim_vm_uuid
=None):
1463 """Returns the VM instance information from VIM"""
1465 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1466 vca
= self
.connect()
1468 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1470 vdc
= vca
.get_vdc(self
.tenant_name
)
1472 raise vimconn
.vimconnConnectionException(
1473 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1475 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1476 if not vm_info_dict
:
1477 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1478 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1480 status_key
= vm_info_dict
['status']
1483 vm_dict
= {'created': vm_info_dict
['created'],
1484 'description': vm_info_dict
['name'],
1485 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1486 'hostId': vm_info_dict
['vmuuid'],
1488 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1490 if 'interfaces' in vm_info_dict
:
1491 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1493 vm_dict
['interfaces'] = []
1495 vm_dict
= {'created': '',
1497 'status': vcdStatusCode2manoFormat
[int(-1)],
1498 'hostId': vm_info_dict
['vmuuid'],
1499 'error_msg': "Inconsistency state",
1500 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1504 def delete_vminstance(self
, vm__vim_uuid
):
1505 """Method poweroff and remove VM instance from vcloud director network.
1508 vm__vim_uuid: VM UUID
1511 Returns the instance identifier
1514 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1515 vca
= self
.connect()
1517 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1519 vdc
= vca
.get_vdc(self
.tenant_name
)
1521 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1523 raise vimconn
.vimconnException(
1524 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1527 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1528 if vapp_name
is None:
1529 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1530 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1532 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1534 # Delete vApp and wait for status change if task executed and vApp is None.
1535 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1538 if vapp
.me
.deployed
:
1539 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1543 while wait_time
<= MAX_WAIT_TIME
:
1544 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1546 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1547 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1549 power_off_task
= vapp
.poweroff()
1550 if type(power_off_task
) is GenericTask
:
1551 result
= vca
.block_until_completed(power_off_task
)
1556 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1557 time
.sleep(INTERVAL_TIME
)
1559 wait_time
+=INTERVAL_TIME
1561 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1563 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1566 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1569 while wait_time
<= MAX_WAIT_TIME
:
1570 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1572 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1573 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1574 undeploy_task
= vapp
.undeploy(action
='powerOff')
1576 if type(undeploy_task
) is GenericTask
:
1577 result
= vca
.block_until_completed(undeploy_task
)
1582 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1583 time
.sleep(INTERVAL_TIME
)
1585 wait_time
+=INTERVAL_TIME
1588 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1591 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1592 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1594 if vapp
is not None:
1598 while wait_time
<= MAX_WAIT_TIME
:
1599 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1601 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1602 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1604 delete_task
= vapp
.delete()
1606 if type(delete_task
) is GenericTask
:
1607 vca
.block_until_completed(delete_task
)
1608 result
= vca
.block_until_completed(delete_task
)
1612 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1613 time
.sleep(INTERVAL_TIME
)
1615 wait_time
+=INTERVAL_TIME
1618 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1621 self
.logger
.debug(traceback
.format_exc())
1622 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1624 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1625 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1628 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1630 def refresh_vms_status(self
, vm_list
):
1631 """Get the status of the virtual machines and their interfaces/ports
1632 Params: the list of VM identifiers
1633 Returns a dictionary with:
1634 vm_id: #VIM id of this Virtual Machine
1635 status: #Mandatory. Text with one of:
1636 # DELETED (not found at vim)
1637 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1638 # OTHER (Vim reported other status not understood)
1639 # ERROR (VIM indicates an ERROR status)
1640 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1641 # CREATING (on building process), ERROR
1642 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1644 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1645 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1647 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1648 mac_address: #Text format XX:XX:XX:XX:XX:XX
1649 vim_net_id: #network id where this interface is connected
1650 vim_interface_id: #interface/port VIM id
1651 ip_address: #null, or text with IPv4, IPv6 address
1654 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1655 vca
= self
.connect()
1657 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1659 vdc
= vca
.get_vdc(self
.tenant_name
)
1661 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1664 for vmuuid
in vm_list
:
1665 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1666 if vmname
is not None:
1668 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1669 vm_info
= the_vapp
.get_vms_details()
1670 vm_status
= vm_info
[0]['status']
1671 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1672 vm_info
[0].update(vm_pci_details
)
1674 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1675 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1676 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1680 vm_app_networks
= the_vapp
.get_vms_network_info()
1681 for vapp_network
in vm_app_networks
:
1682 for vm_network
in vapp_network
:
1683 if vm_network
['name'] == vmname
:
1684 interface
= {"mac_address": vm_network
['mac'],
1685 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1686 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1687 'ip_address': vm_network
['ip']}
1688 # interface['vim_info'] = yaml.safe_dump(vm_network)
1689 vm_dict
["interfaces"].append(interface
)
1690 # add a vm to vm dict
1691 vms_dict
.setdefault(vmuuid
, vm_dict
)
1693 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1694 self
.logger
.debug(traceback
.format_exc())
1698 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1699 """Send and action over a VM instance from VIM
1700 Returns the vm_id if the action was successfully sent to the VIM"""
1702 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1703 if vm__vim_uuid
is None or action_dict
is None:
1704 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1706 vca
= self
.connect()
1708 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1710 vdc
= vca
.get_vdc(self
.tenant_name
)
1712 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1714 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1715 if vapp_name
is None:
1716 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1717 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1719 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1722 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1723 # TODO fix all status
1724 if "start" in action_dict
:
1725 if action_dict
["start"] == "rebuild":
1726 the_vapp
.deploy(powerOn
=True)
1728 vm_info
= the_vapp
.get_vms_details()
1729 vm_status
= vm_info
[0]['status']
1730 if vm_status
== "Suspended":
1732 elif vm_status
.status
== "Powered off":
1734 elif "pause" in action_dict
:
1737 elif "resume" in action_dict
:
1740 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1742 elif "forceOff" in action_dict
:
1744 elif "terminate" in action_dict
:
1746 # elif "createImage" in action_dict:
1747 # server.create_image()
1753 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1755 Get a console for the virtual machine
1757 vm_id: uuid of the VM
1758 console_type, can be:
1759 "novnc" (by default), "xvpvnc" for VNC types,
1760 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1761 Returns dict with the console parameters:
1762 protocol: ssh, ftp, http, https, ...
1763 server: usually ip address
1764 port: the http, ssh, ... port
1765 suffix: extra text, e.g. the http path and query string
1767 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1769 # NOT USED METHODS in current version
1771 def host_vim2gui(self
, host
, server_dict
):
1772 """Transform host dictionary from VIM format to GUI format,
1773 and append to the server_dict
1775 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1777 def get_hosts_info(self
):
1778 """Get the information of deployed hosts
1779 Returns the hosts content"""
1780 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1782 def get_hosts(self
, vim_tenant
):
1783 """Get the hosts and deployed instances
1784 Returns the hosts content"""
1785 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1787 def get_processor_rankings(self
):
1788 """Get the processor rankings in the VIM database"""
1789 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1791 def new_host(self
, host_data
):
1792 """Adds a new host to VIM"""
1793 '''Returns status code of the VIM response'''
1794 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1796 def new_external_port(self
, port_data
):
1797 """Adds a external port to VIM"""
1798 '''Returns the port identifier'''
1799 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1801 def new_external_network(self
, net_name
, net_type
):
1802 """Adds a external network to VIM (shared)"""
1803 '''Returns the network identifier'''
1804 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1806 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1807 """Connects a external port to a network"""
1808 '''Returns status code of the VIM response'''
1809 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1811 def new_vminstancefromJSON(self
, vm_data
):
1812 """Adds a VM instance to VIM"""
1813 '''Returns the instance identifier'''
1814 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1816 def get_network_name_by_id(self
, network_uuid
=None):
1817 """Method gets vcloud director network named based on supplied uuid.
1820 network_uuid: network_id
1823 The return network name.
1826 vca
= self
.connect()
1828 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1830 if not network_uuid
:
1834 org_dict
= self
.get_org(self
.org_uuid
)
1835 if 'networks' in org_dict
:
1836 org_network_dict
= org_dict
['networks']
1837 for net_uuid
in org_network_dict
:
1838 if net_uuid
== network_uuid
:
1839 return org_network_dict
[net_uuid
]
1841 self
.logger
.debug("Exception in get_network_name_by_id")
1842 self
.logger
.debug(traceback
.format_exc())
1846 def get_network_id_by_name(self
, network_name
=None):
1847 """Method gets vcloud director network uuid based on supplied name.
1850 network_name: network_name
1852 The return network uuid.
1853 network_uuid: network_id
1856 vca
= self
.connect()
1858 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1860 if not network_name
:
1861 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1865 org_dict
= self
.get_org(self
.org_uuid
)
1866 if org_dict
and 'networks' in org_dict
:
1867 org_network_dict
= org_dict
['networks']
1868 for net_uuid
,net_name
in org_network_dict
.iteritems():
1869 if net_name
== network_name
:
1872 except KeyError as exp
:
1873 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1877 def list_org_action(self
):
1879 Method leverages vCloud director and query for available organization for particular user
1882 vca - is active VCA connection.
1883 vdc_name - is a vdc name that will be used to query vms action
1886 The return XML respond
1889 vca
= self
.connect()
1891 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1893 url_list
= [vca
.host
, '/api/org']
1894 vm_list_rest_call
= ''.join(url_list
)
1896 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1897 response
= Http
.get(url
=vm_list_rest_call
,
1898 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1901 if response
.status_code
== requests
.codes
.ok
:
1902 return response
.content
1906 def get_org_action(self
, org_uuid
=None):
1908 Method leverages vCloud director and retrieve available object fdr organization.
1911 vca - is active VCA connection.
1912 vdc_name - is a vdc name that will be used to query vms action
1915 The return XML respond
1918 vca
= self
.connect()
1920 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1922 if org_uuid
is None:
1925 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1926 vm_list_rest_call
= ''.join(url_list
)
1928 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1929 response
= Http
.get(url
=vm_list_rest_call
,
1930 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1933 if response
.status_code
== requests
.codes
.ok
:
1934 return response
.content
1938 def get_org(self
, org_uuid
=None):
1940 Method retrieves available organization in vCloud Director
1943 org_uuid - is a organization uuid.
1946 The return dictionary with following key
1947 "network" - for network list under the org
1948 "catalogs" - for network list under the org
1949 "vdcs" - for vdc list under org
1953 vca
= self
.connect()
1955 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1957 if org_uuid
is None:
1960 content
= self
.get_org_action(org_uuid
=org_uuid
)
1965 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1966 for child
in vm_list_xmlroot
:
1967 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1968 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1969 org_dict
['vdcs'] = vdc_list
1970 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1971 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1972 org_dict
['networks'] = network_list
1973 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1974 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1975 org_dict
['catalogs'] = catalog_list
1981 def get_org_list(self
):
1983 Method retrieves available organization in vCloud Director
1986 vca - is active VCA connection.
1989 The return dictionary and key for each entry VDC UUID
1993 vca
= self
.connect()
1995 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1997 content
= self
.list_org_action()
1999 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2000 for vm_xml
in vm_list_xmlroot
:
2001 if vm_xml
.tag
.split("}")[1] == 'Org':
2002 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2003 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2009 def vms_view_action(self
, vdc_name
=None):
2010 """ Method leverages vCloud director vms query call
2013 vca - is active VCA connection.
2014 vdc_name - is a vdc name that will be used to query vms action
2017 The return XML respond
2019 vca
= self
.connect()
2020 if vdc_name
is None:
2023 url_list
= [vca
.host
, '/api/vms/query']
2024 vm_list_rest_call
= ''.join(url_list
)
2026 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2027 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2028 vca
.vcloud_session
.organization
.Link
)
2030 response
= Http
.get(url
=vm_list_rest_call
,
2031 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2034 if response
.status_code
== requests
.codes
.ok
:
2035 return response
.content
2039 def get_vapp_list(self
, vdc_name
=None):
2041 Method retrieves vApp list deployed vCloud director and returns a dictionary
2042 contains a list of all vapp deployed for queried VDC.
2043 The key for a dictionary is vApp UUID
2047 vca - is active VCA connection.
2048 vdc_name - is a vdc name that will be used to query vms action
2051 The return dictionary and key for each entry vapp UUID
2055 if vdc_name
is None:
2058 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2060 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2061 for vm_xml
in vm_list_xmlroot
:
2062 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2063 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2064 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2065 if 'vappTemplate-' in rawuuid
[0]:
2066 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2067 # vm and use raw UUID as key
2068 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2074 def get_vm_list(self
, vdc_name
=None):
2076 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2077 contains a list of all VM's deployed for queried VDC.
2078 The key for a dictionary is VM UUID
2082 vca - is active VCA connection.
2083 vdc_name - is a vdc name that will be used to query vms action
2086 The return dictionary and key for each entry vapp UUID
2090 if vdc_name
is None:
2093 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2095 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2096 for vm_xml
in vm_list_xmlroot
:
2097 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2098 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2099 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2100 if 'vm-' in rawuuid
[0]:
2101 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2102 # vm and use raw UUID as key
2103 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2109 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2111 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2112 contains a list of all VM's deployed for queried VDC.
2113 The key for a dictionary is VM UUID
2117 vca - is active VCA connection.
2118 vdc_name - is a vdc name that will be used to query vms action
2121 The return dictionary and key for each entry vapp UUID
2124 vca
= self
.connect()
2126 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2128 if vdc_name
is None:
2131 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2133 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2134 for vm_xml
in vm_list_xmlroot
:
2135 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2136 # lookup done by UUID
2138 if vapp_name
in vm_xml
.attrib
['container']:
2139 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2140 if 'vm-' in rawuuid
[0]:
2141 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2143 # lookup done by Name
2145 if vapp_name
in vm_xml
.attrib
['name']:
2146 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2147 if 'vm-' in rawuuid
[0]:
2148 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2155 def get_network_action(self
, network_uuid
=None):
2157 Method leverages vCloud director and query network based on network uuid
2160 vca - is active VCA connection.
2161 network_uuid - is a network uuid
2164 The return XML respond
2167 vca
= self
.connect()
2169 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2171 if network_uuid
is None:
2174 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2175 vm_list_rest_call
= ''.join(url_list
)
2177 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2178 response
= Http
.get(url
=vm_list_rest_call
,
2179 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2182 if response
.status_code
== requests
.codes
.ok
:
2183 return response
.content
2187 def get_vcd_network(self
, network_uuid
=None):
2189 Method retrieves available network from vCloud Director
2192 network_uuid - is VCD network UUID
2194 Each element serialized as key : value pair
2196 Following keys available for access. network_configuration['Gateway'}
2200 <IsInherited>true</IsInherited>
2201 <Gateway>172.16.252.100</Gateway>
2202 <Netmask>255.255.255.0</Netmask>
2203 <Dns1>172.16.254.201</Dns1>
2204 <Dns2>172.16.254.202</Dns2>
2205 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2206 <IsEnabled>true</IsEnabled>
2209 <StartAddress>172.16.252.1</StartAddress>
2210 <EndAddress>172.16.252.99</EndAddress>
2215 <FenceMode>bridged</FenceMode>
2218 The return dictionary and key for each entry vapp UUID
2221 network_configuration
= {}
2222 if network_uuid
is None:
2225 content
= self
.get_network_action(network_uuid
=network_uuid
)
2227 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2229 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2230 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2231 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2233 for child
in vm_list_xmlroot
:
2234 if child
.tag
.split("}")[1] == 'IsShared':
2235 network_configuration
['isShared'] = child
.text
.strip()
2236 if child
.tag
.split("}")[1] == 'Configuration':
2237 for configuration
in child
.iter():
2238 tagKey
= configuration
.tag
.split("}")[1].strip()
2240 network_configuration
[tagKey
] = configuration
.text
.strip()
2241 return network_configuration
2245 return network_configuration
2247 def delete_network_action(self
, network_uuid
=None):
2249 Method delete given network from vCloud director
2252 network_uuid - is a network uuid that client wish to delete
2255 The return None or XML respond or false
2258 vca
= self
.connect_as_admin()
2260 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2261 if network_uuid
is None:
2264 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2265 vm_list_rest_call
= ''.join(url_list
)
2267 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2268 response
= Http
.delete(url
=vm_list_rest_call
,
2269 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2273 if response
.status_code
== 202:
2278 def create_network(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 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2296 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2297 ip_profile
=ip_profile
,
2299 parent_network_uuid
=parent_network_uuid
,
2302 self
.logger
.debug("Failed create network {}.".format(network_name
))
2306 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2307 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2308 if len(vcd_uuid
) == 4:
2309 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2312 self
.logger
.debug("Failed create network {}".format(network_name
))
2315 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2316 ip_profile
=None, isshared
='true'):
2318 Method create network in vCloud director
2321 network_name - is network name to be created.
2322 net_type - can be 'bridge','data','ptp','mgmt'.
2323 ip_profile is a dict containing the IP parameters of the network
2324 isshared - is a boolean
2325 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2326 It optional attribute. by default if no parent network indicate the first available will be used.
2329 The return network uuid or return None
2332 vca
= self
.connect_as_admin()
2334 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2335 if network_name
is None:
2338 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2339 vm_list_rest_call
= ''.join(url_list
)
2340 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2341 response
= Http
.get(url
=vm_list_rest_call
,
2342 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2346 provider_network
= None
2347 available_networks
= None
2348 add_vdc_rest_url
= None
2350 if response
.status_code
!= requests
.codes
.ok
:
2351 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2352 response
.status_code
))
2356 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2357 for child
in vm_list_xmlroot
:
2358 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2359 provider_network
= child
.attrib
.get('href')
2360 # application/vnd.vmware.admin.providervdc+xml
2361 if child
.tag
.split("}")[1] == 'Link':
2362 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2363 and child
.attrib
.get('rel') == 'add':
2364 add_vdc_rest_url
= child
.attrib
.get('href')
2366 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2367 self
.logger
.debug("Respond body {}".format(response
.content
))
2370 # find pvdc provided available network
2371 response
= Http
.get(url
=provider_network
,
2372 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2375 if response
.status_code
!= requests
.codes
.ok
:
2376 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2377 response
.status_code
))
2380 # available_networks.split("/")[-1]
2382 if parent_network_uuid
is None:
2384 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2385 for child
in vm_list_xmlroot
.iter():
2386 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2387 for networks
in child
.iter():
2388 # application/vnd.vmware.admin.network+xml
2389 if networks
.attrib
.get('href') is not None:
2390 available_networks
= networks
.attrib
.get('href')
2395 #Configure IP profile of the network
2396 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2398 gateway_address
=ip_profile
['gateway_address']
2399 dhcp_count
=int(ip_profile
['dhcp_count'])
2400 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2402 if ip_profile
['dhcp_enabled']==True:
2405 dhcp_enabled
='false'
2406 dhcp_start_address
=ip_profile
['dhcp_start_address']
2408 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2409 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2410 end_ip_int
+= dhcp_count
- 1
2411 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2413 ip_version
=ip_profile
['ip_version']
2414 dns_address
=ip_profile
['dns_address']
2416 # either use client provided UUID or search for a first available
2417 # if both are not defined we return none
2418 if parent_network_uuid
is not None:
2419 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2420 add_vdc_rest_url
= ''.join(url_list
)
2423 fence_mode
="isolated"
2425 is_inherited
='false'
2426 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2427 <Description>Openmano created</Description>
2431 <IsInherited>{1:s}</IsInherited>
2432 <Gateway>{2:s}</Gateway>
2433 <Netmask>{3:s}</Netmask>
2435 <IsEnabled>{5:s}</IsEnabled>
2438 <StartAddress>{6:s}</StartAddress>
2439 <EndAddress>{7:s}</EndAddress>
2444 <FenceMode>{8:s}</FenceMode>
2446 <IsShared>{9:s}</IsShared>
2447 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2448 subnet_address
, dns_address
, dhcp_enabled
,
2449 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2452 fence_mode
="bridged"
2453 is_inherited
='false'
2454 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2455 <Description>Openmano created</Description>
2459 <IsInherited>{1:s}</IsInherited>
2460 <Gateway>{2:s}</Gateway>
2461 <Netmask>{3:s}</Netmask>
2463 <IsEnabled>{5:s}</IsEnabled>
2466 <StartAddress>{6:s}</StartAddress>
2467 <EndAddress>{7:s}</EndAddress>
2472 <ParentNetwork href="{8:s}"/>
2473 <FenceMode>{9:s}</FenceMode>
2475 <IsShared>{10:s}</IsShared>
2476 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2477 subnet_address
, dns_address
, dhcp_enabled
,
2478 dhcp_start_address
, dhcp_end_address
, available_networks
,
2479 fence_mode
, isshared
)
2481 headers
= vca
.vcloud_session
.get_vcloud_headers()
2482 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2484 response
= Http
.post(url
=add_vdc_rest_url
,
2490 if response
.status_code
!= 201:
2491 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2492 .format(response
.status_code
))
2494 network
= networkType
.parseString(response
.content
, True)
2495 create_nw_task
= network
.get_Tasks().get_Task()[0]
2497 # if we all ok we respond with content after network creation completes
2498 # otherwise by default return None
2499 if create_nw_task
is not None:
2500 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2501 status
= vca
.block_until_completed(create_nw_task
)
2503 return response
.content
2505 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2506 .format(response
.content
))
2507 except Exception as exp
:
2508 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2512 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2514 Method sets convert CIDR netmask address to normal IP format
2516 cidr_ip : CIDR IP address
2518 netmask : Converted netmask
2520 if cidr_ip
is not None:
2522 network
, net_bits
= cidr_ip
.split('/')
2523 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2529 def get_provider_rest(self
, vca
=None):
2531 Method gets provider vdc view from vcloud director
2534 network_name - is network name to be created.
2535 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2536 It optional attribute. by default if no parent network indicate the first available will be used.
2539 The return xml content of respond or None
2542 url_list
= [vca
.host
, '/api/admin']
2543 response
= Http
.get(url
=''.join(url_list
),
2544 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2548 if response
.status_code
== requests
.codes
.ok
:
2549 return response
.content
2552 def create_vdc(self
, vdc_name
=None):
2556 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2557 if xml_content
is not None:
2559 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2560 for child
in task_resp_xmlroot
:
2561 if child
.tag
.split("}")[1] == 'Owner':
2562 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2563 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2566 self
.logger
.debug("Respond body {}".format(xml_content
))
2570 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2572 Method create vdc in vCloud director based on VDC template.
2573 it uses pre-defined template that must be named openmano
2576 vdc_name - name of a new vdc.
2579 The return xml content of respond or None
2582 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2583 vca
= self
.connect()
2585 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2586 if vdc_name
is None:
2589 url_list
= [vca
.host
, '/api/vdcTemplates']
2590 vm_list_rest_call
= ''.join(url_list
)
2591 response
= Http
.get(url
=vm_list_rest_call
,
2592 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2596 # container url to a template
2597 vdc_template_ref
= None
2599 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2600 for child
in vm_list_xmlroot
:
2601 # application/vnd.vmware.admin.providervdc+xml
2602 # we need find a template from witch we instantiate VDC
2603 if child
.tag
.split("}")[1] == 'VdcTemplate':
2604 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2605 'name') == 'openmano':
2606 vdc_template_ref
= child
.attrib
.get('href')
2608 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2609 self
.logger
.debug("Respond body {}".format(response
.content
))
2612 # if we didn't found required pre defined template we return None
2613 if vdc_template_ref
is None:
2618 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2619 vm_list_rest_call
= ''.join(url_list
)
2620 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2621 <Source href="{1:s}"></Source>
2622 <Description>opnemano</Description>
2623 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2624 headers
= vca
.vcloud_session
.get_vcloud_headers()
2625 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2626 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2628 # if we all ok we respond with content otherwise by default None
2629 if response
.status_code
>= 200 and response
.status_code
< 300:
2630 return response
.content
2633 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2634 self
.logger
.debug("Respond body {}".format(response
.content
))
2638 def create_vdc_rest(self
, vdc_name
=None):
2640 Method create network in vCloud director
2643 network_name - is network name to be created.
2644 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2645 It optional attribute. by default if no parent network indicate the first available will be used.
2648 The return network uuid or return None
2651 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2653 vca
= self
.connect_as_admin()
2655 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2656 if vdc_name
is None:
2659 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2660 vm_list_rest_call
= ''.join(url_list
)
2661 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2662 response
= Http
.get(url
=vm_list_rest_call
,
2663 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2667 provider_vdc_ref
= None
2668 add_vdc_rest_url
= None
2669 available_networks
= None
2671 if response
.status_code
!= requests
.codes
.ok
:
2672 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2673 response
.status_code
))
2677 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2678 for child
in vm_list_xmlroot
:
2679 # application/vnd.vmware.admin.providervdc+xml
2680 if child
.tag
.split("}")[1] == 'Link':
2681 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2682 and child
.attrib
.get('rel') == 'add':
2683 add_vdc_rest_url
= child
.attrib
.get('href')
2685 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2686 self
.logger
.debug("Respond body {}".format(response
.content
))
2689 response
= self
.get_provider_rest(vca
=vca
)
2691 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2692 for child
in vm_list_xmlroot
:
2693 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2694 for sub_child
in child
:
2695 provider_vdc_ref
= sub_child
.attrib
.get('href')
2697 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2698 self
.logger
.debug("Respond body {}".format(response
))
2701 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2702 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2703 <AllocationModel>ReservationPool</AllocationModel>
2704 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2705 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2706 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2707 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2708 <ProviderVdcReference
2709 name="Main Provider"
2711 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2715 headers
= vca
.vcloud_session
.get_vcloud_headers()
2716 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2717 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2720 # if we all ok we respond with content otherwise by default None
2721 if response
.status_code
== 201:
2722 return response
.content
2725 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2727 Method retrieve vapp detail from vCloud director
2730 vapp_uuid - is vapp identifier.
2733 The return network uuid or return None
2739 if need_admin_access
:
2740 vca
= self
.connect_as_admin()
2742 vca
= self
.connect()
2745 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2746 if vapp_uuid
is None:
2749 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2750 get_vapp_restcall
= ''.join(url_list
)
2752 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2753 response
= Http
.get(url
=get_vapp_restcall
,
2754 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2758 if response
.status_code
!= requests
.codes
.ok
:
2759 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2760 response
.status_code
))
2761 return parsed_respond
2764 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2765 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2767 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2768 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2769 'vmw': 'http://www.vmware.com/schema/ovf',
2770 'vm': 'http://www.vmware.com/vcloud/v1.5',
2771 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2772 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2773 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2776 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2777 if created_section
is not None:
2778 parsed_respond
['created'] = created_section
.text
2780 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2781 if network_section
is not None and 'networkName' in network_section
.attrib
:
2782 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2784 ipscopes_section
= \
2785 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2787 if ipscopes_section
is not None:
2788 for ipscope
in ipscopes_section
:
2789 for scope
in ipscope
:
2790 tag_key
= scope
.tag
.split("}")[1]
2791 if tag_key
== 'IpRanges':
2792 ip_ranges
= scope
.getchildren()
2793 for ipblock
in ip_ranges
:
2794 for block
in ipblock
:
2795 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2797 parsed_respond
[tag_key
] = scope
.text
2799 # parse children section for other attrib
2800 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2801 if children_section
is not None:
2802 parsed_respond
['name'] = children_section
.attrib
['name']
2803 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2804 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2805 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2806 parsed_respond
['status'] = children_section
.attrib
['status']
2807 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2808 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2810 for adapters
in network_adapter
:
2811 adapter_key
= adapters
.tag
.split("}")[1]
2812 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2813 parsed_respond
['primarynetwork'] = adapters
.text
2814 if adapter_key
== 'NetworkConnection':
2816 if 'network' in adapters
.attrib
:
2817 vnic
['network'] = adapters
.attrib
['network']
2818 for adapter
in adapters
:
2819 setting_key
= adapter
.tag
.split("}")[1]
2820 vnic
[setting_key
] = adapter
.text
2821 nic_list
.append(vnic
)
2823 for link
in children_section
:
2824 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2825 if link
.attrib
['rel'] == 'screen:acquireTicket':
2826 parsed_respond
['acquireTicket'] = link
.attrib
2827 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2828 parsed_respond
['acquireMksTicket'] = link
.attrib
2830 parsed_respond
['interfaces'] = nic_list
2831 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2832 if vCloud_extension_section
is not None:
2833 vm_vcenter_info
= {}
2834 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2835 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2836 if vmext
is not None:
2837 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2838 vm_vcenter_info
["vim_server_href"] = vmext
.find('vmext:VimServerRef', namespaces
).attrib
['href']
2839 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2841 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2842 vm_virtual_hardware_info
= {}
2843 if virtual_hardware_section
is not None:
2844 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2845 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2846 disk_size
= item
.find("rasd:HostResource" ,namespaces
2847 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2849 vm_virtual_hardware_info
["disk_size"]= disk_size
2852 for link
in virtual_hardware_section
:
2853 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2854 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2855 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2858 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2859 except Exception as exp
:
2860 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2861 return parsed_respond
2863 def acuire_console(self
, vm_uuid
=None):
2865 vca
= self
.connect()
2867 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2871 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2872 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2873 console_dict
= vm_dict
['acquireTicket']
2874 console_rest_call
= console_dict
['href']
2876 response
= Http
.post(url
=console_rest_call
,
2877 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2881 if response
.status_code
== requests
.codes
.ok
:
2882 return response
.content
2886 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2888 Method retrieve vm disk details
2891 vapp_uuid - is vapp identifier.
2892 flavor_disk - disk size as specified in VNFD (flavor)
2895 The return network uuid or return None
2899 #Flavor disk is in GB convert it into MB
2900 flavor_disk
= int(flavor_disk
) * 1024
2901 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2903 vm_name
= vm_details
["name"]
2904 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2906 if vm_details
and "vm_virtual_hardware" in vm_details
:
2907 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2908 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2910 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2912 if flavor_disk
> vm_disk
:
2913 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2914 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2915 vm_disk
, flavor_disk
))
2918 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
2921 except Exception as exp
:
2922 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
2925 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
2927 Method retrieve modify vm disk size
2930 disk_href - vCD API URL to GET and PUT disk data
2931 disk_size - disk size as specified in VNFD (flavor)
2934 The return network uuid or return None
2936 vca
= self
.connect()
2938 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2939 if disk_href
is None or disk_size
is None:
2942 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2943 response
= Http
.get(url
=disk_href
,
2944 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2948 if response
.status_code
!= requests
.codes
.ok
:
2949 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
2950 response
.status_code
))
2953 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
2954 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
2955 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2957 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
2958 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2959 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
2960 if disk_item
is not None:
2961 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
2964 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
2965 xml_declaration
=True)
2967 #Send PUT request to modify disk size
2968 headers
= vca
.vcloud_session
.get_vcloud_headers()
2969 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2971 response
= Http
.put(url
=disk_href
,
2974 verify
=vca
.verify
, logger
=self
.logger
)
2976 if response
.status_code
!= 202:
2977 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
2978 response
.status_code
))
2980 modify_disk_task
= taskType
.parseString(response
.content
, True)
2981 if type(modify_disk_task
) is GenericTask
:
2982 status
= vca
.block_until_completed(modify_disk_task
)
2987 except Exception as exp
:
2988 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
2991 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
2993 Method to attach pci devices to VM
2996 vapp_uuid - uuid of vApp/VM
2997 pci_devices - pci devices infromation as specified in VNFD (flavor)
3000 The status of add pci device task , vm object and
3001 vcenter_conect object
3004 vcenter_conect
= None
3005 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3006 #Assuming password of vCenter user is same as password of vCloud user
3007 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vapp_uuid
)
3008 self
.logger
.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
3009 "vm_vcenter_port{}".format(
3010 vm_moref_id
, vm_vcenter_host
,
3011 vm_vcenter_username
, vm_vcenter_port
))
3012 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3014 if hasattr(ssl
, '_create_unverified_context'):
3015 context
= ssl
._create
_unverified
_context
()
3017 no_of_pci_devices
= len(pci_devices
)
3018 if no_of_pci_devices
> 0:
3019 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3020 pwd
=self
.passwd
, port
=int(vm_vcenter_port
) ,
3022 atexit
.register(Disconnect
, vcenter_conect
)
3023 content
= vcenter_conect
.RetrieveContent()
3025 #Get VM and its host
3026 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3027 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3028 if host_obj
and vm_obj
:
3029 #get PCI devies from host on which vapp is currently installed
3030 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3032 if avilable_pci_devices
is None:
3033 #find other hosts with active pci devices
3034 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3039 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3040 #Migrate vm to the host where PCI devices are availble
3041 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3042 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3043 if task
is not None:
3044 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3045 self
.logger
.info("Migrate VM status: {}".format(result
))
3046 host_obj
= new_host_obj
3048 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3049 raise vimconn
.vimconnNotFoundException(
3050 "Fail to migrate VM : {} to host {}".format(
3055 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3056 #Add PCI devices one by one
3057 for pci_device
in avilable_pci_devices
:
3058 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3060 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3062 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3064 self
.logger
.info("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3065 return True, vm_obj
, vcenter_conect
3067 self
.logger
.error("Currently there is no host with"\
3068 " {} number of avaialble PCI devices required for VM {}".format(
3072 raise vimconn
.vimconnNotFoundException(
3073 "Currently there is no host with {} "\
3074 "number of avaialble PCI devices required for VM {}".format(
3078 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3080 except vmodl
.MethodFault
as error
:
3081 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3082 return None, vm_obj
, vcenter_conect
3084 def get_vm_obj(self
, content
, mob_id
):
3086 Method to get the vsphere VM object associated with a given morf ID
3088 vapp_uuid - uuid of vApp/VM
3089 content - vCenter content object
3090 mob_id - mob_id of VM
3098 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3099 [vim
.VirtualMachine
], True
3101 for vm
in container
.view
:
3102 mobID
= vm
._GetMoId
()
3105 host_obj
= vm_obj
.runtime
.host
3107 except Exception as exp
:
3108 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3109 return host_obj
, vm_obj
3111 def get_pci_devices(self
, host
, need_devices
):
3113 Method to get the details of pci devices on given host
3115 host - vSphere host object
3116 need_devices - number of pci devices needed on host
3119 array of pci devices
3123 used_devices_ids
= []
3127 pciPassthruInfo
= host
.config
.pciPassthruInfo
3128 pciDevies
= host
.hardware
.pciDevice
3130 for pci_status
in pciPassthruInfo
:
3131 if pci_status
.passthruActive
:
3132 for device
in pciDevies
:
3133 if device
.id == pci_status
.id:
3134 all_device_ids
.append(device
.id)
3135 all_devices
.append(device
)
3137 #check if devices are in use
3138 avalible_devices
= all_devices
3140 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3141 vm_devices
= vm
.config
.hardware
.device
3142 for device
in vm_devices
:
3143 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3144 if device
.backing
.id in all_device_ids
:
3145 for use_device
in avalible_devices
:
3146 if use_device
.id == device
.backing
.id:
3147 avalible_devices
.remove(use_device
)
3148 used_devices_ids
.append(device
.backing
.id)
3149 self
.logger
.debug("Device {} from devices {}"\
3150 "is in use".format(device
.backing
.id,
3153 if len(avalible_devices
) < need_devices
:
3154 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3156 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3160 required_devices
= avalible_devices
[:need_devices
]
3161 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3162 len(avalible_devices
),
3165 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3167 return required_devices
3169 except Exception as exp
:
3170 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3174 def get_host_and_PCIdevices(self
, content
, need_devices
):
3176 Method to get the details of pci devices infromation on all hosts
3179 content - vSphere host object
3180 need_devices - number of pci devices needed on host
3183 array of pci devices and host object
3186 pci_device_objs
= None
3189 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3190 [vim
.HostSystem
], True)
3191 for host
in container
.view
:
3192 devices
= self
.get_pci_devices(host
, need_devices
)
3195 pci_device_objs
= devices
3197 except Exception as exp
:
3198 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3200 return host_obj
,pci_device_objs
3202 def relocate_vm(self
, dest_host
, vm
) :
3204 Method to get the relocate VM to new host
3207 dest_host - vSphere host object
3208 vm - vSphere VM object
3215 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3216 task
= vm
.Relocate(relocate_spec
)
3217 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3218 except Exception as exp
:
3219 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3220 dest_host
, vm
, exp
))
3223 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3225 Waits and provides updates on a vSphere task
3227 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3230 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3231 if task
.info
.result
is not None and not hideResult
:
3232 self
.logger
.info('{} completed successfully, result: {}'.format(
3236 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3238 self
.logger
.error('{} did not complete successfully: {} '.format(
3243 return task
.info
.result
3245 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3247 Method to add pci device in given VM
3250 host_object - vSphere host object
3251 vm_object - vSphere VM object
3252 host_pci_dev - host_pci_dev must be one of the devices from the
3253 host_object.hardware.pciDevice list
3254 which is configured as a PCI passthrough device
3260 if vm_object
and host_object
and host_pci_dev
:
3262 #Add PCI device to VM
3263 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3264 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3266 if host_pci_dev
.id not in systemid_by_pciid
:
3267 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3270 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3271 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3273 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3274 vendorId
=host_pci_dev
.vendorId
,
3275 deviceName
=host_pci_dev
.deviceName
)
3277 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3279 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3280 new_device_config
.operation
= "add"
3281 vmConfigSpec
= vim
.vm
.ConfigSpec()
3282 vmConfigSpec
.deviceChange
= [new_device_config
]
3284 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3285 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3286 host_pci_dev
, vm_object
, host_object
)
3288 except Exception as exp
:
3289 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3295 def get_vcenter_info_rest(self
, vapp_uuid
):
3297 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3298 Method to get details of vCenter
3301 vapp_uuid - uuid of vApp or VM
3304 Moref Id of VM and deails of vCenter
3308 vm_vcenter_username
= None
3309 vm_vcenter_port
= None
3311 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3312 if vm_details
and "vm_vcenter_info" in vm_details
:
3313 vm_moref_id
= vm_details
["vm_vcenter_info"]["vm_moref_id"]
3314 vim_server_href
= vm_details
["vm_vcenter_info"]["vim_server_href"]
3317 vca
= self
.connect_as_admin()
3319 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3320 if vim_server_href
is None:
3321 self
.logger
.error("No url to get vcenter details")
3323 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3324 response
= Http
.get(url
=vim_server_href
,
3325 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3329 if response
.status_code
!= requests
.codes
.ok
:
3330 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href
,
3331 response
.status_code
))
3333 namespaces
={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3334 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3336 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3337 vm_vcenter_username
= xmlroot_respond
.find('vmext:Username', namespaces
).text
3338 vcenter_url
= xmlroot_respond
.find('vmext:Url', namespaces
).text
3339 vm_vcenter_port
= vcenter_url
.split(":")[2]
3340 vm_vcenter
= vcenter_url
.split(":")[1].split("//")[1]
3342 except Exception as exp
:
3343 self
.logger
.info("Error occurred calling rest api for vcenter information {}".format(exp
))
3345 return vm_moref_id
, vm_vcenter
, vm_vcenter_username
, vm_vcenter_port
3348 def get_vm_pci_details(self
, vmuuid
):
3350 Method to get VM PCI device details from vCenter
3353 vm_obj - vSphere VM object
3356 dict of PCI devives attached to VM
3359 vm_pci_devices_info
= {}
3361 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3362 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3364 if hasattr(ssl
, '_create_unverified_context'):
3365 context
= ssl
._create
_unverified
_context
()
3366 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3367 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3369 atexit
.register(Disconnect
, vcenter_conect
)
3370 content
= vcenter_conect
.RetrieveContent()
3372 #Get VM and its host
3373 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3374 for device
in vm_obj
.config
.hardware
.device
:
3375 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3376 device_details
={'devide_id':device
.backing
.id,
3377 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
3379 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3380 except Exception as exp
:
3381 self
.logger
.info("Error occurred while getting PCI devices infromationn"\
3382 " for VM {} : {}".format(vm_obj
,exp
))
3383 return vm_pci_devices_info