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
36 from xml
.etree
import ElementTree
as XmlElementTree
39 from pyvcloud
import Http
40 from pyvcloud
.vcloudair
import VCA
41 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
42 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
43 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
44 from xml
.sax
.saxutils
import escape
46 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
48 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
49 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
61 # global variable for vcd connector type
62 STANDALONE
= 'standalone'
64 # key for flavor dicts
65 FLAVOR_RAM_KEY
= 'ram'
66 FLAVOR_VCPUS_KEY
= 'vcpus'
68 DEFAULT_IP_PROFILE
= {'gateway_address':"192.168.1.1",
70 'subnet_address':"192.168.1.0/24",
72 'dhcp_start_address':"192.168.1.3",
74 'dns_address':"192.168.1.2"
76 # global variable for wait time
82 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
83 __date__
= "$23-Dec-2016 11:09:29$"
86 # -1: "Could not be created",
92 # 5: "Waiting for user input",
94 # 7: "Unrecognized state",
96 # 9: "Inconsistent state",
97 # 10: "Children do not all have the same status",
98 # 11: "Upload initiated, OVF descriptor pending",
99 # 12: "Upload initiated, copying contents",
100 # 13: "Upload initiated , disk contents pending",
101 # 14: "Upload has been quarantined",
102 # 15: "Upload quarantine period has expired"
104 # mapping vCD status to MANO
105 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
114 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
115 'ERROR': 'ERROR', 'DELETED': 'DELETED'
118 # dict used to store flavor in memory
122 class vimconnector(vimconn
.vimconnector
):
123 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
124 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
126 Constructor create vmware connector to vCloud director.
128 By default construct doesn't validate connection state. So client can create object with None arguments.
129 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
131 a) It initialize organization UUID
132 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
135 uuid - is organization uuid.
136 name - is organization name that must be presented in vCloud director.
137 tenant_id - is VDC uuid it must be presented in vCloud director
138 tenant_name - is VDC name.
139 url - is hostname or ip address of vCloud director
140 url_admin - same as above.
141 user - is user that administrator for organization. Caller must make sure that
142 username has right privileges.
144 password - is password for a user.
146 VMware connector also requires PVDC administrative privileges and separate account.
147 This variables must be passed via config argument dict contains keys
149 dict['admin_username']
150 dict['admin_password']
156 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
157 url_admin
, user
, passwd
, log_level
, config
)
159 self
.logger
= logging
.getLogger('openmano.vim.vmware')
160 self
.logger
.setLevel(10)
165 self
.url_admin
= url_admin
166 self
.tenant_id
= tenant_id
167 self
.tenant_name
= tenant_name
171 self
.admin_password
= None
172 self
.admin_user
= None
175 if tenant_name
is not None:
176 orgnameandtenant
= tenant_name
.split(":")
177 if len(orgnameandtenant
) == 2:
178 self
.tenant_name
= orgnameandtenant
[1]
179 self
.org_name
= orgnameandtenant
[0]
181 self
.tenant_name
= tenant_name
182 elif "orgname" in config
:
183 self
.org_name
= config
['orgname']
186 self
.logger
.setLevel(getattr(logging
, log_level
))
189 self
.admin_user
= config
['admin_username']
190 self
.admin_password
= config
['admin_password']
192 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
198 raise vimconn
.vimconnException('url param can not be NoneType')
200 if not self
.url_admin
: # try to use normal url
201 self
.url_admin
= self
.url
203 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
204 self
.tenant_id
, self
.tenant_name
))
205 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
206 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
208 # initialize organization
209 if self
.user
is not None and self
.passwd
is not None and self
.url
:
210 self
.init_organization()
212 def __getitem__(self
, index
):
215 if index
== 'tenant_id':
216 return self
.tenant_id
217 if index
== 'tenant_name':
218 return self
.tenant_name
221 elif index
== 'org_name':
223 elif index
== 'org_uuid':
225 elif index
== 'user':
227 elif index
== 'passwd':
231 elif index
== 'url_admin':
232 return self
.url_admin
233 elif index
== "config":
236 raise KeyError("Invalid key '%s'" % str(index
))
238 def __setitem__(self
, index
, value
):
241 if index
== 'tenant_id':
242 self
.tenant_id
= value
243 if index
== 'tenant_name':
244 self
.tenant_name
= value
247 elif index
== 'org_name':
248 self
.org_name
= value
249 elif index
== 'org_uuid':
250 self
.org_uuid
= value
251 elif index
== 'user':
253 elif index
== 'passwd':
257 elif index
== 'url_admin':
258 self
.url_admin
= value
260 raise KeyError("Invalid key '%s'" % str(index
))
262 def connect_as_admin(self
):
263 """ Method connect as pvdc admin user to vCloud director.
264 There are certain action that can be done only by provider vdc admin user.
265 Organization creation / provider network creation etc.
268 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
271 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
273 vca_admin
= VCA(host
=self
.url
,
274 username
=self
.admin_user
,
275 service_type
=STANDALONE
,
279 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
281 raise vimconn
.vimconnConnectionException(
282 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
283 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
286 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
291 """ Method connect as normal user to vCloud director.
294 The return vca object that letter can be used to connect to vCloud director as admin for VDC
298 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
301 vca
= VCA(host
=self
.url
,
303 service_type
=STANDALONE
,
308 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
310 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
311 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
314 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
317 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
318 "{} as user: {}".format(self
.org_name
, self
.user
))
322 def init_organization(self
):
323 """ Method initialize organization UUID and VDC parameters.
325 At bare minimum client must provide organization name that present in vCloud director and VDC.
327 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
328 The Org - UUID will be initialized at the run time if data center present in vCloud director.
331 The return vca object that letter can be used to connect to vcloud direct as admin
334 if self
.org_uuid
is None:
335 org_dict
= self
.get_org_list()
337 # we set org UUID at the init phase but we can do it only when we have valid credential.
338 if org_dict
[org
] == self
.org_name
:
340 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
343 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
345 # if well good we require for org details
346 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
348 # we have two case if we want to initialize VDC ID or VDC name at run time
349 # tenant_name provided but no tenant id
350 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
351 vdcs_dict
= org_details_dict
['vdcs']
352 for vdc
in vdcs_dict
:
353 if vdcs_dict
[vdc
] == self
.tenant_name
:
355 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
359 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
360 # case two we have tenant_id but we don't have tenant name so we find and set it.
361 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
362 vdcs_dict
= org_details_dict
['vdcs']
363 for vdc
in vdcs_dict
:
364 if vdc
== self
.tenant_id
:
365 self
.tenant_name
= vdcs_dict
[vdc
]
366 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
370 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
371 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
373 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
374 self
.logger
.debug(traceback
.format_exc())
377 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
378 """ Method adds a new tenant to VIM with this name.
379 This action requires access to create VDC action in vCloud director.
382 tenant_name is tenant_name to be created.
383 tenant_description not used for this call
386 returns the tenant identifier in UUID format.
387 If action is failed method will throw vimconn.vimconnException method
389 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
390 if vdc_task
is not None:
391 vdc_uuid
, value
= vdc_task
.popitem()
392 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
395 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
397 def delete_tenant(self
, tenant_id
=None):
398 """Delete a tenant from VIM"""
399 'Returns the tenant identifier'
400 raise vimconn
.vimconnNotImplemented("Should have implemented this")
402 def get_tenant_list(self
, filter_dict
={}):
403 """Obtain tenants of VIM
404 filter_dict can contain the following keys:
405 name: filter by tenant name
406 id: filter by tenant uuid/id
408 Returns the tenant list of dictionaries:
409 [{'name':'<name>, 'id':'<id>, ...}, ...]
412 org_dict
= self
.get_org(self
.org_uuid
)
413 vdcs_dict
= org_dict
['vdcs']
418 entry
= {'name': vdcs_dict
[k
], 'id': k
}
419 # if caller didn't specify dictionary we return all tenants.
420 if filter_dict
is not None and filter_dict
:
421 filtered_entry
= entry
.copy()
422 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
423 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
424 if filter_dict
== entry
:
425 vdclist
.append(filtered_entry
)
427 vdclist
.append(entry
)
429 self
.logger
.debug("Error in get_tenant_list()")
430 self
.logger
.debug(traceback
.format_exc())
431 raise vimconn
.vimconnException("Incorrect state. {}")
435 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
436 """Adds a tenant network to VIM
438 net_type can be 'bridge','data'.'ptp'.
439 ip_profile is a dict containing the IP parameters of the network
441 Returns the network identifier"""
443 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
444 .format(net_name
, net_type
, ip_profile
, shared
))
450 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
451 ip_profile
=ip_profile
, isshared
=isshared
)
452 if network_uuid
is not None:
455 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
457 def get_vcd_network_list(self
):
458 """ Method available organization for a logged in tenant
461 The return vca object that letter can be used to connect to vcloud direct as admin
464 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
467 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
469 if not self
.tenant_name
:
470 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
472 vdc
= vca
.get_vdc(self
.tenant_name
)
474 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
476 vdc_uuid
= vdc
.get_id().split(":")[3]
477 networks
= vca
.get_networks(vdc
.get_name())
480 for network
in networks
:
482 netid
= network
.get_id().split(":")
486 filter_dict
["name"] = network
.get_name()
487 filter_dict
["id"] = netid
[3]
488 filter_dict
["shared"] = network
.get_IsShared()
489 filter_dict
["tenant_id"] = vdc_uuid
490 if network
.get_status() == 1:
491 filter_dict
["admin_state_up"] = True
493 filter_dict
["admin_state_up"] = False
494 filter_dict
["status"] = "ACTIVE"
495 filter_dict
["type"] = "bridge"
496 network_list
.append(filter_dict
)
497 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
499 self
.logger
.debug("Error in get_vcd_network_list")
500 self
.logger
.debug(traceback
.format_exc())
503 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
506 def get_network_list(self
, filter_dict
={}):
507 """Obtain tenant networks of VIM
509 name: network name OR/AND
510 id: network uuid OR/AND
511 shared: boolean OR/AND
512 tenant_id: tenant OR/AND
513 admin_state_up: boolean
516 [{key : value , key : value}]
518 Returns the network list of dictionaries:
519 [{<the fields at Filter_dict plus some VIM specific>}, ...]
523 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
526 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
528 if not self
.tenant_name
:
529 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
531 vdc
= vca
.get_vdc(self
.tenant_name
)
533 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
535 vdcid
= vdc
.get_id().split(":")[3]
536 networks
= vca
.get_networks(vdc
.get_name())
540 for network
in networks
:
542 net_uuid
= network
.get_id().split(":")
543 if len(net_uuid
) != 4:
546 net_uuid
= net_uuid
[3]
548 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
551 filter_entry
["name"] = network
.get_name()
552 filter_entry
["id"] = net_uuid
553 filter_entry
["shared"] = network
.get_IsShared()
554 filter_entry
["tenant_id"] = vdcid
555 if network
.get_status() == 1:
556 filter_entry
["admin_state_up"] = True
558 filter_entry
["admin_state_up"] = False
559 filter_entry
["status"] = "ACTIVE"
560 filter_entry
["type"] = "bridge"
561 filtered_entry
= filter_entry
.copy()
563 if filter_dict
is not None and filter_dict
:
564 # we remove all the key : value we don't care and match only
566 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
567 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
568 if filter_dict
== filter_entry
:
569 network_list
.append(filtered_entry
)
571 network_list
.append(filtered_entry
)
573 self
.logger
.debug("Error in get_vcd_network_list")
574 self
.logger
.debug(traceback
.format_exc())
576 self
.logger
.debug("Returning {}".format(network_list
))
579 def get_network(self
, net_id
):
580 """Method obtains network details of net_id VIM network
581 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
585 raise vimconn
.vimconnConnectionException("self.connect() is failed")
587 vdc
= vca
.get_vdc(self
.tenant_name
)
588 vdc_id
= vdc
.get_id().split(":")[3]
590 networks
= vca
.get_networks(vdc
.get_name())
594 for network
in networks
:
595 vdc_network_id
= network
.get_id().split(":")
596 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
597 filter_dict
["name"] = network
.get_name()
598 filter_dict
["id"] = vdc_network_id
[3]
599 filter_dict
["shared"] = network
.get_IsShared()
600 filter_dict
["tenant_id"] = vdc_id
601 if network
.get_status() == 1:
602 filter_dict
["admin_state_up"] = True
604 filter_dict
["admin_state_up"] = False
605 filter_dict
["status"] = "ACTIVE"
606 filter_dict
["type"] = "bridge"
607 self
.logger
.debug("Returning {}".format(filter_dict
))
610 self
.logger
.debug("Error in get_network")
611 self
.logger
.debug(traceback
.format_exc())
615 def delete_network(self
, net_id
):
617 Method Deletes a tenant network from VIM, provide the network id.
619 Returns the network identifier or raise an exception
624 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
626 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
627 if vcd_network
is not None and vcd_network
:
628 if self
.delete_network_action(network_uuid
=net_id
):
631 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
633 def refresh_nets_status(self
, net_list
):
634 """Get the status of the networks
635 Params: the list of network identifiers
636 Returns a dictionary with:
637 net_id: #VIM id of this network
638 status: #Mandatory. Text with one of:
639 # DELETED (not found at vim)
640 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
641 # OTHER (Vim reported other status not understood)
642 # ERROR (VIM indicates an ERROR status)
643 # ACTIVE, INACTIVE, DOWN (admin down),
644 # BUILD (on building process)
646 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
647 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
653 raise vimconn
.vimconnConnectionException("self.connect() is failed")
659 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
660 if vcd_network
is not None and vcd_network
:
661 if vcd_network
['status'] == '1':
667 errormsg
= 'Network not found.'
669 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
670 'vim_info': yaml
.safe_dump(vcd_network
)}
672 self
.logger
.debug("Error in refresh_nets_status")
673 self
.logger
.debug(traceback
.format_exc())
677 def get_flavor(self
, flavor_id
):
678 """Obtain flavor details from the VIM
679 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
681 if flavor_id
not in flavorlist
:
682 raise vimconn
.vimconnNotFoundException("Flavor not found.")
683 return flavorlist
[flavor_id
]
685 def new_flavor(self
, flavor_data
):
686 """Adds a tenant flavor to VIM
687 flavor_data contains a dictionary with information, keys:
689 ram: memory (cloud type) in MBytes
690 vpcus: cpus (cloud type)
691 extended: EPA parameters
692 - numas: #items requested in same NUMA
693 memory: number of 1G huge pages memory
694 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
695 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
696 - name: interface name
697 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
698 bandwidth: X Gbps; requested guarantee bandwidth
699 vpci: requested virtual PCI address
706 Returns the flavor identifier"""
708 # generate a new uuid put to internal dict and return it.
709 flavor_id
= uuid
.uuid4()
710 flavorlist
[str(flavor_id
)] = flavor_data
712 return str(flavor_id
)
714 def delete_flavor(self
, flavor_id
):
715 """Deletes a tenant flavor from VIM identify by its id
717 Returns the used id or raise an exception
719 if flavor_id
not in flavorlist
:
720 raise vimconn
.vimconnNotFoundException("Flavor not found.")
722 flavorlist
.pop(flavor_id
, None)
725 def new_image(self
, image_dict
):
727 Adds a tenant image to VIM
729 200, image-id if the image is created
730 <0, message if there is an error
733 return self
.get_image_id_from_path(image_dict
['location'])
735 def delete_image(self
, image_id
):
742 raise vimconn
.vimconnNotImplemented("Should have implemented this")
744 def catalog_exists(self
, catalog_name
, catalogs
):
751 for catalog
in catalogs
:
752 if catalog
.name
== catalog_name
:
756 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
757 """ Create new catalog entry in vCloud director.
760 vca: vCloud director.
761 catalog_name catalog that client wish to create. Note no validation done for a name.
762 Client must make sure that provide valid string representation.
764 Return (bool) True if catalog created.
768 task
= vca
.create_catalog(catalog_name
, catalog_name
)
769 result
= vca
.block_until_completed(task
)
772 catalogs
= vca
.get_catalogs()
775 return self
.catalog_exists(catalog_name
, catalogs
)
777 # noinspection PyIncorrectDocstring
778 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
779 description
='', progress
=False, chunk_bytes
=128 * 1024):
781 Uploads a OVF file to a vCloud catalog
788 :param catalog_name: (str): The name of the catalog to upload the media.
789 :param media_file_name: (str): The name of the local media file to upload.
790 :return: (bool) True if the media file was successfully uploaded, false otherwise.
792 os
.path
.isfile(media_file_name
)
793 statinfo
= os
.stat(media_file_name
)
795 # find a catalog entry where we upload OVF.
796 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
798 # if VCD can parse OVF we upload VMDK file
799 for catalog
in vca
.get_catalogs():
800 if catalog_name
!= catalog
.name
:
802 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
803 link
.get_rel() == 'add', catalog
.get_Link())
804 assert len(link
) == 1
806 <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>
807 """ % (escape(catalog_name
), escape(description
))
808 headers
= vca
.vcloud_session
.get_vcloud_headers()
809 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
810 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
811 if response
.status_code
== requests
.codes
.created
:
812 catalogItem
= XmlElementTree
.fromstring(response
.content
)
813 entity
= [child
for child
in catalogItem
if
814 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
815 href
= entity
.get('href')
817 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
818 verify
=vca
.verify
, logger
=self
.logger
)
820 if response
.status_code
== requests
.codes
.ok
:
821 media
= mediaType
.parseString(response
.content
, True)
822 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
823 media
.get_Files().get_File()[0].get_Link())[0]
824 headers
= vca
.vcloud_session
.get_vcloud_headers()
825 headers
['Content-Type'] = 'Content-Type text/xml'
826 response
= Http
.put(link
.get_href(),
827 data
=open(media_file_name
, 'rb'),
829 verify
=vca
.verify
, logger
=self
.logger
)
830 if response
.status_code
!= requests
.codes
.ok
:
832 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
836 # TODO fix this with aync block
839 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
841 # uploading VMDK file
842 # check status of OVF upload and upload remaining files.
843 response
= Http
.get(template
,
844 headers
=vca
.vcloud_session
.get_vcloud_headers(),
848 if response
.status_code
== requests
.codes
.ok
:
849 media
= mediaType
.parseString(response
.content
, True)
850 number_of_files
= len(media
.get_Files().get_File())
851 for index
in xrange(0, number_of_files
):
852 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
853 media
.get_Files().get_File()[index
].get_Link())
854 for link
in links_list
:
855 # we skip ovf since it already uploaded.
856 if 'ovf' in link
.get_href():
858 # The OVF file and VMDK must be in a same directory
859 head
, tail
= os
.path
.split(media_file_name
)
860 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
861 if not os
.path
.isfile(file_vmdk
):
863 statinfo
= os
.stat(file_vmdk
)
864 if statinfo
.st_size
== 0:
866 hrefvmdk
= link
.get_href()
869 print("Uploading file: {}".format(file_vmdk
))
871 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
873 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
875 bytes_transferred
= 0
876 f
= open(file_vmdk
, 'rb')
877 while bytes_transferred
< statinfo
.st_size
:
878 my_bytes
= f
.read(chunk_bytes
)
879 if len(my_bytes
) <= chunk_bytes
:
880 headers
= vca
.vcloud_session
.get_vcloud_headers()
881 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
882 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
883 headers
['Content-Length'] = str(len(my_bytes
))
884 response
= Http
.put(hrefvmdk
,
890 if response
.status_code
== requests
.codes
.ok
:
891 bytes_transferred
+= len(my_bytes
)
893 progress_bar
.update(bytes_transferred
)
896 'file upload failed with error: [%s] %s' % (response
.status_code
,
903 progress_bar
.finish()
906 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
907 format(catalog_name
, media_file_name
))
910 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
913 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
914 """Upload media file"""
915 # TODO add named parameters for readability
917 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
918 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
920 def validate_uuid4(self
, uuid_string
=None):
921 """ Method validate correct format of UUID.
923 Return: true if string represent valid uuid
926 val
= uuid
.UUID(uuid_string
, version
=4)
931 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
932 """ Method check catalog and return catalog ID in UUID format.
935 catalog_name: catalog name as string
936 catalogs: list of catalogs.
938 Return: catalogs uuid
941 for catalog
in catalogs
:
942 if catalog
.name
== catalog_name
:
943 catalog_id
= catalog
.get_id().split(":")
947 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
948 """ Method check catalog and return catalog name lookup done by catalog UUID.
951 catalog_name: catalog name as string
952 catalogs: list of catalogs.
954 Return: catalogs name or None
957 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
960 for catalog
in catalogs
:
961 catalog_id
= catalog
.get_id().split(":")[3]
962 if catalog_id
== catalog_uuid
:
966 def get_image_id_from_path(self
, path
=None, progress
=False):
967 """ Method upload OVF image to vCloud director.
969 Each OVF image represented as single catalog entry in vcloud director.
970 The method check for existing catalog entry. The check done by file name without file extension.
972 if given catalog name already present method will respond with existing catalog uuid otherwise
973 it will create new catalog entry and upload OVF file to newly created catalog.
975 If method can't create catalog entry or upload a file it will throw exception.
977 Method accept boolean flag progress that will output progress bar. It useful method
978 for standalone upload use case. In case to test large file upload.
981 path: - valid path to OVF file.
982 progress - boolean progress bar show progress bar.
984 Return: if image uploaded correct method will provide image catalog UUID.
988 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
991 raise vimconn
.vimconnException("Image path can't be None.")
993 if not os
.path
.isfile(path
):
994 raise vimconn
.vimconnException("Can't read file. File not found.")
996 if not os
.access(path
, os
.R_OK
):
997 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
999 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1001 dirpath
, filename
= os
.path
.split(path
)
1002 flname
, file_extension
= os
.path
.splitext(path
)
1003 if file_extension
!= '.ovf':
1004 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1005 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1007 catalog_name
= os
.path
.splitext(filename
)[0]
1008 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1009 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1010 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1012 catalogs
= vca
.get_catalogs()
1013 if len(catalogs
) == 0:
1014 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1015 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1017 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1018 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1019 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1021 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1022 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1024 for catalog
in catalogs
:
1025 # search for existing catalog if we find same name we return ID
1026 # TODO optimize this
1027 if catalog
.name
== catalog_md5_name
:
1028 self
.logger
.debug("Found existing catalog entry for {} "
1029 "catalog id {}".format(catalog_name
,
1030 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1031 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1033 # if we didn't find existing catalog we create a new one and upload image.
1034 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1035 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1037 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1039 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1040 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1042 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1044 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1046 def get_vappid(self
, vdc
=None, vapp_name
=None):
1047 """ Method takes vdc object and vApp name and returns vapp uuid or None
1050 vdc: The VDC object.
1051 vapp_name: is application vappp name identifier
1054 The return vApp name otherwise None
1056 if vdc
is None or vapp_name
is None:
1058 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1060 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1061 vdc
.ResourceEntities
.ResourceEntity
)
1063 return refs
[0].href
.split("vapp")[1][1:]
1064 except Exception as e
:
1065 self
.logger
.exception(e
)
1069 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1070 """ Method Method returns True or False if vapp deployed in vCloud director
1073 vca: Connector to VCA
1074 vdc: The VDC object.
1075 vappid: vappid is application identifier
1078 The return True if vApp deployed
1083 refs
= filter(lambda ref
:
1084 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1085 vdc
.ResourceEntities
.ResourceEntity
)
1087 vappid
= ref
.href
.split("vapp")[1][1:]
1088 # find vapp with respected vapp uuid
1089 if vappid
== vapp_uuid
:
1091 except Exception as e
:
1092 self
.logger
.exception(e
)
1096 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1097 """Method returns vApp name from vCD and lookup done by vapp_id.
1100 vca: Connector to VCA
1101 vdc: The VDC object.
1102 vapp_uuid: vappid is application identifier
1105 The return vApp name otherwise None
1109 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1110 vdc
.ResourceEntities
.ResourceEntity
)
1112 # we care only about UUID the rest doesn't matter
1113 vappid
= ref
.href
.split("vapp")[1][1:]
1114 if vappid
== vapp_uuid
:
1115 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1117 tree
= XmlElementTree
.fromstring(response
.content
)
1118 return tree
.attrib
['name']
1119 except Exception as e
:
1120 self
.logger
.exception(e
)
1124 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1125 cloud_config
=None, disk_list
=None):
1126 """Adds a VM instance to VIM
1128 start: indicates if VM must start or boot in pause mode. Ignored
1129 image_id,flavor_id: image and flavor uuid
1130 net_list: list of interfaces, each one is a dictionary with:
1132 net_id: network uuid to connect
1133 vpci: virtual vcpi to assign
1134 model: interface model, virtio, e2000, ...
1136 use: 'data', 'bridge', 'mgmt'
1137 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1138 vim_id: filled/added by this function
1139 cloud_config: can be a text script to be passed directly to cloud-init,
1140 or an object to inject users and ssh keys with format:
1141 key-pairs: [] list of keys to install to the default user
1142 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1143 #TODO ip, security groups
1144 Returns >=0, the instance identifier
1148 self
.logger
.info("Creating new instance for entry {}".format(name
))
1149 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1150 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1151 vca
= self
.connect()
1153 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1155 #new vm name = vmname + tenant_id + uuid
1156 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1157 vmname_andid
= ''.join(new_vm_name
)
1159 # if vm already deployed we return existing uuid
1160 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1161 # if vapp_uuid is not None:
1164 # we check for presence of VDC, Catalog entry and Flavor.
1165 vdc
= vca
.get_vdc(self
.tenant_name
)
1167 raise vimconn
.vimconnNotFoundException(
1168 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1169 catalogs
= vca
.get_catalogs()
1170 if catalogs
is None:
1171 raise vimconn
.vimconnNotFoundException(
1172 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1174 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1175 if catalog_hash_name
:
1176 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1178 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1179 "(Failed retrieve catalog information {})".format(name
, image_id
))
1182 # Set vCPU and Memory based on flavor.
1186 if flavor_id
is not None:
1187 if flavor_id
not in flavorlist
:
1188 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1189 "Failed retrieve flavor information "
1190 "flavor id {}".format(name
, flavor_id
))
1193 flavor
= flavorlist
[flavor_id
]
1194 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1195 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1197 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1199 # image upload creates template name as catalog name space Template.
1200 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1205 # client must provide at least one entry in net_list if not we report error
1206 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1207 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1209 primary_netname
= None
1210 network_mode
= 'bridged'
1211 if net_list
is not None and len(net_list
) > 0:
1212 for net
in net_list
:
1213 if 'use' in net
and net
['use'] == 'mgmt':
1215 if primary_net
is None:
1216 primary_net
= net_list
[0]
1219 primary_net_id
= primary_net
['net_id']
1220 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1221 if 'name' in network_dict
:
1222 primary_netname
= network_dict
['name']
1225 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1227 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1229 # use: 'data', 'bridge', 'mgmt'
1230 # create vApp. Set vcpu and ram based on flavor id.
1231 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1232 self
.get_catalogbyid(image_id
, catalogs
),
1233 network_name
=None, # None while creating vapp
1234 network_mode
=network_mode
,
1235 vm_name
=vmname_andid
,
1236 vm_cpus
=vm_cpus
, # can be None if flavor is None
1237 vm_memory
=vm_memory
) # can be None if flavor is None
1239 if vapptask
is None or vapptask
is False:
1240 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1241 if type(vapptask
) is VappTask
:
1242 vca
.block_until_completed(vapptask
)
1244 # we should have now vapp in undeployed state.
1245 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1247 raise vimconn
.vimconnUnexpectedResponse(
1248 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(vmname_andid
))
1250 # add NICs & connect to networks in netlist
1252 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1254 primary_nic_index
= 0
1255 for net
in net_list
:
1256 # openmano uses network id in UUID format.
1257 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1258 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1259 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1261 if 'net_id' not in net
:
1264 interface_net_id
= net
['net_id']
1265 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1266 interface_network_mode
= net
['use']
1268 if interface_network_mode
== 'mgmt':
1269 primary_nic_index
= nicIndex
1271 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1272 - DHCP (The IP address is obtained from a DHCP service.)
1273 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1274 - NONE (No IP addressing mode specified.)"""
1276 if primary_netname
is not None:
1277 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1279 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1280 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1281 if type(task
) is GenericTask
:
1282 vca
.block_until_completed(task
)
1283 # connect network to VM - with all DHCP by default
1284 self
.logger
.info("new_vminstance(): Connecting VM to a network {}".format(nets
[0].name
))
1285 task
= vapp
.connect_vms(nets
[0].name
,
1286 connection_index
=nicIndex
,
1287 connections_primary_index
=primary_nic_index
,
1288 ip_allocation_mode
='DHCP')
1289 if type(task
) is GenericTask
:
1290 vca
.block_until_completed(task
)
1293 # it might be a case if specific mandatory entry in dict is empty
1294 self
.logger
.debug("Key error {}".format(KeyError.message
))
1295 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1297 # deploy and power on vm
1298 task
= vapp
.poweron()
1299 if type(task
) is TaskType
:
1300 vca
.block_until_completed(task
)
1301 deploytask
= vapp
.deploy(powerOn
='True')
1302 if type(task
) is TaskType
:
1303 vca
.block_until_completed(deploytask
)
1305 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1308 while wait_time
<= MAX_WAIT_TIME
:
1309 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1310 if vapp
and vapp
.me
.deployed
:
1311 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1314 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1315 time
.sleep(INTERVAL_TIME
)
1317 wait_time
+=INTERVAL_TIME
1319 if vapp_uuid
is not None:
1322 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1326 ## based on current discussion
1330 # created: '2016-09-08T11:51:58'
1331 # description: simple-instance.linux1.1
1332 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1333 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1334 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1339 def get_vminstance(self
, vim_vm_uuid
=None):
1340 """Returns the VM instance information from VIM"""
1342 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1343 vca
= self
.connect()
1345 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1347 vdc
= vca
.get_vdc(self
.tenant_name
)
1349 raise vimconn
.vimconnConnectionException(
1350 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1352 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1353 if not vm_info_dict
:
1354 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1355 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1357 status_key
= vm_info_dict
['status']
1360 vm_dict
= {'created': vm_info_dict
['created'],
1361 'description': vm_info_dict
['name'],
1362 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1363 'hostId': vm_info_dict
['vmuuid'],
1365 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1367 if 'interfaces' in vm_info_dict
:
1368 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1370 vm_dict
['interfaces'] = []
1372 vm_dict
= {'created': '',
1374 'status': vcdStatusCode2manoFormat
[int(-1)],
1375 'hostId': vm_info_dict
['vmuuid'],
1376 'error_msg': "Inconsistency state",
1377 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1381 def delete_vminstance(self
, vm__vim_uuid
):
1382 """Method poweroff and remove VM instance from vcloud director network.
1385 vm__vim_uuid: VM UUID
1388 Returns the instance identifier
1391 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1392 vca
= self
.connect()
1394 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1396 vdc
= vca
.get_vdc(self
.tenant_name
)
1398 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1400 raise vimconn
.vimconnException(
1401 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1404 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1405 if vapp_name
is None:
1406 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1407 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1409 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1411 # Delete vApp and wait for status change if task executed and vApp is None.
1412 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1415 if vapp
.me
.deployed
:
1416 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1420 while wait_time
<= MAX_WAIT_TIME
:
1421 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1423 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1424 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1426 power_off_task
= vapp
.poweroff()
1427 if type(power_off_task
) is GenericTask
:
1428 result
= vca
.block_until_completed(power_off_task
)
1433 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1434 time
.sleep(INTERVAL_TIME
)
1436 wait_time
+=INTERVAL_TIME
1438 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1440 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1443 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1446 while wait_time
<= MAX_WAIT_TIME
:
1447 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1449 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1450 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1451 undeploy_task
= vapp
.undeploy(action
='powerOff')
1453 if type(undeploy_task
) is GenericTask
:
1454 result
= vca
.block_until_completed(undeploy_task
)
1459 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1460 time
.sleep(INTERVAL_TIME
)
1462 wait_time
+=INTERVAL_TIME
1465 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1468 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1469 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1471 if vapp
is not None:
1475 while wait_time
<= MAX_WAIT_TIME
:
1476 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1478 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1479 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1481 delete_task
= vapp
.delete()
1483 if type(delete_task
) is GenericTask
:
1484 vca
.block_until_completed(delete_task
)
1485 result
= vca
.block_until_completed(delete_task
)
1489 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1490 time
.sleep(INTERVAL_TIME
)
1492 wait_time
+=INTERVAL_TIME
1495 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1498 self
.logger
.debug(traceback
.format_exc())
1499 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1501 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1502 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1505 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1507 def refresh_vms_status(self
, vm_list
):
1508 """Get the status of the virtual machines and their interfaces/ports
1509 Params: the list of VM identifiers
1510 Returns a dictionary with:
1511 vm_id: #VIM id of this Virtual Machine
1512 status: #Mandatory. Text with one of:
1513 # DELETED (not found at vim)
1514 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1515 # OTHER (Vim reported other status not understood)
1516 # ERROR (VIM indicates an ERROR status)
1517 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1518 # CREATING (on building process), ERROR
1519 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1521 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1522 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1524 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1525 mac_address: #Text format XX:XX:XX:XX:XX:XX
1526 vim_net_id: #network id where this interface is connected
1527 vim_interface_id: #interface/port VIM id
1528 ip_address: #null, or text with IPv4, IPv6 address
1531 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1532 vca
= self
.connect()
1534 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1536 vdc
= vca
.get_vdc(self
.tenant_name
)
1538 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1541 for vmuuid
in vm_list
:
1542 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1543 if vmname
is not None:
1545 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1546 vm_info
= the_vapp
.get_vms_details()
1547 vm_status
= vm_info
[0]['status']
1549 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1550 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1551 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1555 vm_app_networks
= the_vapp
.get_vms_network_info()
1556 for vapp_network
in vm_app_networks
:
1557 for vm_network
in vapp_network
:
1558 if vm_network
['name'] == vmname
:
1559 interface
= {"mac_address": vm_network
['mac'],
1560 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1561 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1562 'ip_address': vm_network
['ip']}
1563 # interface['vim_info'] = yaml.safe_dump(vm_network)
1564 vm_dict
["interfaces"].append(interface
)
1565 # add a vm to vm dict
1566 vms_dict
.setdefault(vmuuid
, vm_dict
)
1568 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1569 self
.logger
.debug(traceback
.format_exc())
1573 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1574 """Send and action over a VM instance from VIM
1575 Returns the vm_id if the action was successfully sent to the VIM"""
1577 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1578 if vm__vim_uuid
is None or action_dict
is None:
1579 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1581 vca
= self
.connect()
1583 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1585 vdc
= vca
.get_vdc(self
.tenant_name
)
1587 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1589 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1590 if vapp_name
is None:
1591 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1592 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1594 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1597 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1598 # TODO fix all status
1599 if "start" in action_dict
:
1600 if action_dict
["start"] == "rebuild":
1601 the_vapp
.deploy(powerOn
=True)
1603 vm_info
= the_vapp
.get_vms_details()
1604 vm_status
= vm_info
[0]['status']
1605 if vm_status
== "Suspended":
1607 elif vm_status
.status
== "Powered off":
1609 elif "pause" in action_dict
:
1612 elif "resume" in action_dict
:
1615 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1617 elif "forceOff" in action_dict
:
1619 elif "terminate" in action_dict
:
1621 # elif "createImage" in action_dict:
1622 # server.create_image()
1628 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1630 Get a console for the virtual machine
1632 vm_id: uuid of the VM
1633 console_type, can be:
1634 "novnc" (by default), "xvpvnc" for VNC types,
1635 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1636 Returns dict with the console parameters:
1637 protocol: ssh, ftp, http, https, ...
1638 server: usually ip address
1639 port: the http, ssh, ... port
1640 suffix: extra text, e.g. the http path and query string
1642 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1644 # NOT USED METHODS in current version
1646 def host_vim2gui(self
, host
, server_dict
):
1647 """Transform host dictionary from VIM format to GUI format,
1648 and append to the server_dict
1650 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1652 def get_hosts_info(self
):
1653 """Get the information of deployed hosts
1654 Returns the hosts content"""
1655 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1657 def get_hosts(self
, vim_tenant
):
1658 """Get the hosts and deployed instances
1659 Returns the hosts content"""
1660 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1662 def get_processor_rankings(self
):
1663 """Get the processor rankings in the VIM database"""
1664 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1666 def new_host(self
, host_data
):
1667 """Adds a new host to VIM"""
1668 '''Returns status code of the VIM response'''
1669 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1671 def new_external_port(self
, port_data
):
1672 """Adds a external port to VIM"""
1673 '''Returns the port identifier'''
1674 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1676 def new_external_network(self
, net_name
, net_type
):
1677 """Adds a external network to VIM (shared)"""
1678 '''Returns the network identifier'''
1679 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1681 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1682 """Connects a external port to a network"""
1683 '''Returns status code of the VIM response'''
1684 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1686 def new_vminstancefromJSON(self
, vm_data
):
1687 """Adds a VM instance to VIM"""
1688 '''Returns the instance identifier'''
1689 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1691 def get_network_name_by_id(self
, network_uuid
=None):
1692 """Method gets vcloud director network named based on supplied uuid.
1695 network_uuid: network_id
1698 The return network name.
1701 vca
= self
.connect()
1703 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1705 if not network_uuid
:
1709 org_dict
= self
.get_org(self
.org_uuid
)
1710 if 'networks' in org_dict
:
1711 org_network_dict
= org_dict
['networks']
1712 for net_uuid
in org_network_dict
:
1713 if net_uuid
== network_uuid
:
1714 return org_network_dict
[net_uuid
]
1716 self
.logger
.debug("Exception in get_network_name_by_id")
1717 self
.logger
.debug(traceback
.format_exc())
1721 def get_network_id_by_name(self
, network_name
=None):
1722 """Method gets vcloud director network uuid based on supplied name.
1725 network_name: network_name
1727 The return network uuid.
1728 network_uuid: network_id
1731 vca
= self
.connect()
1733 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1735 if not network_name
:
1736 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1740 org_dict
= self
.get_org(self
.org_uuid
)
1741 if org_dict
and 'networks' in org_dict
:
1742 org_network_dict
= org_dict
['networks']
1743 for net_uuid
,net_name
in org_network_dict
.iteritems():
1744 if net_name
== network_name
:
1747 except KeyError as exp
:
1748 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1752 def list_org_action(self
):
1754 Method leverages vCloud director and query for available organization for particular user
1757 vca - is active VCA connection.
1758 vdc_name - is a vdc name that will be used to query vms action
1761 The return XML respond
1764 vca
= self
.connect()
1766 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1768 url_list
= [vca
.host
, '/api/org']
1769 vm_list_rest_call
= ''.join(url_list
)
1771 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1772 response
= Http
.get(url
=vm_list_rest_call
,
1773 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1776 if response
.status_code
== requests
.codes
.ok
:
1777 return response
.content
1781 def get_org_action(self
, org_uuid
=None):
1783 Method leverages vCloud director and retrieve available object fdr organization.
1786 vca - is active VCA connection.
1787 vdc_name - is a vdc name that will be used to query vms action
1790 The return XML respond
1793 vca
= self
.connect()
1795 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1797 if org_uuid
is None:
1800 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1801 vm_list_rest_call
= ''.join(url_list
)
1803 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1804 response
= Http
.get(url
=vm_list_rest_call
,
1805 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1808 if response
.status_code
== requests
.codes
.ok
:
1809 return response
.content
1813 def get_org(self
, org_uuid
=None):
1815 Method retrieves available organization in vCloud Director
1818 org_uuid - is a organization uuid.
1821 The return dictionary with following key
1822 "network" - for network list under the org
1823 "catalogs" - for network list under the org
1824 "vdcs" - for vdc list under org
1828 vca
= self
.connect()
1830 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1832 if org_uuid
is None:
1835 content
= self
.get_org_action(org_uuid
=org_uuid
)
1840 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1841 for child
in vm_list_xmlroot
:
1842 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1843 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1844 org_dict
['vdcs'] = vdc_list
1845 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1846 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1847 org_dict
['networks'] = network_list
1848 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1849 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1850 org_dict
['catalogs'] = catalog_list
1856 def get_org_list(self
):
1858 Method retrieves available organization in vCloud Director
1861 vca - is active VCA connection.
1864 The return dictionary and key for each entry VDC UUID
1868 vca
= self
.connect()
1870 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1872 content
= self
.list_org_action()
1874 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1875 for vm_xml
in vm_list_xmlroot
:
1876 if vm_xml
.tag
.split("}")[1] == 'Org':
1877 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1878 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1884 def vms_view_action(self
, vdc_name
=None):
1885 """ Method leverages vCloud director vms query call
1888 vca - is active VCA connection.
1889 vdc_name - is a vdc name that will be used to query vms action
1892 The return XML respond
1894 vca
= self
.connect()
1895 if vdc_name
is None:
1898 url_list
= [vca
.host
, '/api/vms/query']
1899 vm_list_rest_call
= ''.join(url_list
)
1901 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1902 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1903 vca
.vcloud_session
.organization
.Link
)
1905 response
= Http
.get(url
=vm_list_rest_call
,
1906 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1909 if response
.status_code
== requests
.codes
.ok
:
1910 return response
.content
1914 def get_vapp_list(self
, vdc_name
=None):
1916 Method retrieves vApp list deployed vCloud director and returns a dictionary
1917 contains a list of all vapp deployed for queried VDC.
1918 The key for a dictionary is vApp UUID
1922 vca - is active VCA connection.
1923 vdc_name - is a vdc name that will be used to query vms action
1926 The return dictionary and key for each entry vapp UUID
1930 if vdc_name
is None:
1933 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1935 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1936 for vm_xml
in vm_list_xmlroot
:
1937 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1938 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1939 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1940 if 'vappTemplate-' in rawuuid
[0]:
1941 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1942 # vm and use raw UUID as key
1943 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1949 def get_vm_list(self
, vdc_name
=None):
1951 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1952 contains a list of all VM's deployed for queried VDC.
1953 The key for a dictionary is VM UUID
1957 vca - is active VCA connection.
1958 vdc_name - is a vdc name that will be used to query vms action
1961 The return dictionary and key for each entry vapp UUID
1965 if vdc_name
is None:
1968 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1970 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1971 for vm_xml
in vm_list_xmlroot
:
1972 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1973 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1974 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1975 if 'vm-' in rawuuid
[0]:
1976 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1977 # vm and use raw UUID as key
1978 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1984 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1986 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1987 contains a list of all VM's deployed for queried VDC.
1988 The key for a dictionary is VM UUID
1992 vca - is active VCA connection.
1993 vdc_name - is a vdc name that will be used to query vms action
1996 The return dictionary and key for each entry vapp UUID
1999 vca
= self
.connect()
2001 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2003 if vdc_name
is None:
2006 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2008 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2009 for vm_xml
in vm_list_xmlroot
:
2010 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2011 # lookup done by UUID
2013 if vapp_name
in vm_xml
.attrib
['container']:
2014 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2015 if 'vm-' in rawuuid
[0]:
2016 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2018 # lookup done by Name
2020 if vapp_name
in vm_xml
.attrib
['name']:
2021 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2022 if 'vm-' in rawuuid
[0]:
2023 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2030 def get_network_action(self
, network_uuid
=None):
2032 Method leverages vCloud director and query network based on network uuid
2035 vca - is active VCA connection.
2036 network_uuid - is a network uuid
2039 The return XML respond
2042 vca
= self
.connect()
2044 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2046 if network_uuid
is None:
2049 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2050 vm_list_rest_call
= ''.join(url_list
)
2052 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2053 response
= Http
.get(url
=vm_list_rest_call
,
2054 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2057 if response
.status_code
== requests
.codes
.ok
:
2058 return response
.content
2062 def get_vcd_network(self
, network_uuid
=None):
2064 Method retrieves available network from vCloud Director
2067 network_uuid - is VCD network UUID
2069 Each element serialized as key : value pair
2071 Following keys available for access. network_configuration['Gateway'}
2075 <IsInherited>true</IsInherited>
2076 <Gateway>172.16.252.100</Gateway>
2077 <Netmask>255.255.255.0</Netmask>
2078 <Dns1>172.16.254.201</Dns1>
2079 <Dns2>172.16.254.202</Dns2>
2080 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2081 <IsEnabled>true</IsEnabled>
2084 <StartAddress>172.16.252.1</StartAddress>
2085 <EndAddress>172.16.252.99</EndAddress>
2090 <FenceMode>bridged</FenceMode>
2093 The return dictionary and key for each entry vapp UUID
2096 network_configuration
= {}
2097 if network_uuid
is None:
2100 content
= self
.get_network_action(network_uuid
=network_uuid
)
2102 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2104 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2105 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2106 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2108 for child
in vm_list_xmlroot
:
2109 if child
.tag
.split("}")[1] == 'IsShared':
2110 network_configuration
['isShared'] = child
.text
.strip()
2111 if child
.tag
.split("}")[1] == 'Configuration':
2112 for configuration
in child
.iter():
2113 tagKey
= configuration
.tag
.split("}")[1].strip()
2115 network_configuration
[tagKey
] = configuration
.text
.strip()
2116 return network_configuration
2120 return network_configuration
2122 def delete_network_action(self
, network_uuid
=None):
2124 Method delete given network from vCloud director
2127 network_uuid - is a network uuid that client wish to delete
2130 The return None or XML respond or false
2133 vca
= self
.connect_as_admin()
2135 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2136 if network_uuid
is None:
2139 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2140 vm_list_rest_call
= ''.join(url_list
)
2142 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2143 response
= Http
.delete(url
=vm_list_rest_call
,
2144 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2148 if response
.status_code
== 202:
2153 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2154 ip_profile
=None, isshared
='true'):
2156 Method create network in vCloud director
2159 network_name - is network name to be created.
2160 net_type - can be 'bridge','data','ptp','mgmt'.
2161 ip_profile is a dict containing the IP parameters of the network
2162 isshared - is a boolean
2163 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2164 It optional attribute. by default if no parent network indicate the first available will be used.
2167 The return network uuid or return None
2170 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2171 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2172 ip_profile
=ip_profile
,
2174 parent_network_uuid
=parent_network_uuid
,
2177 self
.logger
.debug("Failed create network {}.".format(network_name
))
2181 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2182 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2183 if len(vcd_uuid
) == 4:
2184 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2187 self
.logger
.debug("Failed create network {}".format(network_name
))
2190 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2191 ip_profile
=None, isshared
='true'):
2193 Method create network in vCloud director
2196 network_name - is network name to be created.
2197 net_type - can be 'bridge','data','ptp','mgmt'.
2198 ip_profile is a dict containing the IP parameters of the network
2199 isshared - is a boolean
2200 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2201 It optional attribute. by default if no parent network indicate the first available will be used.
2204 The return network uuid or return None
2207 vca
= self
.connect_as_admin()
2209 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2210 if network_name
is None:
2213 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2214 vm_list_rest_call
= ''.join(url_list
)
2215 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2216 response
= Http
.get(url
=vm_list_rest_call
,
2217 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2221 provider_network
= None
2222 available_networks
= None
2223 add_vdc_rest_url
= None
2225 if response
.status_code
!= requests
.codes
.ok
:
2226 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2227 response
.status_code
))
2231 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2232 for child
in vm_list_xmlroot
:
2233 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2234 provider_network
= child
.attrib
.get('href')
2235 # application/vnd.vmware.admin.providervdc+xml
2236 if child
.tag
.split("}")[1] == 'Link':
2237 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2238 and child
.attrib
.get('rel') == 'add':
2239 add_vdc_rest_url
= child
.attrib
.get('href')
2241 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2242 self
.logger
.debug("Respond body {}".format(response
.content
))
2245 # find pvdc provided available network
2246 response
= Http
.get(url
=provider_network
,
2247 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2250 if response
.status_code
!= requests
.codes
.ok
:
2251 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2252 response
.status_code
))
2255 # available_networks.split("/")[-1]
2257 if parent_network_uuid
is None:
2259 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2260 for child
in vm_list_xmlroot
.iter():
2261 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2262 for networks
in child
.iter():
2263 # application/vnd.vmware.admin.network+xml
2264 if networks
.attrib
.get('href') is not None:
2265 available_networks
= networks
.attrib
.get('href')
2270 #Configure IP profile of the network
2271 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2273 gateway_address
=ip_profile
['gateway_address']
2274 dhcp_count
=int(ip_profile
['dhcp_count'])
2275 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2277 if ip_profile
['dhcp_enabled']==True:
2280 dhcp_enabled
='false'
2281 dhcp_start_address
=ip_profile
['dhcp_start_address']
2283 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2284 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2285 end_ip_int
+= dhcp_count
- 1
2286 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2288 ip_version
=ip_profile
['ip_version']
2289 dns_address
=ip_profile
['dns_address']
2291 # either use client provided UUID or search for a first available
2292 # if both are not defined we return none
2293 if parent_network_uuid
is not None:
2294 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2295 add_vdc_rest_url
= ''.join(url_list
)
2298 fence_mode
="isolated"
2300 is_inherited
='false'
2301 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2302 <Description>Openmano created</Description>
2306 <IsInherited>{1:s}</IsInherited>
2307 <Gateway>{2:s}</Gateway>
2308 <Netmask>{3:s}</Netmask>
2310 <IsEnabled>{5:s}</IsEnabled>
2313 <StartAddress>{6:s}</StartAddress>
2314 <EndAddress>{7:s}</EndAddress>
2319 <FenceMode>{8:s}</FenceMode>
2321 <IsShared>{9:s}</IsShared>
2322 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2323 subnet_address
, dns_address
, dhcp_enabled
,
2324 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2327 fence_mode
="bridged"
2328 is_inherited
='false'
2329 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2330 <Description>Openmano created</Description>
2334 <IsInherited>{1:s}</IsInherited>
2335 <Gateway>{2:s}</Gateway>
2336 <Netmask>{3:s}</Netmask>
2338 <IsEnabled>{5:s}</IsEnabled>
2341 <StartAddress>{6:s}</StartAddress>
2342 <EndAddress>{7:s}</EndAddress>
2347 <ParentNetwork href="{8:s}"/>
2348 <FenceMode>{9:s}</FenceMode>
2350 <IsShared>{10:s}</IsShared>
2351 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2352 subnet_address
, dns_address
, dhcp_enabled
,
2353 dhcp_start_address
, dhcp_end_address
, available_networks
,
2354 fence_mode
, isshared
)
2356 headers
= vca
.vcloud_session
.get_vcloud_headers()
2357 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2359 response
= Http
.post(url
=add_vdc_rest_url
,
2365 if response
.status_code
!= 201:
2366 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}"
2367 .format(response
.status_code
))
2369 network
= networkType
.parseString(response
.content
, True)
2370 create_nw_task
= network
.get_Tasks().get_Task()[0]
2372 # if we all ok we respond with content after network creation completes
2373 # otherwise by default return None
2374 if create_nw_task
is not None:
2375 self
.logger
.debug("Create Network REST : Waiting for Nw creation complete")
2376 status
= vca
.block_until_completed(create_nw_task
)
2378 return response
.content
2380 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2381 .format(response
.content
))
2382 except Exception as exp
:
2383 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2387 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2389 Method sets convert CIDR netmask address to normal IP format
2391 cidr_ip : CIDR IP address
2393 netmask : Converted netmask
2395 if cidr_ip
is not None:
2397 network
, net_bits
= cidr_ip
.split('/')
2398 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2404 def get_provider_rest(self
, vca
=None):
2406 Method gets provider vdc view from vcloud director
2409 network_name - is network name to be created.
2410 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2411 It optional attribute. by default if no parent network indicate the first available will be used.
2414 The return xml content of respond or None
2417 url_list
= [vca
.host
, '/api/admin']
2418 response
= Http
.get(url
=''.join(url_list
),
2419 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2423 if response
.status_code
== requests
.codes
.ok
:
2424 return response
.content
2427 def create_vdc(self
, vdc_name
=None):
2431 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2432 if xml_content
is not None:
2434 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2435 for child
in task_resp_xmlroot
:
2436 if child
.tag
.split("}")[1] == 'Owner':
2437 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2438 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2441 self
.logger
.debug("Respond body {}".format(xml_content
))
2445 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2447 Method create vdc in vCloud director based on VDC template.
2448 it uses pre-defined template that must be named openmano
2451 vdc_name - name of a new vdc.
2454 The return xml content of respond or None
2457 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2458 vca
= self
.connect()
2460 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2461 if vdc_name
is None:
2464 url_list
= [vca
.host
, '/api/vdcTemplates']
2465 vm_list_rest_call
= ''.join(url_list
)
2466 response
= Http
.get(url
=vm_list_rest_call
,
2467 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2471 # container url to a template
2472 vdc_template_ref
= None
2474 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2475 for child
in vm_list_xmlroot
:
2476 # application/vnd.vmware.admin.providervdc+xml
2477 # we need find a template from witch we instantiate VDC
2478 if child
.tag
.split("}")[1] == 'VdcTemplate':
2479 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2480 'name') == 'openmano':
2481 vdc_template_ref
= child
.attrib
.get('href')
2483 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2484 self
.logger
.debug("Respond body {}".format(response
.content
))
2487 # if we didn't found required pre defined template we return None
2488 if vdc_template_ref
is None:
2493 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2494 vm_list_rest_call
= ''.join(url_list
)
2495 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2496 <Source href="{1:s}"></Source>
2497 <Description>opnemano</Description>
2498 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2499 headers
= vca
.vcloud_session
.get_vcloud_headers()
2500 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2501 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2503 # if we all ok we respond with content otherwise by default None
2504 if response
.status_code
>= 200 and response
.status_code
< 300:
2505 return response
.content
2508 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2509 self
.logger
.debug("Respond body {}".format(response
.content
))
2513 def create_vdc_rest(self
, vdc_name
=None):
2515 Method create network in vCloud director
2518 network_name - is network name to be created.
2519 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2520 It optional attribute. by default if no parent network indicate the first available will be used.
2523 The return network uuid or return None
2526 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2528 vca
= self
.connect_as_admin()
2530 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2531 if vdc_name
is None:
2534 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2535 vm_list_rest_call
= ''.join(url_list
)
2536 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2537 response
= Http
.get(url
=vm_list_rest_call
,
2538 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2542 provider_vdc_ref
= None
2543 add_vdc_rest_url
= None
2544 available_networks
= None
2546 if response
.status_code
!= requests
.codes
.ok
:
2547 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2548 response
.status_code
))
2552 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2553 for child
in vm_list_xmlroot
:
2554 # application/vnd.vmware.admin.providervdc+xml
2555 if child
.tag
.split("}")[1] == 'Link':
2556 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2557 and child
.attrib
.get('rel') == 'add':
2558 add_vdc_rest_url
= child
.attrib
.get('href')
2560 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2561 self
.logger
.debug("Respond body {}".format(response
.content
))
2564 response
= self
.get_provider_rest(vca
=vca
)
2566 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2567 for child
in vm_list_xmlroot
:
2568 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2569 for sub_child
in child
:
2570 provider_vdc_ref
= sub_child
.attrib
.get('href')
2572 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2573 self
.logger
.debug("Respond body {}".format(response
))
2576 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2577 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2578 <AllocationModel>ReservationPool</AllocationModel>
2579 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2580 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2581 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2582 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2583 <ProviderVdcReference
2584 name="Main Provider"
2586 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2590 headers
= vca
.vcloud_session
.get_vcloud_headers()
2591 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2592 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2595 # if we all ok we respond with content otherwise by default None
2596 if response
.status_code
== 201:
2597 return response
.content
2600 def get_vapp_details_rest(self
, vapp_uuid
=None):
2602 Method retrieve vapp detail from vCloud director
2605 vapp_uuid - is vapp identifier.
2608 The return network uuid or return None
2613 vca
= self
.connect()
2615 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2616 if vapp_uuid
is None:
2619 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2620 get_vapp_restcall
= ''.join(url_list
)
2621 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2622 response
= Http
.get(url
=get_vapp_restcall
,
2623 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2627 if response
.status_code
!= requests
.codes
.ok
:
2628 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2629 response
.status_code
))
2630 return parsed_respond
2633 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2634 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2636 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2637 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2638 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2640 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2641 if created_section
is not None:
2642 parsed_respond
['created'] = created_section
.text
2644 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2645 if network_section
is not None and 'networkName' in network_section
.attrib
:
2646 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2648 ipscopes_section
= \
2649 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2651 if ipscopes_section
is not None:
2652 for ipscope
in ipscopes_section
:
2653 for scope
in ipscope
:
2654 tag_key
= scope
.tag
.split("}")[1]
2655 if tag_key
== 'IpRanges':
2656 ip_ranges
= scope
.getchildren()
2657 for ipblock
in ip_ranges
:
2658 for block
in ipblock
:
2659 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2661 parsed_respond
[tag_key
] = scope
.text
2663 # parse children section for other attrib
2664 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2665 if children_section
is not None:
2666 parsed_respond
['name'] = children_section
.attrib
['name']
2667 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2668 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2669 parsed_respond
['status'] = children_section
.attrib
['status']
2670 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2671 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2673 for adapters
in network_adapter
:
2674 adapter_key
= adapters
.tag
.split("}")[1]
2675 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2676 parsed_respond
['primarynetwork'] = adapters
.text
2677 if adapter_key
== 'NetworkConnection':
2679 if 'network' in adapters
.attrib
:
2680 vnic
['network'] = adapters
.attrib
['network']
2681 for adapter
in adapters
:
2682 setting_key
= adapter
.tag
.split("}")[1]
2683 vnic
[setting_key
] = adapter
.text
2684 nic_list
.append(vnic
)
2686 for link
in children_section
:
2687 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2688 if link
.attrib
['rel'] == 'screen:acquireTicket':
2689 parsed_respond
['acquireTicket'] = link
.attrib
2690 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2691 parsed_respond
['acquireMksTicket'] = link
.attrib
2693 parsed_respond
['interfaces'] = nic_list
2697 return parsed_respond
2699 def acuire_console(self
, vm_uuid
=None):
2701 vca
= self
.connect()
2703 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2707 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2708 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2709 console_dict
= vm_dict
['acquireTicket']
2710 console_rest_call
= console_dict
['href']
2712 response
= Http
.post(url
=console_rest_call
,
2713 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2717 if response
.status_code
== requests
.codes
.ok
:
2718 return response
.content