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]
1100 if ("name" not in filter_dict
and "id" not in filter_dict
) or \
1101 (filter_dict
.get('id') == catalog_uuid
or filter_dict
.get('name') == name
):
1102 filtered_dict
["name"] = name
1103 filtered_dict
["id"] = catalog_uuid
1104 image_list
.append(filtered_dict
)
1106 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1108 except Exception as exp
:
1109 self
.logger
.error("Exception occured while retriving catalog items {}".format(exp
))
1111 def get_vappid(self
, vdc
=None, vapp_name
=None):
1112 """ Method takes vdc object and vApp name and returns vapp uuid or None
1115 vdc: The VDC object.
1116 vapp_name: is application vappp name identifier
1119 The return vApp name otherwise None
1121 if vdc
is None or vapp_name
is None:
1123 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1125 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1126 vdc
.ResourceEntities
.ResourceEntity
)
1128 return refs
[0].href
.split("vapp")[1][1:]
1129 except Exception as e
:
1130 self
.logger
.exception(e
)
1134 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1135 """ Method Method returns True or False if vapp deployed in vCloud director
1138 vca: Connector to VCA
1139 vdc: The VDC object.
1140 vappid: vappid is application identifier
1143 The return True if vApp deployed
1148 refs
= filter(lambda ref
:
1149 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1150 vdc
.ResourceEntities
.ResourceEntity
)
1152 vappid
= ref
.href
.split("vapp")[1][1:]
1153 # find vapp with respected vapp uuid
1154 if vappid
== vapp_uuid
:
1156 except Exception as e
:
1157 self
.logger
.exception(e
)
1161 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1162 """Method returns vApp name from vCD and lookup done by vapp_id.
1165 vca: Connector to VCA
1166 vdc: The VDC object.
1167 vapp_uuid: vappid is application identifier
1170 The return vApp name otherwise None
1174 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1175 vdc
.ResourceEntities
.ResourceEntity
)
1177 # we care only about UUID the rest doesn't matter
1178 vappid
= ref
.href
.split("vapp")[1][1:]
1179 if vappid
== vapp_uuid
:
1180 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1182 tree
= XmlElementTree
.fromstring(response
.content
)
1183 return tree
.attrib
['name']
1184 except Exception as e
:
1185 self
.logger
.exception(e
)
1189 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1190 cloud_config
=None, disk_list
=None):
1191 """Adds a VM instance to VIM
1193 start: indicates if VM must start or boot in pause mode. Ignored
1194 image_id,flavor_id: image and flavor uuid
1195 net_list: list of interfaces, each one is a dictionary with:
1197 net_id: network uuid to connect
1198 vpci: virtual vcpi to assign
1199 model: interface model, virtio, e2000, ...
1201 use: 'data', 'bridge', 'mgmt'
1202 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1203 vim_id: filled/added by this function
1204 cloud_config: can be a text script to be passed directly to cloud-init,
1205 or an object to inject users and ssh keys with format:
1206 key-pairs: [] list of keys to install to the default user
1207 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1208 #TODO ip, security groups
1209 Returns >=0, the instance identifier
1213 self
.logger
.info("Creating new instance for entry {}".format(name
))
1214 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1215 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1216 vca
= self
.connect()
1218 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1220 #new vm name = vmname + tenant_id + uuid
1221 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1222 vmname_andid
= ''.join(new_vm_name
)
1224 # if vm already deployed we return existing uuid
1225 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1226 # if vapp_uuid is not None:
1229 # we check for presence of VDC, Catalog entry and Flavor.
1230 vdc
= vca
.get_vdc(self
.tenant_name
)
1232 raise vimconn
.vimconnNotFoundException(
1233 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1234 catalogs
= vca
.get_catalogs()
1235 if catalogs
is None:
1236 raise vimconn
.vimconnNotFoundException(
1237 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1239 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1240 if catalog_hash_name
:
1241 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1243 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1244 "(Failed retrieve catalog information {})".format(name
, image_id
))
1247 # Set vCPU and Memory based on flavor.
1252 pci_devices_info
= []
1253 if flavor_id
is not None:
1254 if flavor_id
not in flavorlist
:
1255 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1256 "Failed retrieve flavor information "
1257 "flavor id {}".format(name
, flavor_id
))
1260 flavor
= flavorlist
[flavor_id
]
1261 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1262 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1263 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1264 extended
= flavor
.get("extended", None)
1266 numas
=extended
.get("numas", None)
1269 for interface
in numa
.get("interfaces",() ):
1270 if interface
["dedicated"].strip()=="yes":
1271 pci_devices_info
.append(interface
)
1273 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1275 # image upload creates template name as catalog name space Template.
1276 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1281 # client must provide at least one entry in net_list if not we report error
1282 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1283 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1285 primary_netname
= None
1286 network_mode
= 'bridged'
1287 if net_list
is not None and len(net_list
) > 0:
1288 for net
in net_list
:
1289 if 'use' in net
and net
['use'] == 'mgmt':
1291 if primary_net
is None:
1292 primary_net
= net_list
[0]
1295 primary_net_id
= primary_net
['net_id']
1296 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1297 if 'name' in network_dict
:
1298 primary_netname
= network_dict
['name']
1301 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1303 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1305 # use: 'data', 'bridge', 'mgmt'
1306 # create vApp. Set vcpu and ram based on flavor id.
1307 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1308 self
.get_catalogbyid(image_id
, catalogs
),
1309 network_name
=None, # None while creating vapp
1310 network_mode
=network_mode
,
1311 vm_name
=vmname_andid
,
1312 vm_cpus
=vm_cpus
, # can be None if flavor is None
1313 vm_memory
=vm_memory
) # can be None if flavor is None
1315 if vapptask
is None or vapptask
is False:
1316 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1317 if type(vapptask
) is VappTask
:
1318 vca
.block_until_completed(vapptask
)
1320 # we should have now vapp in undeployed state.
1321 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1322 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1324 raise vimconn
.vimconnUnexpectedResponse(
1325 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(
1328 #Add PCI passthrough configrations
1329 PCI_devices_status
= False
1332 if len(pci_devices_info
) > 0:
1333 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1335 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1338 if PCI_devices_status
:
1339 self
.logger
.info("Added PCI devives {} to VM {}".format(
1344 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1350 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1351 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1353 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1355 # add NICs & connect to networks in netlist
1357 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1359 primary_nic_index
= 0
1360 for net
in net_list
:
1361 # openmano uses network id in UUID format.
1362 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1363 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1364 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1366 if 'net_id' not in net
:
1369 interface_net_id
= net
['net_id']
1370 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1371 interface_network_mode
= net
['use']
1373 if interface_network_mode
== 'mgmt':
1374 primary_nic_index
= nicIndex
1376 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1377 - DHCP (The IP address is obtained from a DHCP service.)
1378 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1379 - NONE (No IP addressing mode specified.)"""
1381 if primary_netname
is not None:
1382 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1384 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1385 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1386 if type(task
) is GenericTask
:
1387 vca
.block_until_completed(task
)
1388 # connect network to VM - with all DHCP by default
1389 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1390 task
= vapp
.connect_vms(nets
[0].name
,
1391 connection_index
=nicIndex
,
1392 connections_primary_index
=primary_nic_index
,
1393 ip_allocation_mode
='DHCP')
1394 if type(task
) is GenericTask
:
1395 vca
.block_until_completed(task
)
1398 # it might be a case if specific mandatory entry in dict is empty
1399 self
.logger
.debug("Key error {}".format(KeyError.message
))
1400 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1402 # deploy and power on vm
1403 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1404 deploytask
= vapp
.deploy(powerOn
=False)
1405 if type(deploytask
) is GenericTask
:
1406 vca
.block_until_completed(deploytask
)
1408 # If VM has PCI devices reserve memory for VM
1409 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1410 memReserve
= vm_obj
.config
.hardware
.memoryMB
1411 spec
= vim
.vm
.ConfigSpec()
1412 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1413 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1415 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1416 self
.logger
.info("Reserved memmoery {} MB for "\
1417 "VM VM status: {}".format(str(memReserve
),result
))
1419 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1420 str(memReserve
),str(vm_obj
)))
1422 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1423 poweron_task
= vapp
.poweron()
1424 if type(poweron_task
) is GenericTask
:
1425 vca
.block_until_completed(poweron_task
)
1427 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1430 while wait_time
<= MAX_WAIT_TIME
:
1431 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1432 if vapp
and vapp
.me
.deployed
:
1433 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1436 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1437 time
.sleep(INTERVAL_TIME
)
1439 wait_time
+=INTERVAL_TIME
1441 if vapp_uuid
is not None:
1444 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1448 ## based on current discussion
1452 # created: '2016-09-08T11:51:58'
1453 # description: simple-instance.linux1.1
1454 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1455 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1456 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1461 def get_vminstance(self
, vim_vm_uuid
=None):
1462 """Returns the VM instance information from VIM"""
1464 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1465 vca
= self
.connect()
1467 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1469 vdc
= vca
.get_vdc(self
.tenant_name
)
1471 raise vimconn
.vimconnConnectionException(
1472 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1474 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1475 if not vm_info_dict
:
1476 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1477 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1479 status_key
= vm_info_dict
['status']
1482 vm_dict
= {'created': vm_info_dict
['created'],
1483 'description': vm_info_dict
['name'],
1484 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1485 'hostId': vm_info_dict
['vmuuid'],
1487 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1489 if 'interfaces' in vm_info_dict
:
1490 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1492 vm_dict
['interfaces'] = []
1494 vm_dict
= {'created': '',
1496 'status': vcdStatusCode2manoFormat
[int(-1)],
1497 'hostId': vm_info_dict
['vmuuid'],
1498 'error_msg': "Inconsistency state",
1499 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1503 def delete_vminstance(self
, vm__vim_uuid
):
1504 """Method poweroff and remove VM instance from vcloud director network.
1507 vm__vim_uuid: VM UUID
1510 Returns the instance identifier
1513 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1514 vca
= self
.connect()
1516 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1518 vdc
= vca
.get_vdc(self
.tenant_name
)
1520 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1522 raise vimconn
.vimconnException(
1523 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1526 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1527 if vapp_name
is None:
1528 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1529 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1531 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1533 # Delete vApp and wait for status change if task executed and vApp is None.
1534 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1537 if vapp
.me
.deployed
:
1538 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1542 while wait_time
<= MAX_WAIT_TIME
:
1543 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1545 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1546 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1548 power_off_task
= vapp
.poweroff()
1549 if type(power_off_task
) is GenericTask
:
1550 result
= vca
.block_until_completed(power_off_task
)
1555 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1556 time
.sleep(INTERVAL_TIME
)
1558 wait_time
+=INTERVAL_TIME
1560 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1562 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1565 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1568 while wait_time
<= MAX_WAIT_TIME
:
1569 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1571 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1572 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1573 undeploy_task
= vapp
.undeploy(action
='powerOff')
1575 if type(undeploy_task
) is GenericTask
:
1576 result
= vca
.block_until_completed(undeploy_task
)
1581 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1582 time
.sleep(INTERVAL_TIME
)
1584 wait_time
+=INTERVAL_TIME
1587 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1590 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1591 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1593 if vapp
is not None:
1597 while wait_time
<= MAX_WAIT_TIME
:
1598 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1600 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1601 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1603 delete_task
= vapp
.delete()
1605 if type(delete_task
) is GenericTask
:
1606 vca
.block_until_completed(delete_task
)
1607 result
= vca
.block_until_completed(delete_task
)
1611 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1612 time
.sleep(INTERVAL_TIME
)
1614 wait_time
+=INTERVAL_TIME
1617 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1620 self
.logger
.debug(traceback
.format_exc())
1621 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1623 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1624 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1627 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1629 def refresh_vms_status(self
, vm_list
):
1630 """Get the status of the virtual machines and their interfaces/ports
1631 Params: the list of VM identifiers
1632 Returns a dictionary with:
1633 vm_id: #VIM id of this Virtual Machine
1634 status: #Mandatory. Text with one of:
1635 # DELETED (not found at vim)
1636 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1637 # OTHER (Vim reported other status not understood)
1638 # ERROR (VIM indicates an ERROR status)
1639 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1640 # CREATING (on building process), ERROR
1641 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1643 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1644 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1646 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1647 mac_address: #Text format XX:XX:XX:XX:XX:XX
1648 vim_net_id: #network id where this interface is connected
1649 vim_interface_id: #interface/port VIM id
1650 ip_address: #null, or text with IPv4, IPv6 address
1653 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1654 vca
= self
.connect()
1656 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1658 vdc
= vca
.get_vdc(self
.tenant_name
)
1660 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1663 for vmuuid
in vm_list
:
1664 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1665 if vmname
is not None:
1667 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1668 vm_info
= the_vapp
.get_vms_details()
1669 vm_status
= vm_info
[0]['status']
1670 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1671 vm_info
[0].update(vm_pci_details
)
1673 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1674 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1675 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1679 vm_app_networks
= the_vapp
.get_vms_network_info()
1680 for vapp_network
in vm_app_networks
:
1681 for vm_network
in vapp_network
:
1682 if vm_network
['name'] == vmname
:
1683 interface
= {"mac_address": vm_network
['mac'],
1684 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1685 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1686 'ip_address': vm_network
['ip']}
1687 # interface['vim_info'] = yaml.safe_dump(vm_network)
1688 vm_dict
["interfaces"].append(interface
)
1689 # add a vm to vm dict
1690 vms_dict
.setdefault(vmuuid
, vm_dict
)
1692 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1693 self
.logger
.debug(traceback
.format_exc())
1697 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1698 """Send and action over a VM instance from VIM
1699 Returns the vm_id if the action was successfully sent to the VIM"""
1701 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1702 if vm__vim_uuid
is None or action_dict
is None:
1703 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1705 vca
= self
.connect()
1707 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1709 vdc
= vca
.get_vdc(self
.tenant_name
)
1711 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1713 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1714 if vapp_name
is None:
1715 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1716 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1718 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1721 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1722 # TODO fix all status
1723 if "start" in action_dict
:
1724 if action_dict
["start"] == "rebuild":
1725 the_vapp
.deploy(powerOn
=True)
1727 vm_info
= the_vapp
.get_vms_details()
1728 vm_status
= vm_info
[0]['status']
1729 if vm_status
== "Suspended":
1731 elif vm_status
.status
== "Powered off":
1733 elif "pause" in action_dict
:
1736 elif "resume" in action_dict
:
1739 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1741 elif "forceOff" in action_dict
:
1743 elif "terminate" in action_dict
:
1745 # elif "createImage" in action_dict:
1746 # server.create_image()
1752 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1754 Get a console for the virtual machine
1756 vm_id: uuid of the VM
1757 console_type, can be:
1758 "novnc" (by default), "xvpvnc" for VNC types,
1759 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1760 Returns dict with the console parameters:
1761 protocol: ssh, ftp, http, https, ...
1762 server: usually ip address
1763 port: the http, ssh, ... port
1764 suffix: extra text, e.g. the http path and query string
1766 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1768 # NOT USED METHODS in current version
1770 def host_vim2gui(self
, host
, server_dict
):
1771 """Transform host dictionary from VIM format to GUI format,
1772 and append to the server_dict
1774 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1776 def get_hosts_info(self
):
1777 """Get the information of deployed hosts
1778 Returns the hosts content"""
1779 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1781 def get_hosts(self
, vim_tenant
):
1782 """Get the hosts and deployed instances
1783 Returns the hosts content"""
1784 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1786 def get_processor_rankings(self
):
1787 """Get the processor rankings in the VIM database"""
1788 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1790 def new_host(self
, host_data
):
1791 """Adds a new host to VIM"""
1792 '''Returns status code of the VIM response'''
1793 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1795 def new_external_port(self
, port_data
):
1796 """Adds a external port to VIM"""
1797 '''Returns the port identifier'''
1798 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1800 def new_external_network(self
, net_name
, net_type
):
1801 """Adds a external network to VIM (shared)"""
1802 '''Returns the network identifier'''
1803 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1805 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1806 """Connects a external port to a network"""
1807 '''Returns status code of the VIM response'''
1808 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1810 def new_vminstancefromJSON(self
, vm_data
):
1811 """Adds a VM instance to VIM"""
1812 '''Returns the instance identifier'''
1813 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1815 def get_network_name_by_id(self
, network_uuid
=None):
1816 """Method gets vcloud director network named based on supplied uuid.
1819 network_uuid: network_id
1822 The return network name.
1825 vca
= self
.connect()
1827 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1829 if not network_uuid
:
1833 org_dict
= self
.get_org(self
.org_uuid
)
1834 if 'networks' in org_dict
:
1835 org_network_dict
= org_dict
['networks']
1836 for net_uuid
in org_network_dict
:
1837 if net_uuid
== network_uuid
:
1838 return org_network_dict
[net_uuid
]
1840 self
.logger
.debug("Exception in get_network_name_by_id")
1841 self
.logger
.debug(traceback
.format_exc())
1845 def get_network_id_by_name(self
, network_name
=None):
1846 """Method gets vcloud director network uuid based on supplied name.
1849 network_name: network_name
1851 The return network uuid.
1852 network_uuid: network_id
1855 vca
= self
.connect()
1857 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1859 if not network_name
:
1860 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1864 org_dict
= self
.get_org(self
.org_uuid
)
1865 if org_dict
and 'networks' in org_dict
:
1866 org_network_dict
= org_dict
['networks']
1867 for net_uuid
,net_name
in org_network_dict
.iteritems():
1868 if net_name
== network_name
:
1871 except KeyError as exp
:
1872 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1876 def list_org_action(self
):
1878 Method leverages vCloud director and query for available organization for particular user
1881 vca - is active VCA connection.
1882 vdc_name - is a vdc name that will be used to query vms action
1885 The return XML respond
1888 vca
= self
.connect()
1890 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1892 url_list
= [vca
.host
, '/api/org']
1893 vm_list_rest_call
= ''.join(url_list
)
1895 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1896 response
= Http
.get(url
=vm_list_rest_call
,
1897 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1900 if response
.status_code
== requests
.codes
.ok
:
1901 return response
.content
1905 def get_org_action(self
, org_uuid
=None):
1907 Method leverages vCloud director and retrieve available object fdr organization.
1910 vca - is active VCA connection.
1911 vdc_name - is a vdc name that will be used to query vms action
1914 The return XML respond
1917 vca
= self
.connect()
1919 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1921 if org_uuid
is None:
1924 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1925 vm_list_rest_call
= ''.join(url_list
)
1927 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1928 response
= Http
.get(url
=vm_list_rest_call
,
1929 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1932 if response
.status_code
== requests
.codes
.ok
:
1933 return response
.content
1937 def get_org(self
, org_uuid
=None):
1939 Method retrieves available organization in vCloud Director
1942 org_uuid - is a organization uuid.
1945 The return dictionary with following key
1946 "network" - for network list under the org
1947 "catalogs" - for network list under the org
1948 "vdcs" - for vdc list under org
1952 vca
= self
.connect()
1954 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1956 if org_uuid
is None:
1959 content
= self
.get_org_action(org_uuid
=org_uuid
)
1964 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1965 for child
in vm_list_xmlroot
:
1966 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1967 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1968 org_dict
['vdcs'] = vdc_list
1969 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1970 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1971 org_dict
['networks'] = network_list
1972 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1973 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1974 org_dict
['catalogs'] = catalog_list
1980 def get_org_list(self
):
1982 Method retrieves available organization in vCloud Director
1985 vca - is active VCA connection.
1988 The return dictionary and key for each entry VDC UUID
1992 vca
= self
.connect()
1994 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1996 content
= self
.list_org_action()
1998 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1999 for vm_xml
in vm_list_xmlroot
:
2000 if vm_xml
.tag
.split("}")[1] == 'Org':
2001 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2002 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2008 def vms_view_action(self
, vdc_name
=None):
2009 """ Method leverages vCloud director vms query call
2012 vca - is active VCA connection.
2013 vdc_name - is a vdc name that will be used to query vms action
2016 The return XML respond
2018 vca
= self
.connect()
2019 if vdc_name
is None:
2022 url_list
= [vca
.host
, '/api/vms/query']
2023 vm_list_rest_call
= ''.join(url_list
)
2025 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2026 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2027 vca
.vcloud_session
.organization
.Link
)
2029 response
= Http
.get(url
=vm_list_rest_call
,
2030 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2033 if response
.status_code
== requests
.codes
.ok
:
2034 return response
.content
2038 def get_vapp_list(self
, vdc_name
=None):
2040 Method retrieves vApp list deployed vCloud director and returns a dictionary
2041 contains a list of all vapp deployed for queried VDC.
2042 The key for a dictionary is vApp UUID
2046 vca - is active VCA connection.
2047 vdc_name - is a vdc name that will be used to query vms action
2050 The return dictionary and key for each entry vapp UUID
2054 if vdc_name
is None:
2057 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2059 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2060 for vm_xml
in vm_list_xmlroot
:
2061 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2062 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2063 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2064 if 'vappTemplate-' in rawuuid
[0]:
2065 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2066 # vm and use raw UUID as key
2067 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2073 def get_vm_list(self
, vdc_name
=None):
2075 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2076 contains a list of all VM's deployed for queried VDC.
2077 The key for a dictionary is VM UUID
2081 vca - is active VCA connection.
2082 vdc_name - is a vdc name that will be used to query vms action
2085 The return dictionary and key for each entry vapp UUID
2089 if vdc_name
is None:
2092 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2094 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2095 for vm_xml
in vm_list_xmlroot
:
2096 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2097 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2098 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2099 if 'vm-' in rawuuid
[0]:
2100 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2101 # vm and use raw UUID as key
2102 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2108 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2110 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2111 contains a list of all VM's deployed for queried VDC.
2112 The key for a dictionary is VM UUID
2116 vca - is active VCA connection.
2117 vdc_name - is a vdc name that will be used to query vms action
2120 The return dictionary and key for each entry vapp UUID
2123 vca
= self
.connect()
2125 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2127 if vdc_name
is None:
2130 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2132 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2133 for vm_xml
in vm_list_xmlroot
:
2134 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2135 # lookup done by UUID
2137 if vapp_name
in vm_xml
.attrib
['container']:
2138 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2139 if 'vm-' in rawuuid
[0]:
2140 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2142 # lookup done by Name
2144 if vapp_name
in vm_xml
.attrib
['name']:
2145 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2146 if 'vm-' in rawuuid
[0]:
2147 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2154 def get_network_action(self
, network_uuid
=None):
2156 Method leverages vCloud director and query network based on network uuid
2159 vca - is active VCA connection.
2160 network_uuid - is a network uuid
2163 The return XML respond
2166 vca
= self
.connect()
2168 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2170 if network_uuid
is None:
2173 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2174 vm_list_rest_call
= ''.join(url_list
)
2176 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2177 response
= Http
.get(url
=vm_list_rest_call
,
2178 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2181 if response
.status_code
== requests
.codes
.ok
:
2182 return response
.content
2186 def get_vcd_network(self
, network_uuid
=None):
2188 Method retrieves available network from vCloud Director
2191 network_uuid - is VCD network UUID
2193 Each element serialized as key : value pair
2195 Following keys available for access. network_configuration['Gateway'}
2199 <IsInherited>true</IsInherited>
2200 <Gateway>172.16.252.100</Gateway>
2201 <Netmask>255.255.255.0</Netmask>
2202 <Dns1>172.16.254.201</Dns1>
2203 <Dns2>172.16.254.202</Dns2>
2204 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2205 <IsEnabled>true</IsEnabled>
2208 <StartAddress>172.16.252.1</StartAddress>
2209 <EndAddress>172.16.252.99</EndAddress>
2214 <FenceMode>bridged</FenceMode>
2217 The return dictionary and key for each entry vapp UUID
2220 network_configuration
= {}
2221 if network_uuid
is None:
2224 content
= self
.get_network_action(network_uuid
=network_uuid
)
2226 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2228 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2229 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2230 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2232 for child
in vm_list_xmlroot
:
2233 if child
.tag
.split("}")[1] == 'IsShared':
2234 network_configuration
['isShared'] = child
.text
.strip()
2235 if child
.tag
.split("}")[1] == 'Configuration':
2236 for configuration
in child
.iter():
2237 tagKey
= configuration
.tag
.split("}")[1].strip()
2239 network_configuration
[tagKey
] = configuration
.text
.strip()
2240 return network_configuration
2244 return network_configuration
2246 def delete_network_action(self
, network_uuid
=None):
2248 Method delete given network from vCloud director
2251 network_uuid - is a network uuid that client wish to delete
2254 The return None or XML respond or false
2257 vca
= self
.connect_as_admin()
2259 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2260 if network_uuid
is None:
2263 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2264 vm_list_rest_call
= ''.join(url_list
)
2266 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2267 response
= Http
.delete(url
=vm_list_rest_call
,
2268 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2272 if response
.status_code
== 202:
2277 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2278 ip_profile
=None, isshared
='true'):
2280 Method create network in vCloud director
2283 network_name - is network name to be created.
2284 net_type - can be 'bridge','data','ptp','mgmt'.
2285 ip_profile is a dict containing the IP parameters of the network
2286 isshared - is a boolean
2287 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2288 It optional attribute. by default if no parent network indicate the first available will be used.
2291 The return network uuid or return None
2294 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2295 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2296 ip_profile
=ip_profile
,
2298 parent_network_uuid
=parent_network_uuid
,
2301 self
.logger
.debug("Failed create network {}.".format(network_name
))
2305 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2306 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2307 if len(vcd_uuid
) == 4:
2308 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2311 self
.logger
.debug("Failed create network {}".format(network_name
))
2314 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2315 ip_profile
=None, isshared
='true'):
2317 Method create network in vCloud director
2320 network_name - is network name to be created.
2321 net_type - can be 'bridge','data','ptp','mgmt'.
2322 ip_profile is a dict containing the IP parameters of the network
2323 isshared - is a boolean
2324 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2325 It optional attribute. by default if no parent network indicate the first available will be used.
2328 The return network uuid or return None
2331 vca
= self
.connect_as_admin()
2333 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2334 if network_name
is None:
2337 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2338 vm_list_rest_call
= ''.join(url_list
)
2339 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2340 response
= Http
.get(url
=vm_list_rest_call
,
2341 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2345 provider_network
= None
2346 available_networks
= None
2347 add_vdc_rest_url
= None
2349 if response
.status_code
!= requests
.codes
.ok
:
2350 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2351 response
.status_code
))
2355 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2356 for child
in vm_list_xmlroot
:
2357 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2358 provider_network
= child
.attrib
.get('href')
2359 # application/vnd.vmware.admin.providervdc+xml
2360 if child
.tag
.split("}")[1] == 'Link':
2361 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2362 and child
.attrib
.get('rel') == 'add':
2363 add_vdc_rest_url
= child
.attrib
.get('href')
2365 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2366 self
.logger
.debug("Respond body {}".format(response
.content
))
2369 # find pvdc provided available network
2370 response
= Http
.get(url
=provider_network
,
2371 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2374 if response
.status_code
!= requests
.codes
.ok
:
2375 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2376 response
.status_code
))
2379 # available_networks.split("/")[-1]
2381 if parent_network_uuid
is None:
2383 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2384 for child
in vm_list_xmlroot
.iter():
2385 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2386 for networks
in child
.iter():
2387 # application/vnd.vmware.admin.network+xml
2388 if networks
.attrib
.get('href') is not None:
2389 available_networks
= networks
.attrib
.get('href')
2394 #Configure IP profile of the network
2395 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2397 gateway_address
=ip_profile
['gateway_address']
2398 dhcp_count
=int(ip_profile
['dhcp_count'])
2399 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2401 if ip_profile
['dhcp_enabled']==True:
2404 dhcp_enabled
='false'
2405 dhcp_start_address
=ip_profile
['dhcp_start_address']
2407 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2408 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2409 end_ip_int
+= dhcp_count
- 1
2410 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2412 ip_version
=ip_profile
['ip_version']
2413 dns_address
=ip_profile
['dns_address']
2415 # either use client provided UUID or search for a first available
2416 # if both are not defined we return none
2417 if parent_network_uuid
is not None:
2418 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2419 add_vdc_rest_url
= ''.join(url_list
)
2422 fence_mode
="isolated"
2424 is_inherited
='false'
2425 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2426 <Description>Openmano created</Description>
2430 <IsInherited>{1:s}</IsInherited>
2431 <Gateway>{2:s}</Gateway>
2432 <Netmask>{3:s}</Netmask>
2434 <IsEnabled>{5:s}</IsEnabled>
2437 <StartAddress>{6:s}</StartAddress>
2438 <EndAddress>{7:s}</EndAddress>
2443 <FenceMode>{8:s}</FenceMode>
2445 <IsShared>{9:s}</IsShared>
2446 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2447 subnet_address
, dns_address
, dhcp_enabled
,
2448 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2451 fence_mode
="bridged"
2452 is_inherited
='false'
2453 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2454 <Description>Openmano created</Description>
2458 <IsInherited>{1:s}</IsInherited>
2459 <Gateway>{2:s}</Gateway>
2460 <Netmask>{3:s}</Netmask>
2462 <IsEnabled>{5:s}</IsEnabled>
2465 <StartAddress>{6:s}</StartAddress>
2466 <EndAddress>{7:s}</EndAddress>
2471 <ParentNetwork href="{8:s}"/>
2472 <FenceMode>{9:s}</FenceMode>
2474 <IsShared>{10:s}</IsShared>
2475 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2476 subnet_address
, dns_address
, dhcp_enabled
,
2477 dhcp_start_address
, dhcp_end_address
, available_networks
,
2478 fence_mode
, isshared
)
2480 headers
= vca
.vcloud_session
.get_vcloud_headers()
2481 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2483 response
= Http
.post(url
=add_vdc_rest_url
,
2489 if response
.status_code
!= 201:
2490 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2491 .format(response
.status_code
))
2493 network
= networkType
.parseString(response
.content
, True)
2494 create_nw_task
= network
.get_Tasks().get_Task()[0]
2496 # if we all ok we respond with content after network creation completes
2497 # otherwise by default return None
2498 if create_nw_task
is not None:
2499 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2500 status
= vca
.block_until_completed(create_nw_task
)
2502 return response
.content
2504 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2505 .format(response
.content
))
2506 except Exception as exp
:
2507 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2511 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2513 Method sets convert CIDR netmask address to normal IP format
2515 cidr_ip : CIDR IP address
2517 netmask : Converted netmask
2519 if cidr_ip
is not None:
2521 network
, net_bits
= cidr_ip
.split('/')
2522 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2528 def get_provider_rest(self
, vca
=None):
2530 Method gets provider vdc view from vcloud director
2533 network_name - is network name to be created.
2534 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2535 It optional attribute. by default if no parent network indicate the first available will be used.
2538 The return xml content of respond or None
2541 url_list
= [vca
.host
, '/api/admin']
2542 response
= Http
.get(url
=''.join(url_list
),
2543 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2547 if response
.status_code
== requests
.codes
.ok
:
2548 return response
.content
2551 def create_vdc(self
, vdc_name
=None):
2555 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2556 if xml_content
is not None:
2558 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2559 for child
in task_resp_xmlroot
:
2560 if child
.tag
.split("}")[1] == 'Owner':
2561 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2562 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2565 self
.logger
.debug("Respond body {}".format(xml_content
))
2569 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2571 Method create vdc in vCloud director based on VDC template.
2572 it uses pre-defined template that must be named openmano
2575 vdc_name - name of a new vdc.
2578 The return xml content of respond or None
2581 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2582 vca
= self
.connect()
2584 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2585 if vdc_name
is None:
2588 url_list
= [vca
.host
, '/api/vdcTemplates']
2589 vm_list_rest_call
= ''.join(url_list
)
2590 response
= Http
.get(url
=vm_list_rest_call
,
2591 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2595 # container url to a template
2596 vdc_template_ref
= None
2598 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2599 for child
in vm_list_xmlroot
:
2600 # application/vnd.vmware.admin.providervdc+xml
2601 # we need find a template from witch we instantiate VDC
2602 if child
.tag
.split("}")[1] == 'VdcTemplate':
2603 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2604 'name') == 'openmano':
2605 vdc_template_ref
= child
.attrib
.get('href')
2607 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2608 self
.logger
.debug("Respond body {}".format(response
.content
))
2611 # if we didn't found required pre defined template we return None
2612 if vdc_template_ref
is None:
2617 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2618 vm_list_rest_call
= ''.join(url_list
)
2619 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2620 <Source href="{1:s}"></Source>
2621 <Description>opnemano</Description>
2622 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2623 headers
= vca
.vcloud_session
.get_vcloud_headers()
2624 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2625 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2627 # if we all ok we respond with content otherwise by default None
2628 if response
.status_code
>= 200 and response
.status_code
< 300:
2629 return response
.content
2632 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2633 self
.logger
.debug("Respond body {}".format(response
.content
))
2637 def create_vdc_rest(self
, vdc_name
=None):
2639 Method create network in vCloud director
2642 network_name - is network name to be created.
2643 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2644 It optional attribute. by default if no parent network indicate the first available will be used.
2647 The return network uuid or return None
2650 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2652 vca
= self
.connect_as_admin()
2654 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2655 if vdc_name
is None:
2658 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2659 vm_list_rest_call
= ''.join(url_list
)
2660 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2661 response
= Http
.get(url
=vm_list_rest_call
,
2662 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2666 provider_vdc_ref
= None
2667 add_vdc_rest_url
= None
2668 available_networks
= None
2670 if response
.status_code
!= requests
.codes
.ok
:
2671 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2672 response
.status_code
))
2676 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2677 for child
in vm_list_xmlroot
:
2678 # application/vnd.vmware.admin.providervdc+xml
2679 if child
.tag
.split("}")[1] == 'Link':
2680 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2681 and child
.attrib
.get('rel') == 'add':
2682 add_vdc_rest_url
= child
.attrib
.get('href')
2684 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2685 self
.logger
.debug("Respond body {}".format(response
.content
))
2688 response
= self
.get_provider_rest(vca
=vca
)
2690 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2691 for child
in vm_list_xmlroot
:
2692 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2693 for sub_child
in child
:
2694 provider_vdc_ref
= sub_child
.attrib
.get('href')
2696 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2697 self
.logger
.debug("Respond body {}".format(response
))
2700 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2701 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2702 <AllocationModel>ReservationPool</AllocationModel>
2703 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2704 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2705 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2706 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2707 <ProviderVdcReference
2708 name="Main Provider"
2710 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2714 headers
= vca
.vcloud_session
.get_vcloud_headers()
2715 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2716 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2719 # if we all ok we respond with content otherwise by default None
2720 if response
.status_code
== 201:
2721 return response
.content
2724 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2726 Method retrieve vapp detail from vCloud director
2729 vapp_uuid - is vapp identifier.
2732 The return network uuid or return None
2738 if need_admin_access
:
2739 vca
= self
.connect_as_admin()
2741 vca
= self
.connect()
2744 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2745 if vapp_uuid
is None:
2748 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2749 get_vapp_restcall
= ''.join(url_list
)
2751 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2752 response
= Http
.get(url
=get_vapp_restcall
,
2753 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2757 if response
.status_code
!= requests
.codes
.ok
:
2758 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2759 response
.status_code
))
2760 return parsed_respond
2763 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2764 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2766 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2767 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2768 'vmw': 'http://www.vmware.com/schema/ovf',
2769 'vm': 'http://www.vmware.com/vcloud/v1.5',
2770 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2771 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2772 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2775 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
2776 if created_section
is not None:
2777 parsed_respond
['created'] = created_section
.text
2779 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
2780 if network_section
is not None and 'networkName' in network_section
.attrib
:
2781 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2783 ipscopes_section
= \
2784 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2786 if ipscopes_section
is not None:
2787 for ipscope
in ipscopes_section
:
2788 for scope
in ipscope
:
2789 tag_key
= scope
.tag
.split("}")[1]
2790 if tag_key
== 'IpRanges':
2791 ip_ranges
= scope
.getchildren()
2792 for ipblock
in ip_ranges
:
2793 for block
in ipblock
:
2794 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2796 parsed_respond
[tag_key
] = scope
.text
2798 # parse children section for other attrib
2799 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
2800 if children_section
is not None:
2801 parsed_respond
['name'] = children_section
.attrib
['name']
2802 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
2803 if "nestedHypervisorEnabled" in children_section
.attrib
else None
2804 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2805 parsed_respond
['status'] = children_section
.attrib
['status']
2806 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2807 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
2809 for adapters
in network_adapter
:
2810 adapter_key
= adapters
.tag
.split("}")[1]
2811 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2812 parsed_respond
['primarynetwork'] = adapters
.text
2813 if adapter_key
== 'NetworkConnection':
2815 if 'network' in adapters
.attrib
:
2816 vnic
['network'] = adapters
.attrib
['network']
2817 for adapter
in adapters
:
2818 setting_key
= adapter
.tag
.split("}")[1]
2819 vnic
[setting_key
] = adapter
.text
2820 nic_list
.append(vnic
)
2822 for link
in children_section
:
2823 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2824 if link
.attrib
['rel'] == 'screen:acquireTicket':
2825 parsed_respond
['acquireTicket'] = link
.attrib
2826 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2827 parsed_respond
['acquireMksTicket'] = link
.attrib
2829 parsed_respond
['interfaces'] = nic_list
2830 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
2831 if vCloud_extension_section
is not None:
2832 vm_vcenter_info
= {}
2833 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
2834 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
2835 if vmext
is not None:
2836 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
2837 vm_vcenter_info
["vim_server_href"] = vmext
.find('vmext:VimServerRef', namespaces
).attrib
['href']
2838 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
2840 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
2841 vm_virtual_hardware_info
= {}
2842 if virtual_hardware_section
is not None:
2843 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
2844 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2845 disk_size
= item
.find("rasd:HostResource" ,namespaces
2846 ).attrib
["{"+namespaces
['vm']+"}capacity"]
2848 vm_virtual_hardware_info
["disk_size"]= disk_size
2851 for link
in virtual_hardware_section
:
2852 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2853 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
2854 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
2857 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
2858 except Exception as exp
:
2859 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
2860 return parsed_respond
2862 def acuire_console(self
, vm_uuid
=None):
2864 vca
= self
.connect()
2866 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2870 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2871 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2872 console_dict
= vm_dict
['acquireTicket']
2873 console_rest_call
= console_dict
['href']
2875 response
= Http
.post(url
=console_rest_call
,
2876 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2880 if response
.status_code
== requests
.codes
.ok
:
2881 return response
.content
2885 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
2887 Method retrieve vm disk details
2890 vapp_uuid - is vapp identifier.
2891 flavor_disk - disk size as specified in VNFD (flavor)
2894 The return network uuid or return None
2898 #Flavor disk is in GB convert it into MB
2899 flavor_disk
= int(flavor_disk
) * 1024
2900 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
2902 vm_name
= vm_details
["name"]
2903 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
2905 if vm_details
and "vm_virtual_hardware" in vm_details
:
2906 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
2907 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
2909 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
2911 if flavor_disk
> vm_disk
:
2912 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
2913 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
2914 vm_disk
, flavor_disk
))
2917 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
2920 except Exception as exp
:
2921 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
2924 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
2926 Method retrieve modify vm disk size
2929 disk_href - vCD API URL to GET and PUT disk data
2930 disk_size - disk size as specified in VNFD (flavor)
2933 The return network uuid or return None
2935 vca
= self
.connect()
2937 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2938 if disk_href
is None or disk_size
is None:
2941 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2942 response
= Http
.get(url
=disk_href
,
2943 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2947 if response
.status_code
!= requests
.codes
.ok
:
2948 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
2949 response
.status_code
))
2952 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
2953 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
2954 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
2956 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
2957 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
2958 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
2959 if disk_item
is not None:
2960 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
2963 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
2964 xml_declaration
=True)
2966 #Send PUT request to modify disk size
2967 headers
= vca
.vcloud_session
.get_vcloud_headers()
2968 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
2970 response
= Http
.put(url
=disk_href
,
2973 verify
=vca
.verify
, logger
=self
.logger
)
2975 if response
.status_code
!= 202:
2976 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
2977 response
.status_code
))
2979 modify_disk_task
= taskType
.parseString(response
.content
, True)
2980 if type(modify_disk_task
) is GenericTask
:
2981 status
= vca
.block_until_completed(modify_disk_task
)
2986 except Exception as exp
:
2987 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
2990 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
2992 Method to attach pci devices to VM
2995 vapp_uuid - uuid of vApp/VM
2996 pci_devices - pci devices infromation as specified in VNFD (flavor)
2999 The status of add pci device task , vm object and
3000 vcenter_conect object
3003 vcenter_conect
= None
3004 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3005 #Assuming password of vCenter user is same as password of vCloud user
3006 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vapp_uuid
)
3007 self
.logger
.info("vm_moref_id, {} vm_vcenter_host {} vm_vcenter_username{} "\
3008 "vm_vcenter_port{}".format(
3009 vm_moref_id
, vm_vcenter_host
,
3010 vm_vcenter_username
, vm_vcenter_port
))
3011 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3013 if hasattr(ssl
, '_create_unverified_context'):
3014 context
= ssl
._create
_unverified
_context
()
3016 no_of_pci_devices
= len(pci_devices
)
3017 if no_of_pci_devices
> 0:
3018 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3019 pwd
=self
.passwd
, port
=int(vm_vcenter_port
) ,
3021 atexit
.register(Disconnect
, vcenter_conect
)
3022 content
= vcenter_conect
.RetrieveContent()
3024 #Get VM and its host
3025 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3026 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3027 if host_obj
and vm_obj
:
3028 #get PCI devies from host on which vapp is currently installed
3029 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3031 if avilable_pci_devices
is None:
3032 #find other hosts with active pci devices
3033 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3038 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3039 #Migrate vm to the host where PCI devices are availble
3040 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3041 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3042 if task
is not None:
3043 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3044 self
.logger
.info("Migrate VM status: {}".format(result
))
3045 host_obj
= new_host_obj
3047 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3048 raise vimconn
.vimconnNotFoundException(
3049 "Fail to migrate VM : {} to host {}".format(
3054 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3055 #Add PCI devices one by one
3056 for pci_device
in avilable_pci_devices
:
3057 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3059 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3061 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3063 self
.logger
.info("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3064 return True, vm_obj
, vcenter_conect
3066 self
.logger
.error("Currently there is no host with"\
3067 " {} number of avaialble PCI devices required for VM {}".format(
3071 raise vimconn
.vimconnNotFoundException(
3072 "Currently there is no host with {} "\
3073 "number of avaialble PCI devices required for VM {}".format(
3077 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3079 except vmodl
.MethodFault
as error
:
3080 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3081 return None, vm_obj
, vcenter_conect
3083 def get_vm_obj(self
, content
, mob_id
):
3085 Method to get the vsphere VM object associated with a given morf ID
3087 vapp_uuid - uuid of vApp/VM
3088 content - vCenter content object
3089 mob_id - mob_id of VM
3097 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3098 [vim
.VirtualMachine
], True
3100 for vm
in container
.view
:
3101 mobID
= vm
._GetMoId
()
3104 host_obj
= vm_obj
.runtime
.host
3106 except Exception as exp
:
3107 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3108 return host_obj
, vm_obj
3110 def get_pci_devices(self
, host
, need_devices
):
3112 Method to get the details of pci devices on given host
3114 host - vSphere host object
3115 need_devices - number of pci devices needed on host
3118 array of pci devices
3122 used_devices_ids
= []
3126 pciPassthruInfo
= host
.config
.pciPassthruInfo
3127 pciDevies
= host
.hardware
.pciDevice
3129 for pci_status
in pciPassthruInfo
:
3130 if pci_status
.passthruActive
:
3131 for device
in pciDevies
:
3132 if device
.id == pci_status
.id:
3133 all_device_ids
.append(device
.id)
3134 all_devices
.append(device
)
3136 #check if devices are in use
3137 avalible_devices
= all_devices
3139 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3140 vm_devices
= vm
.config
.hardware
.device
3141 for device
in vm_devices
:
3142 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3143 if device
.backing
.id in all_device_ids
:
3144 for use_device
in avalible_devices
:
3145 if use_device
.id == device
.backing
.id:
3146 avalible_devices
.remove(use_device
)
3147 used_devices_ids
.append(device
.backing
.id)
3148 self
.logger
.debug("Device {} from devices {}"\
3149 "is in use".format(device
.backing
.id,
3152 if len(avalible_devices
) < need_devices
:
3153 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3155 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3159 required_devices
= avalible_devices
[:need_devices
]
3160 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3161 len(avalible_devices
),
3164 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3166 return required_devices
3168 except Exception as exp
:
3169 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3173 def get_host_and_PCIdevices(self
, content
, need_devices
):
3175 Method to get the details of pci devices infromation on all hosts
3178 content - vSphere host object
3179 need_devices - number of pci devices needed on host
3182 array of pci devices and host object
3185 pci_device_objs
= None
3188 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3189 [vim
.HostSystem
], True)
3190 for host
in container
.view
:
3191 devices
= self
.get_pci_devices(host
, need_devices
)
3194 pci_device_objs
= devices
3196 except Exception as exp
:
3197 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3199 return host_obj
,pci_device_objs
3201 def relocate_vm(self
, dest_host
, vm
) :
3203 Method to get the relocate VM to new host
3206 dest_host - vSphere host object
3207 vm - vSphere VM object
3214 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3215 task
= vm
.Relocate(relocate_spec
)
3216 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3217 except Exception as exp
:
3218 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3219 dest_host
, vm
, exp
))
3222 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3224 Waits and provides updates on a vSphere task
3226 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3229 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3230 if task
.info
.result
is not None and not hideResult
:
3231 self
.logger
.info('{} completed successfully, result: {}'.format(
3235 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3237 self
.logger
.error('{} did not complete successfully: {} '.format(
3242 return task
.info
.result
3244 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3246 Method to add pci device in given VM
3249 host_object - vSphere host object
3250 vm_object - vSphere VM object
3251 host_pci_dev - host_pci_dev must be one of the devices from the
3252 host_object.hardware.pciDevice list
3253 which is configured as a PCI passthrough device
3259 if vm_object
and host_object
and host_pci_dev
:
3261 #Add PCI device to VM
3262 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3263 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3265 if host_pci_dev
.id not in systemid_by_pciid
:
3266 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3269 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3270 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3272 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3273 vendorId
=host_pci_dev
.vendorId
,
3274 deviceName
=host_pci_dev
.deviceName
)
3276 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3278 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3279 new_device_config
.operation
= "add"
3280 vmConfigSpec
= vim
.vm
.ConfigSpec()
3281 vmConfigSpec
.deviceChange
= [new_device_config
]
3283 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3284 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3285 host_pci_dev
, vm_object
, host_object
)
3287 except Exception as exp
:
3288 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3294 def get_vcenter_info_rest(self
, vapp_uuid
):
3296 https://192.169.241.105/api/admin/extension/vimServer/cc82baf9-9f80-4468-bfe9-ce42b3f9dde5
3297 Method to get details of vCenter
3300 vapp_uuid - uuid of vApp or VM
3303 Moref Id of VM and deails of vCenter
3307 vm_vcenter_username
= None
3308 vm_vcenter_port
= None
3310 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3311 if vm_details
and "vm_vcenter_info" in vm_details
:
3312 vm_moref_id
= vm_details
["vm_vcenter_info"]["vm_moref_id"]
3313 vim_server_href
= vm_details
["vm_vcenter_info"]["vim_server_href"]
3316 vca
= self
.connect_as_admin()
3318 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3319 if vim_server_href
is None:
3320 self
.logger
.error("No url to get vcenter details")
3322 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3323 response
= Http
.get(url
=vim_server_href
,
3324 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3328 if response
.status_code
!= requests
.codes
.ok
:
3329 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(vim_server_href
,
3330 response
.status_code
))
3332 namespaces
={"vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3333 "vcloud":"http://www.vmware.com/vcloud/v1.5"
3335 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3336 vm_vcenter_username
= xmlroot_respond
.find('vmext:Username', namespaces
).text
3337 vcenter_url
= xmlroot_respond
.find('vmext:Url', namespaces
).text
3338 vm_vcenter_port
= vcenter_url
.split(":")[2]
3339 vm_vcenter
= vcenter_url
.split(":")[1].split("//")[1]
3341 except Exception as exp
:
3342 self
.logger
.info("Error occurred calling rest api for vcenter information {}".format(exp
))
3344 return vm_moref_id
, vm_vcenter
, vm_vcenter_username
, vm_vcenter_port
3347 def get_vm_pci_details(self
, vmuuid
):
3349 Method to get VM PCI device details from vCenter
3352 vm_obj - vSphere VM object
3355 dict of PCI devives attached to VM
3358 vm_pci_devices_info
= {}
3360 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3361 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3363 if hasattr(ssl
, '_create_unverified_context'):
3364 context
= ssl
._create
_unverified
_context
()
3365 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3366 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3368 atexit
.register(Disconnect
, vcenter_conect
)
3369 content
= vcenter_conect
.RetrieveContent()
3371 #Get VM and its host
3372 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3373 for device
in vm_obj
.config
.hardware
.device
:
3374 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3375 device_details
={'devide_id':device
.backing
.id,
3376 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
3378 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3379 except Exception as exp
:
3380 self
.logger
.info("Error occurred while getting PCI devices infromationn"\
3381 " for VM {} : {}".format(vm_obj
,exp
))
3382 return vm_pci_devices_info