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.
35 from xml
.etree
import ElementTree
as XmlElementTree
38 from pyvcloud
import Http
39 from pyvcloud
.vcloudair
import VCA
40 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
41 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
42 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
43 from xml
.sax
.saxutils
import escape
45 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
46 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
48 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
57 # global variable for vcd connector type
58 STANDALONE
= 'standalone'
60 # global variable for number of retry
61 DELETE_INSTANCE_RETRY
= 3
65 __author__
= "Mustafa Bayramov"
66 __date__
= "$26-Aug-2016 11:09:29$"
68 # -1: "Could not be created",
74 # 5: "Waiting for user input",
76 # 7: "Unrecognized state",
78 # 9: "Inconsistent state",
79 # 10: "Children do not all have the same status",
80 # 11: "Upload initiated, OVF descriptor pending",
81 # 12: "Upload initiated, copying contents",
82 # 13: "Upload initiated , disk contents pending",
83 # 14: "Upload has been quarantined",
84 # 15: "Upload quarantine period has expired"
86 # mapping vCD status to MANO
87 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
96 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
97 'ERROR': 'ERROR', 'DELETED': 'DELETED'
100 # dict used to store flavor in memory
104 class vimconnector(vimconn
.vimconnector
):
105 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
106 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
="ERROR", config
={}):
108 Constructor create vmware connector to vCloud director.
110 By default construct doesn't validate connection state. So client can create object with None arguments.
111 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
113 a) It initialize organization UUID
114 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
117 uuid - is organization uuid.
118 name - is organization name that must be presented in vCloud director.
119 tenant_id - is VDC uuid it must be presented in vCloud director
120 tenant_name - is VDC name.
121 url - is hostname or ip address of vCloud director
122 url_admin - same as above.
123 user - is user that administrator for organization. Caller must make sure that
124 username has right privileges.
126 password - is password for a user.
128 VMware connector also requires PVDC administrative privileges and separate account.
129 This variables must be passed via config argument dict contains keys
131 dict['admin_username']
132 dict['admin_password']
138 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
139 url_admin
, user
, passwd
, log_level
, config
)
144 self
.url_admin
= url_admin
145 self
.tenant_id
= tenant_id
146 self
.tenant_name
= tenant_name
150 self
.admin_password
= None
151 self
.admin_user
= None
153 self
.logger
= logging
.getLogger('openmano.vim.vmware')
154 self
.logger
.setLevel(10)
157 self
.admin_user
= config
['admin_username']
158 self
.admin_password
= config
['admin_password']
160 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
162 self
.logger
= logging
.getLogger('mano.vim.vmware')
167 raise TypeError, 'url param can not be NoneType'
169 if not self
.url_admin
: # try to use normal url
170 self
.url_admin
= self
.url
172 logging
.debug("Calling constructor with following paramters")
173 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.name
,
174 self
.tenant_id
, self
.tenant_name
))
175 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
176 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
178 # initialize organization
179 if self
.user
is not None and self
.passwd
is not None and self
.url
:
180 self
.init_organization()
182 def __getitem__(self
, index
):
183 if index
== 'tenant_id':
184 return self
.tenant_id
185 if index
== 'tenant_name':
186 return self
.tenant_name
189 elif index
== 'name':
191 elif index
== 'org_name':
193 elif index
== 'org_uuid':
195 elif index
== 'user':
197 elif index
== 'passwd':
201 elif index
== 'url_admin':
202 return self
.url_admin
203 elif index
== "config":
206 raise KeyError("Invalid key '%s'" % str(index
))
208 def __setitem__(self
, index
, value
):
209 if index
== 'tenant_id':
210 self
.tenant_id
= value
211 if index
== 'tenant_name':
212 self
.tenant_name
= value
215 # we use name = org #TODO later refactor
216 elif index
== 'name':
219 elif index
== 'org_name':
220 self
.org_name
= value
222 elif index
== 'org_uuid':
223 self
.org_name
= value
224 elif index
== 'user':
226 elif index
== 'passwd':
230 elif index
== 'url_admin':
231 self
.url_admin
= value
233 raise KeyError("Invalid key '%s'" % str(index
))
235 def connect_as_admin(self
):
236 """ Method connect as pvdc admin user to vCloud director.
237 There are certain action that can be done only by provider vdc admin user.
238 Organization creation / provider network creation etc.
241 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
244 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.name
))
246 vca_admin
= VCA(host
=self
.url
,
247 username
=self
.admin_user
,
248 service_type
=STANDALONE
,
252 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
254 raise vimconn
.vimconnConnectionException(
255 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
256 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
259 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
264 """ Method connect as normal user to vCloud director.
267 The return vca object that letter can be used to connect to vCloud director as admin for VDC
271 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.name
, self
.user
, self
.name
))
272 vca
= VCA(host
=self
.url
,
274 service_type
=STANDALONE
,
279 result
= vca
.login(password
=self
.passwd
, org
=self
.name
)
281 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
282 result
= vca
.login(token
=vca
.token
, org
=self
.name
, org_url
=vca
.vcloud_session
.org_url
)
284 self
.logger
.info("Successfully logged to a vcloud direct org: {} as user: {}".format(self
.name
, self
.user
))
287 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
291 def init_organization(self
):
292 """ Method initialize organization UUID and VDC parameters.
294 At bare minimum client must provide organization name that present in vCloud director and VDC.
296 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
297 The Org - UUID will be initialized at the run time if data center present in vCloud director.
300 The return vca object that letter can be used to connect to vcloud direct as admin
303 if self
.org_uuid
is None:
304 org_dict
= self
.get_org_list()
306 # we set org UUID at the init phase but we can do it only when we have valid credential.
307 if org_dict
[org
] == self
.org_name
:
309 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
313 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
315 # if well good we require for org details
316 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
318 # we have two case if we want to initialize VDC ID or VDC name at run time
319 # tenant_name provided but no tenant id
320 if self
.tenant_id
is None and self
.tenant_name
is not None and org_details_dict
.has_key('vdcs'):
321 vdcs_dict
= org_details_dict
['vdcs']
322 for vdc
in vdcs_dict
:
323 if vdcs_dict
[vdc
] == self
.tenant_name
:
325 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
329 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
330 # case two we have tenant_id but we don't have tenant name so we find and set it.
331 if self
.tenant_id
is not None and self
.tenant_name
is None and org_details_dict
.has_key('vdcs'):
332 vdcs_dict
= org_details_dict
['vdcs']
333 for vdc
in vdcs_dict
:
334 if vdc
== self
.tenant_id
:
335 self
.tenant_name
= vdcs_dict
[vdc
]
336 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
340 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
341 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
343 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
344 self
.logger
.debug(traceback
.format_exc())
347 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
348 """ Method adds a new tenant to VIM with this name.
349 This action requires access to create VDC action in vCloud director.
352 tenant_name is tenant_name to be created.
353 tenant_description not used for this call
356 returns the tenant identifier in UUID format.
357 If action is failed method will throw vimconn.vimconnException method
359 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
360 if vdc_task
is not None:
361 vdc_uuid
, value
= vdc_task
.popitem()
362 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
365 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
367 def delete_tenant(self
, tenant_id
, ):
368 """Delete a tenant from VIM"""
369 'Returns the tenant identifier'
370 raise vimconn
.vimconnNotImplemented("Should have implemented this")
372 def get_tenant_list(self
, filter_dict
={}):
373 """Obtain tenants of VIM
374 filter_dict can contain the following keys:
375 name: filter by tenant name
376 id: filter by tenant uuid/id
378 Returns the tenant list of dictionaries:
379 [{'name':'<name>, 'id':'<id>, ...}, ...]
382 org_dict
= self
.get_org(self
.org_uuid
)
383 vdcs_dict
= org_dict
['vdcs']
388 entry
= {'name': vdcs_dict
[k
], 'id': k
}
389 # if caller didn't specify dictionary we return all tenants.
390 if filter_dict
is not None and filter_dict
:
391 filtered_entry
= entry
.copy()
392 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
393 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
394 if filter_dict
== entry
:
395 vdclist
.append(filtered_entry
)
397 vdclist
.append(entry
)
399 self
.logger
.debug("Error in get_tenant_list()")
400 self
.logger
.debug(traceback
.format_exc())
401 raise vimconn
.vimconnException("Incorrect state. {}")
405 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
406 """Adds a tenant network to VIM
408 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
409 ip_profile is a dict containing the IP parameters of the network
411 Returns the network identifier"""
414 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name
, net_type
, ip_profile
, shared
))
420 network_uuid
= self
.create_network(network_name
=net_name
, isshared
=isshared
)
421 if network_uuid
is not None:
424 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
426 def get_vcd_network_list(self
):
427 """ Method available organization for a logged in tenant
430 The return vca object that letter can be used to connect to vcloud direct as admin
433 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd")
436 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
438 vdc
= vca
.get_vdc(self
.tenant_name
)
439 vdc_uuid
= vdc
.get_id().split(":")[3]
440 networks
= vca
.get_networks(vdc
.get_name())
443 for network
in networks
:
445 netid
= network
.get_id().split(":")
449 filter_dict
["name"] = network
.get_name()
450 filter_dict
["id"] = netid
[3]
451 filter_dict
["shared"] = network
.get_IsShared()
452 filter_dict
["tenant_id"] = vdc_uuid
453 if network
.get_status() == 1:
454 filter_dict
["admin_state_up"] = True
456 filter_dict
["admin_state_up"] = False
457 filter_dict
["status"] = "ACTIVE"
458 filter_dict
["type"] = "bridge"
459 network_list
.append(filter_dict
)
460 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
462 self
.logger
.debug("Error in get_vcd_network_list")
463 self
.logger
.debug(traceback
.format_exc())
466 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
469 def get_network_list(self
, filter_dict
={}):
470 """Obtain tenant networks of VIM
472 name: network name OR/AND
473 id: network uuid OR/AND
474 shared: boolean OR/AND
475 tenant_id: tenant OR/AND
476 admin_state_up: boolean
479 [{key : value , key : value}]
481 Returns the network list of dictionaries:
482 [{<the fields at Filter_dict plus some VIM specific>}, ...]
488 raise vimconn
.vimconnConnectionException("self.connect() is failed")
490 vdc
= vca
.get_vdc(self
.tenant_name
)
491 vdcid
= vdc
.get_id().split(":")[3]
493 networks
= vca
.get_networks(vdc
.get_name())
497 for network
in networks
:
499 net_uuid
= network
.get_id().split(":")
500 if len(net_uuid
) != 4:
503 net_uuid
= net_uuid
[3]
505 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
508 filter_entry
["name"] = network
.get_name()
509 filter_entry
["id"] = net_uuid
510 filter_entry
["shared"] = network
.get_IsShared()
511 filter_entry
["tenant_id"] = vdcid
512 if network
.get_status() == 1:
513 filter_entry
["admin_state_up"] = True
515 filter_entry
["admin_state_up"] = False
516 filter_entry
["status"] = "ACTIVE"
517 filter_entry
["type"] = "bridge"
518 filtered_entry
= filter_entry
.copy()
520 if filter_dict
is not None and filter_dict
:
521 # we remove all the key : value we don't care and match only
523 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
524 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
525 if filter_dict
== filter_entry
:
526 network_list
.append(filtered_entry
)
528 network_list
.append(filtered_entry
)
530 self
.logger
.debug("Error in get_vcd_network_list")
531 self
.logger
.debug(traceback
.format_exc())
533 self
.logger
.debug("Returning {}".format(network_list
))
536 def get_network(self
, net_id
):
537 """Method bbtain network details of net_id VIM network
538 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
542 raise vimconn
.vimconnConnectionException("self.connect() is failed")
544 vdc
= vca
.get_vdc(self
.tenant_name
)
545 vdc_id
= vdc
.get_id().split(":")[3]
547 networks
= vca
.get_networks(vdc
.get_name())
551 for network
in networks
:
552 vdc_network_id
= network
.get_id().split(":")
553 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
554 filter_dict
["name"] = network
.get_name()
555 filter_dict
["id"] = vdc_network_id
[3]
556 filter_dict
["shared"] = network
.get_IsShared()
557 filter_dict
["tenant_id"] = vdc_id
558 if network
.get_status() == 1:
559 filter_dict
["admin_state_up"] = True
561 filter_dict
["admin_state_up"] = False
562 filter_dict
["status"] = "ACTIVE"
563 filter_dict
["type"] = "bridge"
564 self
.logger
.debug("Returning {}".format(filter_dict
))
567 self
.logger
.debug("Error in get_network")
568 self
.logger
.debug(traceback
.format_exc())
572 def delete_network(self
, net_id
):
574 Method Deletes a tenant network from VIM, provide the network id.
576 Returns the network identifier or raise an exception
581 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed".format(self
.tenant_name
))
583 if self
.delete_network_action(net_id
):
586 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
588 def refresh_nets_status(self
, net_list
):
589 """Get the status of the networks
590 Params: the list of network identifiers
591 Returns a dictionary with:
592 net_id: #VIM id of this network
593 status: #Mandatory. Text with one of:
594 # DELETED (not found at vim)
595 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
596 # OTHER (Vim reported other status not understood)
597 # ERROR (VIM indicates an ERROR status)
598 # ACTIVE, INACTIVE, DOWN (admin down),
599 # BUILD (on building process)
601 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
602 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
606 # for net in net_list:
611 # vim vimcon failed == ERROR
614 raise vimconn
.vimconnConnectionException("self.connect() is failed")
621 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
622 if vcd_network
is not None:
623 if vcd_network
['status'] == 1:
629 errormsg
= 'network not found'
630 dict_entry
['net'] = {'status': status
, 'error_msg': errormsg
,
631 'vm_info': yaml
.safe_dump(vcd_network
)}
633 self
.logger
.debug("Error in refresh_nets_status")
634 self
.logger
.debug(traceback
.format_exc())
638 def get_flavor(self
, flavor_id
):
639 """Obtain flavor details from the VIM
640 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
642 return flavorlist
[flavor_id
]
644 def new_flavor(self
, flavor_data
):
645 """Adds a tenant flavor to VIM
646 flavor_data contains a dictionary with information, keys:
648 ram: memory (cloud type) in MBytes
649 vpcus: cpus (cloud type)
650 extended: EPA parameters
651 - numas: #items requested in same NUMA
652 memory: number of 1G huge pages memory
653 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
654 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
655 - name: interface name
656 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
657 bandwidth: X Gbps; requested guarantee bandwidth
658 vpci: requested virtual PCI address
665 Returns the flavor identifier"""
667 # generate a new uuid put to internal dict and return it.
668 flavor_id
= uuid
.uuid4()
669 flavorlist
[str(flavor_id
)] = flavor_data
671 return str(flavor_id
)
673 def delete_flavor(self
, flavor_id
):
674 """Deletes a tenant flavor from VIM identify by its id
676 Returns the used id or raise an exception
679 # if key not present it will raise KeyError
680 # TODO check do I need raise any specific exception
681 flavorlist
.pop(flavor_id
, None)
684 def new_image(self
, image_dict
):
686 Adds a tenant image to VIM
688 200, image-id if the image is created
689 <0, message if there is an error
692 return self
.get_image_id_from_path(image_dict
['location'])
694 def delete_image(self
, image_id
):
695 '''Deletes a tenant image from VIM'''
696 '''Returns the HTTP response code and a message indicating details of the success or fail'''
697 raise vimconn
.vimconnNotImplemented("Should have implemented this")
699 def catalog_exists(self
, catalog_name
, catalogs
):
700 for catalog
in catalogs
:
701 if catalog
.name
== catalog_name
:
705 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
706 """ Create new catalog entry in vcloud director.
710 vca: vCloud director.
711 catalog_name catalog that client wish to create. Note no validation done for a name.
712 Client must make sure that provide valid string representation.
714 Return (bool) True if catalog created.
718 task
= vca
.create_catalog(catalog_name
, catalog_name
)
719 result
= vca
.block_until_completed(task
)
722 catalogs
= vca
.get_catalogs()
725 return self
.catalog_exists(catalog_name
, catalogs
)
727 def upload_ovf(self
, vca
, catalog_name
, item_name
, media_file_name
, description
='', display_progress
=False,
728 chunk_bytes
=128 * 1024):
730 Uploads a OVF file to a vCloud catalog
732 :param catalog_name: (str): The name of the catalog to upload the media.
733 :param item_name: (str): The name of the media file in the catalog.
734 :param media_file_name: (str): The name of the local media file to upload.
735 :return: (bool) True if the media file was successfully uploaded, false otherwise.
737 os
.path
.isfile(media_file_name
)
738 statinfo
= os
.stat(media_file_name
)
741 # find a catalog entry where we upload OVF.
742 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
744 # if VCD can parse OVF we upload VMDK file
745 for catalog
in vca
.get_catalogs():
746 if catalog_name
!= catalog
.name
:
748 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
749 link
.get_rel() == 'add', catalog
.get_Link())
750 assert len(link
) == 1
752 <UploadVAppTemplateParams name="%s Template" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
753 """ % (escape(item_name
), escape(description
))
754 headers
= vca
.vcloud_session
.get_vcloud_headers()
755 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
756 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
757 if response
.status_code
== requests
.codes
.created
:
758 catalogItem
= XmlElementTree
.fromstring(response
.content
)
759 entity
= [child
for child
in catalogItem
if
760 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
761 href
= entity
.get('href')
763 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
764 verify
=vca
.verify
, logger
=self
.logger
)
766 if response
.status_code
== requests
.codes
.ok
:
767 media
= mediaType
.parseString(response
.content
, True)
769 filter(lambda link
: link
.get_rel() == 'upload:default',
770 media
.get_Files().get_File()[0].get_Link())[
772 headers
= vca
.vcloud_session
.get_vcloud_headers()
773 headers
['Content-Type'] = 'Content-Type text/xml'
774 response
= Http
.put(link
.get_href(), data
=open(media_file_name
, 'rb'), headers
=headers
,
775 verify
=vca
.verify
, logger
=self
.logger
)
776 if response
.status_code
!= requests
.codes
.ok
:
778 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
782 # TODO fix this with aync block
785 self
.logger
.debug("Failed create vApp template for catalog name {} and image {}".
786 format(catalog_name
, media_file_name
))
788 # uploading VMDK file
789 # check status of OVF upload
790 response
= Http
.get(template
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
792 if response
.status_code
== requests
.codes
.ok
:
793 media
= mediaType
.parseString(response
.content
, True)
795 filter(lambda link
: link
.get_rel() == 'upload:default',
796 media
.get_Files().get_File()[0].get_Link())[
799 # The OVF file and VMDK must be in a same directory
800 head
, tail
= os
.path
.split(media_file_name
)
801 filevmdk
= head
+ '/' + os
.path
.basename(link
.get_href())
803 os
.path
.isfile(filevmdk
)
804 statinfo
= os
.stat(filevmdk
)
806 # TODO debug output remove it
807 # print media.get_Files().get_File()[0].get_Link()[0].get_href()
808 # print media.get_Files().get_File()[1].get_Link()[0].get_href()
809 # print link.get_href()
811 # in case first element is pointer to OVF.
812 hrefvmdk
= link
.get_href().replace("descriptor.ovf", "Cirros-disk1.vmdk")
814 f
= open(filevmdk
, 'rb')
815 bytes_transferred
= 0
816 while bytes_transferred
< statinfo
.st_size
:
817 my_bytes
= f
.read(chunk_bytes
)
818 if len(my_bytes
) <= chunk_bytes
:
819 headers
= vca
.vcloud_session
.get_vcloud_headers()
820 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
821 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
822 headers
['Content-Length'] = str(len(my_bytes
))
823 response
= Http
.put(hrefvmdk
,
828 if response
.status_code
== requests
.codes
.ok
:
829 bytes_transferred
+= len(my_bytes
)
830 self
.logger
.debug('transferred %s of %s bytes' % (str(bytes_transferred
),
831 str(statinfo
.st_size
)))
833 self
.logger
.debug('file upload failed with error: [%s] %s' % (response
.status_code
,
839 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
840 format(catalog_name
, media_file_name
))
843 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
846 def upload_vimimage(self
, vca
, catalog_name
, media_name
, medial_file_name
):
847 """Upload media file"""
848 # TODO add named parameters for readbility
849 return self
.upload_ovf(vca
, catalog_name
, media_name
.split(".")[0], medial_file_name
, medial_file_name
, True)
851 def validate_uuid4(self
, uuid_string
=None):
852 """ Method validate correct format of UUID.
854 Return: true if string represent valid uuid
857 val
= uuid
.UUID(uuid_string
, version
=4)
862 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
863 """ Method check catalog and return catalog ID in UUID format.
866 catalog_name: catalog name as string
867 catalogs: list of catalogs.
869 Return: catalogs uuid
872 for catalog
in catalogs
:
873 if catalog
.name
== catalog_name
:
874 catalog_id
= catalog
.get_id().split(":")
878 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
879 """ Method check catalog and return catalog name lookup done by catalog UUID.
882 catalog_name: catalog name as string
883 catalogs: list of catalogs.
885 Return: catalogs name or None
888 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
891 for catalog
in catalogs
:
892 catalog_id
= catalog
.get_id().split(":")[3]
893 if catalog_id
== catalog_uuid
:
897 def get_image_id_from_path(self
, path
=None):
898 """ Method upload OVF image to vCloud director.
900 Each OVF image represented as single catalog entry in vcloud director.
901 The method check for existing catalog entry. The check done by file name without file extension.
903 if given catalog name already present method will respond with existing catalog uuid otherwise
904 it will create new catalog entry and upload OVF file to newly created catalog.
906 If method can't create catalog entry or upload a file it will throw exception.
909 path: valid path to OVF file.
911 Return: if image uploaded correct method will provide image catalog UUID.
915 raise vimconn
.vimconnConnectionException("self.connect() is failed")
917 self
.logger
.debug("get_image_id_from_path path {}".format(path
))
919 dirpath
, filename
= os
.path
.split(path
)
920 flname
, file_extension
= os
.path
.splitext(path
)
921 if file_extension
!= '.ovf':
922 self
.logger
.debug("Wrong file extension {}".format(file_extension
))
923 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
924 catalog_name
= os
.path
.splitext(filename
)[0]
925 self
.logger
.debug("File name {} Catalog Name {} file path {}".format(filename
, catalog_name
, path
))
926 self
.logger
.debug("Catalog name {}".format(catalog_name
))
928 catalogs
= vca
.get_catalogs()
929 if len(catalogs
) == 0:
930 self
.logger
.info("Creating new catalog entry {} in vcloud director".format(catalog_name
))
931 result
= self
.create_vimcatalog(vca
, catalog_name
)
933 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
934 result
= self
.upload_vimimage(vca
, catalog_name
, filename
, path
)
936 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
937 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
939 for catalog
in catalogs
:
940 # search for existing catalog if we find same name we return ID
942 if catalog
.name
== catalog_name
:
943 self
.logger
.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name
,
947 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
949 # if we didn't find existing catalog we create a new one.
950 self
.logger
.debug("Creating new catalog entry".format(catalog_name
))
951 result
= self
.create_vimcatalog(vca
, catalog_name
)
953 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
954 result
= self
.upload_vimimage(vca
, catalog_name
, filename
, path
)
956 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
958 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
960 def get_vappid(self
, vdc
=None, vapp_name
=None):
961 """ Method takes vdc object and vApp name and returns vapp uuid or None
964 vca: Connector to VCA
966 vapp_name: is application vappp name identifier
969 The return vApp name otherwise None
971 if vdc
is None or vapp_name
is None:
973 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
975 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
976 vdc
.ResourceEntities
.ResourceEntity
)
978 return refs
[0].href
.split("vapp")[1][1:]
979 except Exception as e
:
980 self
.logger
.exception(e
)
984 def check_vapp(self
, vdc
, vapp_id
):
985 """ Take VDC object and vApp ID and return True if given ID in vCloud director
986 otherwise return False
989 """ Method Method returns vApp name from vCD and lookup done by vapp_id.
992 vca: Connector to VCA
994 vappid: vappid is application identifier
997 The return vApp name otherwise None
1000 refs
= filter(lambda ref
:
1001 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1002 vdc
.ResourceEntities
.ResourceEntity
)
1004 vappid
= ref
.href
.split("vapp")[1][1:]
1005 # find vapp with respected vapp uuid
1006 if vappid
== vapp_id
:
1008 except Exception as e
:
1009 self
.logger
.exception(e
)
1013 def get_namebyvappid(self
, vca
, vdc
, vapp_id
):
1014 """Method returns vApp name from vCD and lookup done by vapp_id.
1017 vca: Connector to VCA
1018 vdc: The VDC object.
1019 vapp_id: vappid is application identifier
1022 The return vApp name otherwise None
1026 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1027 vdc
.ResourceEntities
.ResourceEntity
)
1029 # we care only about UUID the rest doesn't matter
1030 vappid
= ref
.href
.split("vapp")[1][1:]
1031 if vappid
== vapp_id
:
1032 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1034 tree
= XmlElementTree
.fromstring(response
.content
)
1035 return tree
.attrib
['name']
1036 except Exception as e
:
1037 self
.logger
.exception(e
)
1041 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={}, cloud_config
=None):
1042 """Adds a VM instance to VIM
1044 start: indicates if VM must start or boot in pause mode. Ignored
1045 image_id,flavor_id: image and flavor uuid
1046 net_list: list of interfaces, each one is a dictionary with:
1048 net_id: network uuid to connect
1049 vpci: virtual vcpi to assign
1050 model: interface model, virtio, e2000, ...
1052 use: 'data', 'bridge', 'mgmt'
1053 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1054 vim_id: filled/added by this function
1055 cloud_config: can be a text script to be passed directly to cloud-init,
1056 or an object to inject users and ssh keys with format:
1057 key-pairs: [] list of keys to install to the default user
1058 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1059 #TODO ip, security groups
1060 Returns >=0, the instance identifier
1064 self
.logger
.info("Creating new instance for entry".format(name
))
1065 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1066 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1067 vca
= self
.connect()
1069 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1071 # if vm already deployed we return existing uuid
1072 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), name
)
1073 if vapp_uuid
is not None:
1076 # we check for presence of VDC, Catalog entry and Flavor.
1077 vdc
= vca
.get_vdc(self
.tenant_name
)
1079 raise vimconn
.vimconnUnexpectedResponse(
1080 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1081 catalogs
= vca
.get_catalogs()
1082 if catalogs
is None:
1083 raise vimconn
.vimconnUnexpectedResponse(
1084 "new_vminstance(): Failed create vApp {}: Failed create vApp {}: "
1085 "(Failed retrieve catalog information)".format(name
))
1089 if flavor_id
is not None:
1090 flavor
= flavorlist
[flavor_id
]
1092 raise vimconn
.vimconnUnexpectedResponse(
1093 "new_vminstance(): Failed create vApp {}: (Failed retrieve flavor information)".format(name
))
1096 vm_cpus
= flavor
['vcpus']
1097 vm_memory
= flavor
['ram']
1099 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1101 # image upload creates template name as catalog name space Template.
1105 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
) + ' Template'
1110 # client must provide at least one entry in net_list if not we report error
1112 primary_net_name
= None
1113 if net_list
is not None and len(net_list
) > 0:
1114 primary_net
= net_list
[0]
1115 if primary_net
is None:
1116 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1120 primary_net_id
= primary_net
['net_id']
1121 primary_net_name
= self
.get_network_name_by_id(primary_net_id
)
1122 network_mode
= primary_net
['use']
1124 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1126 # use: 'data', 'bridge', 'mgmt'
1127 # create vApp. Set vcpu and ram based on flavor id.
1128 vapptask
= vca
.create_vapp(self
.tenant_name
, name
, templateName
,
1129 self
.get_catalogbyid(image_id
, catalogs
),
1130 network_name
=primary_net_name
, # can be None if net_list None
1131 network_mode
='bridged',
1133 vm_cpus
=vm_cpus
, # can be None if flavor is None
1134 vm_memory
=vm_memory
) # can be None if flavor is None
1136 if vapptask
is None or vapptask
is False:
1137 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(name
))
1138 if type(vapptask
) is VappTask
:
1139 vca
.block_until_completed(vapptask
)
1141 # we should have now vapp in undeployed state.
1142 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), name
)
1144 raise vimconn
.vimconnUnexpectedResponse(
1145 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(name
))
1150 for net
in net_list
:
1151 # openmano uses network id in UUID format.
1152 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1153 interface_net_id
= net
['net_id']
1154 interface_net_name
= self
.get_network_name_by_id(interface_net_id
)
1155 interface_network_mode
= net
['use']
1157 if primary_net_name
is not None:
1158 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1160 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1161 if type(task
) is GenericTask
:
1162 vca
.block_until_completed(task
)
1163 # connect network to VM
1164 # TODO figure out mapping between openmano representation to vCloud director.
1165 # one idea use first nic as management DHCP all remaining in bridge mode
1166 task
= vapp
.connect_vms(nets
[0].name
, connection_index
=nicIndex
,
1167 connections_primary_index
=nicIndex
,
1168 ip_allocation_mode
='DHCP')
1169 if type(task
) is GenericTask
:
1170 vca
.block_until_completed(task
)
1173 # it might be a case if specific mandatory entry in dict is empty
1174 self
.logger
.debug("Key error {}".format(KeyError.message
))
1175 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1177 # deploy and power on vm
1178 task
= vapp
.poweron()
1179 if type(task
) is TaskType
:
1180 vca
.block_until_completed(task
)
1181 deploytask
= vapp
.deploy(powerOn
='True')
1182 if type(task
) is TaskType
:
1183 vca
.block_until_completed(deploytask
)
1185 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1186 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), name
)
1187 if vapp_uuid
is not None:
1190 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1194 ## based on current discussion
1198 # created: '2016-09-08T11:51:58'
1199 # description: simple-instance.linux1.1
1200 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1201 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1202 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1207 def get_vminstance(self
, vim_vm_uuid
):
1208 '''Returns the VM instance information from VIM'''
1210 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1211 vca
= self
.connect()
1213 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1215 vdc
= vca
.get_vdc(self
.tenant_name
)
1217 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1219 vm_name
= self
.get_namebyvappid(vca
, vdc
, vim_vm_uuid
)
1221 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1222 return None, "Failed to get vApp name by UUID {}".format(vim_vm_uuid
)
1224 the_vapp
= vca
.get_vapp(vdc
, vm_name
)
1225 vm_info
= the_vapp
.get_vms_details()
1227 vm_dict
= {'description': vm_info
[0]['name'], 'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1228 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1229 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1232 vm_app_networks
= the_vapp
.get_vms_network_info()
1236 org_network_dict
= self
.get_org(self
.org_uuid
)['networks']
1237 for vapp_network
in vm_app_networks
:
1238 for vm_network
in vapp_network
:
1239 if vm_network
['name'] == vm_name
:
1241 # interface['vim_info'] = yaml.safe_dump(vm_network)
1242 interface
["mac_address"] = vm_network
['mac']
1243 for net_uuid
in org_network_dict
:
1244 if org_network_dict
[net_uuid
] == vm_network
['network_name']:
1245 interface
["vim_net_id"] = net_uuid
1246 interface
["vim_interface_id"] = vm_network
['network_name']
1247 interface
['ip_address'] = vm_network
['ip']
1248 interfaces
.append(interface
)
1250 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1251 self
.logger
.debug(traceback
.format_exc())
1253 vm_dict
['interfaces'] = interfaces
1257 def delete_vminstance(self
, vm__vim_uuid
):
1258 """Method poweroff and remove VM instance from vcloud director network.
1261 vm__vim_uuid: VM UUID
1264 Returns the instance identifier
1267 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1268 vca
= self
.connect()
1270 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1272 vdc
= vca
.get_vdc(self
.tenant_name
)
1274 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1276 raise vimconn
.vimconnException(
1277 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1280 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1281 if vapp_name
is None:
1282 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1283 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1285 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1287 # Delete vApp and wait for status change if task executed and vApp is None.
1288 # We successfully delete vApp from vCloud
1289 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1290 # poweroff vapp / undeploy and delete
1291 power_off_task
= vapp
.poweroff()
1292 if type(power_off_task
) is GenericTask
:
1293 vca
.block_until_completed(power_off_task
)
1295 if not power_off_task
:
1296 self
.logger
.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid
))
1299 if vapp
.me
.deployed
:
1300 undeploy_task
= vapp
.undeploy()
1301 if type(undeploy_task
) is GenericTask
:
1303 while retry
<= DELETE_INSTANCE_RETRY
:
1304 result
= vca
.block_until_completed(undeploy_task
)
1312 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1313 if vapp
is not None:
1314 delete_task
= vapp
.delete()
1316 while retry
<= DELETE_INSTANCE_RETRY
:
1317 task
= vapp
.delete()
1318 if type(task
) is GenericTask
:
1319 vca
.block_until_completed(delete_task
)
1321 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1325 self
.logger
.debug(traceback
.format_exc())
1326 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1328 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1331 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1333 def refresh_vms_status(self
, vm_list
):
1334 """Get the status of the virtual machines and their interfaces/ports
1335 Params: the list of VM identifiers
1336 Returns a dictionary with:
1337 vm_id: #VIM id of this Virtual Machine
1338 status: #Mandatory. Text with one of:
1339 # DELETED (not found at vim)
1340 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1341 # OTHER (Vim reported other status not understood)
1342 # ERROR (VIM indicates an ERROR status)
1343 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1344 # CREATING (on building process), ERROR
1345 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1347 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1348 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1350 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1351 mac_address: #Text format XX:XX:XX:XX:XX:XX
1352 vim_net_id: #network id where this interface is connected
1353 vim_interface_id: #interface/port VIM id
1354 ip_address: #null, or text with IPv4, IPv6 address
1357 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1358 vca
= self
.connect()
1360 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1362 vdc
= vca
.get_vdc(self
.tenant_name
)
1364 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1367 for vmuuid
in vm_list
:
1368 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1369 if vmname
is not None:
1371 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1372 vm_info
= the_vapp
.get_vms_details()
1373 vm_status
= vm_info
[0]['status']
1375 vm_dict
= {'status': None, 'error_msg': None, 'vim_info': None, 'interfaces': []}
1376 vm_dict
['status'] = vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()]
1377 vm_dict
['error_msg'] = vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()]
1378 vm_dict
['vim_info'] = yaml
.safe_dump(the_vapp
.get_vms_details())
1382 vm_app_networks
= the_vapp
.get_vms_network_info()
1383 for vapp_network
in vm_app_networks
:
1384 for vm_network
in vapp_network
:
1385 if vm_network
['name'] == vmname
:
1387 # interface['vim_info'] = yaml.safe_dump(vm_network)
1388 interface
["mac_address"] = vm_network
['mac']
1389 interface
["vim_net_id"] = self
.get_network_name_by_id(vm_network
['network_name'])
1390 interface
["vim_interface_id"] = vm_network
['network_name']
1391 interface
['ip_address'] = vm_network
['ip']
1392 vm_dict
["interfaces"].append(interface
)
1393 # add a vm to vm dict
1394 vms_dict
.setdefault(vmuuid
, vm_dict
)
1396 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1397 self
.logger
.debug(traceback
.format_exc())
1401 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1402 """Send and action over a VM instance from VIM
1403 Returns the vm_id if the action was successfully sent to the VIM"""
1405 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1406 if vm__vim_uuid
is None or action_dict
is None:
1407 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1409 vca
= self
.connect()
1411 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1413 vdc
= vca
.get_vdc(self
.tenant_name
)
1415 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1417 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1418 if vapp_name
is None:
1419 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1420 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1422 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1425 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1426 # TODO fix all status
1427 if "start" in action_dict
:
1428 if action_dict
["start"] == "rebuild":
1429 the_vapp
.deploy(powerOn
=True)
1431 vm_info
= the_vapp
.get_vms_details()
1432 vm_status
= vm_info
[0]['status']
1433 if vm_status
== "Suspended":
1435 elif vm_status
.status
== "Powered off":
1437 elif "pause" in action_dict
:
1440 elif "resume" in action_dict
:
1443 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1445 elif "forceOff" in action_dict
:
1447 elif "terminate" in action_dict
:
1449 # elif "createImage" in action_dict:
1450 # server.create_image()
1456 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1458 Get a console for the virtual machine
1460 vm_id: uuid of the VM
1461 console_type, can be:
1462 "novnc" (by default), "xvpvnc" for VNC types,
1463 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1464 Returns dict with the console parameters:
1465 protocol: ssh, ftp, http, https, ...
1466 server: usually ip address
1467 port: the http, ssh, ... port
1468 suffix: extra text, e.g. the http path and query string
1470 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1472 # NOT USED METHODS in current version
1474 def host_vim2gui(self
, host
, server_dict
):
1475 '''Transform host dictionary from VIM format to GUI format,
1476 and append to the server_dict
1478 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1480 def get_hosts_info(self
):
1481 '''Get the information of deployed hosts
1482 Returns the hosts content'''
1483 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1485 def get_hosts(self
, vim_tenant
):
1486 '''Get the hosts and deployed instances
1487 Returns the hosts content'''
1488 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1490 def get_processor_rankings(self
):
1491 '''Get the processor rankings in the VIM database'''
1492 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1494 def new_host(self
, host_data
):
1495 '''Adds a new host to VIM'''
1496 '''Returns status code of the VIM response'''
1497 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1499 def new_external_port(self
, port_data
):
1500 '''Adds a external port to VIM'''
1501 '''Returns the port identifier'''
1502 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1504 def new_external_network(self
, net_name
, net_type
):
1505 '''Adds a external network to VIM (shared)'''
1506 '''Returns the network identifier'''
1507 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1509 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1510 '''Connects a external port to a network'''
1511 '''Returns status code of the VIM response'''
1512 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1514 def new_vminstancefromJSON(self
, vm_data
):
1515 '''Adds a VM instance to VIM'''
1516 '''Returns the instance identifier'''
1517 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1519 def get_network_name_by_id(self
, network_name
=None):
1520 """Method gets vcloud director network named based on supplied uuid.
1523 network_name: network_id
1526 The return network name.
1529 vca
= self
.connect()
1531 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1533 if network_name
is None:
1537 org_network_dict
= self
.get_org(self
.org_uuid
)['networks']
1538 for net_uuid
in org_network_dict
:
1539 if org_network_dict
[net_uuid
] == network_name
:
1542 self
.logger
.debug("Exception in get_network_name_by_id")
1543 self
.logger
.debug(traceback
.format_exc())
1547 def list_org_action(self
):
1549 Method leverages vCloud director and query for available organization for particular user
1552 vca - is active VCA connection.
1553 vdc_name - is a vdc name that will be used to query vms action
1556 The return XML respond
1559 vca
= self
.connect()
1561 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1563 url_list
= [vca
.host
, '/api/org']
1564 vm_list_rest_call
= ''.join(url_list
)
1566 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1567 response
= Http
.get(url
=vm_list_rest_call
,
1568 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1571 if response
.status_code
== requests
.codes
.ok
:
1572 return response
.content
1576 def get_org_action(self
, org_uuid
=None):
1578 Method leverages vCloud director and retrieve available object fdr organization.
1581 vca - is active VCA connection.
1582 vdc_name - is a vdc name that will be used to query vms action
1585 The return XML respond
1588 vca
= self
.connect()
1590 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1592 if org_uuid
is None:
1595 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1596 vm_list_rest_call
= ''.join(url_list
)
1598 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1599 response
= Http
.get(url
=vm_list_rest_call
,
1600 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1603 if response
.status_code
== requests
.codes
.ok
:
1604 return response
.content
1608 def get_org(self
, org_uuid
=None):
1610 Method retrieves available organization in vCloud Director
1613 org_uuid - is a organization uuid.
1616 The return dictionary with following key
1617 "network" - for network list under the org
1618 "catalogs" - for network list under the org
1619 "vdcs" - for vdc list under org
1623 vca
= self
.connect()
1625 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1627 if org_uuid
is None:
1630 content
= self
.get_org_action(org_uuid
=org_uuid
)
1635 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1636 for child
in vm_list_xmlroot
:
1637 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1638 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1639 org_dict
['vdcs'] = vdc_list
1640 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1641 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1642 org_dict
['networks'] = network_list
1643 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1644 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1645 org_dict
['catalogs'] = catalog_list
1651 def get_org_list(self
):
1653 Method retrieves available organization in vCloud Director
1656 vca - is active VCA connection.
1659 The return dictionary and key for each entry VDC UUID
1663 vca
= self
.connect()
1665 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1667 content
= self
.list_org_action()
1669 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1670 for vm_xml
in vm_list_xmlroot
:
1671 if vm_xml
.tag
.split("}")[1] == 'Org':
1672 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1673 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1679 def vms_view_action(self
, vdc_name
=None):
1680 """ Method leverages vCloud director vms query call
1683 vca - is active VCA connection.
1684 vdc_name - is a vdc name that will be used to query vms action
1687 The return XML respond
1689 vca
= self
.connect()
1690 if vdc_name
is None:
1693 url_list
= [vca
.host
, '/api/vms/query']
1694 vm_list_rest_call
= ''.join(url_list
)
1696 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1697 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1698 vca
.vcloud_session
.organization
.Link
)
1700 response
= Http
.get(url
=vm_list_rest_call
,
1701 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1704 if response
.status_code
== requests
.codes
.ok
:
1705 return response
.content
1709 def get_vapp_list(self
, vdc_name
=None):
1711 Method retrieves vApp list deployed vCloud director and returns a dictionary
1712 contains a list of all vapp deployed for queried VDC.
1713 The key for a dictionary is vApp UUID
1717 vca - is active VCA connection.
1718 vdc_name - is a vdc name that will be used to query vms action
1721 The return dictionary and key for each entry vapp UUID
1725 if vdc_name
is None:
1728 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1730 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1731 for vm_xml
in vm_list_xmlroot
:
1732 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1733 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1734 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1735 if 'vappTemplate-' in rawuuid
[0]:
1736 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1737 # vm and use raw UUID as key
1738 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1744 def get_vm_list(self
, vdc_name
=None):
1746 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1747 contains a list of all VM's deployed for queried VDC.
1748 The key for a dictionary is VM UUID
1752 vca - is active VCA connection.
1753 vdc_name - is a vdc name that will be used to query vms action
1756 The return dictionary and key for each entry vapp UUID
1760 if vdc_name
is None:
1763 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1765 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1766 for vm_xml
in vm_list_xmlroot
:
1767 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1768 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1769 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1770 if 'vm-' in rawuuid
[0]:
1771 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1772 # vm and use raw UUID as key
1773 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1779 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1781 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1782 contains a list of all VM's deployed for queried VDC.
1783 The key for a dictionary is VM UUID
1787 vca - is active VCA connection.
1788 vdc_name - is a vdc name that will be used to query vms action
1791 The return dictionary and key for each entry vapp UUID
1794 vca
= self
.connect()
1796 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1798 if vdc_name
is None:
1801 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1803 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1804 for vm_xml
in vm_list_xmlroot
:
1805 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1806 # lookup done by UUID
1808 if vapp_name
in vm_xml
.attrib
['container']:
1809 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1810 if 'vm-' in rawuuid
[0]:
1811 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1813 # lookup done by Name
1815 if vapp_name
in vm_xml
.attrib
['name']:
1816 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1817 if 'vm-' in rawuuid
[0]:
1818 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1825 def get_network_action(self
, network_uuid
=None):
1827 Method leverages vCloud director and query network based on network uuid
1830 vca - is active VCA connection.
1831 network_uuid - is a network uuid
1834 The return XML respond
1837 vca
= self
.connect()
1839 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1841 if network_uuid
is None:
1844 url_list
= [vca
.host
, '/api/network/', network_uuid
]
1845 vm_list_rest_call
= ''.join(url_list
)
1847 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1848 response
= Http
.get(url
=vm_list_rest_call
,
1849 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1852 if response
.status_code
== requests
.codes
.ok
:
1853 return response
.content
1857 def get_vcd_network(self
, network_uuid
=None):
1859 Method retrieves available network from vCloud Director
1862 network_uuid - is VCD network UUID
1864 Each element serialized as key : value pair
1866 Following keys available for access. network_configuration['Gateway'}
1870 <IsInherited>true</IsInherited>
1871 <Gateway>172.16.252.100</Gateway>
1872 <Netmask>255.255.255.0</Netmask>
1873 <Dns1>172.16.254.201</Dns1>
1874 <Dns2>172.16.254.202</Dns2>
1875 <DnsSuffix>vmwarelab.edu</DnsSuffix>
1876 <IsEnabled>true</IsEnabled>
1879 <StartAddress>172.16.252.1</StartAddress>
1880 <EndAddress>172.16.252.99</EndAddress>
1885 <FenceMode>bridged</FenceMode>
1888 The return dictionary and key for each entry vapp UUID
1891 network_configuration
= {}
1892 if network_uuid
is None:
1895 content
= self
.get_network_action(network_uuid
=network_uuid
)
1897 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1899 network_configuration
['status'] = vm_list_xmlroot
.get("status")
1900 network_configuration
['name'] = vm_list_xmlroot
.get("name")
1901 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
1903 for child
in vm_list_xmlroot
:
1904 if child
.tag
.split("}")[1] == 'IsShared':
1905 network_configuration
['isShared'] = child
.text
.strip()
1906 if child
.tag
.split("}")[1] == 'Configuration':
1907 for configuration
in child
.iter():
1908 tagKey
= configuration
.tag
.split("}")[1].strip()
1910 network_configuration
[tagKey
] = configuration
.text
.strip()
1911 return network_configuration
1915 return network_configuration
1917 def delete_network_action(self
, network_uuid
=None):
1919 Method delete given network from vCloud director
1922 network_uuid - is a network uuid that client wish to delete
1925 The return None or XML respond or false
1928 vca
= self
.connect_as_admin()
1930 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1931 if network_uuid
is None:
1934 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
1935 vm_list_rest_call
= ''.join(url_list
)
1937 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1938 response
= Http
.delete(url
=vm_list_rest_call
,
1939 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1943 if response
.status_code
== 202:
1948 def create_network(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
1950 Method create network in vCloud director
1953 network_name - is network name to be created.
1954 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1955 It optional attribute. by default if no parent network indicate the first available will be used.
1958 The return network uuid or return None
1961 content
= self
.create_network_rest(network_name
=network_name
,
1962 parent_network_uuid
=parent_network_uuid
,
1965 self
.logger
.debug("Failed create network {}.".format(network_name
))
1969 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1970 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
1971 if len(vcd_uuid
) == 4:
1972 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
1975 self
.logger
.debug("Failed create network {}".format(network_name
))
1978 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
1980 Method create network in vCloud director
1983 network_name - is network name to be created.
1984 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1985 It optional attribute. by default if no parent network indicate the first available will be used.
1988 The return network uuid or return None
1991 vca
= self
.connect_as_admin()
1993 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1994 if network_name
is None:
1997 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
1998 vm_list_rest_call
= ''.join(url_list
)
1999 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2000 response
= Http
.get(url
=vm_list_rest_call
,
2001 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2005 provider_network
= None
2006 available_networks
= None
2007 add_vdc_rest_url
= None
2009 if response
.status_code
!= requests
.codes
.ok
:
2010 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2011 response
.status_code
))
2015 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2016 for child
in vm_list_xmlroot
:
2017 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2018 provider_network
= child
.attrib
.get('href')
2019 # application/vnd.vmware.admin.providervdc+xml
2020 if child
.tag
.split("}")[1] == 'Link':
2021 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2022 and child
.attrib
.get('rel') == 'add':
2023 add_vdc_rest_url
= child
.attrib
.get('href')
2025 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2026 self
.logger
.debug("Respond body {}".format(response
.content
))
2029 # find pvdc provided available network
2030 response
= Http
.get(url
=provider_network
,
2031 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2034 if response
.status_code
!= requests
.codes
.ok
:
2035 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2036 response
.status_code
))
2039 # available_networks.split("/")[-1]
2041 if parent_network_uuid
is None:
2043 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2044 for child
in vm_list_xmlroot
.iter():
2045 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2046 for networks
in child
.iter():
2047 # application/vnd.vmware.admin.network+xml
2048 if networks
.attrib
.get('href') is not None:
2049 available_networks
= networks
.attrib
.get('href')
2054 # either use client provided UUID or search for a first available
2055 # if both are not defined we return none
2056 if parent_network_uuid
is not None:
2057 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2058 add_vdc_rest_url
= ''.join(url_list
)
2060 # return response.content
2061 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2062 <Description>Openmano created</Description>
2064 <ParentNetwork href="{1:s}"/>
2065 <FenceMode>{2:s}</FenceMode>
2067 <IsShared>{3:s}</IsShared>
2068 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2070 headers
= vca
.vcloud_session
.get_vcloud_headers()
2071 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2072 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2074 # if we all ok we respond with content otherwise by default None
2075 if response
.status_code
== 201:
2076 return response
.content
2079 def get_provider_rest(self
, vca
=None):
2081 Method gets provider vdc view from vcloud director
2084 network_name - is network name to be created.
2085 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2086 It optional attribute. by default if no parent network indicate the first available will be used.
2089 The return xml content of respond or None
2092 url_list
= [vca
.host
, '/api/admin']
2093 response
= Http
.get(url
=''.join(url_list
),
2094 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2098 if response
.status_code
== requests
.codes
.ok
:
2099 return response
.content
2102 def create_vdc(self
, vdc_name
=None):
2106 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2107 if xml_content
is not None:
2109 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2110 for child
in task_resp_xmlroot
:
2111 if child
.tag
.split("}")[1] == 'Owner':
2112 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2113 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2116 self
.logger
.debug("Respond body {}".format(xml_content
))
2120 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2122 Method create vdc in vCloud director based on VDC template.
2123 it uses pre-defined template that must be named openmano
2126 vdc_name - name of a new vdc.
2129 The return xml content of respond or None
2132 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2133 vca
= self
.connect()
2135 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2136 if vdc_name
is None:
2139 url_list
= [vca
.host
, '/api/vdcTemplates']
2140 vm_list_rest_call
= ''.join(url_list
)
2141 response
= Http
.get(url
=vm_list_rest_call
,
2142 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2146 # container url to a template
2147 vdc_template_ref
= None
2149 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2150 for child
in vm_list_xmlroot
:
2151 # application/vnd.vmware.admin.providervdc+xml
2152 # we need find a template from witch we instantiate VDC
2153 if child
.tag
.split("}")[1] == 'VdcTemplate':
2154 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2155 'name') == 'openmano':
2156 vdc_template_ref
= child
.attrib
.get('href')
2158 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2159 self
.logger
.debug("Respond body {}".format(response
.content
))
2162 # if we didn't found required pre defined template we return None
2163 if vdc_template_ref
is None:
2168 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2169 vm_list_rest_call
= ''.join(url_list
)
2170 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2171 <Source href="{1:s}"></Source>
2172 <Description>opnemano</Description>
2173 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2174 headers
= vca
.vcloud_session
.get_vcloud_headers()
2175 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2176 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2178 # if we all ok we respond with content otherwise by default None
2179 if response
.status_code
>= 200 and response
.status_code
< 300:
2180 return response
.content
2183 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2184 self
.logger
.debug("Respond body {}".format(response
.content
))
2188 def create_vdc_rest(self
, vdc_name
=None):
2190 Method create network in vCloud director
2193 network_name - is network name to be created.
2194 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2195 It optional attribute. by default if no parent network indicate the first available will be used.
2198 The return network uuid or return None
2201 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2203 vca
= self
.connect_as_admin()
2205 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2206 if vdc_name
is None:
2209 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2210 vm_list_rest_call
= ''.join(url_list
)
2211 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2212 response
= Http
.get(url
=vm_list_rest_call
,
2213 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2217 provider_vdc_ref
= None
2218 add_vdc_rest_url
= None
2219 available_networks
= None
2221 if response
.status_code
!= requests
.codes
.ok
:
2222 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2223 response
.status_code
))
2227 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2228 for child
in vm_list_xmlroot
:
2229 # application/vnd.vmware.admin.providervdc+xml
2230 if child
.tag
.split("}")[1] == 'Link':
2231 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2232 and child
.attrib
.get('rel') == 'add':
2233 add_vdc_rest_url
= child
.attrib
.get('href')
2235 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2236 self
.logger
.debug("Respond body {}".format(response
.content
))
2239 response
= self
.get_provider_rest(vca
=vca
)
2241 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2242 for child
in vm_list_xmlroot
:
2243 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2244 for sub_child
in child
:
2245 provider_vdc_ref
= sub_child
.attrib
.get('href')
2247 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2248 self
.logger
.debug("Respond body {}".format(response
))
2251 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2252 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2253 <AllocationModel>ReservationPool</AllocationModel>
2254 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2255 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2256 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2257 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2258 <ProviderVdcReference
2259 name="Main Provider"
2261 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2265 headers
= vca
.vcloud_session
.get_vcloud_headers()
2266 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2267 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2270 # if we all ok we respond with content otherwise by default None
2271 if response
.status_code
== 201:
2272 return response
.content