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
58 # global variable for vcd connector type
59 STANDALONE
= 'standalone'
61 # key for flavor dicts
62 FLAVOR_RAM_KEY
= 'ram'
63 FLAVOR_VCPUS_KEY
= 'vcpus'
65 # global variable for number of retry
66 DELETE_INSTANCE_RETRY
= 3
70 __author__
= "Mustafa Bayramov, Arpita Kate"
71 __date__
= "$23-Dec-2016 11:09:29$"
74 # -1: "Could not be created",
80 # 5: "Waiting for user input",
82 # 7: "Unrecognized state",
84 # 9: "Inconsistent state",
85 # 10: "Children do not all have the same status",
86 # 11: "Upload initiated, OVF descriptor pending",
87 # 12: "Upload initiated, copying contents",
88 # 13: "Upload initiated , disk contents pending",
89 # 14: "Upload has been quarantined",
90 # 15: "Upload quarantine period has expired"
92 # mapping vCD status to MANO
93 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
102 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
103 'ERROR': 'ERROR', 'DELETED': 'DELETED'
106 # dict used to store flavor in memory
110 class vimconnector(vimconn
.vimconnector
):
111 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
112 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
114 Constructor create vmware connector to vCloud director.
116 By default construct doesn't validate connection state. So client can create object with None arguments.
117 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
119 a) It initialize organization UUID
120 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
123 uuid - is organization uuid.
124 name - is organization name that must be presented in vCloud director.
125 tenant_id - is VDC uuid it must be presented in vCloud director
126 tenant_name - is VDC name.
127 url - is hostname or ip address of vCloud director
128 url_admin - same as above.
129 user - is user that administrator for organization. Caller must make sure that
130 username has right privileges.
132 password - is password for a user.
134 VMware connector also requires PVDC administrative privileges and separate account.
135 This variables must be passed via config argument dict contains keys
137 dict['admin_username']
138 dict['admin_password']
144 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
145 url_admin
, user
, passwd
, log_level
, config
)
147 self
.logger
= logging
.getLogger('openmano.vim.vmware')
148 self
.logger
.setLevel(10)
153 self
.url_admin
= url_admin
154 self
.tenant_id
= tenant_id
155 self
.tenant_name
= tenant_name
159 self
.admin_password
= None
160 self
.admin_user
= None
163 if tenant_name
is not None:
164 orgnameandtenant
= tenant_name
.split(":")
165 if len(orgnameandtenant
) == 2:
166 self
.tenant_name
= orgnameandtenant
[1]
167 self
.org_name
= orgnameandtenant
[0]
169 self
.tenant_name
= tenant_name
170 elif "orgname" in config
:
171 self
.org_name
= config
['orgname']
174 self
.logger
.setLevel(getattr(logging
, log_level
))
177 self
.admin_user
= config
['admin_username']
178 self
.admin_password
= config
['admin_password']
180 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
186 raise vimconn
.vimconnException('url param can not be NoneType')
188 if not self
.url_admin
: # try to use normal url
189 self
.url_admin
= self
.url
191 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
192 self
.tenant_id
, self
.tenant_name
))
193 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
194 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
196 # initialize organization
197 if self
.user
is not None and self
.passwd
is not None and self
.url
:
198 self
.init_organization()
200 def __getitem__(self
, index
):
203 if index
== 'tenant_id':
204 return self
.tenant_id
205 if index
== 'tenant_name':
206 return self
.tenant_name
209 elif index
== 'org_name':
211 elif index
== 'org_uuid':
213 elif index
== 'user':
215 elif index
== 'passwd':
219 elif index
== 'url_admin':
220 return self
.url_admin
221 elif index
== "config":
224 raise KeyError("Invalid key '%s'" % str(index
))
226 def __setitem__(self
, index
, value
):
229 if index
== 'tenant_id':
230 self
.tenant_id
= value
231 if index
== 'tenant_name':
232 self
.tenant_name
= value
235 elif index
== 'org_name':
236 self
.org_name
= value
237 elif index
== 'org_uuid':
238 self
.org_uuid
= value
239 elif index
== 'user':
241 elif index
== 'passwd':
245 elif index
== 'url_admin':
246 self
.url_admin
= value
248 raise KeyError("Invalid key '%s'" % str(index
))
250 def connect_as_admin(self
):
251 """ Method connect as pvdc admin user to vCloud director.
252 There are certain action that can be done only by provider vdc admin user.
253 Organization creation / provider network creation etc.
256 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
259 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
261 vca_admin
= VCA(host
=self
.url
,
262 username
=self
.admin_user
,
263 service_type
=STANDALONE
,
267 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
269 raise vimconn
.vimconnConnectionException(
270 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
271 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
274 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
279 """ Method connect as normal user to vCloud director.
282 The return vca object that letter can be used to connect to vCloud director as admin for VDC
286 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
289 vca
= VCA(host
=self
.url
,
291 service_type
=STANDALONE
,
296 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
298 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
299 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
302 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
305 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
306 "{} as user: {}".format(self
.org_name
, self
.user
))
310 def init_organization(self
):
311 """ Method initialize organization UUID and VDC parameters.
313 At bare minimum client must provide organization name that present in vCloud director and VDC.
315 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
316 The Org - UUID will be initialized at the run time if data center present in vCloud director.
319 The return vca object that letter can be used to connect to vcloud direct as admin
322 if self
.org_uuid
is None:
323 org_dict
= self
.get_org_list()
325 # we set org UUID at the init phase but we can do it only when we have valid credential.
326 if org_dict
[org
] == self
.org_name
:
328 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
331 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
333 # if well good we require for org details
334 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
336 # we have two case if we want to initialize VDC ID or VDC name at run time
337 # tenant_name provided but no tenant id
338 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
339 vdcs_dict
= org_details_dict
['vdcs']
340 for vdc
in vdcs_dict
:
341 if vdcs_dict
[vdc
] == self
.tenant_name
:
343 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
347 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
348 # case two we have tenant_id but we don't have tenant name so we find and set it.
349 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
350 vdcs_dict
= org_details_dict
['vdcs']
351 for vdc
in vdcs_dict
:
352 if vdc
== self
.tenant_id
:
353 self
.tenant_name
= vdcs_dict
[vdc
]
354 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
358 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
359 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
361 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
362 self
.logger
.debug(traceback
.format_exc())
365 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
366 """ Method adds a new tenant to VIM with this name.
367 This action requires access to create VDC action in vCloud director.
370 tenant_name is tenant_name to be created.
371 tenant_description not used for this call
374 returns the tenant identifier in UUID format.
375 If action is failed method will throw vimconn.vimconnException method
377 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
378 if vdc_task
is not None:
379 vdc_uuid
, value
= vdc_task
.popitem()
380 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
383 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
385 def delete_tenant(self
, tenant_id
=None):
386 """Delete a tenant from VIM"""
387 'Returns the tenant identifier'
388 raise vimconn
.vimconnNotImplemented("Should have implemented this")
390 def get_tenant_list(self
, filter_dict
={}):
391 """Obtain tenants of VIM
392 filter_dict can contain the following keys:
393 name: filter by tenant name
394 id: filter by tenant uuid/id
396 Returns the tenant list of dictionaries:
397 [{'name':'<name>, 'id':'<id>, ...}, ...]
400 org_dict
= self
.get_org(self
.org_uuid
)
401 vdcs_dict
= org_dict
['vdcs']
406 entry
= {'name': vdcs_dict
[k
], 'id': k
}
407 # if caller didn't specify dictionary we return all tenants.
408 if filter_dict
is not None and filter_dict
:
409 filtered_entry
= entry
.copy()
410 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
411 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
412 if filter_dict
== entry
:
413 vdclist
.append(filtered_entry
)
415 vdclist
.append(entry
)
417 self
.logger
.debug("Error in get_tenant_list()")
418 self
.logger
.debug(traceback
.format_exc())
419 raise vimconn
.vimconnException("Incorrect state. {}")
423 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
424 """Adds a tenant network to VIM
426 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
427 ip_profile is a dict containing the IP parameters of the network
429 Returns the network identifier"""
432 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name
, net_type
, ip_profile
, shared
))
438 network_uuid
= self
.create_network(network_name
=net_name
, isshared
=isshared
)
439 if network_uuid
is not None:
442 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
444 def get_vcd_network_list(self
):
445 """ Method available organization for a logged in tenant
448 The return vca object that letter can be used to connect to vcloud direct as admin
451 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
454 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
456 if not self
.tenant_name
:
457 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
459 vdc
= vca
.get_vdc(self
.tenant_name
)
461 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
463 vdc_uuid
= vdc
.get_id().split(":")[3]
464 networks
= vca
.get_networks(vdc
.get_name())
467 for network
in networks
:
469 netid
= network
.get_id().split(":")
473 filter_dict
["name"] = network
.get_name()
474 filter_dict
["id"] = netid
[3]
475 filter_dict
["shared"] = network
.get_IsShared()
476 filter_dict
["tenant_id"] = vdc_uuid
477 if network
.get_status() == 1:
478 filter_dict
["admin_state_up"] = True
480 filter_dict
["admin_state_up"] = False
481 filter_dict
["status"] = "ACTIVE"
482 filter_dict
["type"] = "bridge"
483 network_list
.append(filter_dict
)
484 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
486 self
.logger
.debug("Error in get_vcd_network_list")
487 self
.logger
.debug(traceback
.format_exc())
490 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
493 def get_network_list(self
, filter_dict
={}):
494 """Obtain tenant networks of VIM
496 name: network name OR/AND
497 id: network uuid OR/AND
498 shared: boolean OR/AND
499 tenant_id: tenant OR/AND
500 admin_state_up: boolean
503 [{key : value , key : value}]
505 Returns the network list of dictionaries:
506 [{<the fields at Filter_dict plus some VIM specific>}, ...]
510 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
513 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
515 if not self
.tenant_name
:
516 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
518 vdc
= vca
.get_vdc(self
.tenant_name
)
520 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
522 vdcid
= vdc
.get_id().split(":")[3]
523 networks
= vca
.get_networks(vdc
.get_name())
527 for network
in networks
:
529 net_uuid
= network
.get_id().split(":")
530 if len(net_uuid
) != 4:
533 net_uuid
= net_uuid
[3]
535 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
538 filter_entry
["name"] = network
.get_name()
539 filter_entry
["id"] = net_uuid
540 filter_entry
["shared"] = network
.get_IsShared()
541 filter_entry
["tenant_id"] = vdcid
542 if network
.get_status() == 1:
543 filter_entry
["admin_state_up"] = True
545 filter_entry
["admin_state_up"] = False
546 filter_entry
["status"] = "ACTIVE"
547 filter_entry
["type"] = "bridge"
548 filtered_entry
= filter_entry
.copy()
550 if filter_dict
is not None and filter_dict
:
551 # we remove all the key : value we don't care and match only
553 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
554 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
555 if filter_dict
== filter_entry
:
556 network_list
.append(filtered_entry
)
558 network_list
.append(filtered_entry
)
560 self
.logger
.debug("Error in get_vcd_network_list")
561 self
.logger
.debug(traceback
.format_exc())
563 self
.logger
.debug("Returning {}".format(network_list
))
566 def get_network(self
, net_id
):
567 """Method obtains network details of net_id VIM network
568 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
572 raise vimconn
.vimconnConnectionException("self.connect() is failed")
574 vdc
= vca
.get_vdc(self
.tenant_name
)
575 vdc_id
= vdc
.get_id().split(":")[3]
577 networks
= vca
.get_networks(vdc
.get_name())
581 for network
in networks
:
582 vdc_network_id
= network
.get_id().split(":")
583 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
584 filter_dict
["name"] = network
.get_name()
585 filter_dict
["id"] = vdc_network_id
[3]
586 filter_dict
["shared"] = network
.get_IsShared()
587 filter_dict
["tenant_id"] = vdc_id
588 if network
.get_status() == 1:
589 filter_dict
["admin_state_up"] = True
591 filter_dict
["admin_state_up"] = False
592 filter_dict
["status"] = "ACTIVE"
593 filter_dict
["type"] = "bridge"
594 self
.logger
.debug("Returning {}".format(filter_dict
))
597 self
.logger
.debug("Error in get_network")
598 self
.logger
.debug(traceback
.format_exc())
602 def delete_network(self
, net_id
):
604 Method Deletes a tenant network from VIM, provide the network id.
606 Returns the network identifier or raise an exception
611 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
613 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
614 if vcd_network
is not None and vcd_network
:
615 if self
.delete_network_action(network_uuid
=net_id
):
618 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
620 def refresh_nets_status(self
, net_list
):
621 """Get the status of the networks
622 Params: the list of network identifiers
623 Returns a dictionary with:
624 net_id: #VIM id of this network
625 status: #Mandatory. Text with one of:
626 # DELETED (not found at vim)
627 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
628 # OTHER (Vim reported other status not understood)
629 # ERROR (VIM indicates an ERROR status)
630 # ACTIVE, INACTIVE, DOWN (admin down),
631 # BUILD (on building process)
633 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
634 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
640 raise vimconn
.vimconnConnectionException("self.connect() is failed")
646 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
647 if vcd_network
is not None and vcd_network
:
648 if vcd_network
['status'] == '1':
654 errormsg
= 'Network not found.'
656 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
657 'vim_info': yaml
.safe_dump(vcd_network
)}
659 self
.logger
.debug("Error in refresh_nets_status")
660 self
.logger
.debug(traceback
.format_exc())
664 def get_flavor(self
, flavor_id
):
665 """Obtain flavor details from the VIM
666 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
668 if flavor_id
not in flavorlist
:
669 raise vimconn
.vimconnNotFoundException("Flavor not found.")
670 return flavorlist
[flavor_id
]
672 def new_flavor(self
, flavor_data
):
673 """Adds a tenant flavor to VIM
674 flavor_data contains a dictionary with information, keys:
676 ram: memory (cloud type) in MBytes
677 vpcus: cpus (cloud type)
678 extended: EPA parameters
679 - numas: #items requested in same NUMA
680 memory: number of 1G huge pages memory
681 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
682 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
683 - name: interface name
684 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
685 bandwidth: X Gbps; requested guarantee bandwidth
686 vpci: requested virtual PCI address
693 Returns the flavor identifier"""
695 # generate a new uuid put to internal dict and return it.
696 flavor_id
= uuid
.uuid4()
697 flavorlist
[str(flavor_id
)] = flavor_data
699 return str(flavor_id
)
701 def delete_flavor(self
, flavor_id
):
702 """Deletes a tenant flavor from VIM identify by its id
704 Returns the used id or raise an exception
706 if flavor_id
not in flavorlist
:
707 raise vimconn
.vimconnNotFoundException("Flavor not found.")
709 flavorlist
.pop(flavor_id
, None)
712 def new_image(self
, image_dict
):
714 Adds a tenant image to VIM
716 200, image-id if the image is created
717 <0, message if there is an error
720 return self
.get_image_id_from_path(image_dict
['location'])
722 def delete_image(self
, image_id
):
729 raise vimconn
.vimconnNotImplemented("Should have implemented this")
731 def catalog_exists(self
, catalog_name
, catalogs
):
738 for catalog
in catalogs
:
739 if catalog
.name
== catalog_name
:
743 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
744 """ Create new catalog entry in vCloud director.
747 vca: vCloud director.
748 catalog_name catalog that client wish to create. Note no validation done for a name.
749 Client must make sure that provide valid string representation.
751 Return (bool) True if catalog created.
755 task
= vca
.create_catalog(catalog_name
, catalog_name
)
756 result
= vca
.block_until_completed(task
)
759 catalogs
= vca
.get_catalogs()
762 return self
.catalog_exists(catalog_name
, catalogs
)
764 # noinspection PyIncorrectDocstring
765 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
766 description
='', progress
=False, chunk_bytes
=128 * 1024):
768 Uploads a OVF file to a vCloud catalog
775 :param catalog_name: (str): The name of the catalog to upload the media.
776 :param media_file_name: (str): The name of the local media file to upload.
777 :return: (bool) True if the media file was successfully uploaded, false otherwise.
779 os
.path
.isfile(media_file_name
)
780 statinfo
= os
.stat(media_file_name
)
782 # find a catalog entry where we upload OVF.
783 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
785 # if VCD can parse OVF we upload VMDK file
786 for catalog
in vca
.get_catalogs():
787 if catalog_name
!= catalog
.name
:
789 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
790 link
.get_rel() == 'add', catalog
.get_Link())
791 assert len(link
) == 1
793 <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>
794 """ % (escape(catalog_name
), escape(description
))
795 headers
= vca
.vcloud_session
.get_vcloud_headers()
796 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
797 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
798 if response
.status_code
== requests
.codes
.created
:
799 catalogItem
= XmlElementTree
.fromstring(response
.content
)
800 entity
= [child
for child
in catalogItem
if
801 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
802 href
= entity
.get('href')
804 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
805 verify
=vca
.verify
, logger
=self
.logger
)
807 if response
.status_code
== requests
.codes
.ok
:
808 media
= mediaType
.parseString(response
.content
, True)
809 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
810 media
.get_Files().get_File()[0].get_Link())[0]
811 headers
= vca
.vcloud_session
.get_vcloud_headers()
812 headers
['Content-Type'] = 'Content-Type text/xml'
813 response
= Http
.put(link
.get_href(),
814 data
=open(media_file_name
, 'rb'),
816 verify
=vca
.verify
, logger
=self
.logger
)
817 if response
.status_code
!= requests
.codes
.ok
:
819 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
823 # TODO fix this with aync block
826 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
828 # uploading VMDK file
829 # check status of OVF upload and upload remaining files.
830 response
= Http
.get(template
,
831 headers
=vca
.vcloud_session
.get_vcloud_headers(),
835 if response
.status_code
== requests
.codes
.ok
:
836 media
= mediaType
.parseString(response
.content
, True)
837 number_of_files
= len(media
.get_Files().get_File())
838 for index
in xrange(0, number_of_files
):
839 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
840 media
.get_Files().get_File()[index
].get_Link())
841 for link
in links_list
:
842 # we skip ovf since it already uploaded.
843 if 'ovf' in link
.get_href():
845 # The OVF file and VMDK must be in a same directory
846 head
, tail
= os
.path
.split(media_file_name
)
847 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
848 if not os
.path
.isfile(file_vmdk
):
850 statinfo
= os
.stat(file_vmdk
)
851 if statinfo
.st_size
== 0:
853 hrefvmdk
= link
.get_href()
856 print("Uploading file: {}".format(file_vmdk
))
858 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
860 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
862 bytes_transferred
= 0
863 f
= open(file_vmdk
, 'rb')
864 while bytes_transferred
< statinfo
.st_size
:
865 my_bytes
= f
.read(chunk_bytes
)
866 if len(my_bytes
) <= chunk_bytes
:
867 headers
= vca
.vcloud_session
.get_vcloud_headers()
868 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
869 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
870 headers
['Content-Length'] = str(len(my_bytes
))
871 response
= Http
.put(hrefvmdk
,
877 if response
.status_code
== requests
.codes
.ok
:
878 bytes_transferred
+= len(my_bytes
)
880 progress_bar
.update(bytes_transferred
)
883 'file upload failed with error: [%s] %s' % (response
.status_code
,
890 progress_bar
.finish()
893 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
894 format(catalog_name
, media_file_name
))
897 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
900 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
901 """Upload media file"""
902 # TODO add named parameters for readability
904 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
905 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
907 def validate_uuid4(self
, uuid_string
=None):
908 """ Method validate correct format of UUID.
910 Return: true if string represent valid uuid
913 val
= uuid
.UUID(uuid_string
, version
=4)
918 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
919 """ Method check catalog and return catalog ID in UUID format.
922 catalog_name: catalog name as string
923 catalogs: list of catalogs.
925 Return: catalogs uuid
928 for catalog
in catalogs
:
929 if catalog
.name
== catalog_name
:
930 catalog_id
= catalog
.get_id().split(":")
934 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
935 """ Method check catalog and return catalog name lookup done by catalog UUID.
938 catalog_name: catalog name as string
939 catalogs: list of catalogs.
941 Return: catalogs name or None
944 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
947 for catalog
in catalogs
:
948 catalog_id
= catalog
.get_id().split(":")[3]
949 if catalog_id
== catalog_uuid
:
953 def get_image_id_from_path(self
, path
=None, progress
=False):
954 """ Method upload OVF image to vCloud director.
956 Each OVF image represented as single catalog entry in vcloud director.
957 The method check for existing catalog entry. The check done by file name without file extension.
959 if given catalog name already present method will respond with existing catalog uuid otherwise
960 it will create new catalog entry and upload OVF file to newly created catalog.
962 If method can't create catalog entry or upload a file it will throw exception.
964 Method accept boolean flag progress that will output progress bar. It useful method
965 for standalone upload use case. In case to test large file upload.
968 path: - valid path to OVF file.
969 progress - boolean progress bar show progress bar.
971 Return: if image uploaded correct method will provide image catalog UUID.
975 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
978 raise vimconn
.vimconnException("Image path can't be None.")
980 if not os
.path
.isfile(path
):
981 raise vimconn
.vimconnException("Can't read file. File not found.")
983 if not os
.access(path
, os
.R_OK
):
984 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
986 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
988 dirpath
, filename
= os
.path
.split(path
)
989 flname
, file_extension
= os
.path
.splitext(path
)
990 if file_extension
!= '.ovf':
991 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
992 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
994 catalog_name
= os
.path
.splitext(filename
)[0]
995 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
996 self
.logger
.debug("File name {} Catalog Name {} file path {} "
997 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
999 catalogs
= vca
.get_catalogs()
1000 if len(catalogs
) == 0:
1001 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1002 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1004 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1005 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1006 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1008 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1009 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1011 for catalog
in catalogs
:
1012 # search for existing catalog if we find same name we return ID
1013 # TODO optimize this
1014 if catalog
.name
== catalog_md5_name
:
1015 self
.logger
.debug("Found existing catalog entry for {} "
1016 "catalog id {}".format(catalog_name
,
1017 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1018 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1020 # if we didn't find existing catalog we create a new one and upload image.
1021 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1022 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1024 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1026 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1027 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1029 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1031 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1033 def get_vappid(self
, vdc
=None, vapp_name
=None):
1034 """ Method takes vdc object and vApp name and returns vapp uuid or None
1037 vdc: The VDC object.
1038 vapp_name: is application vappp name identifier
1041 The return vApp name otherwise None
1043 if vdc
is None or vapp_name
is None:
1045 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1047 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1048 vdc
.ResourceEntities
.ResourceEntity
)
1050 return refs
[0].href
.split("vapp")[1][1:]
1051 except Exception as e
:
1052 self
.logger
.exception(e
)
1056 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1057 """ Method Method returns True or False if vapp deployed in vCloud director
1060 vca: Connector to VCA
1061 vdc: The VDC object.
1062 vappid: vappid is application identifier
1065 The return True if vApp deployed
1070 refs
= filter(lambda ref
:
1071 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1072 vdc
.ResourceEntities
.ResourceEntity
)
1074 vappid
= ref
.href
.split("vapp")[1][1:]
1075 # find vapp with respected vapp uuid
1076 if vappid
== vapp_uuid
:
1078 except Exception as e
:
1079 self
.logger
.exception(e
)
1083 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1084 """Method returns vApp name from vCD and lookup done by vapp_id.
1087 vca: Connector to VCA
1088 vdc: The VDC object.
1089 vapp_uuid: vappid is application identifier
1092 The return vApp name otherwise None
1096 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1097 vdc
.ResourceEntities
.ResourceEntity
)
1099 # we care only about UUID the rest doesn't matter
1100 vappid
= ref
.href
.split("vapp")[1][1:]
1101 if vappid
== vapp_uuid
:
1102 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1104 tree
= XmlElementTree
.fromstring(response
.content
)
1105 return tree
.attrib
['name']
1106 except Exception as e
:
1107 self
.logger
.exception(e
)
1111 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1112 cloud_config
=None, disk_list
=None):
1113 """Adds a VM instance to VIM
1115 start: indicates if VM must start or boot in pause mode. Ignored
1116 image_id,flavor_id: image and flavor uuid
1117 net_list: list of interfaces, each one is a dictionary with:
1119 net_id: network uuid to connect
1120 vpci: virtual vcpi to assign
1121 model: interface model, virtio, e2000, ...
1123 use: 'data', 'bridge', 'mgmt'
1124 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1125 vim_id: filled/added by this function
1126 cloud_config: can be a text script to be passed directly to cloud-init,
1127 or an object to inject users and ssh keys with format:
1128 key-pairs: [] list of keys to install to the default user
1129 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1130 #TODO ip, security groups
1131 Returns >=0, the instance identifier
1135 self
.logger
.info("Creating new instance for entry {}".format(name
))
1136 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1137 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1138 vca
= self
.connect()
1140 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1142 #new vm name = vmname + tenant_id + uuid
1143 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1144 vmname_andid
= ''.join(new_vm_name
)
1146 # if vm already deployed we return existing uuid
1147 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1148 # if vapp_uuid is not None:
1151 # we check for presence of VDC, Catalog entry and Flavor.
1152 vdc
= vca
.get_vdc(self
.tenant_name
)
1154 raise vimconn
.vimconnNotFoundException(
1155 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1156 catalogs
= vca
.get_catalogs()
1157 if catalogs
is None:
1158 raise vimconn
.vimconnNotFoundException(
1159 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1161 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1162 if catalog_hash_name
:
1163 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1165 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1166 "(Failed retrieve catalog information {})".format(name
, image_id
))
1169 # Set vCPU and Memory based on flavor.
1173 if flavor_id
is not None:
1174 if flavor_id
not in flavorlist
:
1175 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1176 "Failed retrieve flavor information "
1177 "flavor id {}".format(name
, flavor_id
))
1180 flavor
= flavorlist
[flavor_id
]
1181 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1182 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1184 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1186 # image upload creates template name as catalog name space Template.
1187 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1192 # client must provide at least one entry in net_list if not we report error
1194 primary_netname
= None
1195 network_mode
= 'bridged'
1196 if net_list
is not None and len(net_list
) > 0:
1197 primary_net
= net_list
[0]
1198 if primary_net
is None:
1199 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1202 primary_net_id
= primary_net
['net_id']
1203 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1204 if 'name' in network_dict
:
1205 primary_netname
= network_dict
['name']
1206 self
.logger
.info("Connecting VM to a network name {} "
1207 " network id {}".format(primary_netname
, primary_net_id
))
1208 if 'use' in primary_net
:
1209 if primary_net
['use'] == 'bridge':
1210 network_mode
= 'bridged'
1212 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1214 # use: 'data', 'bridge', 'mgmt'
1215 # create vApp. Set vcpu and ram based on flavor id.
1216 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1217 self
.get_catalogbyid(image_id
, catalogs
),
1218 network_name
=primary_netname
, # can be None if net_list None
1219 network_mode
=network_mode
,
1220 vm_name
=vmname_andid
,
1221 vm_cpus
=vm_cpus
, # can be None if flavor is None
1222 vm_memory
=vm_memory
) # can be None if flavor is None
1224 if vapptask
is None or vapptask
is False:
1225 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1226 if type(vapptask
) is VappTask
:
1227 vca
.block_until_completed(vapptask
)
1229 # we should have now vapp in undeployed state.
1230 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1232 raise vimconn
.vimconnUnexpectedResponse(
1233 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(vmname_andid
))
1237 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1239 for net
in net_list
:
1240 # openmano uses network id in UUID format.
1241 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1242 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1243 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1245 if 'net_id' not in net
:
1248 interface_net_id
= net
['net_id']
1249 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1250 interface_network_mode
= net
['use']
1252 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1253 - DHCP (The IP address is obtained from a DHCP service.)
1254 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1255 - NONE (No IP addressing mode specified.)"""
1257 if primary_netname
is not None:
1258 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1260 self
.logger
.info("Found requested network: {}".format(nets
[0].name
))
1261 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1262 if type(task
) is GenericTask
:
1263 vca
.block_until_completed(task
)
1264 # connect network to VM
1265 # TODO figure out mapping between openmano representation to vCloud director.
1266 # one idea use first nic as management DHCP all remaining in bridge mode
1267 self
.logger
.info("Connecting VM to a network network {}".format(nets
[0].name
))
1268 task
= vapp
.connect_vms(nets
[0].name
,
1269 connection_index
=nicIndex
,
1270 connections_primary_index
=nicIndex
,
1271 ip_allocation_mode
='DHCP')
1272 if type(task
) is GenericTask
:
1273 vca
.block_until_completed(task
)
1276 # it might be a case if specific mandatory entry in dict is empty
1277 self
.logger
.debug("Key error {}".format(KeyError.message
))
1278 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1280 # deploy and power on vm
1281 task
= vapp
.poweron()
1282 if type(task
) is TaskType
:
1283 vca
.block_until_completed(task
)
1284 deploytask
= vapp
.deploy(powerOn
='True')
1285 if type(task
) is TaskType
:
1286 vca
.block_until_completed(deploytask
)
1288 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1289 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1290 if vapp_uuid
is not None:
1293 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1297 ## based on current discussion
1301 # created: '2016-09-08T11:51:58'
1302 # description: simple-instance.linux1.1
1303 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1304 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1305 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1310 def get_vminstance(self
, vim_vm_uuid
=None):
1311 """Returns the VM instance information from VIM"""
1313 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1314 vca
= self
.connect()
1316 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1318 vdc
= vca
.get_vdc(self
.tenant_name
)
1320 raise vimconn
.vimconnConnectionException(
1321 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1323 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1324 if not vm_info_dict
:
1325 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1326 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1328 status_key
= vm_info_dict
['status']
1331 vm_dict
= {'created': vm_info_dict
['created'],
1332 'description': vm_info_dict
['name'],
1333 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1334 'hostId': vm_info_dict
['vmuuid'],
1336 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1338 if 'interfaces' in vm_info_dict
:
1339 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1341 vm_dict
['interfaces'] = []
1343 vm_dict
= {'created': '',
1345 'status': vcdStatusCode2manoFormat
[int(-1)],
1346 'hostId': vm_info_dict
['vmuuid'],
1347 'error_msg': "Inconsistency state",
1348 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1352 def delete_vminstance(self
, vm__vim_uuid
):
1353 """Method poweroff and remove VM instance from vcloud director network.
1356 vm__vim_uuid: VM UUID
1359 Returns the instance identifier
1362 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1363 vca
= self
.connect()
1365 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1367 vdc
= vca
.get_vdc(self
.tenant_name
)
1369 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1371 raise vimconn
.vimconnException(
1372 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1375 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1376 if vapp_name
is None:
1377 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1378 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1380 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1382 # Delete vApp and wait for status change if task executed and vApp is None.
1383 # We successfully delete vApp from vCloud
1384 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1385 # poweroff vapp / undeploy and delete
1386 power_off_task
= vapp
.poweroff()
1387 if type(power_off_task
) is GenericTask
:
1388 vca
.block_until_completed(power_off_task
)
1390 if not power_off_task
:
1391 self
.logger
.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid
))
1394 if vapp
.me
.deployed
:
1395 undeploy_task
= vapp
.undeploy()
1396 if type(undeploy_task
) is GenericTask
:
1398 while retry
<= DELETE_INSTANCE_RETRY
:
1399 result
= vca
.block_until_completed(undeploy_task
)
1407 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1408 if vapp
is not None:
1409 delete_task
= vapp
.delete()
1411 while retry
<= DELETE_INSTANCE_RETRY
:
1412 task
= vapp
.delete()
1413 if type(task
) is GenericTask
:
1414 vca
.block_until_completed(delete_task
)
1416 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1420 self
.logger
.debug(traceback
.format_exc())
1421 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1423 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1426 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1428 def refresh_vms_status(self
, vm_list
):
1429 """Get the status of the virtual machines and their interfaces/ports
1430 Params: the list of VM identifiers
1431 Returns a dictionary with:
1432 vm_id: #VIM id of this Virtual Machine
1433 status: #Mandatory. Text with one of:
1434 # DELETED (not found at vim)
1435 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1436 # OTHER (Vim reported other status not understood)
1437 # ERROR (VIM indicates an ERROR status)
1438 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1439 # CREATING (on building process), ERROR
1440 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1442 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1443 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1445 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1446 mac_address: #Text format XX:XX:XX:XX:XX:XX
1447 vim_net_id: #network id where this interface is connected
1448 vim_interface_id: #interface/port VIM id
1449 ip_address: #null, or text with IPv4, IPv6 address
1452 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1453 vca
= self
.connect()
1455 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1457 vdc
= vca
.get_vdc(self
.tenant_name
)
1459 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1462 for vmuuid
in vm_list
:
1463 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1464 if vmname
is not None:
1466 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1467 vm_info
= the_vapp
.get_vms_details()
1468 vm_status
= vm_info
[0]['status']
1470 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1471 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1472 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1476 vm_app_networks
= the_vapp
.get_vms_network_info()
1477 for vapp_network
in vm_app_networks
:
1478 for vm_network
in vapp_network
:
1479 if vm_network
['name'] == vmname
:
1480 interface
= {"mac_address": vm_network
['mac'],
1481 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1482 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1483 'ip_address': vm_network
['ip']}
1484 # interface['vim_info'] = yaml.safe_dump(vm_network)
1485 vm_dict
["interfaces"].append(interface
)
1486 # add a vm to vm dict
1487 vms_dict
.setdefault(vmuuid
, vm_dict
)
1489 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1490 self
.logger
.debug(traceback
.format_exc())
1494 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1495 """Send and action over a VM instance from VIM
1496 Returns the vm_id if the action was successfully sent to the VIM"""
1498 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1499 if vm__vim_uuid
is None or action_dict
is None:
1500 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1502 vca
= self
.connect()
1504 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1506 vdc
= vca
.get_vdc(self
.tenant_name
)
1508 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1510 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1511 if vapp_name
is None:
1512 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1513 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1515 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1518 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1519 # TODO fix all status
1520 if "start" in action_dict
:
1521 if action_dict
["start"] == "rebuild":
1522 the_vapp
.deploy(powerOn
=True)
1524 vm_info
= the_vapp
.get_vms_details()
1525 vm_status
= vm_info
[0]['status']
1526 if vm_status
== "Suspended":
1528 elif vm_status
.status
== "Powered off":
1530 elif "pause" in action_dict
:
1533 elif "resume" in action_dict
:
1536 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1538 elif "forceOff" in action_dict
:
1540 elif "terminate" in action_dict
:
1542 # elif "createImage" in action_dict:
1543 # server.create_image()
1549 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1551 Get a console for the virtual machine
1553 vm_id: uuid of the VM
1554 console_type, can be:
1555 "novnc" (by default), "xvpvnc" for VNC types,
1556 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1557 Returns dict with the console parameters:
1558 protocol: ssh, ftp, http, https, ...
1559 server: usually ip address
1560 port: the http, ssh, ... port
1561 suffix: extra text, e.g. the http path and query string
1563 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1565 # NOT USED METHODS in current version
1567 def host_vim2gui(self
, host
, server_dict
):
1568 """Transform host dictionary from VIM format to GUI format,
1569 and append to the server_dict
1571 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1573 def get_hosts_info(self
):
1574 """Get the information of deployed hosts
1575 Returns the hosts content"""
1576 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1578 def get_hosts(self
, vim_tenant
):
1579 """Get the hosts and deployed instances
1580 Returns the hosts content"""
1581 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1583 def get_processor_rankings(self
):
1584 """Get the processor rankings in the VIM database"""
1585 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1587 def new_host(self
, host_data
):
1588 """Adds a new host to VIM"""
1589 '''Returns status code of the VIM response'''
1590 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1592 def new_external_port(self
, port_data
):
1593 """Adds a external port to VIM"""
1594 '''Returns the port identifier'''
1595 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1597 def new_external_network(self
, net_name
, net_type
):
1598 """Adds a external network to VIM (shared)"""
1599 '''Returns the network identifier'''
1600 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1602 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1603 """Connects a external port to a network"""
1604 '''Returns status code of the VIM response'''
1605 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1607 def new_vminstancefromJSON(self
, vm_data
):
1608 """Adds a VM instance to VIM"""
1609 '''Returns the instance identifier'''
1610 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1612 def get_network_name_by_id(self
, network_uuid
=None):
1613 """Method gets vcloud director network named based on supplied uuid.
1616 network_uuid: network_id
1619 The return network name.
1622 vca
= self
.connect()
1624 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1626 if not network_uuid
:
1630 org_dict
= self
.get_org(self
.org_uuid
)
1631 if 'networks' in org_dict
:
1632 org_network_dict
= org_dict
['networks']
1633 for net_uuid
in org_network_dict
:
1634 if net_uuid
== network_uuid
:
1635 return org_network_dict
[net_uuid
]
1637 self
.logger
.debug("Exception in get_network_name_by_id")
1638 self
.logger
.debug(traceback
.format_exc())
1642 def get_network_id_by_name(self
, network_name
=None):
1643 """Method gets vcloud director network uuid based on supplied name.
1646 network_name: network_name
1648 The return network uuid.
1649 network_uuid: network_id
1652 vca
= self
.connect()
1654 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1656 if not network_name
:
1657 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1661 org_dict
= self
.get_org(self
.org_uuid
)
1662 if org_dict
and 'networks' in org_dict
:
1663 org_network_dict
= org_dict
['networks']
1664 for net_uuid
,net_name
in org_network_dict
.iteritems():
1665 if net_name
== network_name
:
1668 except KeyError as exp
:
1669 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1673 def list_org_action(self
):
1675 Method leverages vCloud director and query for available organization for particular user
1678 vca - is active VCA connection.
1679 vdc_name - is a vdc name that will be used to query vms action
1682 The return XML respond
1685 vca
= self
.connect()
1687 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1689 url_list
= [vca
.host
, '/api/org']
1690 vm_list_rest_call
= ''.join(url_list
)
1692 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1693 response
= Http
.get(url
=vm_list_rest_call
,
1694 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1697 if response
.status_code
== requests
.codes
.ok
:
1698 return response
.content
1702 def get_org_action(self
, org_uuid
=None):
1704 Method leverages vCloud director and retrieve available object fdr organization.
1707 vca - is active VCA connection.
1708 vdc_name - is a vdc name that will be used to query vms action
1711 The return XML respond
1714 vca
= self
.connect()
1716 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1718 if org_uuid
is None:
1721 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1722 vm_list_rest_call
= ''.join(url_list
)
1724 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1725 response
= Http
.get(url
=vm_list_rest_call
,
1726 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1729 if response
.status_code
== requests
.codes
.ok
:
1730 return response
.content
1734 def get_org(self
, org_uuid
=None):
1736 Method retrieves available organization in vCloud Director
1739 org_uuid - is a organization uuid.
1742 The return dictionary with following key
1743 "network" - for network list under the org
1744 "catalogs" - for network list under the org
1745 "vdcs" - for vdc list under org
1749 vca
= self
.connect()
1751 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1753 if org_uuid
is None:
1756 content
= self
.get_org_action(org_uuid
=org_uuid
)
1761 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1762 for child
in vm_list_xmlroot
:
1763 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1764 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1765 org_dict
['vdcs'] = vdc_list
1766 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1767 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1768 org_dict
['networks'] = network_list
1769 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1770 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1771 org_dict
['catalogs'] = catalog_list
1777 def get_org_list(self
):
1779 Method retrieves available organization in vCloud Director
1782 vca - is active VCA connection.
1785 The return dictionary and key for each entry VDC UUID
1789 vca
= self
.connect()
1791 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1793 content
= self
.list_org_action()
1795 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1796 for vm_xml
in vm_list_xmlroot
:
1797 if vm_xml
.tag
.split("}")[1] == 'Org':
1798 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1799 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1805 def vms_view_action(self
, vdc_name
=None):
1806 """ Method leverages vCloud director vms query call
1809 vca - is active VCA connection.
1810 vdc_name - is a vdc name that will be used to query vms action
1813 The return XML respond
1815 vca
= self
.connect()
1816 if vdc_name
is None:
1819 url_list
= [vca
.host
, '/api/vms/query']
1820 vm_list_rest_call
= ''.join(url_list
)
1822 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1823 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1824 vca
.vcloud_session
.organization
.Link
)
1826 response
= Http
.get(url
=vm_list_rest_call
,
1827 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1830 if response
.status_code
== requests
.codes
.ok
:
1831 return response
.content
1835 def get_vapp_list(self
, vdc_name
=None):
1837 Method retrieves vApp list deployed vCloud director and returns a dictionary
1838 contains a list of all vapp deployed for queried VDC.
1839 The key for a dictionary is vApp UUID
1843 vca - is active VCA connection.
1844 vdc_name - is a vdc name that will be used to query vms action
1847 The return dictionary and key for each entry vapp UUID
1851 if vdc_name
is None:
1854 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1856 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1857 for vm_xml
in vm_list_xmlroot
:
1858 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1859 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1860 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1861 if 'vappTemplate-' in rawuuid
[0]:
1862 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1863 # vm and use raw UUID as key
1864 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1870 def get_vm_list(self
, vdc_name
=None):
1872 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1873 contains a list of all VM's deployed for queried VDC.
1874 The key for a dictionary is VM UUID
1878 vca - is active VCA connection.
1879 vdc_name - is a vdc name that will be used to query vms action
1882 The return dictionary and key for each entry vapp UUID
1886 if vdc_name
is None:
1889 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1891 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1892 for vm_xml
in vm_list_xmlroot
:
1893 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1894 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1895 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1896 if 'vm-' in rawuuid
[0]:
1897 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1898 # vm and use raw UUID as key
1899 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1905 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1907 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1908 contains a list of all VM's deployed for queried VDC.
1909 The key for a dictionary is VM UUID
1913 vca - is active VCA connection.
1914 vdc_name - is a vdc name that will be used to query vms action
1917 The return dictionary and key for each entry vapp UUID
1920 vca
= self
.connect()
1922 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1924 if vdc_name
is None:
1927 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1929 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1930 for vm_xml
in vm_list_xmlroot
:
1931 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1932 # lookup done by UUID
1934 if vapp_name
in vm_xml
.attrib
['container']:
1935 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1936 if 'vm-' in rawuuid
[0]:
1937 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1939 # lookup done by Name
1941 if vapp_name
in vm_xml
.attrib
['name']:
1942 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1943 if 'vm-' in rawuuid
[0]:
1944 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1951 def get_network_action(self
, network_uuid
=None):
1953 Method leverages vCloud director and query network based on network uuid
1956 vca - is active VCA connection.
1957 network_uuid - is a network uuid
1960 The return XML respond
1963 vca
= self
.connect()
1965 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1967 if network_uuid
is None:
1970 url_list
= [vca
.host
, '/api/network/', network_uuid
]
1971 vm_list_rest_call
= ''.join(url_list
)
1973 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1974 response
= Http
.get(url
=vm_list_rest_call
,
1975 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1978 if response
.status_code
== requests
.codes
.ok
:
1979 return response
.content
1983 def get_vcd_network(self
, network_uuid
=None):
1985 Method retrieves available network from vCloud Director
1988 network_uuid - is VCD network UUID
1990 Each element serialized as key : value pair
1992 Following keys available for access. network_configuration['Gateway'}
1996 <IsInherited>true</IsInherited>
1997 <Gateway>172.16.252.100</Gateway>
1998 <Netmask>255.255.255.0</Netmask>
1999 <Dns1>172.16.254.201</Dns1>
2000 <Dns2>172.16.254.202</Dns2>
2001 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2002 <IsEnabled>true</IsEnabled>
2005 <StartAddress>172.16.252.1</StartAddress>
2006 <EndAddress>172.16.252.99</EndAddress>
2011 <FenceMode>bridged</FenceMode>
2014 The return dictionary and key for each entry vapp UUID
2017 network_configuration
= {}
2018 if network_uuid
is None:
2021 content
= self
.get_network_action(network_uuid
=network_uuid
)
2023 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2025 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2026 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2027 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2029 for child
in vm_list_xmlroot
:
2030 if child
.tag
.split("}")[1] == 'IsShared':
2031 network_configuration
['isShared'] = child
.text
.strip()
2032 if child
.tag
.split("}")[1] == 'Configuration':
2033 for configuration
in child
.iter():
2034 tagKey
= configuration
.tag
.split("}")[1].strip()
2036 network_configuration
[tagKey
] = configuration
.text
.strip()
2037 return network_configuration
2041 return network_configuration
2043 def delete_network_action(self
, network_uuid
=None):
2045 Method delete given network from vCloud director
2048 network_uuid - is a network uuid that client wish to delete
2051 The return None or XML respond or false
2054 vca
= self
.connect_as_admin()
2056 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2057 if network_uuid
is None:
2060 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2061 vm_list_rest_call
= ''.join(url_list
)
2063 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2064 response
= Http
.delete(url
=vm_list_rest_call
,
2065 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2069 if response
.status_code
== 202:
2074 def create_network(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2076 Method create network in vCloud director
2079 network_name - is network name to be created.
2080 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2081 It optional attribute. by default if no parent network indicate the first available will be used.
2084 The return network uuid or return None
2087 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2088 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2089 parent_network_uuid
=parent_network_uuid
,
2092 self
.logger
.debug("Failed create network {}.".format(network_name
))
2096 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2097 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2098 if len(vcd_uuid
) == 4:
2099 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2102 self
.logger
.debug("Failed create network {}".format(network_name
))
2105 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2107 Method create network in vCloud director
2110 network_name - is network name to be created.
2111 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2112 It optional attribute. by default if no parent network indicate the first available will be used.
2115 The return network uuid or return None
2118 vca
= self
.connect_as_admin()
2120 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2121 if network_name
is None:
2124 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2125 vm_list_rest_call
= ''.join(url_list
)
2126 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2127 response
= Http
.get(url
=vm_list_rest_call
,
2128 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2132 provider_network
= None
2133 available_networks
= None
2134 add_vdc_rest_url
= None
2136 if response
.status_code
!= requests
.codes
.ok
:
2137 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2138 response
.status_code
))
2142 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2143 for child
in vm_list_xmlroot
:
2144 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2145 provider_network
= child
.attrib
.get('href')
2146 # application/vnd.vmware.admin.providervdc+xml
2147 if child
.tag
.split("}")[1] == 'Link':
2148 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2149 and child
.attrib
.get('rel') == 'add':
2150 add_vdc_rest_url
= child
.attrib
.get('href')
2152 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2153 self
.logger
.debug("Respond body {}".format(response
.content
))
2156 # find pvdc provided available network
2157 response
= Http
.get(url
=provider_network
,
2158 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2161 if response
.status_code
!= requests
.codes
.ok
:
2162 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2163 response
.status_code
))
2166 # available_networks.split("/")[-1]
2168 if parent_network_uuid
is None:
2170 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2171 for child
in vm_list_xmlroot
.iter():
2172 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2173 for networks
in child
.iter():
2174 # application/vnd.vmware.admin.network+xml
2175 if networks
.attrib
.get('href') is not None:
2176 available_networks
= networks
.attrib
.get('href')
2181 # either use client provided UUID or search for a first available
2182 # if both are not defined we return none
2183 if parent_network_uuid
is not None:
2184 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2185 add_vdc_rest_url
= ''.join(url_list
)
2187 # return response.content
2188 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2189 <Description>Openmano created</Description>
2191 <ParentNetwork href="{1:s}"/>
2192 <FenceMode>{2:s}</FenceMode>
2194 <IsShared>{3:s}</IsShared>
2195 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2197 headers
= vca
.vcloud_session
.get_vcloud_headers()
2198 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2199 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2201 # if we all ok we respond with content otherwise by default None
2202 if response
.status_code
== 201:
2203 return response
.content
2206 def get_provider_rest(self
, vca
=None):
2208 Method gets provider vdc view from vcloud director
2211 network_name - is network name to be created.
2212 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2213 It optional attribute. by default if no parent network indicate the first available will be used.
2216 The return xml content of respond or None
2219 url_list
= [vca
.host
, '/api/admin']
2220 response
= Http
.get(url
=''.join(url_list
),
2221 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2225 if response
.status_code
== requests
.codes
.ok
:
2226 return response
.content
2229 def create_vdc(self
, vdc_name
=None):
2233 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2234 if xml_content
is not None:
2236 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2237 for child
in task_resp_xmlroot
:
2238 if child
.tag
.split("}")[1] == 'Owner':
2239 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2240 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2243 self
.logger
.debug("Respond body {}".format(xml_content
))
2247 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2249 Method create vdc in vCloud director based on VDC template.
2250 it uses pre-defined template that must be named openmano
2253 vdc_name - name of a new vdc.
2256 The return xml content of respond or None
2259 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2260 vca
= self
.connect()
2262 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2263 if vdc_name
is None:
2266 url_list
= [vca
.host
, '/api/vdcTemplates']
2267 vm_list_rest_call
= ''.join(url_list
)
2268 response
= Http
.get(url
=vm_list_rest_call
,
2269 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2273 # container url to a template
2274 vdc_template_ref
= None
2276 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2277 for child
in vm_list_xmlroot
:
2278 # application/vnd.vmware.admin.providervdc+xml
2279 # we need find a template from witch we instantiate VDC
2280 if child
.tag
.split("}")[1] == 'VdcTemplate':
2281 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2282 'name') == 'openmano':
2283 vdc_template_ref
= child
.attrib
.get('href')
2285 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2286 self
.logger
.debug("Respond body {}".format(response
.content
))
2289 # if we didn't found required pre defined template we return None
2290 if vdc_template_ref
is None:
2295 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2296 vm_list_rest_call
= ''.join(url_list
)
2297 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2298 <Source href="{1:s}"></Source>
2299 <Description>opnemano</Description>
2300 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2301 headers
= vca
.vcloud_session
.get_vcloud_headers()
2302 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2303 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2305 # if we all ok we respond with content otherwise by default None
2306 if response
.status_code
>= 200 and response
.status_code
< 300:
2307 return response
.content
2310 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2311 self
.logger
.debug("Respond body {}".format(response
.content
))
2315 def create_vdc_rest(self
, vdc_name
=None):
2317 Method create network in vCloud director
2320 network_name - is network name to be created.
2321 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2322 It optional attribute. by default if no parent network indicate the first available will be used.
2325 The return network uuid or return None
2328 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2330 vca
= self
.connect_as_admin()
2332 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2333 if vdc_name
is None:
2336 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2337 vm_list_rest_call
= ''.join(url_list
)
2338 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2339 response
= Http
.get(url
=vm_list_rest_call
,
2340 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2344 provider_vdc_ref
= None
2345 add_vdc_rest_url
= None
2346 available_networks
= None
2348 if response
.status_code
!= requests
.codes
.ok
:
2349 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2350 response
.status_code
))
2354 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2355 for child
in vm_list_xmlroot
:
2356 # application/vnd.vmware.admin.providervdc+xml
2357 if child
.tag
.split("}")[1] == 'Link':
2358 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2359 and child
.attrib
.get('rel') == 'add':
2360 add_vdc_rest_url
= child
.attrib
.get('href')
2362 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2363 self
.logger
.debug("Respond body {}".format(response
.content
))
2366 response
= self
.get_provider_rest(vca
=vca
)
2368 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2369 for child
in vm_list_xmlroot
:
2370 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2371 for sub_child
in child
:
2372 provider_vdc_ref
= sub_child
.attrib
.get('href')
2374 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2375 self
.logger
.debug("Respond body {}".format(response
))
2378 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2379 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2380 <AllocationModel>ReservationPool</AllocationModel>
2381 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2382 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2383 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2384 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2385 <ProviderVdcReference
2386 name="Main Provider"
2388 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2392 headers
= vca
.vcloud_session
.get_vcloud_headers()
2393 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2394 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2397 # if we all ok we respond with content otherwise by default None
2398 if response
.status_code
== 201:
2399 return response
.content
2402 def get_vapp_details_rest(self
, vapp_uuid
=None):
2404 Method retrieve vapp detail from vCloud director
2407 vapp_uuid - is vapp identifier.
2410 The return network uuid or return None
2415 vca
= self
.connect()
2417 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2418 if vapp_uuid
is None:
2421 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2422 get_vapp_restcall
= ''.join(url_list
)
2423 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2424 response
= Http
.get(url
=get_vapp_restcall
,
2425 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2429 if response
.status_code
!= requests
.codes
.ok
:
2430 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2431 response
.status_code
))
2432 return parsed_respond
2435 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2436 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2438 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2439 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2440 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2442 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2443 if created_section
is not None:
2444 parsed_respond
['created'] = created_section
.text
2446 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2447 if network_section
is not None and 'networkName' in network_section
.attrib
:
2448 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2450 ipscopes_section
= \
2451 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2453 if ipscopes_section
is not None:
2454 for ipscope
in ipscopes_section
:
2455 for scope
in ipscope
:
2456 tag_key
= scope
.tag
.split("}")[1]
2457 if tag_key
== 'IpRanges':
2458 ip_ranges
= scope
.getchildren()
2459 for ipblock
in ip_ranges
:
2460 for block
in ipblock
:
2461 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2463 parsed_respond
[tag_key
] = scope
.text
2465 # parse children section for other attrib
2466 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2467 if children_section
is not None:
2468 parsed_respond
['name'] = children_section
.attrib
['name']
2469 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2470 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2471 parsed_respond
['status'] = children_section
.attrib
['status']
2472 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2473 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2475 for adapters
in network_adapter
:
2476 adapter_key
= adapters
.tag
.split("}")[1]
2477 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2478 parsed_respond
['primarynetwork'] = adapters
.text
2479 if adapter_key
== 'NetworkConnection':
2481 if 'network' in adapters
.attrib
:
2482 vnic
['network'] = adapters
.attrib
['network']
2483 for adapter
in adapters
:
2484 setting_key
= adapter
.tag
.split("}")[1]
2485 vnic
[setting_key
] = adapter
.text
2486 nic_list
.append(vnic
)
2488 for link
in children_section
:
2489 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2490 if link
.attrib
['rel'] == 'screen:acquireTicket':
2491 parsed_respond
['acquireTicket'] = link
.attrib
2492 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2493 parsed_respond
['acquireMksTicket'] = link
.attrib
2495 parsed_respond
['interfaces'] = nic_list
2499 return parsed_respond
2501 def acuire_console(self
, vm_uuid
=None):
2503 vca
= self
.connect()
2505 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2509 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2510 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2511 console_dict
= vm_dict
['acquireTicket']
2512 console_rest_call
= console_dict
['href']
2514 response
= Http
.post(url
=console_rest_call
,
2515 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2519 if response
.status_code
== requests
.codes
.ok
:
2520 return response
.content