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 variables for number of retry
71 __author__
= "Mustafa Bayramov, Arpita Kate"
72 __date__
= "$23-Dec-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
[1]
168 self
.org_name
= orgnameandtenant
[0]
170 self
.tenant_name
= tenant_name
171 elif "orgname" in config
:
172 self
.org_name
= config
['orgname']
175 self
.logger
.setLevel(getattr(logging
, log_level
))
178 self
.admin_user
= config
['admin_username']
179 self
.admin_password
= config
['admin_password']
181 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
187 raise vimconn
.vimconnException('url param can not be NoneType')
189 if not self
.url_admin
: # try to use normal url
190 self
.url_admin
= self
.url
192 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
193 self
.tenant_id
, self
.tenant_name
))
194 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
195 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
197 # initialize organization
198 if self
.user
is not None and self
.passwd
is not None and self
.url
:
199 self
.init_organization()
201 def __getitem__(self
, index
):
204 if index
== 'tenant_id':
205 return self
.tenant_id
206 if index
== 'tenant_name':
207 return self
.tenant_name
210 elif index
== 'org_name':
212 elif index
== 'org_uuid':
214 elif index
== 'user':
216 elif index
== 'passwd':
220 elif index
== 'url_admin':
221 return self
.url_admin
222 elif index
== "config":
225 raise KeyError("Invalid key '%s'" % str(index
))
227 def __setitem__(self
, index
, value
):
230 if index
== 'tenant_id':
231 self
.tenant_id
= value
232 if index
== 'tenant_name':
233 self
.tenant_name
= value
236 elif index
== 'org_name':
237 self
.org_name
= value
238 elif index
== 'org_uuid':
239 self
.org_uuid
= value
240 elif index
== 'user':
242 elif index
== 'passwd':
246 elif index
== 'url_admin':
247 self
.url_admin
= value
249 raise KeyError("Invalid key '%s'" % str(index
))
251 def connect_as_admin(self
):
252 """ Method connect as pvdc admin user to vCloud director.
253 There are certain action that can be done only by provider vdc admin user.
254 Organization creation / provider network creation etc.
257 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
260 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
262 vca_admin
= VCA(host
=self
.url
,
263 username
=self
.admin_user
,
264 service_type
=STANDALONE
,
268 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
270 raise vimconn
.vimconnConnectionException(
271 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
272 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
275 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
280 """ Method connect as normal user to vCloud director.
283 The return vca object that letter can be used to connect to vCloud director as admin for VDC
287 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
290 vca
= VCA(host
=self
.url
,
292 service_type
=STANDALONE
,
297 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
299 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
300 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
303 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
306 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
307 "{} as user: {}".format(self
.org_name
, self
.user
))
311 def init_organization(self
):
312 """ Method initialize organization UUID and VDC parameters.
314 At bare minimum client must provide organization name that present in vCloud director and VDC.
316 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
317 The Org - UUID will be initialized at the run time if data center present in vCloud director.
320 The return vca object that letter can be used to connect to vcloud direct as admin
323 if self
.org_uuid
is None:
324 org_dict
= self
.get_org_list()
326 # we set org UUID at the init phase but we can do it only when we have valid credential.
327 if org_dict
[org
] == self
.org_name
:
329 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
332 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
334 # if well good we require for org details
335 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
337 # we have two case if we want to initialize VDC ID or VDC name at run time
338 # tenant_name provided but no tenant id
339 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
340 vdcs_dict
= org_details_dict
['vdcs']
341 for vdc
in vdcs_dict
:
342 if vdcs_dict
[vdc
] == self
.tenant_name
:
344 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
348 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
349 # case two we have tenant_id but we don't have tenant name so we find and set it.
350 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
351 vdcs_dict
= org_details_dict
['vdcs']
352 for vdc
in vdcs_dict
:
353 if vdc
== self
.tenant_id
:
354 self
.tenant_name
= vdcs_dict
[vdc
]
355 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
359 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
360 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
362 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
363 self
.logger
.debug(traceback
.format_exc())
366 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
367 """ Method adds a new tenant to VIM with this name.
368 This action requires access to create VDC action in vCloud director.
371 tenant_name is tenant_name to be created.
372 tenant_description not used for this call
375 returns the tenant identifier in UUID format.
376 If action is failed method will throw vimconn.vimconnException method
378 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
379 if vdc_task
is not None:
380 vdc_uuid
, value
= vdc_task
.popitem()
381 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
384 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
386 def delete_tenant(self
, tenant_id
=None):
387 """Delete a tenant from VIM"""
388 'Returns the tenant identifier'
389 raise vimconn
.vimconnNotImplemented("Should have implemented this")
391 def get_tenant_list(self
, filter_dict
={}):
392 """Obtain tenants of VIM
393 filter_dict can contain the following keys:
394 name: filter by tenant name
395 id: filter by tenant uuid/id
397 Returns the tenant list of dictionaries:
398 [{'name':'<name>, 'id':'<id>, ...}, ...]
401 org_dict
= self
.get_org(self
.org_uuid
)
402 vdcs_dict
= org_dict
['vdcs']
407 entry
= {'name': vdcs_dict
[k
], 'id': k
}
408 # if caller didn't specify dictionary we return all tenants.
409 if filter_dict
is not None and filter_dict
:
410 filtered_entry
= entry
.copy()
411 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
412 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
413 if filter_dict
== entry
:
414 vdclist
.append(filtered_entry
)
416 vdclist
.append(entry
)
418 self
.logger
.debug("Error in get_tenant_list()")
419 self
.logger
.debug(traceback
.format_exc())
420 raise vimconn
.vimconnException("Incorrect state. {}")
424 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
425 """Adds a tenant network to VIM
427 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
428 ip_profile is a dict containing the IP parameters of the network
430 Returns the network identifier"""
433 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name
, net_type
, ip_profile
, shared
))
439 network_uuid
= self
.create_network(network_name
=net_name
, isshared
=isshared
)
440 if network_uuid
is not None:
443 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
445 def get_vcd_network_list(self
):
446 """ Method available organization for a logged in tenant
449 The return vca object that letter can be used to connect to vcloud direct as admin
452 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
455 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
457 if not self
.tenant_name
:
458 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
460 vdc
= vca
.get_vdc(self
.tenant_name
)
462 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
464 vdc_uuid
= vdc
.get_id().split(":")[3]
465 networks
= vca
.get_networks(vdc
.get_name())
468 for network
in networks
:
470 netid
= network
.get_id().split(":")
474 filter_dict
["name"] = network
.get_name()
475 filter_dict
["id"] = netid
[3]
476 filter_dict
["shared"] = network
.get_IsShared()
477 filter_dict
["tenant_id"] = vdc_uuid
478 if network
.get_status() == 1:
479 filter_dict
["admin_state_up"] = True
481 filter_dict
["admin_state_up"] = False
482 filter_dict
["status"] = "ACTIVE"
483 filter_dict
["type"] = "bridge"
484 network_list
.append(filter_dict
)
485 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
487 self
.logger
.debug("Error in get_vcd_network_list")
488 self
.logger
.debug(traceback
.format_exc())
491 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
494 def get_network_list(self
, filter_dict
={}):
495 """Obtain tenant networks of VIM
497 name: network name OR/AND
498 id: network uuid OR/AND
499 shared: boolean OR/AND
500 tenant_id: tenant OR/AND
501 admin_state_up: boolean
504 [{key : value , key : value}]
506 Returns the network list of dictionaries:
507 [{<the fields at Filter_dict plus some VIM specific>}, ...]
511 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
514 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
516 if not self
.tenant_name
:
517 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
519 vdc
= vca
.get_vdc(self
.tenant_name
)
521 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
523 vdcid
= vdc
.get_id().split(":")[3]
524 networks
= vca
.get_networks(vdc
.get_name())
528 for network
in networks
:
530 net_uuid
= network
.get_id().split(":")
531 if len(net_uuid
) != 4:
534 net_uuid
= net_uuid
[3]
536 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
539 filter_entry
["name"] = network
.get_name()
540 filter_entry
["id"] = net_uuid
541 filter_entry
["shared"] = network
.get_IsShared()
542 filter_entry
["tenant_id"] = vdcid
543 if network
.get_status() == 1:
544 filter_entry
["admin_state_up"] = True
546 filter_entry
["admin_state_up"] = False
547 filter_entry
["status"] = "ACTIVE"
548 filter_entry
["type"] = "bridge"
549 filtered_entry
= filter_entry
.copy()
551 if filter_dict
is not None and filter_dict
:
552 # we remove all the key : value we don't care and match only
554 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
555 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
556 if filter_dict
== filter_entry
:
557 network_list
.append(filtered_entry
)
559 network_list
.append(filtered_entry
)
561 self
.logger
.debug("Error in get_vcd_network_list")
562 self
.logger
.debug(traceback
.format_exc())
564 self
.logger
.debug("Returning {}".format(network_list
))
567 def get_network(self
, net_id
):
568 """Method obtains network details of net_id VIM network
569 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
573 raise vimconn
.vimconnConnectionException("self.connect() is failed")
575 vdc
= vca
.get_vdc(self
.tenant_name
)
576 vdc_id
= vdc
.get_id().split(":")[3]
578 networks
= vca
.get_networks(vdc
.get_name())
582 for network
in networks
:
583 vdc_network_id
= network
.get_id().split(":")
584 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
585 filter_dict
["name"] = network
.get_name()
586 filter_dict
["id"] = vdc_network_id
[3]
587 filter_dict
["shared"] = network
.get_IsShared()
588 filter_dict
["tenant_id"] = vdc_id
589 if network
.get_status() == 1:
590 filter_dict
["admin_state_up"] = True
592 filter_dict
["admin_state_up"] = False
593 filter_dict
["status"] = "ACTIVE"
594 filter_dict
["type"] = "bridge"
595 self
.logger
.debug("Returning {}".format(filter_dict
))
598 self
.logger
.debug("Error in get_network")
599 self
.logger
.debug(traceback
.format_exc())
603 def delete_network(self
, net_id
):
605 Method Deletes a tenant network from VIM, provide the network id.
607 Returns the network identifier or raise an exception
612 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
614 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
615 if vcd_network
is not None and vcd_network
:
616 if self
.delete_network_action(network_uuid
=net_id
):
619 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
621 def refresh_nets_status(self
, net_list
):
622 """Get the status of the networks
623 Params: the list of network identifiers
624 Returns a dictionary with:
625 net_id: #VIM id of this network
626 status: #Mandatory. Text with one of:
627 # DELETED (not found at vim)
628 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
629 # OTHER (Vim reported other status not understood)
630 # ERROR (VIM indicates an ERROR status)
631 # ACTIVE, INACTIVE, DOWN (admin down),
632 # BUILD (on building process)
634 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
635 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
641 raise vimconn
.vimconnConnectionException("self.connect() is failed")
647 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
648 if vcd_network
is not None and vcd_network
:
649 if vcd_network
['status'] == '1':
655 errormsg
= 'Network not found.'
657 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
658 'vim_info': yaml
.safe_dump(vcd_network
)}
660 self
.logger
.debug("Error in refresh_nets_status")
661 self
.logger
.debug(traceback
.format_exc())
665 def get_flavor(self
, flavor_id
):
666 """Obtain flavor details from the VIM
667 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
669 if flavor_id
not in flavorlist
:
670 raise vimconn
.vimconnNotFoundException("Flavor not found.")
671 return flavorlist
[flavor_id
]
673 def new_flavor(self
, flavor_data
):
674 """Adds a tenant flavor to VIM
675 flavor_data contains a dictionary with information, keys:
677 ram: memory (cloud type) in MBytes
678 vpcus: cpus (cloud type)
679 extended: EPA parameters
680 - numas: #items requested in same NUMA
681 memory: number of 1G huge pages memory
682 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
683 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
684 - name: interface name
685 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
686 bandwidth: X Gbps; requested guarantee bandwidth
687 vpci: requested virtual PCI address
694 Returns the flavor identifier"""
696 # generate a new uuid put to internal dict and return it.
697 flavor_id
= uuid
.uuid4()
698 flavorlist
[str(flavor_id
)] = flavor_data
700 return str(flavor_id
)
702 def delete_flavor(self
, flavor_id
):
703 """Deletes a tenant flavor from VIM identify by its id
705 Returns the used id or raise an exception
707 if flavor_id
not in flavorlist
:
708 raise vimconn
.vimconnNotFoundException("Flavor not found.")
710 flavorlist
.pop(flavor_id
, None)
713 def new_image(self
, image_dict
):
715 Adds a tenant image to VIM
717 200, image-id if the image is created
718 <0, message if there is an error
721 return self
.get_image_id_from_path(image_dict
['location'])
723 def delete_image(self
, image_id
):
730 raise vimconn
.vimconnNotImplemented("Should have implemented this")
732 def catalog_exists(self
, catalog_name
, catalogs
):
739 for catalog
in catalogs
:
740 if catalog
.name
== catalog_name
:
744 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
745 """ Create new catalog entry in vCloud director.
748 vca: vCloud director.
749 catalog_name catalog that client wish to create. Note no validation done for a name.
750 Client must make sure that provide valid string representation.
752 Return (bool) True if catalog created.
756 task
= vca
.create_catalog(catalog_name
, catalog_name
)
757 result
= vca
.block_until_completed(task
)
760 catalogs
= vca
.get_catalogs()
763 return self
.catalog_exists(catalog_name
, catalogs
)
765 # noinspection PyIncorrectDocstring
766 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
767 description
='', progress
=False, chunk_bytes
=128 * 1024):
769 Uploads a OVF file to a vCloud catalog
776 :param catalog_name: (str): The name of the catalog to upload the media.
777 :param media_file_name: (str): The name of the local media file to upload.
778 :return: (bool) True if the media file was successfully uploaded, false otherwise.
780 os
.path
.isfile(media_file_name
)
781 statinfo
= os
.stat(media_file_name
)
783 # find a catalog entry where we upload OVF.
784 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
786 # if VCD can parse OVF we upload VMDK file
787 for catalog
in vca
.get_catalogs():
788 if catalog_name
!= catalog
.name
:
790 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
791 link
.get_rel() == 'add', catalog
.get_Link())
792 assert len(link
) == 1
794 <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>
795 """ % (escape(catalog_name
), escape(description
))
796 headers
= vca
.vcloud_session
.get_vcloud_headers()
797 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
798 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
799 if response
.status_code
== requests
.codes
.created
:
800 catalogItem
= XmlElementTree
.fromstring(response
.content
)
801 entity
= [child
for child
in catalogItem
if
802 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
803 href
= entity
.get('href')
805 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
806 verify
=vca
.verify
, logger
=self
.logger
)
808 if response
.status_code
== requests
.codes
.ok
:
809 media
= mediaType
.parseString(response
.content
, True)
810 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
811 media
.get_Files().get_File()[0].get_Link())[0]
812 headers
= vca
.vcloud_session
.get_vcloud_headers()
813 headers
['Content-Type'] = 'Content-Type text/xml'
814 response
= Http
.put(link
.get_href(),
815 data
=open(media_file_name
, 'rb'),
817 verify
=vca
.verify
, logger
=self
.logger
)
818 if response
.status_code
!= requests
.codes
.ok
:
820 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
824 # TODO fix this with aync block
827 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
829 # uploading VMDK file
830 # check status of OVF upload and upload remaining files.
831 response
= Http
.get(template
,
832 headers
=vca
.vcloud_session
.get_vcloud_headers(),
836 if response
.status_code
== requests
.codes
.ok
:
837 media
= mediaType
.parseString(response
.content
, True)
838 number_of_files
= len(media
.get_Files().get_File())
839 for index
in xrange(0, number_of_files
):
840 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
841 media
.get_Files().get_File()[index
].get_Link())
842 for link
in links_list
:
843 # we skip ovf since it already uploaded.
844 if 'ovf' in link
.get_href():
846 # The OVF file and VMDK must be in a same directory
847 head
, tail
= os
.path
.split(media_file_name
)
848 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
849 if not os
.path
.isfile(file_vmdk
):
851 statinfo
= os
.stat(file_vmdk
)
852 if statinfo
.st_size
== 0:
854 hrefvmdk
= link
.get_href()
857 print("Uploading file: {}".format(file_vmdk
))
859 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
861 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
863 bytes_transferred
= 0
864 f
= open(file_vmdk
, 'rb')
865 while bytes_transferred
< statinfo
.st_size
:
866 my_bytes
= f
.read(chunk_bytes
)
867 if len(my_bytes
) <= chunk_bytes
:
868 headers
= vca
.vcloud_session
.get_vcloud_headers()
869 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
870 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
871 headers
['Content-Length'] = str(len(my_bytes
))
872 response
= Http
.put(hrefvmdk
,
878 if response
.status_code
== requests
.codes
.ok
:
879 bytes_transferred
+= len(my_bytes
)
881 progress_bar
.update(bytes_transferred
)
884 'file upload failed with error: [%s] %s' % (response
.status_code
,
891 progress_bar
.finish()
894 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
895 format(catalog_name
, media_file_name
))
898 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
901 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
902 """Upload media file"""
903 # TODO add named parameters for readability
905 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
906 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
908 def validate_uuid4(self
, uuid_string
=None):
909 """ Method validate correct format of UUID.
911 Return: true if string represent valid uuid
914 val
= uuid
.UUID(uuid_string
, version
=4)
919 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
920 """ Method check catalog and return catalog ID in UUID format.
923 catalog_name: catalog name as string
924 catalogs: list of catalogs.
926 Return: catalogs uuid
929 for catalog
in catalogs
:
930 if catalog
.name
== catalog_name
:
931 catalog_id
= catalog
.get_id().split(":")
935 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
936 """ Method check catalog and return catalog name lookup done by catalog UUID.
939 catalog_name: catalog name as string
940 catalogs: list of catalogs.
942 Return: catalogs name or None
945 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
948 for catalog
in catalogs
:
949 catalog_id
= catalog
.get_id().split(":")[3]
950 if catalog_id
== catalog_uuid
:
954 def get_image_id_from_path(self
, path
=None, progress
=False):
955 """ Method upload OVF image to vCloud director.
957 Each OVF image represented as single catalog entry in vcloud director.
958 The method check for existing catalog entry. The check done by file name without file extension.
960 if given catalog name already present method will respond with existing catalog uuid otherwise
961 it will create new catalog entry and upload OVF file to newly created catalog.
963 If method can't create catalog entry or upload a file it will throw exception.
965 Method accept boolean flag progress that will output progress bar. It useful method
966 for standalone upload use case. In case to test large file upload.
969 path: - valid path to OVF file.
970 progress - boolean progress bar show progress bar.
972 Return: if image uploaded correct method will provide image catalog UUID.
976 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
979 raise vimconn
.vimconnException("Image path can't be None.")
981 if not os
.path
.isfile(path
):
982 raise vimconn
.vimconnException("Can't read file. File not found.")
984 if not os
.access(path
, os
.R_OK
):
985 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
987 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
989 dirpath
, filename
= os
.path
.split(path
)
990 flname
, file_extension
= os
.path
.splitext(path
)
991 if file_extension
!= '.ovf':
992 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
993 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
995 catalog_name
= os
.path
.splitext(filename
)[0]
996 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
997 self
.logger
.debug("File name {} Catalog Name {} file path {} "
998 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1000 catalogs
= vca
.get_catalogs()
1001 if len(catalogs
) == 0:
1002 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1003 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1005 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1006 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1007 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1009 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1010 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1012 for catalog
in catalogs
:
1013 # search for existing catalog if we find same name we return ID
1014 # TODO optimize this
1015 if catalog
.name
== catalog_md5_name
:
1016 self
.logger
.debug("Found existing catalog entry for {} "
1017 "catalog id {}".format(catalog_name
,
1018 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1019 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1021 # if we didn't find existing catalog we create a new one and upload image.
1022 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1023 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1025 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1027 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1028 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1030 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1032 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1034 def get_vappid(self
, vdc
=None, vapp_name
=None):
1035 """ Method takes vdc object and vApp name and returns vapp uuid or None
1038 vdc: The VDC object.
1039 vapp_name: is application vappp name identifier
1042 The return vApp name otherwise None
1044 if vdc
is None or vapp_name
is None:
1046 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1048 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1049 vdc
.ResourceEntities
.ResourceEntity
)
1051 return refs
[0].href
.split("vapp")[1][1:]
1052 except Exception as e
:
1053 self
.logger
.exception(e
)
1057 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1058 """ Method Method returns True or False if vapp deployed in vCloud director
1061 vca: Connector to VCA
1062 vdc: The VDC object.
1063 vappid: vappid is application identifier
1066 The return True if vApp deployed
1071 refs
= filter(lambda ref
:
1072 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1073 vdc
.ResourceEntities
.ResourceEntity
)
1075 vappid
= ref
.href
.split("vapp")[1][1:]
1076 # find vapp with respected vapp uuid
1077 if vappid
== vapp_uuid
:
1079 except Exception as e
:
1080 self
.logger
.exception(e
)
1084 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1085 """Method returns vApp name from vCD and lookup done by vapp_id.
1088 vca: Connector to VCA
1089 vdc: The VDC object.
1090 vapp_uuid: vappid is application identifier
1093 The return vApp name otherwise None
1097 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1098 vdc
.ResourceEntities
.ResourceEntity
)
1100 # we care only about UUID the rest doesn't matter
1101 vappid
= ref
.href
.split("vapp")[1][1:]
1102 if vappid
== vapp_uuid
:
1103 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1105 tree
= XmlElementTree
.fromstring(response
.content
)
1106 return tree
.attrib
['name']
1107 except Exception as e
:
1108 self
.logger
.exception(e
)
1112 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1113 cloud_config
=None, disk_list
=None):
1114 """Adds a VM instance to VIM
1116 start: indicates if VM must start or boot in pause mode. Ignored
1117 image_id,flavor_id: image and flavor uuid
1118 net_list: list of interfaces, each one is a dictionary with:
1120 net_id: network uuid to connect
1121 vpci: virtual vcpi to assign
1122 model: interface model, virtio, e2000, ...
1124 use: 'data', 'bridge', 'mgmt'
1125 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1126 vim_id: filled/added by this function
1127 cloud_config: can be a text script to be passed directly to cloud-init,
1128 or an object to inject users and ssh keys with format:
1129 key-pairs: [] list of keys to install to the default user
1130 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1131 #TODO ip, security groups
1132 Returns >=0, the instance identifier
1136 self
.logger
.info("Creating new instance for entry {}".format(name
))
1137 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1138 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1139 vca
= self
.connect()
1141 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1143 #new vm name = vmname + tenant_id + uuid
1144 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1145 vmname_andid
= ''.join(new_vm_name
)
1147 # if vm already deployed we return existing uuid
1148 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1149 # if vapp_uuid is not None:
1152 # we check for presence of VDC, Catalog entry and Flavor.
1153 vdc
= vca
.get_vdc(self
.tenant_name
)
1155 raise vimconn
.vimconnNotFoundException(
1156 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1157 catalogs
= vca
.get_catalogs()
1158 if catalogs
is None:
1159 raise vimconn
.vimconnNotFoundException(
1160 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1162 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1163 if catalog_hash_name
:
1164 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1166 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1167 "(Failed retrieve catalog information {})".format(name
, image_id
))
1170 # Set vCPU and Memory based on flavor.
1174 if flavor_id
is not None:
1175 if flavor_id
not in flavorlist
:
1176 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1177 "Failed retrieve flavor information "
1178 "flavor id {}".format(name
, flavor_id
))
1181 flavor
= flavorlist
[flavor_id
]
1182 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1183 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1185 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1187 # image upload creates template name as catalog name space Template.
1188 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1193 # client must provide at least one entry in net_list if not we report error
1195 primary_netname
= None
1196 network_mode
= 'bridged'
1197 if net_list
is not None and len(net_list
) > 0:
1198 primary_net
= net_list
[0]
1199 if primary_net
is None:
1200 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1203 primary_net_id
= primary_net
['net_id']
1204 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1205 if 'name' in network_dict
:
1206 primary_netname
= network_dict
['name']
1207 self
.logger
.info("Connecting VM to a network name {} "
1208 " network id {}".format(primary_netname
, primary_net_id
))
1209 if 'use' in primary_net
:
1210 if primary_net
['use'] == 'bridge':
1211 network_mode
= 'bridged'
1213 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1215 # use: 'data', 'bridge', 'mgmt'
1216 # create vApp. Set vcpu and ram based on flavor id.
1217 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1218 self
.get_catalogbyid(image_id
, catalogs
),
1219 network_name
=primary_netname
, # can be None if net_list None
1220 network_mode
=network_mode
,
1221 vm_name
=vmname_andid
,
1222 vm_cpus
=vm_cpus
, # can be None if flavor is None
1223 vm_memory
=vm_memory
) # can be None if flavor is None
1225 if vapptask
is None or vapptask
is False:
1226 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(vmname_andid
))
1227 if type(vapptask
) is VappTask
:
1228 vca
.block_until_completed(vapptask
)
1230 # we should have now vapp in undeployed state.
1231 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1233 raise vimconn
.vimconnUnexpectedResponse(
1234 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(vmname_andid
))
1238 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1240 for net
in net_list
:
1241 # openmano uses network id in UUID format.
1242 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1243 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1244 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1246 if 'net_id' not in net
:
1249 interface_net_id
= net
['net_id']
1250 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1251 interface_network_mode
= net
['use']
1253 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1254 - DHCP (The IP address is obtained from a DHCP service.)
1255 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1256 - NONE (No IP addressing mode specified.)"""
1258 if primary_netname
is not None:
1259 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1261 self
.logger
.info("Found requested network: {}".format(nets
[0].name
))
1262 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1263 if type(task
) is GenericTask
:
1264 vca
.block_until_completed(task
)
1265 # connect network to VM
1266 # TODO figure out mapping between openmano representation to vCloud director.
1267 # one idea use first nic as management DHCP all remaining in bridge mode
1268 self
.logger
.info("Connecting VM to a network network {}".format(nets
[0].name
))
1269 task
= vapp
.connect_vms(nets
[0].name
,
1270 connection_index
=nicIndex
,
1271 connections_primary_index
=nicIndex
,
1272 ip_allocation_mode
='DHCP')
1273 if type(task
) is GenericTask
:
1274 vca
.block_until_completed(task
)
1277 # it might be a case if specific mandatory entry in dict is empty
1278 self
.logger
.debug("Key error {}".format(KeyError.message
))
1279 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1281 # deploy and power on vm
1282 task
= vapp
.poweron()
1283 if type(task
) is TaskType
:
1284 vca
.block_until_completed(task
)
1285 deploytask
= vapp
.deploy(powerOn
='True')
1286 if type(task
) is TaskType
:
1287 vca
.block_until_completed(deploytask
)
1289 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1292 while wait_time
<= MAX_WAIT_TIME
:
1293 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1294 if vapp
and vapp
.me
.deployed
:
1295 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1298 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1299 time
.sleep(INTERVAL_TIME
)
1301 wait_time
+=INTERVAL_TIME
1303 if vapp_uuid
is not None:
1306 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1310 ## based on current discussion
1314 # created: '2016-09-08T11:51:58'
1315 # description: simple-instance.linux1.1
1316 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1317 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1318 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1323 def get_vminstance(self
, vim_vm_uuid
=None):
1324 """Returns the VM instance information from VIM"""
1326 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1327 vca
= self
.connect()
1329 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1331 vdc
= vca
.get_vdc(self
.tenant_name
)
1333 raise vimconn
.vimconnConnectionException(
1334 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1336 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1337 if not vm_info_dict
:
1338 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1339 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1341 status_key
= vm_info_dict
['status']
1344 vm_dict
= {'created': vm_info_dict
['created'],
1345 'description': vm_info_dict
['name'],
1346 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1347 'hostId': vm_info_dict
['vmuuid'],
1349 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1351 if 'interfaces' in vm_info_dict
:
1352 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1354 vm_dict
['interfaces'] = []
1356 vm_dict
= {'created': '',
1358 'status': vcdStatusCode2manoFormat
[int(-1)],
1359 'hostId': vm_info_dict
['vmuuid'],
1360 'error_msg': "Inconsistency state",
1361 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1365 def delete_vminstance(self
, vm__vim_uuid
):
1366 """Method poweroff and remove VM instance from vcloud director network.
1369 vm__vim_uuid: VM UUID
1372 Returns the instance identifier
1375 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1376 vca
= self
.connect()
1378 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1380 vdc
= vca
.get_vdc(self
.tenant_name
)
1382 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1384 raise vimconn
.vimconnException(
1385 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1388 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1389 if vapp_name
is None:
1390 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1391 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1393 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1395 # Delete vApp and wait for status change if task executed and vApp is None.
1396 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1399 if vapp
.me
.deployed
:
1400 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1404 while wait_time
<= MAX_WAIT_TIME
:
1405 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1407 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1408 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1410 power_off_task
= vapp
.poweroff()
1411 if type(power_off_task
) is GenericTask
:
1412 result
= vca
.block_until_completed(power_off_task
)
1417 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1418 time
.sleep(INTERVAL_TIME
)
1420 wait_time
+=INTERVAL_TIME
1422 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1424 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1427 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1430 while wait_time
<= MAX_WAIT_TIME
:
1431 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1433 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1434 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1435 undeploy_task
= vapp
.undeploy(action
='powerOff')
1437 if type(undeploy_task
) is GenericTask
:
1438 result
= vca
.block_until_completed(undeploy_task
)
1443 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1444 time
.sleep(INTERVAL_TIME
)
1446 wait_time
+=INTERVAL_TIME
1449 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1452 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1453 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1455 if vapp
is not None:
1459 while wait_time
<= MAX_WAIT_TIME
:
1460 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1462 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1463 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1465 delete_task
= vapp
.delete()
1467 if type(delete_task
) is GenericTask
:
1468 vca
.block_until_completed(delete_task
)
1469 result
= vca
.block_until_completed(delete_task
)
1473 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1474 time
.sleep(INTERVAL_TIME
)
1476 wait_time
+=INTERVAL_TIME
1479 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1482 self
.logger
.debug(traceback
.format_exc())
1483 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1485 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1486 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1489 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1491 def refresh_vms_status(self
, vm_list
):
1492 """Get the status of the virtual machines and their interfaces/ports
1493 Params: the list of VM identifiers
1494 Returns a dictionary with:
1495 vm_id: #VIM id of this Virtual Machine
1496 status: #Mandatory. Text with one of:
1497 # DELETED (not found at vim)
1498 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1499 # OTHER (Vim reported other status not understood)
1500 # ERROR (VIM indicates an ERROR status)
1501 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1502 # CREATING (on building process), ERROR
1503 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1505 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1506 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1508 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1509 mac_address: #Text format XX:XX:XX:XX:XX:XX
1510 vim_net_id: #network id where this interface is connected
1511 vim_interface_id: #interface/port VIM id
1512 ip_address: #null, or text with IPv4, IPv6 address
1515 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1516 vca
= self
.connect()
1518 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1520 vdc
= vca
.get_vdc(self
.tenant_name
)
1522 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1525 for vmuuid
in vm_list
:
1526 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1527 if vmname
is not None:
1529 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1530 vm_info
= the_vapp
.get_vms_details()
1531 vm_status
= vm_info
[0]['status']
1533 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1534 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1535 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1539 vm_app_networks
= the_vapp
.get_vms_network_info()
1540 for vapp_network
in vm_app_networks
:
1541 for vm_network
in vapp_network
:
1542 if vm_network
['name'] == vmname
:
1543 interface
= {"mac_address": vm_network
['mac'],
1544 "vim_net_id": self
.get_network_id_by_name(vm_network
['network_name']),
1545 "vim_interface_id": self
.get_network_id_by_name(vm_network
['network_name']),
1546 'ip_address': vm_network
['ip']}
1547 # interface['vim_info'] = yaml.safe_dump(vm_network)
1548 vm_dict
["interfaces"].append(interface
)
1549 # add a vm to vm dict
1550 vms_dict
.setdefault(vmuuid
, vm_dict
)
1552 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1553 self
.logger
.debug(traceback
.format_exc())
1557 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1558 """Send and action over a VM instance from VIM
1559 Returns the vm_id if the action was successfully sent to the VIM"""
1561 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1562 if vm__vim_uuid
is None or action_dict
is None:
1563 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1565 vca
= self
.connect()
1567 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1569 vdc
= vca
.get_vdc(self
.tenant_name
)
1571 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1573 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1574 if vapp_name
is None:
1575 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1576 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1578 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1581 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1582 # TODO fix all status
1583 if "start" in action_dict
:
1584 if action_dict
["start"] == "rebuild":
1585 the_vapp
.deploy(powerOn
=True)
1587 vm_info
= the_vapp
.get_vms_details()
1588 vm_status
= vm_info
[0]['status']
1589 if vm_status
== "Suspended":
1591 elif vm_status
.status
== "Powered off":
1593 elif "pause" in action_dict
:
1596 elif "resume" in action_dict
:
1599 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1601 elif "forceOff" in action_dict
:
1603 elif "terminate" in action_dict
:
1605 # elif "createImage" in action_dict:
1606 # server.create_image()
1612 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1614 Get a console for the virtual machine
1616 vm_id: uuid of the VM
1617 console_type, can be:
1618 "novnc" (by default), "xvpvnc" for VNC types,
1619 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1620 Returns dict with the console parameters:
1621 protocol: ssh, ftp, http, https, ...
1622 server: usually ip address
1623 port: the http, ssh, ... port
1624 suffix: extra text, e.g. the http path and query string
1626 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1628 # NOT USED METHODS in current version
1630 def host_vim2gui(self
, host
, server_dict
):
1631 """Transform host dictionary from VIM format to GUI format,
1632 and append to the server_dict
1634 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1636 def get_hosts_info(self
):
1637 """Get the information of deployed hosts
1638 Returns the hosts content"""
1639 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1641 def get_hosts(self
, vim_tenant
):
1642 """Get the hosts and deployed instances
1643 Returns the hosts content"""
1644 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1646 def get_processor_rankings(self
):
1647 """Get the processor rankings in the VIM database"""
1648 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1650 def new_host(self
, host_data
):
1651 """Adds a new host to VIM"""
1652 '''Returns status code of the VIM response'''
1653 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1655 def new_external_port(self
, port_data
):
1656 """Adds a external port to VIM"""
1657 '''Returns the port identifier'''
1658 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1660 def new_external_network(self
, net_name
, net_type
):
1661 """Adds a external network to VIM (shared)"""
1662 '''Returns the network identifier'''
1663 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1665 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1666 """Connects a external port to a network"""
1667 '''Returns status code of the VIM response'''
1668 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1670 def new_vminstancefromJSON(self
, vm_data
):
1671 """Adds a VM instance to VIM"""
1672 '''Returns the instance identifier'''
1673 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1675 def get_network_name_by_id(self
, network_uuid
=None):
1676 """Method gets vcloud director network named based on supplied uuid.
1679 network_uuid: network_id
1682 The return network name.
1685 vca
= self
.connect()
1687 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1689 if not network_uuid
:
1693 org_dict
= self
.get_org(self
.org_uuid
)
1694 if 'networks' in org_dict
:
1695 org_network_dict
= org_dict
['networks']
1696 for net_uuid
in org_network_dict
:
1697 if net_uuid
== network_uuid
:
1698 return org_network_dict
[net_uuid
]
1700 self
.logger
.debug("Exception in get_network_name_by_id")
1701 self
.logger
.debug(traceback
.format_exc())
1705 def get_network_id_by_name(self
, network_name
=None):
1706 """Method gets vcloud director network uuid based on supplied name.
1709 network_name: network_name
1711 The return network uuid.
1712 network_uuid: network_id
1715 vca
= self
.connect()
1717 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1719 if not network_name
:
1720 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
1724 org_dict
= self
.get_org(self
.org_uuid
)
1725 if org_dict
and 'networks' in org_dict
:
1726 org_network_dict
= org_dict
['networks']
1727 for net_uuid
,net_name
in org_network_dict
.iteritems():
1728 if net_name
== network_name
:
1731 except KeyError as exp
:
1732 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
1736 def list_org_action(self
):
1738 Method leverages vCloud director and query for available organization for particular user
1741 vca - is active VCA connection.
1742 vdc_name - is a vdc name that will be used to query vms action
1745 The return XML respond
1748 vca
= self
.connect()
1750 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1752 url_list
= [vca
.host
, '/api/org']
1753 vm_list_rest_call
= ''.join(url_list
)
1755 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1756 response
= Http
.get(url
=vm_list_rest_call
,
1757 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1760 if response
.status_code
== requests
.codes
.ok
:
1761 return response
.content
1765 def get_org_action(self
, org_uuid
=None):
1767 Method leverages vCloud director and retrieve available object fdr organization.
1770 vca - is active VCA connection.
1771 vdc_name - is a vdc name that will be used to query vms action
1774 The return XML respond
1777 vca
= self
.connect()
1779 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1781 if org_uuid
is None:
1784 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1785 vm_list_rest_call
= ''.join(url_list
)
1787 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1788 response
= Http
.get(url
=vm_list_rest_call
,
1789 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1792 if response
.status_code
== requests
.codes
.ok
:
1793 return response
.content
1797 def get_org(self
, org_uuid
=None):
1799 Method retrieves available organization in vCloud Director
1802 org_uuid - is a organization uuid.
1805 The return dictionary with following key
1806 "network" - for network list under the org
1807 "catalogs" - for network list under the org
1808 "vdcs" - for vdc list under org
1812 vca
= self
.connect()
1814 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1816 if org_uuid
is None:
1819 content
= self
.get_org_action(org_uuid
=org_uuid
)
1824 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1825 for child
in vm_list_xmlroot
:
1826 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1827 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1828 org_dict
['vdcs'] = vdc_list
1829 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1830 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1831 org_dict
['networks'] = network_list
1832 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1833 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1834 org_dict
['catalogs'] = catalog_list
1840 def get_org_list(self
):
1842 Method retrieves available organization in vCloud Director
1845 vca - is active VCA connection.
1848 The return dictionary and key for each entry VDC UUID
1852 vca
= self
.connect()
1854 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1856 content
= self
.list_org_action()
1858 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1859 for vm_xml
in vm_list_xmlroot
:
1860 if vm_xml
.tag
.split("}")[1] == 'Org':
1861 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1862 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1868 def vms_view_action(self
, vdc_name
=None):
1869 """ Method leverages vCloud director vms query call
1872 vca - is active VCA connection.
1873 vdc_name - is a vdc name that will be used to query vms action
1876 The return XML respond
1878 vca
= self
.connect()
1879 if vdc_name
is None:
1882 url_list
= [vca
.host
, '/api/vms/query']
1883 vm_list_rest_call
= ''.join(url_list
)
1885 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1886 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1887 vca
.vcloud_session
.organization
.Link
)
1889 response
= Http
.get(url
=vm_list_rest_call
,
1890 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1893 if response
.status_code
== requests
.codes
.ok
:
1894 return response
.content
1898 def get_vapp_list(self
, vdc_name
=None):
1900 Method retrieves vApp list deployed vCloud director and returns a dictionary
1901 contains a list of all vapp deployed for queried VDC.
1902 The key for a dictionary is vApp UUID
1906 vca - is active VCA connection.
1907 vdc_name - is a vdc name that will be used to query vms action
1910 The return dictionary and key for each entry vapp UUID
1914 if vdc_name
is None:
1917 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1919 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1920 for vm_xml
in vm_list_xmlroot
:
1921 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1922 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1923 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1924 if 'vappTemplate-' in rawuuid
[0]:
1925 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1926 # vm and use raw UUID as key
1927 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1933 def get_vm_list(self
, vdc_name
=None):
1935 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1936 contains a list of all VM's deployed for queried VDC.
1937 The key for a dictionary is VM UUID
1941 vca - is active VCA connection.
1942 vdc_name - is a vdc name that will be used to query vms action
1945 The return dictionary and key for each entry vapp UUID
1949 if vdc_name
is None:
1952 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1954 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1955 for vm_xml
in vm_list_xmlroot
:
1956 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1957 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1958 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1959 if 'vm-' in rawuuid
[0]:
1960 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1961 # vm and use raw UUID as key
1962 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1968 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1970 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1971 contains a list of all VM's deployed for queried VDC.
1972 The key for a dictionary is VM UUID
1976 vca - is active VCA connection.
1977 vdc_name - is a vdc name that will be used to query vms action
1980 The return dictionary and key for each entry vapp UUID
1983 vca
= self
.connect()
1985 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1987 if vdc_name
is None:
1990 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1992 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1993 for vm_xml
in vm_list_xmlroot
:
1994 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1995 # lookup done by UUID
1997 if vapp_name
in vm_xml
.attrib
['container']:
1998 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1999 if 'vm-' in rawuuid
[0]:
2000 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2002 # lookup done by Name
2004 if vapp_name
in vm_xml
.attrib
['name']:
2005 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2006 if 'vm-' in rawuuid
[0]:
2007 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2014 def get_network_action(self
, network_uuid
=None):
2016 Method leverages vCloud director and query network based on network uuid
2019 vca - is active VCA connection.
2020 network_uuid - is a network uuid
2023 The return XML respond
2026 vca
= self
.connect()
2028 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2030 if network_uuid
is None:
2033 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2034 vm_list_rest_call
= ''.join(url_list
)
2036 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2037 response
= Http
.get(url
=vm_list_rest_call
,
2038 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2041 if response
.status_code
== requests
.codes
.ok
:
2042 return response
.content
2046 def get_vcd_network(self
, network_uuid
=None):
2048 Method retrieves available network from vCloud Director
2051 network_uuid - is VCD network UUID
2053 Each element serialized as key : value pair
2055 Following keys available for access. network_configuration['Gateway'}
2059 <IsInherited>true</IsInherited>
2060 <Gateway>172.16.252.100</Gateway>
2061 <Netmask>255.255.255.0</Netmask>
2062 <Dns1>172.16.254.201</Dns1>
2063 <Dns2>172.16.254.202</Dns2>
2064 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2065 <IsEnabled>true</IsEnabled>
2068 <StartAddress>172.16.252.1</StartAddress>
2069 <EndAddress>172.16.252.99</EndAddress>
2074 <FenceMode>bridged</FenceMode>
2077 The return dictionary and key for each entry vapp UUID
2080 network_configuration
= {}
2081 if network_uuid
is None:
2084 content
= self
.get_network_action(network_uuid
=network_uuid
)
2086 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2088 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2089 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2090 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2092 for child
in vm_list_xmlroot
:
2093 if child
.tag
.split("}")[1] == 'IsShared':
2094 network_configuration
['isShared'] = child
.text
.strip()
2095 if child
.tag
.split("}")[1] == 'Configuration':
2096 for configuration
in child
.iter():
2097 tagKey
= configuration
.tag
.split("}")[1].strip()
2099 network_configuration
[tagKey
] = configuration
.text
.strip()
2100 return network_configuration
2104 return network_configuration
2106 def delete_network_action(self
, network_uuid
=None):
2108 Method delete given network from vCloud director
2111 network_uuid - is a network uuid that client wish to delete
2114 The return None or XML respond or false
2117 vca
= self
.connect_as_admin()
2119 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2120 if network_uuid
is None:
2123 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2124 vm_list_rest_call
= ''.join(url_list
)
2126 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2127 response
= Http
.delete(url
=vm_list_rest_call
,
2128 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2132 if response
.status_code
== 202:
2137 def create_network(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2139 Method create network in vCloud director
2142 network_name - is network name to be created.
2143 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2144 It optional attribute. by default if no parent network indicate the first available will be used.
2147 The return network uuid or return None
2150 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2151 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2152 parent_network_uuid
=parent_network_uuid
,
2155 self
.logger
.debug("Failed create network {}.".format(network_name
))
2159 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2160 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2161 if len(vcd_uuid
) == 4:
2162 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2165 self
.logger
.debug("Failed create network {}".format(network_name
))
2168 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2170 Method create network in vCloud director
2173 network_name - is network name to be created.
2174 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2175 It optional attribute. by default if no parent network indicate the first available will be used.
2178 The return network uuid or return None
2181 vca
= self
.connect_as_admin()
2183 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2184 if network_name
is None:
2187 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2188 vm_list_rest_call
= ''.join(url_list
)
2189 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2190 response
= Http
.get(url
=vm_list_rest_call
,
2191 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2195 provider_network
= None
2196 available_networks
= None
2197 add_vdc_rest_url
= None
2199 if response
.status_code
!= requests
.codes
.ok
:
2200 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2201 response
.status_code
))
2205 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2206 for child
in vm_list_xmlroot
:
2207 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2208 provider_network
= child
.attrib
.get('href')
2209 # application/vnd.vmware.admin.providervdc+xml
2210 if child
.tag
.split("}")[1] == 'Link':
2211 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2212 and child
.attrib
.get('rel') == 'add':
2213 add_vdc_rest_url
= child
.attrib
.get('href')
2215 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2216 self
.logger
.debug("Respond body {}".format(response
.content
))
2219 # find pvdc provided available network
2220 response
= Http
.get(url
=provider_network
,
2221 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2224 if response
.status_code
!= requests
.codes
.ok
:
2225 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2226 response
.status_code
))
2229 # available_networks.split("/")[-1]
2231 if parent_network_uuid
is None:
2233 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2234 for child
in vm_list_xmlroot
.iter():
2235 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2236 for networks
in child
.iter():
2237 # application/vnd.vmware.admin.network+xml
2238 if networks
.attrib
.get('href') is not None:
2239 available_networks
= networks
.attrib
.get('href')
2244 # either use client provided UUID or search for a first available
2245 # if both are not defined we return none
2246 if parent_network_uuid
is not None:
2247 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2248 add_vdc_rest_url
= ''.join(url_list
)
2250 # return response.content
2251 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2252 <Description>Openmano created</Description>
2254 <ParentNetwork href="{1:s}"/>
2255 <FenceMode>{2:s}</FenceMode>
2257 <IsShared>{3:s}</IsShared>
2258 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2260 headers
= vca
.vcloud_session
.get_vcloud_headers()
2261 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2262 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2264 # if we all ok we respond with content otherwise by default None
2265 if response
.status_code
== 201:
2266 return response
.content
2269 def get_provider_rest(self
, vca
=None):
2271 Method gets provider vdc view from vcloud director
2274 network_name - is network name to be created.
2275 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2276 It optional attribute. by default if no parent network indicate the first available will be used.
2279 The return xml content of respond or None
2282 url_list
= [vca
.host
, '/api/admin']
2283 response
= Http
.get(url
=''.join(url_list
),
2284 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2288 if response
.status_code
== requests
.codes
.ok
:
2289 return response
.content
2292 def create_vdc(self
, vdc_name
=None):
2296 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2297 if xml_content
is not None:
2299 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2300 for child
in task_resp_xmlroot
:
2301 if child
.tag
.split("}")[1] == 'Owner':
2302 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2303 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2306 self
.logger
.debug("Respond body {}".format(xml_content
))
2310 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2312 Method create vdc in vCloud director based on VDC template.
2313 it uses pre-defined template that must be named openmano
2316 vdc_name - name of a new vdc.
2319 The return xml content of respond or None
2322 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2323 vca
= self
.connect()
2325 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2326 if vdc_name
is None:
2329 url_list
= [vca
.host
, '/api/vdcTemplates']
2330 vm_list_rest_call
= ''.join(url_list
)
2331 response
= Http
.get(url
=vm_list_rest_call
,
2332 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2336 # container url to a template
2337 vdc_template_ref
= None
2339 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2340 for child
in vm_list_xmlroot
:
2341 # application/vnd.vmware.admin.providervdc+xml
2342 # we need find a template from witch we instantiate VDC
2343 if child
.tag
.split("}")[1] == 'VdcTemplate':
2344 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2345 'name') == 'openmano':
2346 vdc_template_ref
= child
.attrib
.get('href')
2348 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2349 self
.logger
.debug("Respond body {}".format(response
.content
))
2352 # if we didn't found required pre defined template we return None
2353 if vdc_template_ref
is None:
2358 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2359 vm_list_rest_call
= ''.join(url_list
)
2360 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2361 <Source href="{1:s}"></Source>
2362 <Description>opnemano</Description>
2363 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2364 headers
= vca
.vcloud_session
.get_vcloud_headers()
2365 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2366 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2368 # if we all ok we respond with content otherwise by default None
2369 if response
.status_code
>= 200 and response
.status_code
< 300:
2370 return response
.content
2373 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2374 self
.logger
.debug("Respond body {}".format(response
.content
))
2378 def create_vdc_rest(self
, vdc_name
=None):
2380 Method create network in vCloud director
2383 network_name - is network name to be created.
2384 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2385 It optional attribute. by default if no parent network indicate the first available will be used.
2388 The return network uuid or return None
2391 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2393 vca
= self
.connect_as_admin()
2395 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2396 if vdc_name
is None:
2399 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2400 vm_list_rest_call
= ''.join(url_list
)
2401 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2402 response
= Http
.get(url
=vm_list_rest_call
,
2403 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2407 provider_vdc_ref
= None
2408 add_vdc_rest_url
= None
2409 available_networks
= None
2411 if response
.status_code
!= requests
.codes
.ok
:
2412 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2413 response
.status_code
))
2417 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2418 for child
in vm_list_xmlroot
:
2419 # application/vnd.vmware.admin.providervdc+xml
2420 if child
.tag
.split("}")[1] == 'Link':
2421 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2422 and child
.attrib
.get('rel') == 'add':
2423 add_vdc_rest_url
= child
.attrib
.get('href')
2425 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2426 self
.logger
.debug("Respond body {}".format(response
.content
))
2429 response
= self
.get_provider_rest(vca
=vca
)
2431 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2432 for child
in vm_list_xmlroot
:
2433 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2434 for sub_child
in child
:
2435 provider_vdc_ref
= sub_child
.attrib
.get('href')
2437 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2438 self
.logger
.debug("Respond body {}".format(response
))
2441 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2442 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2443 <AllocationModel>ReservationPool</AllocationModel>
2444 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2445 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2446 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2447 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2448 <ProviderVdcReference
2449 name="Main Provider"
2451 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2455 headers
= vca
.vcloud_session
.get_vcloud_headers()
2456 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2457 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2460 # if we all ok we respond with content otherwise by default None
2461 if response
.status_code
== 201:
2462 return response
.content
2465 def get_vapp_details_rest(self
, vapp_uuid
=None):
2467 Method retrieve vapp detail from vCloud director
2470 vapp_uuid - is vapp identifier.
2473 The return network uuid or return None
2478 vca
= self
.connect()
2480 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2481 if vapp_uuid
is None:
2484 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2485 get_vapp_restcall
= ''.join(url_list
)
2486 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2487 response
= Http
.get(url
=get_vapp_restcall
,
2488 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2492 if response
.status_code
!= requests
.codes
.ok
:
2493 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2494 response
.status_code
))
2495 return parsed_respond
2498 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2499 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2501 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2502 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2503 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2505 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2506 if created_section
is not None:
2507 parsed_respond
['created'] = created_section
.text
2509 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2510 if network_section
is not None and 'networkName' in network_section
.attrib
:
2511 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2513 ipscopes_section
= \
2514 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2516 if ipscopes_section
is not None:
2517 for ipscope
in ipscopes_section
:
2518 for scope
in ipscope
:
2519 tag_key
= scope
.tag
.split("}")[1]
2520 if tag_key
== 'IpRanges':
2521 ip_ranges
= scope
.getchildren()
2522 for ipblock
in ip_ranges
:
2523 for block
in ipblock
:
2524 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2526 parsed_respond
[tag_key
] = scope
.text
2528 # parse children section for other attrib
2529 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2530 if children_section
is not None:
2531 parsed_respond
['name'] = children_section
.attrib
['name']
2532 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2533 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2534 parsed_respond
['status'] = children_section
.attrib
['status']
2535 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2536 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2538 for adapters
in network_adapter
:
2539 adapter_key
= adapters
.tag
.split("}")[1]
2540 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2541 parsed_respond
['primarynetwork'] = adapters
.text
2542 if adapter_key
== 'NetworkConnection':
2544 if 'network' in adapters
.attrib
:
2545 vnic
['network'] = adapters
.attrib
['network']
2546 for adapter
in adapters
:
2547 setting_key
= adapter
.tag
.split("}")[1]
2548 vnic
[setting_key
] = adapter
.text
2549 nic_list
.append(vnic
)
2551 for link
in children_section
:
2552 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2553 if link
.attrib
['rel'] == 'screen:acquireTicket':
2554 parsed_respond
['acquireTicket'] = link
.attrib
2555 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2556 parsed_respond
['acquireMksTicket'] = link
.attrib
2558 parsed_respond
['interfaces'] = nic_list
2562 return parsed_respond
2564 def acuire_console(self
, vm_uuid
=None):
2566 vca
= self
.connect()
2568 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2572 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2573 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2574 console_dict
= vm_dict
['acquireTicket']
2575 console_rest_call
= console_dict
['href']
2577 response
= Http
.post(url
=console_rest_call
,
2578 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2582 if response
.status_code
== requests
.codes
.ok
:
2583 return response
.content