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
59 # global variable for vcd connector type
60 STANDALONE
= 'standalone'
62 # key for flavor dicts
63 FLAVOR_RAM_KEY
= 'ram'
64 FLAVOR_VCPUS_KEY
= 'vcpus'
66 # global variable for number of retry
67 DELETE_INSTANCE_RETRY
= 3
71 __author__
= "Mustafa Bayramov"
72 __date__
= "$26-Aug-2016 11:09:29$"
75 # -1: "Could not be created",
81 # 5: "Waiting for user input",
83 # 7: "Unrecognized state",
85 # 9: "Inconsistent state",
86 # 10: "Children do not all have the same status",
87 # 11: "Upload initiated, OVF descriptor pending",
88 # 12: "Upload initiated, copying contents",
89 # 13: "Upload initiated , disk contents pending",
90 # 14: "Upload has been quarantined",
91 # 15: "Upload quarantine period has expired"
93 # mapping vCD status to MANO
94 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
103 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
104 'ERROR': 'ERROR', 'DELETED': 'DELETED'
107 # dict used to store flavor in memory
111 class vimconnector(vimconn
.vimconnector
):
112 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
113 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
115 Constructor create vmware connector to vCloud director.
117 By default construct doesn't validate connection state. So client can create object with None arguments.
118 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
120 a) It initialize organization UUID
121 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
124 uuid - is organization uuid.
125 name - is organization name that must be presented in vCloud director.
126 tenant_id - is VDC uuid it must be presented in vCloud director
127 tenant_name - is VDC name.
128 url - is hostname or ip address of vCloud director
129 url_admin - same as above.
130 user - is user that administrator for organization. Caller must make sure that
131 username has right privileges.
133 password - is password for a user.
135 VMware connector also requires PVDC administrative privileges and separate account.
136 This variables must be passed via config argument dict contains keys
138 dict['admin_username']
139 dict['admin_password']
145 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
146 url_admin
, user
, passwd
, log_level
, config
)
148 self
.logger
= logging
.getLogger('openmano.vim.vmware')
149 self
.logger
.setLevel(10)
154 self
.url_admin
= url_admin
155 self
.tenant_id
= tenant_id
156 self
.tenant_name
= tenant_name
160 self
.admin_password
= None
161 self
.admin_user
= None
164 if tenant_name
is not None:
165 orgnameandtenant
= tenant_name
.split(":")
166 if len(orgnameandtenant
) == 2:
167 self
.tenant_name
= orgnameandtenant
[0]
168 self
.org_name
= orgnameandtenant
[1]
170 self
.tenant_name
= tenant_name
171 elif "orgname" in config
:
172 self
.org_name
= config
['orgname']
174 raise vimconn
.vimconnException(message
="Please indicate valid organization name. "
175 "Either pass by org config attribute "
176 "or as tenant_name:tenant_id.")
178 self
.logger
.setLevel(getattr(logging
, log_level
))
181 self
.admin_user
= config
['admin_username']
182 self
.admin_password
= config
['admin_password']
184 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
190 raise vimconn
.vimconnException('url param can not be NoneType')
192 if not self
.url_admin
: # try to use normal url
193 self
.url_admin
= self
.url
195 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
196 self
.tenant_id
, self
.tenant_name
))
197 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
198 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
200 # initialize organization
201 if self
.user
is not None and self
.passwd
is not None and self
.url
:
202 self
.init_organization()
204 def __getitem__(self
, index
):
207 if index
== 'tenant_id':
208 return self
.tenant_id
209 if index
== 'tenant_name':
210 return self
.tenant_name
213 elif index
== 'org_name':
215 elif index
== 'org_uuid':
217 elif index
== 'user':
219 elif index
== 'passwd':
223 elif index
== 'url_admin':
224 return self
.url_admin
225 elif index
== "config":
228 raise KeyError("Invalid key '%s'" % str(index
))
230 def __setitem__(self
, index
, value
):
233 if index
== 'tenant_id':
234 self
.tenant_id
= value
235 if index
== 'tenant_name':
236 self
.tenant_name
= value
239 elif index
== 'org_name':
240 self
.org_name
= value
241 elif index
== 'org_uuid':
242 self
.org_uuid
= value
243 elif index
== 'user':
245 elif index
== 'passwd':
249 elif index
== 'url_admin':
250 self
.url_admin
= value
252 raise KeyError("Invalid key '%s'" % str(index
))
254 def connect_as_admin(self
):
255 """ Method connect as pvdc admin user to vCloud director.
256 There are certain action that can be done only by provider vdc admin user.
257 Organization creation / provider network creation etc.
260 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
263 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
265 vca_admin
= VCA(host
=self
.url
,
266 username
=self
.admin_user
,
267 service_type
=STANDALONE
,
271 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
273 raise vimconn
.vimconnConnectionException(
274 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
275 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
278 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
283 """ Method connect as normal user to vCloud director.
286 The return vca object that letter can be used to connect to vCloud director as admin for VDC
290 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
293 vca
= VCA(host
=self
.url
,
295 service_type
=STANDALONE
,
300 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
302 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
303 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
306 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
309 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
310 "{} as user: {}".format(self
.org_name
, self
.user
))
314 def init_organization(self
):
315 """ Method initialize organization UUID and VDC parameters.
317 At bare minimum client must provide organization name that present in vCloud director and VDC.
319 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
320 The Org - UUID will be initialized at the run time if data center present in vCloud director.
323 The return vca object that letter can be used to connect to vcloud direct as admin
326 if self
.org_uuid
is None:
327 org_dict
= self
.get_org_list()
329 # we set org UUID at the init phase but we can do it only when we have valid credential.
330 if org_dict
[org
] == self
.org_name
:
332 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
335 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
337 # if well good we require for org details
338 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
340 # we have two case if we want to initialize VDC ID or VDC name at run time
341 # tenant_name provided but no tenant id
342 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
343 vdcs_dict
= org_details_dict
['vdcs']
344 for vdc
in vdcs_dict
:
345 if vdcs_dict
[vdc
] == self
.tenant_name
:
347 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
351 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
352 # case two we have tenant_id but we don't have tenant name so we find and set it.
353 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
354 vdcs_dict
= org_details_dict
['vdcs']
355 for vdc
in vdcs_dict
:
356 if vdc
== self
.tenant_id
:
357 self
.tenant_name
= vdcs_dict
[vdc
]
358 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
362 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
363 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
365 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
366 self
.logger
.debug(traceback
.format_exc())
369 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
370 """ Method adds a new tenant to VIM with this name.
371 This action requires access to create VDC action in vCloud director.
374 tenant_name is tenant_name to be created.
375 tenant_description not used for this call
378 returns the tenant identifier in UUID format.
379 If action is failed method will throw vimconn.vimconnException method
381 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
382 if vdc_task
is not None:
383 vdc_uuid
, value
= vdc_task
.popitem()
384 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
387 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
389 def delete_tenant(self
, tenant_id
=None):
390 """Delete a tenant from VIM"""
391 'Returns the tenant identifier'
392 raise vimconn
.vimconnNotImplemented("Should have implemented this")
394 def get_tenant_list(self
, filter_dict
={}):
395 """Obtain tenants of VIM
396 filter_dict can contain the following keys:
397 name: filter by tenant name
398 id: filter by tenant uuid/id
400 Returns the tenant list of dictionaries:
401 [{'name':'<name>, 'id':'<id>, ...}, ...]
404 org_dict
= self
.get_org(self
.org_uuid
)
405 vdcs_dict
= org_dict
['vdcs']
410 entry
= {'name': vdcs_dict
[k
], 'id': k
}
411 # if caller didn't specify dictionary we return all tenants.
412 if filter_dict
is not None and filter_dict
:
413 filtered_entry
= entry
.copy()
414 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
415 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
416 if filter_dict
== entry
:
417 vdclist
.append(filtered_entry
)
419 vdclist
.append(entry
)
421 self
.logger
.debug("Error in get_tenant_list()")
422 self
.logger
.debug(traceback
.format_exc())
423 raise vimconn
.vimconnException("Incorrect state. {}")
427 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
428 """Adds a tenant network to VIM
430 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
431 ip_profile is a dict containing the IP parameters of the network
433 Returns the network identifier"""
436 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name
, net_type
, ip_profile
, shared
))
442 network_uuid
= self
.create_network(network_name
=net_name
, isshared
=isshared
)
443 if network_uuid
is not None:
446 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
448 def get_vcd_network_list(self
):
449 """ Method available organization for a logged in tenant
452 The return vca object that letter can be used to connect to vcloud direct as admin
455 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
458 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
460 if not self
.tenant_name
:
461 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
463 vdc
= vca
.get_vdc(self
.tenant_name
)
465 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
467 vdc_uuid
= vdc
.get_id().split(":")[3]
468 networks
= vca
.get_networks(vdc
.get_name())
471 for network
in networks
:
473 netid
= network
.get_id().split(":")
477 filter_dict
["name"] = network
.get_name()
478 filter_dict
["id"] = netid
[3]
479 filter_dict
["shared"] = network
.get_IsShared()
480 filter_dict
["tenant_id"] = vdc_uuid
481 if network
.get_status() == 1:
482 filter_dict
["admin_state_up"] = True
484 filter_dict
["admin_state_up"] = False
485 filter_dict
["status"] = "ACTIVE"
486 filter_dict
["type"] = "bridge"
487 network_list
.append(filter_dict
)
488 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
490 self
.logger
.debug("Error in get_vcd_network_list")
491 self
.logger
.debug(traceback
.format_exc())
494 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
497 def get_network_list(self
, filter_dict
={}):
498 """Obtain tenant networks of VIM
500 name: network name OR/AND
501 id: network uuid OR/AND
502 shared: boolean OR/AND
503 tenant_id: tenant OR/AND
504 admin_state_up: boolean
507 [{key : value , key : value}]
509 Returns the network list of dictionaries:
510 [{<the fields at Filter_dict plus some VIM specific>}, ...]
514 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
517 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
519 if not self
.tenant_name
:
520 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
522 vdc
= vca
.get_vdc(self
.tenant_name
)
524 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
526 vdcid
= vdc
.get_id().split(":")[3]
527 networks
= vca
.get_networks(vdc
.get_name())
531 for network
in networks
:
533 net_uuid
= network
.get_id().split(":")
534 if len(net_uuid
) != 4:
537 net_uuid
= net_uuid
[3]
539 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
542 filter_entry
["name"] = network
.get_name()
543 filter_entry
["id"] = net_uuid
544 filter_entry
["shared"] = network
.get_IsShared()
545 filter_entry
["tenant_id"] = vdcid
546 if network
.get_status() == 1:
547 filter_entry
["admin_state_up"] = True
549 filter_entry
["admin_state_up"] = False
550 filter_entry
["status"] = "ACTIVE"
551 filter_entry
["type"] = "bridge"
552 filtered_entry
= filter_entry
.copy()
554 if filter_dict
is not None and filter_dict
:
555 # we remove all the key : value we don't care and match only
557 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
558 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
559 if filter_dict
== filter_entry
:
560 network_list
.append(filtered_entry
)
562 network_list
.append(filtered_entry
)
564 self
.logger
.debug("Error in get_vcd_network_list")
565 self
.logger
.debug(traceback
.format_exc())
567 self
.logger
.debug("Returning {}".format(network_list
))
570 def get_network(self
, net_id
):
571 """Method obtains network details of net_id VIM network
572 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
576 raise vimconn
.vimconnConnectionException("self.connect() is failed")
578 vdc
= vca
.get_vdc(self
.tenant_name
)
579 vdc_id
= vdc
.get_id().split(":")[3]
581 networks
= vca
.get_networks(vdc
.get_name())
585 for network
in networks
:
586 vdc_network_id
= network
.get_id().split(":")
587 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
588 filter_dict
["name"] = network
.get_name()
589 filter_dict
["id"] = vdc_network_id
[3]
590 filter_dict
["shared"] = network
.get_IsShared()
591 filter_dict
["tenant_id"] = vdc_id
592 if network
.get_status() == 1:
593 filter_dict
["admin_state_up"] = True
595 filter_dict
["admin_state_up"] = False
596 filter_dict
["status"] = "ACTIVE"
597 filter_dict
["type"] = "bridge"
598 self
.logger
.debug("Returning {}".format(filter_dict
))
601 self
.logger
.debug("Error in get_network")
602 self
.logger
.debug(traceback
.format_exc())
606 def delete_network(self
, net_id
):
608 Method Deletes a tenant network from VIM, provide the network id.
610 Returns the network identifier or raise an exception
615 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
617 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
618 if vcd_network
is not None and vcd_network
:
619 if self
.delete_network_action(network_uuid
=net_id
):
622 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
624 def refresh_nets_status(self
, net_list
):
625 """Get the status of the networks
626 Params: the list of network identifiers
627 Returns a dictionary with:
628 net_id: #VIM id of this network
629 status: #Mandatory. Text with one of:
630 # DELETED (not found at vim)
631 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
632 # OTHER (Vim reported other status not understood)
633 # ERROR (VIM indicates an ERROR status)
634 # ACTIVE, INACTIVE, DOWN (admin down),
635 # BUILD (on building process)
637 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
638 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
644 raise vimconn
.vimconnConnectionException("self.connect() is failed")
650 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
651 if vcd_network
is not None and vcd_network
:
652 if vcd_network
['status'] == '1':
658 errormsg
= 'Network not found.'
660 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
661 'vim_info': yaml
.safe_dump(vcd_network
)}
663 self
.logger
.debug("Error in refresh_nets_status")
664 self
.logger
.debug(traceback
.format_exc())
668 def get_flavor(self
, flavor_id
):
669 """Obtain flavor details from the VIM
670 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
672 if flavor_id
not in flavorlist
:
673 raise vimconn
.vimconnNotFoundException("Flavor not found.")
674 return flavorlist
[flavor_id
]
676 def new_flavor(self
, flavor_data
):
677 """Adds a tenant flavor to VIM
678 flavor_data contains a dictionary with information, keys:
680 ram: memory (cloud type) in MBytes
681 vpcus: cpus (cloud type)
682 extended: EPA parameters
683 - numas: #items requested in same NUMA
684 memory: number of 1G huge pages memory
685 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
686 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
687 - name: interface name
688 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
689 bandwidth: X Gbps; requested guarantee bandwidth
690 vpci: requested virtual PCI address
697 Returns the flavor identifier"""
699 # generate a new uuid put to internal dict and return it.
700 flavor_id
= uuid
.uuid4()
701 flavorlist
[str(flavor_id
)] = flavor_data
703 return str(flavor_id
)
705 def delete_flavor(self
, flavor_id
):
706 """Deletes a tenant flavor from VIM identify by its id
708 Returns the used id or raise an exception
710 if flavor_id
not in flavorlist
:
711 raise vimconn
.vimconnNotFoundException("Flavor not found.")
713 flavorlist
.pop(flavor_id
, None)
716 def new_image(self
, image_dict
):
718 Adds a tenant image to VIM
720 200, image-id if the image is created
721 <0, message if there is an error
724 return self
.get_image_id_from_path(image_dict
['location'])
726 def delete_image(self
, image_id
):
733 raise vimconn
.vimconnNotImplemented("Should have implemented this")
735 def catalog_exists(self
, catalog_name
, catalogs
):
742 for catalog
in catalogs
:
743 if catalog
.name
== catalog_name
:
747 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
748 """ Create new catalog entry in vCloud director.
751 vca: vCloud director.
752 catalog_name catalog that client wish to create. Note no validation done for a name.
753 Client must make sure that provide valid string representation.
755 Return (bool) True if catalog created.
759 task
= vca
.create_catalog(catalog_name
, catalog_name
)
760 result
= vca
.block_until_completed(task
)
763 catalogs
= vca
.get_catalogs()
766 return self
.catalog_exists(catalog_name
, catalogs
)
768 # noinspection PyIncorrectDocstring
769 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
770 description
='', progress
=False, chunk_bytes
=128 * 1024):
772 Uploads a OVF file to a vCloud catalog
779 :param catalog_name: (str): The name of the catalog to upload the media.
780 :param media_file_name: (str): The name of the local media file to upload.
781 :return: (bool) True if the media file was successfully uploaded, false otherwise.
783 os
.path
.isfile(media_file_name
)
784 statinfo
= os
.stat(media_file_name
)
786 # find a catalog entry where we upload OVF.
787 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
789 # if VCD can parse OVF we upload VMDK file
790 for catalog
in vca
.get_catalogs():
791 if catalog_name
!= catalog
.name
:
793 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
794 link
.get_rel() == 'add', catalog
.get_Link())
795 assert len(link
) == 1
797 <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>
798 """ % (escape(catalog_name
), escape(description
))
799 headers
= vca
.vcloud_session
.get_vcloud_headers()
800 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
801 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
802 if response
.status_code
== requests
.codes
.created
:
803 catalogItem
= XmlElementTree
.fromstring(response
.content
)
804 entity
= [child
for child
in catalogItem
if
805 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
806 href
= entity
.get('href')
808 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
809 verify
=vca
.verify
, logger
=self
.logger
)
811 if response
.status_code
== requests
.codes
.ok
:
812 media
= mediaType
.parseString(response
.content
, True)
813 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
814 media
.get_Files().get_File()[0].get_Link())[0]
815 headers
= vca
.vcloud_session
.get_vcloud_headers()
816 headers
['Content-Type'] = 'Content-Type text/xml'
817 response
= Http
.put(link
.get_href(),
818 data
=open(media_file_name
, 'rb'),
820 verify
=vca
.verify
, logger
=self
.logger
)
821 if response
.status_code
!= requests
.codes
.ok
:
823 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
827 # TODO fix this with aync block
830 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
832 # uploading VMDK file
833 # check status of OVF upload and upload remaining files.
834 response
= Http
.get(template
,
835 headers
=vca
.vcloud_session
.get_vcloud_headers(),
839 if response
.status_code
== requests
.codes
.ok
:
840 media
= mediaType
.parseString(response
.content
, True)
841 number_of_files
= len(media
.get_Files().get_File())
842 for index
in xrange(0, number_of_files
):
843 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
844 media
.get_Files().get_File()[index
].get_Link())
845 for link
in links_list
:
846 # we skip ovf since it already uploaded.
847 if 'ovf' in link
.get_href():
849 # The OVF file and VMDK must be in a same directory
850 head
, tail
= os
.path
.split(media_file_name
)
851 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
852 if not os
.path
.isfile(file_vmdk
):
854 statinfo
= os
.stat(file_vmdk
)
855 if statinfo
.st_size
== 0:
857 hrefvmdk
= link
.get_href()
860 print("Uploading file: {}".format(file_vmdk
))
862 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
864 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
866 bytes_transferred
= 0
867 f
= open(file_vmdk
, 'rb')
868 while bytes_transferred
< statinfo
.st_size
:
869 my_bytes
= f
.read(chunk_bytes
)
870 if len(my_bytes
) <= chunk_bytes
:
871 headers
= vca
.vcloud_session
.get_vcloud_headers()
872 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
873 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
874 headers
['Content-Length'] = str(len(my_bytes
))
875 response
= Http
.put(hrefvmdk
,
881 if response
.status_code
== requests
.codes
.ok
:
882 bytes_transferred
+= len(my_bytes
)
884 progress_bar
.update(bytes_transferred
)
887 'file upload failed with error: [%s] %s' % (response
.status_code
,
894 progress_bar
.finish()
897 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
898 format(catalog_name
, media_file_name
))
901 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
904 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
905 """Upload media file"""
906 # TODO add named parameters for readability
908 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
909 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
911 def validate_uuid4(self
, uuid_string
=None):
912 """ Method validate correct format of UUID.
914 Return: true if string represent valid uuid
917 val
= uuid
.UUID(uuid_string
, version
=4)
922 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
923 """ Method check catalog and return catalog ID in UUID format.
926 catalog_name: catalog name as string
927 catalogs: list of catalogs.
929 Return: catalogs uuid
932 for catalog
in catalogs
:
933 if catalog
.name
== catalog_name
:
934 catalog_id
= catalog
.get_id().split(":")
938 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
939 """ Method check catalog and return catalog name lookup done by catalog UUID.
942 catalog_name: catalog name as string
943 catalogs: list of catalogs.
945 Return: catalogs name or None
948 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
951 for catalog
in catalogs
:
952 catalog_id
= catalog
.get_id().split(":")[3]
953 if catalog_id
== catalog_uuid
:
957 def get_image_id_from_path(self
, path
=None, progress
=False):
958 """ Method upload OVF image to vCloud director.
960 Each OVF image represented as single catalog entry in vcloud director.
961 The method check for existing catalog entry. The check done by file name without file extension.
963 if given catalog name already present method will respond with existing catalog uuid otherwise
964 it will create new catalog entry and upload OVF file to newly created catalog.
966 If method can't create catalog entry or upload a file it will throw exception.
968 Method accept boolean flag progress that will output progress bar. It useful method
969 for standalone upload use case. In case to test large file upload.
972 path: - valid path to OVF file.
973 progress - boolean progress bar show progress bar.
975 Return: if image uploaded correct method will provide image catalog UUID.
979 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
982 raise vimconn
.vimconnException("Image path can't be None.")
984 if not os
.path
.isfile(path
):
985 raise vimconn
.vimconnException("Can't read file. File not found.")
987 if not os
.access(path
, os
.R_OK
):
988 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
990 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
992 dirpath
, filename
= os
.path
.split(path
)
993 flname
, file_extension
= os
.path
.splitext(path
)
994 if file_extension
!= '.ovf':
995 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
996 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
998 catalog_name
= os
.path
.splitext(filename
)[0]
999 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1000 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1001 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1003 catalogs
= vca
.get_catalogs()
1004 if len(catalogs
) == 0:
1005 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1006 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1008 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1009 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1010 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1012 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1013 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1015 for catalog
in catalogs
:
1016 # search for existing catalog if we find same name we return ID
1017 # TODO optimize this
1018 if catalog
.name
== catalog_md5_name
:
1019 self
.logger
.debug("Found existing catalog entry for {} "
1020 "catalog id {}".format(catalog_name
,
1021 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1022 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1024 # if we didn't find existing catalog we create a new one and upload image.
1025 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1026 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1028 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1030 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1031 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1033 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1035 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1037 def get_vappid(self
, vdc
=None, vapp_name
=None):
1038 """ Method takes vdc object and vApp name and returns vapp uuid or None
1041 vdc: The VDC object.
1042 vapp_name: is application vappp name identifier
1045 The return vApp name otherwise None
1047 if vdc
is None or vapp_name
is None:
1049 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1051 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1052 vdc
.ResourceEntities
.ResourceEntity
)
1054 return refs
[0].href
.split("vapp")[1][1:]
1055 except Exception as e
:
1056 self
.logger
.exception(e
)
1060 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1061 """ Method Method returns True or False if vapp deployed in vCloud director
1064 vca: Connector to VCA
1065 vdc: The VDC object.
1066 vappid: vappid is application identifier
1069 The return True if vApp deployed
1074 refs
= filter(lambda ref
:
1075 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1076 vdc
.ResourceEntities
.ResourceEntity
)
1078 vappid
= ref
.href
.split("vapp")[1][1:]
1079 # find vapp with respected vapp uuid
1080 if vappid
== vapp_uuid
:
1082 except Exception as e
:
1083 self
.logger
.exception(e
)
1087 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1088 """Method returns vApp name from vCD and lookup done by vapp_id.
1091 vca: Connector to VCA
1092 vdc: The VDC object.
1093 vapp_uuid: vappid is application identifier
1096 The return vApp name otherwise None
1100 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1101 vdc
.ResourceEntities
.ResourceEntity
)
1103 # we care only about UUID the rest doesn't matter
1104 vappid
= ref
.href
.split("vapp")[1][1:]
1105 if vappid
== vapp_uuid
:
1106 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1108 tree
= XmlElementTree
.fromstring(response
.content
)
1109 return tree
.attrib
['name']
1110 except Exception as e
:
1111 self
.logger
.exception(e
)
1115 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1117 """Adds a VM instance to VIM
1119 start: indicates if VM must start or boot in pause mode. Ignored
1120 image_id,flavor_id: image and flavor uuid
1121 net_list: list of interfaces, each one is a dictionary with:
1123 net_id: network uuid to connect
1124 vpci: virtual vcpi to assign
1125 model: interface model, virtio, e2000, ...
1127 use: 'data', 'bridge', 'mgmt'
1128 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1129 vim_id: filled/added by this function
1130 cloud_config: can be a text script to be passed directly to cloud-init,
1131 or an object to inject users and ssh keys with format:
1132 key-pairs: [] list of keys to install to the default user
1133 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1134 #TODO ip, security groups
1135 Returns >=0, the instance identifier
1139 self
.logger
.info("Creating new instance for entry {}".format(name
))
1140 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1141 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1142 vca
= self
.connect()
1144 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1146 #new vm name = vmname + tenant_id + uuid
1147 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1148 vmname_andid
= ''.join(new_vm_name
)
1150 # if vm already deployed we return existing uuid
1151 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1152 # if vapp_uuid is not None:
1155 # we check for presence of VDC, Catalog entry and Flavor.
1156 vdc
= vca
.get_vdc(self
.tenant_name
)
1158 raise vimconn
.vimconnNotFoundException(
1159 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1160 catalogs
= vca
.get_catalogs()
1161 if catalogs
is None:
1162 raise vimconn
.vimconnNotFoundException(
1163 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1165 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1166 if catalog_hash_name
:
1167 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1169 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1170 "(Failed retrieve catalog information {})".format(name
, image_id
))
1173 # Set vCPU and Memory based on flavor.
1177 if flavor_id
is not None:
1178 if flavor_id
not in flavorlist
:
1179 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1180 "Failed retrieve flavor information "
1181 "flavor id {}".format(name
, flavor_id
))
1184 flavor
= flavorlist
[flavor_id
]
1185 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1186 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1188 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1190 # image upload creates template name as catalog name space Template.
1191 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1196 # client must provide at least one entry in net_list if not we report error
1198 primary_netname
= None
1199 network_mode
= 'bridged'
1200 if net_list
is not None and len(net_list
) > 0:
1201 primary_net
= net_list
[0]
1202 if primary_net
is None:
1203 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1206 primary_net_id
= primary_net
['net_id']
1207 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1208 if 'name' in network_dict
:
1209 primary_netname
= network_dict
['name']
1210 self
.logger
.info("Connecting VM to a network name {} "
1211 " network id {}".format(primary_netname
, primary_net_id
))
1212 if 'use' in primary_net
:
1213 if primary_net
['use'] == 'bridge':
1214 network_mode
= 'bridged'
1216 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1218 # use: 'data', 'bridge', 'mgmt'
1219 # create vApp. Set vcpu and ram based on flavor id.
1220 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1221 self
.get_catalogbyid(image_id
, catalogs
),
1222 network_name
=primary_netname
, # can be None if net_list None
1223 network_mode
=network_mode
,
1224 vm_name
=vmname_andid
,
1225 vm_cpus
=vm_cpus
, # can be None if flavor is None
1226 vm_memory
=vm_memory
) # can be None if flavor is None
1228 if vapptask
is None or vapptask
is False:
1229 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1230 if type(vapptask
) is VappTask
:
1231 vca
.block_until_completed(vapptask
)
1233 # we should have now vapp in undeployed state.
1234 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1236 raise vimconn
.vimconnUnexpectedResponse(
1237 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(vmname_andid
))
1241 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1243 for net
in net_list
:
1244 # openmano uses network id in UUID format.
1245 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1246 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1247 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1249 if 'net_id' not in net
:
1252 interface_net_id
= net
['net_id']
1253 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1254 interface_network_mode
= net
['use']
1256 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1257 - DHCP (The IP address is obtained from a DHCP service.)
1258 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1259 - NONE (No IP addressing mode specified.)"""
1261 if primary_netname
is not None:
1262 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1264 self
.logger
.info("Found requested network: {}".format(nets
[0].name
))
1265 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1266 if type(task
) is GenericTask
:
1267 vca
.block_until_completed(task
)
1268 # connect network to VM
1269 # TODO figure out mapping between openmano representation to vCloud director.
1270 # one idea use first nic as management DHCP all remaining in bridge mode
1271 self
.logger
.info("Connecting VM to a network network {}".format(nets
[0].name
))
1272 task
= vapp
.connect_vms(nets
[0].name
,
1273 connection_index
=nicIndex
,
1274 connections_primary_index
=nicIndex
,
1275 ip_allocation_mode
='DHCP')
1276 if type(task
) is GenericTask
:
1277 vca
.block_until_completed(task
)
1280 # it might be a case if specific mandatory entry in dict is empty
1281 self
.logger
.debug("Key error {}".format(KeyError.message
))
1282 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1284 # deploy and power on vm
1285 task
= vapp
.poweron()
1286 if type(task
) is TaskType
:
1287 vca
.block_until_completed(task
)
1288 deploytask
= vapp
.deploy(powerOn
='True')
1289 if type(task
) is TaskType
:
1290 vca
.block_until_completed(deploytask
)
1292 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1293 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1294 if vapp_uuid
is not None:
1297 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1301 ## based on current discussion
1305 # created: '2016-09-08T11:51:58'
1306 # description: simple-instance.linux1.1
1307 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1308 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1309 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1314 def get_vminstance(self
, vim_vm_uuid
=None):
1315 """Returns the VM instance information from VIM"""
1317 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1318 vca
= self
.connect()
1320 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1322 vdc
= vca
.get_vdc(self
.tenant_name
)
1324 raise vimconn
.vimconnConnectionException(
1325 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1327 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1328 if not vm_info_dict
:
1329 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1330 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1332 status_key
= vm_info_dict
['status']
1335 vm_dict
= {'created': vm_info_dict
['created'],
1336 'description': vm_info_dict
['name'],
1337 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1338 'hostId': vm_info_dict
['vmuuid'],
1340 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1342 if 'interfaces' in vm_info_dict
:
1343 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1345 vm_dict
['interfaces'] = []
1347 vm_dict
= {'created': '',
1349 'status': vcdStatusCode2manoFormat
[int(-1)],
1350 'hostId': vm_info_dict
['vmuuid'],
1351 'error_msg': "Inconsistency state",
1352 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1356 def delete_vminstance(self
, vm__vim_uuid
):
1357 """Method poweroff and remove VM instance from vcloud director network.
1360 vm__vim_uuid: VM UUID
1363 Returns the instance identifier
1366 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1367 vca
= self
.connect()
1369 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1371 vdc
= vca
.get_vdc(self
.tenant_name
)
1373 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1375 raise vimconn
.vimconnException(
1376 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1379 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1380 if vapp_name
is None:
1381 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1382 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1384 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1386 # Delete vApp and wait for status change if task executed and vApp is None.
1387 # We successfully delete vApp from vCloud
1388 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1389 # poweroff vapp / undeploy and delete
1390 power_off_task
= vapp
.poweroff()
1391 if type(power_off_task
) is GenericTask
:
1392 vca
.block_until_completed(power_off_task
)
1394 if not power_off_task
:
1395 self
.logger
.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid
))
1398 if vapp
.me
.deployed
:
1399 undeploy_task
= vapp
.undeploy()
1400 if type(undeploy_task
) is GenericTask
:
1402 while retry
<= DELETE_INSTANCE_RETRY
:
1403 result
= vca
.block_until_completed(undeploy_task
)
1411 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1412 if vapp
is not None:
1413 delete_task
= vapp
.delete()
1415 while retry
<= DELETE_INSTANCE_RETRY
:
1416 task
= vapp
.delete()
1417 if type(task
) is GenericTask
:
1418 vca
.block_until_completed(delete_task
)
1420 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1424 self
.logger
.debug(traceback
.format_exc())
1425 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1427 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1430 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1432 def refresh_vms_status(self
, vm_list
):
1433 """Get the status of the virtual machines and their interfaces/ports
1434 Params: the list of VM identifiers
1435 Returns a dictionary with:
1436 vm_id: #VIM id of this Virtual Machine
1437 status: #Mandatory. Text with one of:
1438 # DELETED (not found at vim)
1439 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1440 # OTHER (Vim reported other status not understood)
1441 # ERROR (VIM indicates an ERROR status)
1442 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1443 # CREATING (on building process), ERROR
1444 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1446 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1447 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1449 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1450 mac_address: #Text format XX:XX:XX:XX:XX:XX
1451 vim_net_id: #network id where this interface is connected
1452 vim_interface_id: #interface/port VIM id
1453 ip_address: #null, or text with IPv4, IPv6 address
1456 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1457 vca
= self
.connect()
1459 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1461 vdc
= vca
.get_vdc(self
.tenant_name
)
1463 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1466 for vmuuid
in vm_list
:
1467 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1468 if vmname
is not None:
1470 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1471 vm_info
= the_vapp
.get_vms_details()
1472 vm_status
= vm_info
[0]['status']
1474 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1475 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1476 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1480 vm_app_networks
= the_vapp
.get_vms_network_info()
1481 for vapp_network
in vm_app_networks
:
1482 for vm_network
in vapp_network
:
1483 if vm_network
['name'] == vmname
:
1484 interface
= {"mac_address": vm_network
['mac'],
1485 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1486 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1487 'ip_address': vm_network
['ip']}
1488 # interface['vim_info'] = yaml.safe_dump(vm_network)
1489 vm_dict
["interfaces"].append(interface
)
1490 # add a vm to vm dict
1491 vms_dict
.setdefault(vmuuid
, vm_dict
)
1493 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1494 self
.logger
.debug(traceback
.format_exc())
1498 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1499 """Send and action over a VM instance from VIM
1500 Returns the vm_id if the action was successfully sent to the VIM"""
1502 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1503 if vm__vim_uuid
is None or action_dict
is None:
1504 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1506 vca
= self
.connect()
1508 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1510 vdc
= vca
.get_vdc(self
.tenant_name
)
1512 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1514 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1515 if vapp_name
is None:
1516 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1517 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1519 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1522 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1523 # TODO fix all status
1524 if "start" in action_dict
:
1525 if action_dict
["start"] == "rebuild":
1526 the_vapp
.deploy(powerOn
=True)
1528 vm_info
= the_vapp
.get_vms_details()
1529 vm_status
= vm_info
[0]['status']
1530 if vm_status
== "Suspended":
1532 elif vm_status
.status
== "Powered off":
1534 elif "pause" in action_dict
:
1537 elif "resume" in action_dict
:
1540 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1542 elif "forceOff" in action_dict
:
1544 elif "terminate" in action_dict
:
1546 # elif "createImage" in action_dict:
1547 # server.create_image()
1553 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1555 Get a console for the virtual machine
1557 vm_id: uuid of the VM
1558 console_type, can be:
1559 "novnc" (by default), "xvpvnc" for VNC types,
1560 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1561 Returns dict with the console parameters:
1562 protocol: ssh, ftp, http, https, ...
1563 server: usually ip address
1564 port: the http, ssh, ... port
1565 suffix: extra text, e.g. the http path and query string
1567 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1569 # NOT USED METHODS in current version
1571 def host_vim2gui(self
, host
, server_dict
):
1572 """Transform host dictionary from VIM format to GUI format,
1573 and append to the server_dict
1575 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1577 def get_hosts_info(self
):
1578 """Get the information of deployed hosts
1579 Returns the hosts content"""
1580 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1582 def get_hosts(self
, vim_tenant
):
1583 """Get the hosts and deployed instances
1584 Returns the hosts content"""
1585 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1587 def get_processor_rankings(self
):
1588 """Get the processor rankings in the VIM database"""
1589 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1591 def new_host(self
, host_data
):
1592 """Adds a new host to VIM"""
1593 '''Returns status code of the VIM response'''
1594 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1596 def new_external_port(self
, port_data
):
1597 """Adds a external port to VIM"""
1598 '''Returns the port identifier'''
1599 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1601 def new_external_network(self
, net_name
, net_type
):
1602 """Adds a external network to VIM (shared)"""
1603 '''Returns the network identifier'''
1604 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1606 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1607 """Connects a external port to a network"""
1608 '''Returns status code of the VIM response'''
1609 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1611 def new_vminstancefromJSON(self
, vm_data
):
1612 """Adds a VM instance to VIM"""
1613 '''Returns the instance identifier'''
1614 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1616 def get_network_name_by_id(self
, network_uuid
=None):
1617 """Method gets vcloud director network named based on supplied uuid.
1620 network_uuid: network_id
1623 The return network name.
1626 vca
= self
.connect()
1628 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1630 if not network_uuid
:
1634 org_dict
= self
.get_org(self
.org_uuid
)
1635 if 'networks' in org_dict
:
1636 org_network_dict
= org_dict
['networks']
1637 for net_uuid
in org_network_dict
:
1638 if net_uuid
== network_uuid
:
1639 return org_network_dict
[net_uuid
]
1641 self
.logger
.debug("Exception in get_network_name_by_id")
1642 self
.logger
.debug(traceback
.format_exc())
1646 def get_network_id_by_name(self
, network_name
=None):
1647 """Method gets vcloud director network uuid based on supplied name.
1650 network_name: network_name
1652 The return network uuid.
1653 network_uuid: network_id
1656 vca
= self
.connect()
1658 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1660 if not network_name
:
1661 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1665 org_dict
= self
.get_org(self
.org_uuid
)
1666 if org_dict
and 'networks' in org_dict
:
1667 org_network_dict
= org_dict
['networks']
1668 for net_uuid
,net_name
in org_network_dict
.iteritems():
1669 if net_name
== network_name
:
1672 except KeyError as exp
:
1673 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1677 def list_org_action(self
):
1679 Method leverages vCloud director and query for available organization for particular user
1682 vca - is active VCA connection.
1683 vdc_name - is a vdc name that will be used to query vms action
1686 The return XML respond
1689 vca
= self
.connect()
1691 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1693 url_list
= [vca
.host
, '/api/org']
1694 vm_list_rest_call
= ''.join(url_list
)
1696 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1697 response
= Http
.get(url
=vm_list_rest_call
,
1698 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1701 if response
.status_code
== requests
.codes
.ok
:
1702 return response
.content
1706 def get_org_action(self
, org_uuid
=None):
1708 Method leverages vCloud director and retrieve available object fdr organization.
1711 vca - is active VCA connection.
1712 vdc_name - is a vdc name that will be used to query vms action
1715 The return XML respond
1718 vca
= self
.connect()
1720 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1722 if org_uuid
is None:
1725 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1726 vm_list_rest_call
= ''.join(url_list
)
1728 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1729 response
= Http
.get(url
=vm_list_rest_call
,
1730 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1733 if response
.status_code
== requests
.codes
.ok
:
1734 return response
.content
1738 def get_org(self
, org_uuid
=None):
1740 Method retrieves available organization in vCloud Director
1743 org_uuid - is a organization uuid.
1746 The return dictionary with following key
1747 "network" - for network list under the org
1748 "catalogs" - for network list under the org
1749 "vdcs" - for vdc list under org
1753 vca
= self
.connect()
1755 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1757 if org_uuid
is None:
1760 content
= self
.get_org_action(org_uuid
=org_uuid
)
1765 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1766 for child
in vm_list_xmlroot
:
1767 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1768 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1769 org_dict
['vdcs'] = vdc_list
1770 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1771 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1772 org_dict
['networks'] = network_list
1773 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1774 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1775 org_dict
['catalogs'] = catalog_list
1781 def get_org_list(self
):
1783 Method retrieves available organization in vCloud Director
1786 vca - is active VCA connection.
1789 The return dictionary and key for each entry VDC UUID
1793 vca
= self
.connect()
1795 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1797 content
= self
.list_org_action()
1799 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1800 for vm_xml
in vm_list_xmlroot
:
1801 if vm_xml
.tag
.split("}")[1] == 'Org':
1802 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1803 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1809 def vms_view_action(self
, vdc_name
=None):
1810 """ Method leverages vCloud director vms query call
1813 vca - is active VCA connection.
1814 vdc_name - is a vdc name that will be used to query vms action
1817 The return XML respond
1819 vca
= self
.connect()
1820 if vdc_name
is None:
1823 url_list
= [vca
.host
, '/api/vms/query']
1824 vm_list_rest_call
= ''.join(url_list
)
1826 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1827 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1828 vca
.vcloud_session
.organization
.Link
)
1830 response
= Http
.get(url
=vm_list_rest_call
,
1831 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1834 if response
.status_code
== requests
.codes
.ok
:
1835 return response
.content
1839 def get_vapp_list(self
, vdc_name
=None):
1841 Method retrieves vApp list deployed vCloud director and returns a dictionary
1842 contains a list of all vapp deployed for queried VDC.
1843 The key for a dictionary is vApp UUID
1847 vca - is active VCA connection.
1848 vdc_name - is a vdc name that will be used to query vms action
1851 The return dictionary and key for each entry vapp UUID
1855 if vdc_name
is None:
1858 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1860 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1861 for vm_xml
in vm_list_xmlroot
:
1862 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1863 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1864 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1865 if 'vappTemplate-' in rawuuid
[0]:
1866 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1867 # vm and use raw UUID as key
1868 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1874 def get_vm_list(self
, vdc_name
=None):
1876 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1877 contains a list of all VM's deployed for queried VDC.
1878 The key for a dictionary is VM UUID
1882 vca - is active VCA connection.
1883 vdc_name - is a vdc name that will be used to query vms action
1886 The return dictionary and key for each entry vapp UUID
1890 if vdc_name
is None:
1893 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1895 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1896 for vm_xml
in vm_list_xmlroot
:
1897 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1898 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1899 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1900 if 'vm-' in rawuuid
[0]:
1901 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1902 # vm and use raw UUID as key
1903 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1909 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1911 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1912 contains a list of all VM's deployed for queried VDC.
1913 The key for a dictionary is VM UUID
1917 vca - is active VCA connection.
1918 vdc_name - is a vdc name that will be used to query vms action
1921 The return dictionary and key for each entry vapp UUID
1924 vca
= self
.connect()
1926 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1928 if vdc_name
is None:
1931 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1933 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1934 for vm_xml
in vm_list_xmlroot
:
1935 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1936 # lookup done by UUID
1938 if vapp_name
in vm_xml
.attrib
['container']:
1939 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1940 if 'vm-' in rawuuid
[0]:
1941 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1943 # lookup done by Name
1945 if vapp_name
in vm_xml
.attrib
['name']:
1946 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1947 if 'vm-' in rawuuid
[0]:
1948 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1955 def get_network_action(self
, network_uuid
=None):
1957 Method leverages vCloud director and query network based on network uuid
1960 vca - is active VCA connection.
1961 network_uuid - is a network uuid
1964 The return XML respond
1967 vca
= self
.connect()
1969 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1971 if network_uuid
is None:
1974 url_list
= [vca
.host
, '/api/network/', network_uuid
]
1975 vm_list_rest_call
= ''.join(url_list
)
1977 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1978 response
= Http
.get(url
=vm_list_rest_call
,
1979 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1982 if response
.status_code
== requests
.codes
.ok
:
1983 return response
.content
1987 def get_vcd_network(self
, network_uuid
=None):
1989 Method retrieves available network from vCloud Director
1992 network_uuid - is VCD network UUID
1994 Each element serialized as key : value pair
1996 Following keys available for access. network_configuration['Gateway'}
2000 <IsInherited>true</IsInherited>
2001 <Gateway>172.16.252.100</Gateway>
2002 <Netmask>255.255.255.0</Netmask>
2003 <Dns1>172.16.254.201</Dns1>
2004 <Dns2>172.16.254.202</Dns2>
2005 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2006 <IsEnabled>true</IsEnabled>
2009 <StartAddress>172.16.252.1</StartAddress>
2010 <EndAddress>172.16.252.99</EndAddress>
2015 <FenceMode>bridged</FenceMode>
2018 The return dictionary and key for each entry vapp UUID
2021 network_configuration
= {}
2022 if network_uuid
is None:
2025 content
= self
.get_network_action(network_uuid
=network_uuid
)
2027 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2029 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2030 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2031 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2033 for child
in vm_list_xmlroot
:
2034 if child
.tag
.split("}")[1] == 'IsShared':
2035 network_configuration
['isShared'] = child
.text
.strip()
2036 if child
.tag
.split("}")[1] == 'Configuration':
2037 for configuration
in child
.iter():
2038 tagKey
= configuration
.tag
.split("}")[1].strip()
2040 network_configuration
[tagKey
] = configuration
.text
.strip()
2041 return network_configuration
2045 return network_configuration
2047 def delete_network_action(self
, network_uuid
=None):
2049 Method delete given network from vCloud director
2052 network_uuid - is a network uuid that client wish to delete
2055 The return None or XML respond or false
2058 vca
= self
.connect_as_admin()
2060 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2061 if network_uuid
is None:
2064 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2065 vm_list_rest_call
= ''.join(url_list
)
2067 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2068 response
= Http
.delete(url
=vm_list_rest_call
,
2069 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2073 if response
.status_code
== 202:
2078 def create_network(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2080 Method create network in vCloud director
2083 network_name - is network name to be created.
2084 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2085 It optional attribute. by default if no parent network indicate the first available will be used.
2088 The return network uuid or return None
2091 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2092 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2093 parent_network_uuid
=parent_network_uuid
,
2096 self
.logger
.debug("Failed create network {}.".format(network_name
))
2100 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2101 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2102 if len(vcd_uuid
) == 4:
2103 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2106 self
.logger
.debug("Failed create network {}".format(network_name
))
2109 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2111 Method create network in vCloud director
2114 network_name - is network name to be created.
2115 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2116 It optional attribute. by default if no parent network indicate the first available will be used.
2119 The return network uuid or return None
2122 vca
= self
.connect_as_admin()
2124 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2125 if network_name
is None:
2128 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2129 vm_list_rest_call
= ''.join(url_list
)
2130 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2131 response
= Http
.get(url
=vm_list_rest_call
,
2132 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2136 provider_network
= None
2137 available_networks
= None
2138 add_vdc_rest_url
= None
2140 if response
.status_code
!= requests
.codes
.ok
:
2141 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2142 response
.status_code
))
2146 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2147 for child
in vm_list_xmlroot
:
2148 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2149 provider_network
= child
.attrib
.get('href')
2150 # application/vnd.vmware.admin.providervdc+xml
2151 if child
.tag
.split("}")[1] == 'Link':
2152 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2153 and child
.attrib
.get('rel') == 'add':
2154 add_vdc_rest_url
= child
.attrib
.get('href')
2156 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2157 self
.logger
.debug("Respond body {}".format(response
.content
))
2160 # find pvdc provided available network
2161 response
= Http
.get(url
=provider_network
,
2162 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2165 if response
.status_code
!= requests
.codes
.ok
:
2166 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2167 response
.status_code
))
2170 # available_networks.split("/")[-1]
2172 if parent_network_uuid
is None:
2174 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2175 for child
in vm_list_xmlroot
.iter():
2176 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2177 for networks
in child
.iter():
2178 # application/vnd.vmware.admin.network+xml
2179 if networks
.attrib
.get('href') is not None:
2180 available_networks
= networks
.attrib
.get('href')
2185 # either use client provided UUID or search for a first available
2186 # if both are not defined we return none
2187 if parent_network_uuid
is not None:
2188 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2189 add_vdc_rest_url
= ''.join(url_list
)
2191 # return response.content
2192 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2193 <Description>Openmano created</Description>
2195 <ParentNetwork href="{1:s}"/>
2196 <FenceMode>{2:s}</FenceMode>
2198 <IsShared>{3:s}</IsShared>
2199 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2201 headers
= vca
.vcloud_session
.get_vcloud_headers()
2202 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2203 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2205 # if we all ok we respond with content otherwise by default None
2206 if response
.status_code
== 201:
2207 return response
.content
2210 def get_provider_rest(self
, vca
=None):
2212 Method gets provider vdc view from vcloud director
2215 network_name - is network name to be created.
2216 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2217 It optional attribute. by default if no parent network indicate the first available will be used.
2220 The return xml content of respond or None
2223 url_list
= [vca
.host
, '/api/admin']
2224 response
= Http
.get(url
=''.join(url_list
),
2225 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2229 if response
.status_code
== requests
.codes
.ok
:
2230 return response
.content
2233 def create_vdc(self
, vdc_name
=None):
2237 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2238 if xml_content
is not None:
2240 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2241 for child
in task_resp_xmlroot
:
2242 if child
.tag
.split("}")[1] == 'Owner':
2243 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2244 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2247 self
.logger
.debug("Respond body {}".format(xml_content
))
2251 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2253 Method create vdc in vCloud director based on VDC template.
2254 it uses pre-defined template that must be named openmano
2257 vdc_name - name of a new vdc.
2260 The return xml content of respond or None
2263 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2264 vca
= self
.connect()
2266 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2267 if vdc_name
is None:
2270 url_list
= [vca
.host
, '/api/vdcTemplates']
2271 vm_list_rest_call
= ''.join(url_list
)
2272 response
= Http
.get(url
=vm_list_rest_call
,
2273 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2277 # container url to a template
2278 vdc_template_ref
= None
2280 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2281 for child
in vm_list_xmlroot
:
2282 # application/vnd.vmware.admin.providervdc+xml
2283 # we need find a template from witch we instantiate VDC
2284 if child
.tag
.split("}")[1] == 'VdcTemplate':
2285 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2286 'name') == 'openmano':
2287 vdc_template_ref
= child
.attrib
.get('href')
2289 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2290 self
.logger
.debug("Respond body {}".format(response
.content
))
2293 # if we didn't found required pre defined template we return None
2294 if vdc_template_ref
is None:
2299 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2300 vm_list_rest_call
= ''.join(url_list
)
2301 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2302 <Source href="{1:s}"></Source>
2303 <Description>opnemano</Description>
2304 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2305 headers
= vca
.vcloud_session
.get_vcloud_headers()
2306 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2307 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2309 # if we all ok we respond with content otherwise by default None
2310 if response
.status_code
>= 200 and response
.status_code
< 300:
2311 return response
.content
2314 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2315 self
.logger
.debug("Respond body {}".format(response
.content
))
2319 def create_vdc_rest(self
, vdc_name
=None):
2321 Method create network in vCloud director
2324 network_name - is network name to be created.
2325 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2326 It optional attribute. by default if no parent network indicate the first available will be used.
2329 The return network uuid or return None
2332 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2334 vca
= self
.connect_as_admin()
2336 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2337 if vdc_name
is None:
2340 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2341 vm_list_rest_call
= ''.join(url_list
)
2342 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2343 response
= Http
.get(url
=vm_list_rest_call
,
2344 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2348 provider_vdc_ref
= None
2349 add_vdc_rest_url
= None
2350 available_networks
= None
2352 if response
.status_code
!= requests
.codes
.ok
:
2353 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2354 response
.status_code
))
2358 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2359 for child
in vm_list_xmlroot
:
2360 # application/vnd.vmware.admin.providervdc+xml
2361 if child
.tag
.split("}")[1] == 'Link':
2362 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2363 and child
.attrib
.get('rel') == 'add':
2364 add_vdc_rest_url
= child
.attrib
.get('href')
2366 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2367 self
.logger
.debug("Respond body {}".format(response
.content
))
2370 response
= self
.get_provider_rest(vca
=vca
)
2372 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2373 for child
in vm_list_xmlroot
:
2374 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2375 for sub_child
in child
:
2376 provider_vdc_ref
= sub_child
.attrib
.get('href')
2378 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2379 self
.logger
.debug("Respond body {}".format(response
))
2382 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2383 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2384 <AllocationModel>ReservationPool</AllocationModel>
2385 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2386 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2387 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2388 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2389 <ProviderVdcReference
2390 name="Main Provider"
2392 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2396 headers
= vca
.vcloud_session
.get_vcloud_headers()
2397 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2398 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2401 # if we all ok we respond with content otherwise by default None
2402 if response
.status_code
== 201:
2403 return response
.content
2406 def get_vapp_details_rest(self
, vapp_uuid
=None):
2408 Method retrieve vapp detail from vCloud director
2411 vapp_uuid - is vapp identifier.
2414 The return network uuid or return None
2419 vca
= self
.connect()
2421 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2422 if vapp_uuid
is None:
2425 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2426 get_vapp_restcall
= ''.join(url_list
)
2427 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2428 response
= Http
.get(url
=get_vapp_restcall
,
2429 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2433 if response
.status_code
!= requests
.codes
.ok
:
2434 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2435 response
.status_code
))
2436 return parsed_respond
2439 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2440 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2442 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2443 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2444 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2446 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2447 if created_section
is not None:
2448 parsed_respond
['created'] = created_section
.text
2450 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2451 if network_section
is not None and 'networkName' in network_section
.attrib
:
2452 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2454 ipscopes_section
= \
2455 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2457 if ipscopes_section
is not None:
2458 for ipscope
in ipscopes_section
:
2459 for scope
in ipscope
:
2460 tag_key
= scope
.tag
.split("}")[1]
2461 if tag_key
== 'IpRanges':
2462 ip_ranges
= scope
.getchildren()
2463 for ipblock
in ip_ranges
:
2464 for block
in ipblock
:
2465 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2467 parsed_respond
[tag_key
] = scope
.text
2469 # parse children section for other attrib
2470 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2471 if children_section
is not None:
2472 parsed_respond
['name'] = children_section
.attrib
['name']
2473 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2474 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2475 parsed_respond
['status'] = children_section
.attrib
['status']
2476 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2477 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2479 for adapters
in network_adapter
:
2480 adapter_key
= adapters
.tag
.split("}")[1]
2481 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2482 parsed_respond
['primarynetwork'] = adapters
.text
2483 if adapter_key
== 'NetworkConnection':
2485 if 'network' in adapters
.attrib
:
2486 vnic
['network'] = adapters
.attrib
['network']
2487 for adapter
in adapters
:
2488 setting_key
= adapter
.tag
.split("}")[1]
2489 vnic
[setting_key
] = adapter
.text
2490 nic_list
.append(vnic
)
2492 for link
in children_section
:
2493 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2494 if link
.attrib
['rel'] == 'screen:acquireTicket':
2495 parsed_respond
['acquireTicket'] = link
.attrib
2496 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2497 parsed_respond
['acquireMksTicket'] = link
.attrib
2499 parsed_respond
['interfaces'] = nic_list
2503 return parsed_respond
2505 def acuire_console(self
, vm_uuid
=None):
2507 vca
= self
.connect()
2509 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2513 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2514 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2515 console_dict
= vm_dict
['acquireTicket']
2516 console_rest_call
= console_dict
['href']
2518 response
= Http
.post(url
=console_rest_call
,
2519 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2523 if response
.status_code
== requests
.codes
.ok
:
2524 return response
.content