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 # global variable for number of retry
62 DELETE_INSTANCE_RETRY
= 3
66 __author__
= "Mustafa Bayramov"
67 __date__
= "$26-Aug-2016 11:09:29$"
70 # -1: "Could not be created",
76 # 5: "Waiting for user input",
78 # 7: "Unrecognized state",
80 # 9: "Inconsistent state",
81 # 10: "Children do not all have the same status",
82 # 11: "Upload initiated, OVF descriptor pending",
83 # 12: "Upload initiated, copying contents",
84 # 13: "Upload initiated , disk contents pending",
85 # 14: "Upload has been quarantined",
86 # 15: "Upload quarantine period has expired"
88 # mapping vCD status to MANO
89 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
98 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
99 'ERROR': 'ERROR', 'DELETED': 'DELETED'
102 # dict used to store flavor in memory
106 class vimconnector(vimconn
.vimconnector
):
107 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
108 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}):
110 Constructor create vmware connector to vCloud director.
112 By default construct doesn't validate connection state. So client can create object with None arguments.
113 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
115 a) It initialize organization UUID
116 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
119 uuid - is organization uuid.
120 name - is organization name that must be presented in vCloud director.
121 tenant_id - is VDC uuid it must be presented in vCloud director
122 tenant_name - is VDC name.
123 url - is hostname or ip address of vCloud director
124 url_admin - same as above.
125 user - is user that administrator for organization. Caller must make sure that
126 username has right privileges.
128 password - is password for a user.
130 VMware connector also requires PVDC administrative privileges and separate account.
131 This variables must be passed via config argument dict contains keys
133 dict['admin_username']
134 dict['admin_password']
140 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
141 url_admin
, user
, passwd
, log_level
, config
)
146 self
.url_admin
= url_admin
147 self
.tenant_id
= tenant_id
148 self
.tenant_name
= tenant_name
152 self
.admin_password
= None
153 self
.admin_user
= None
155 self
.logger
= logging
.getLogger('openmano.vim.vmware')
156 self
.logger
.setLevel(10)
159 self
.logger
.setLevel(getattr(logging
, log_level
))
162 self
.admin_user
= config
['admin_username']
163 self
.admin_password
= config
['admin_password']
165 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
171 raise vimconn
.vimconnException('url param can not be NoneType')
173 if not self
.url_admin
: # try to use normal url
174 self
.url_admin
= self
.url
176 logging
.debug("Calling constructor with following paramters")
177 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.name
,
178 self
.tenant_id
, self
.tenant_name
))
179 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
180 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
182 # initialize organization
183 if self
.user
is not None and self
.passwd
is not None and self
.url
:
184 self
.init_organization()
186 def __getitem__(self
, index
):
187 if index
== 'tenant_id':
188 return self
.tenant_id
189 if index
== 'tenant_name':
190 return self
.tenant_name
193 elif index
== 'name':
195 elif index
== 'org_name':
197 elif index
== 'org_uuid':
199 elif index
== 'user':
201 elif index
== 'passwd':
205 elif index
== 'url_admin':
206 return self
.url_admin
207 elif index
== "config":
210 raise KeyError("Invalid key '%s'" % str(index
))
212 def __setitem__(self
, index
, value
):
213 if index
== 'tenant_id':
214 self
.tenant_id
= value
215 if index
== 'tenant_name':
216 self
.tenant_name
= value
219 # we use name = org #TODO later refactor
220 elif index
== 'name':
223 elif index
== 'org_name':
224 self
.org_name
= value
226 elif index
== 'org_uuid':
227 self
.org_name
= value
228 elif index
== 'user':
230 elif index
== 'passwd':
234 elif index
== 'url_admin':
235 self
.url_admin
= value
237 raise KeyError("Invalid key '%s'" % str(index
))
239 def connect_as_admin(self
):
240 """ Method connect as pvdc admin user to vCloud director.
241 There are certain action that can be done only by provider vdc admin user.
242 Organization creation / provider network creation etc.
245 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
248 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.name
))
250 vca_admin
= VCA(host
=self
.url
,
251 username
=self
.admin_user
,
252 service_type
=STANDALONE
,
256 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
258 raise vimconn
.vimconnConnectionException(
259 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
260 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
263 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
268 """ Method connect as normal user to vCloud director.
271 The return vca object that letter can be used to connect to vCloud director as admin for VDC
275 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.name
, self
.user
, self
.name
))
276 vca
= VCA(host
=self
.url
,
278 service_type
=STANDALONE
,
283 result
= vca
.login(password
=self
.passwd
, org
=self
.name
)
285 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
286 result
= vca
.login(token
=vca
.token
, org
=self
.name
, org_url
=vca
.vcloud_session
.org_url
)
289 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.name
, self
.user
))
292 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
296 def init_organization(self
):
297 """ Method initialize organization UUID and VDC parameters.
299 At bare minimum client must provide organization name that present in vCloud director and VDC.
301 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
302 The Org - UUID will be initialized at the run time if data center present in vCloud director.
305 The return vca object that letter can be used to connect to vcloud direct as admin
308 if self
.org_uuid
is None:
309 org_dict
= self
.get_org_list()
311 # we set org UUID at the init phase but we can do it only when we have valid credential.
312 if org_dict
[org
] == self
.org_name
:
314 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
318 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
320 # if well good we require for org details
321 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
323 # we have two case if we want to initialize VDC ID or VDC name at run time
324 # tenant_name provided but no tenant id
325 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
326 vdcs_dict
= org_details_dict
['vdcs']
327 for vdc
in vdcs_dict
:
328 if vdcs_dict
[vdc
] == self
.tenant_name
:
330 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
334 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
335 # case two we have tenant_id but we don't have tenant name so we find and set it.
336 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
337 vdcs_dict
= org_details_dict
['vdcs']
338 for vdc
in vdcs_dict
:
339 if vdc
== self
.tenant_id
:
340 self
.tenant_name
= vdcs_dict
[vdc
]
341 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
345 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
346 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
348 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
349 self
.logger
.debug(traceback
.format_exc())
352 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
353 """ Method adds a new tenant to VIM with this name.
354 This action requires access to create VDC action in vCloud director.
357 tenant_name is tenant_name to be created.
358 tenant_description not used for this call
361 returns the tenant identifier in UUID format.
362 If action is failed method will throw vimconn.vimconnException method
364 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
365 if vdc_task
is not None:
366 vdc_uuid
, value
= vdc_task
.popitem()
367 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
370 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
372 def delete_tenant(self
, tenant_id
=None):
373 """Delete a tenant from VIM"""
374 'Returns the tenant identifier'
375 raise vimconn
.vimconnNotImplemented("Should have implemented this")
377 def get_tenant_list(self
, filter_dict
={}):
378 """Obtain tenants of VIM
379 filter_dict can contain the following keys:
380 name: filter by tenant name
381 id: filter by tenant uuid/id
383 Returns the tenant list of dictionaries:
384 [{'name':'<name>, 'id':'<id>, ...}, ...]
387 org_dict
= self
.get_org(self
.org_uuid
)
388 vdcs_dict
= org_dict
['vdcs']
393 entry
= {'name': vdcs_dict
[k
], 'id': k
}
394 # if caller didn't specify dictionary we return all tenants.
395 if filter_dict
is not None and filter_dict
:
396 filtered_entry
= entry
.copy()
397 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
398 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
399 if filter_dict
== entry
:
400 vdclist
.append(filtered_entry
)
402 vdclist
.append(entry
)
404 self
.logger
.debug("Error in get_tenant_list()")
405 self
.logger
.debug(traceback
.format_exc())
406 raise vimconn
.vimconnException("Incorrect state. {}")
410 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
411 """Adds a tenant network to VIM
413 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
414 ip_profile is a dict containing the IP parameters of the network
416 Returns the network identifier"""
419 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name
, net_type
, ip_profile
, shared
))
425 network_uuid
= self
.create_network(network_name
=net_name
, isshared
=isshared
)
426 if network_uuid
is not None:
429 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
431 def get_vcd_network_list(self
):
432 """ Method available organization for a logged in tenant
435 The return vca object that letter can be used to connect to vcloud direct as admin
438 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd")
441 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
443 vdc
= vca
.get_vdc(self
.tenant_name
)
444 vdc_uuid
= vdc
.get_id().split(":")[3]
445 networks
= vca
.get_networks(vdc
.get_name())
448 for network
in networks
:
450 netid
= network
.get_id().split(":")
454 filter_dict
["name"] = network
.get_name()
455 filter_dict
["id"] = netid
[3]
456 filter_dict
["shared"] = network
.get_IsShared()
457 filter_dict
["tenant_id"] = vdc_uuid
458 if network
.get_status() == 1:
459 filter_dict
["admin_state_up"] = True
461 filter_dict
["admin_state_up"] = False
462 filter_dict
["status"] = "ACTIVE"
463 filter_dict
["type"] = "bridge"
464 network_list
.append(filter_dict
)
465 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
467 self
.logger
.debug("Error in get_vcd_network_list")
468 self
.logger
.debug(traceback
.format_exc())
471 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
474 def get_network_list(self
, filter_dict
={}):
475 """Obtain tenant networks of VIM
477 name: network name OR/AND
478 id: network uuid OR/AND
479 shared: boolean OR/AND
480 tenant_id: tenant OR/AND
481 admin_state_up: boolean
484 [{key : value , key : value}]
486 Returns the network list of dictionaries:
487 [{<the fields at Filter_dict plus some VIM specific>}, ...]
493 raise vimconn
.vimconnConnectionException("self.connect() is failed")
495 vdc
= vca
.get_vdc(self
.tenant_name
)
496 vdcid
= vdc
.get_id().split(":")[3]
498 networks
= vca
.get_networks(vdc
.get_name())
502 for network
in networks
:
504 net_uuid
= network
.get_id().split(":")
505 if len(net_uuid
) != 4:
508 net_uuid
= net_uuid
[3]
510 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
513 filter_entry
["name"] = network
.get_name()
514 filter_entry
["id"] = net_uuid
515 filter_entry
["shared"] = network
.get_IsShared()
516 filter_entry
["tenant_id"] = vdcid
517 if network
.get_status() == 1:
518 filter_entry
["admin_state_up"] = True
520 filter_entry
["admin_state_up"] = False
521 filter_entry
["status"] = "ACTIVE"
522 filter_entry
["type"] = "bridge"
523 filtered_entry
= filter_entry
.copy()
525 if filter_dict
is not None and filter_dict
:
526 # we remove all the key : value we don't care and match only
528 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
529 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
530 if filter_dict
== filter_entry
:
531 network_list
.append(filtered_entry
)
533 network_list
.append(filtered_entry
)
535 self
.logger
.debug("Error in get_vcd_network_list")
536 self
.logger
.debug(traceback
.format_exc())
538 self
.logger
.debug("Returning {}".format(network_list
))
541 def get_network(self
, net_id
):
542 """Method obtains network details of net_id VIM network
543 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
547 raise vimconn
.vimconnConnectionException("self.connect() is failed")
549 vdc
= vca
.get_vdc(self
.tenant_name
)
550 vdc_id
= vdc
.get_id().split(":")[3]
552 networks
= vca
.get_networks(vdc
.get_name())
556 for network
in networks
:
557 vdc_network_id
= network
.get_id().split(":")
558 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
559 filter_dict
["name"] = network
.get_name()
560 filter_dict
["id"] = vdc_network_id
[3]
561 filter_dict
["shared"] = network
.get_IsShared()
562 filter_dict
["tenant_id"] = vdc_id
563 if network
.get_status() == 1:
564 filter_dict
["admin_state_up"] = True
566 filter_dict
["admin_state_up"] = False
567 filter_dict
["status"] = "ACTIVE"
568 filter_dict
["type"] = "bridge"
569 self
.logger
.debug("Returning {}".format(filter_dict
))
572 self
.logger
.debug("Error in get_network")
573 self
.logger
.debug(traceback
.format_exc())
577 def delete_network(self
, net_id
):
579 Method Deletes a tenant network from VIM, provide the network id.
581 Returns the network identifier or raise an exception
586 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
588 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
589 if vcd_network
is not None and vcd_network
:
590 if self
.delete_network_action(network_uuid
=net_id
):
593 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
595 def refresh_nets_status(self
, net_list
):
596 """Get the status of the networks
597 Params: the list of network identifiers
598 Returns a dictionary with:
599 net_id: #VIM id of this network
600 status: #Mandatory. Text with one of:
601 # DELETED (not found at vim)
602 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
603 # OTHER (Vim reported other status not understood)
604 # ERROR (VIM indicates an ERROR status)
605 # ACTIVE, INACTIVE, DOWN (admin down),
606 # BUILD (on building process)
608 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
609 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
615 raise vimconn
.vimconnConnectionException("self.connect() is failed")
621 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
622 if vcd_network
is not None and vcd_network
:
623 if vcd_network
['status'] == 1:
629 errormsg
= 'Network not found.'
631 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
632 'vm_info': yaml
.safe_dump(vcd_network
)}
634 self
.logger
.debug("Error in refresh_nets_status")
635 self
.logger
.debug(traceback
.format_exc())
639 def get_flavor(self
, flavor_id
):
640 """Obtain flavor details from the VIM
641 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
643 if flavor_id
not in flavorlist
:
644 raise vimconn
.vimconnNotFoundException("Flavor not found.")
645 return flavorlist
[flavor_id
]
647 def new_flavor(self
, flavor_data
):
648 """Adds a tenant flavor to VIM
649 flavor_data contains a dictionary with information, keys:
651 ram: memory (cloud type) in MBytes
652 vpcus: cpus (cloud type)
653 extended: EPA parameters
654 - numas: #items requested in same NUMA
655 memory: number of 1G huge pages memory
656 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
657 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
658 - name: interface name
659 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
660 bandwidth: X Gbps; requested guarantee bandwidth
661 vpci: requested virtual PCI address
668 Returns the flavor identifier"""
670 # generate a new uuid put to internal dict and return it.
671 flavor_id
= uuid
.uuid4()
672 flavorlist
[str(flavor_id
)] = flavor_data
674 return str(flavor_id
)
676 def delete_flavor(self
, flavor_id
):
677 """Deletes a tenant flavor from VIM identify by its id
679 Returns the used id or raise an exception
681 if flavor_id
not in flavorlist
:
682 raise vimconn
.vimconnNotFoundException("Flavor not found.")
684 flavorlist
.pop(flavor_id
, None)
687 def new_image(self
, image_dict
):
689 Adds a tenant image to VIM
691 200, image-id if the image is created
692 <0, message if there is an error
695 return self
.get_image_id_from_path(image_dict
['location'])
697 def delete_image(self
, image_id
):
704 raise vimconn
.vimconnNotImplemented("Should have implemented this")
706 def catalog_exists(self
, catalog_name
, catalogs
):
713 for catalog
in catalogs
:
714 if catalog
.name
== catalog_name
:
718 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
719 """ Create new catalog entry in vCloud director.
722 vca: vCloud director.
723 catalog_name catalog that client wish to create. Note no validation done for a name.
724 Client must make sure that provide valid string representation.
726 Return (bool) True if catalog created.
730 task
= vca
.create_catalog(catalog_name
, catalog_name
)
731 result
= vca
.block_until_completed(task
)
734 catalogs
= vca
.get_catalogs()
737 return self
.catalog_exists(catalog_name
, catalogs
)
739 # noinspection PyIncorrectDocstring
740 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
741 description
='', progress
=False, chunk_bytes
=128 * 1024):
743 Uploads a OVF file to a vCloud catalog
750 :param catalog_name: (str): The name of the catalog to upload the media.
751 :param media_file_name: (str): The name of the local media file to upload.
752 :return: (bool) True if the media file was successfully uploaded, false otherwise.
754 os
.path
.isfile(media_file_name
)
755 statinfo
= os
.stat(media_file_name
)
757 # find a catalog entry where we upload OVF.
758 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
760 # if VCD can parse OVF we upload VMDK file
761 for catalog
in vca
.get_catalogs():
762 if catalog_name
!= catalog
.name
:
764 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
765 link
.get_rel() == 'add', catalog
.get_Link())
766 assert len(link
) == 1
768 <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>
769 """ % (escape(image_name
), escape(description
))
770 headers
= vca
.vcloud_session
.get_vcloud_headers()
771 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
772 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
773 if response
.status_code
== requests
.codes
.created
:
774 catalogItem
= XmlElementTree
.fromstring(response
.content
)
775 entity
= [child
for child
in catalogItem
if
776 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
777 href
= entity
.get('href')
779 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
780 verify
=vca
.verify
, logger
=self
.logger
)
782 if response
.status_code
== requests
.codes
.ok
:
783 media
= mediaType
.parseString(response
.content
, True)
784 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
785 media
.get_Files().get_File()[0].get_Link())[0]
786 headers
= vca
.vcloud_session
.get_vcloud_headers()
787 headers
['Content-Type'] = 'Content-Type text/xml'
788 response
= Http
.put(link
.get_href(),
789 data
=open(media_file_name
, 'rb'),
791 verify
=vca
.verify
, logger
=self
.logger
)
792 if response
.status_code
!= requests
.codes
.ok
:
794 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
798 # TODO fix this with aync block
801 self
.logger
.debug("Failed create vApp template for catalog name {} and image {}".
802 format(catalog_name
, media_file_name
))
804 # uploading VMDK file
805 # check status of OVF upload and upload remaining files.
806 response
= Http
.get(template
,
807 headers
=vca
.vcloud_session
.get_vcloud_headers(),
811 if response
.status_code
== requests
.codes
.ok
:
812 media
= mediaType
.parseString(response
.content
, True)
813 number_of_files
= len(media
.get_Files().get_File())
814 for index
in xrange(0, number_of_files
):
815 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
816 media
.get_Files().get_File()[index
].get_Link())
817 for link
in links_list
:
818 # we skip ovf since it already uploaded.
819 if 'ovf' in link
.get_href():
821 # The OVF file and VMDK must be in a same directory
822 head
, tail
= os
.path
.split(media_file_name
)
823 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
824 if not os
.path
.isfile(file_vmdk
):
826 statinfo
= os
.stat(file_vmdk
)
827 if statinfo
.st_size
== 0:
829 hrefvmdk
= link
.get_href()
832 print("Uploading file: {}".format(file_vmdk
))
834 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
836 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
838 bytes_transferred
= 0
839 f
= open(file_vmdk
, 'rb')
840 while bytes_transferred
< statinfo
.st_size
:
841 my_bytes
= f
.read(chunk_bytes
)
842 if len(my_bytes
) <= chunk_bytes
:
843 headers
= vca
.vcloud_session
.get_vcloud_headers()
844 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
845 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
846 headers
['Content-Length'] = str(len(my_bytes
))
847 response
= Http
.put(hrefvmdk
,
853 if response
.status_code
== requests
.codes
.ok
:
854 bytes_transferred
+= len(my_bytes
)
856 progress_bar
.update(bytes_transferred
)
859 'file upload failed with error: [%s] %s' % (response
.status_code
,
866 progress_bar
.finish()
869 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
870 format(catalog_name
, media_file_name
))
873 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
876 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
877 """Upload media file"""
878 # TODO add named parameters for readability
880 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
881 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
883 def validate_uuid4(self
, uuid_string
=None):
884 """ Method validate correct format of UUID.
886 Return: true if string represent valid uuid
889 val
= uuid
.UUID(uuid_string
, version
=4)
894 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
895 """ Method check catalog and return catalog ID in UUID format.
898 catalog_name: catalog name as string
899 catalogs: list of catalogs.
901 Return: catalogs uuid
904 for catalog
in catalogs
:
905 if catalog
.name
== catalog_name
:
906 catalog_id
= catalog
.get_id().split(":")
910 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
911 """ Method check catalog and return catalog name lookup done by catalog UUID.
914 catalog_name: catalog name as string
915 catalogs: list of catalogs.
917 Return: catalogs name or None
920 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
923 for catalog
in catalogs
:
924 catalog_id
= catalog
.get_id().split(":")[3]
925 if catalog_id
== catalog_uuid
:
929 def get_image_id_from_path(self
, path
=None, progress
=False):
930 """ Method upload OVF image to vCloud director.
932 Each OVF image represented as single catalog entry in vcloud director.
933 The method check for existing catalog entry. The check done by file name without file extension.
935 if given catalog name already present method will respond with existing catalog uuid otherwise
936 it will create new catalog entry and upload OVF file to newly created catalog.
938 If method can't create catalog entry or upload a file it will throw exception.
940 Method accept boolean flag progress that will output progress bar. It useful method
941 for standalone upload use case. In case to test large file upload.
944 path: - valid path to OVF file.
945 progress - boolean progress bar show progress bar.
947 Return: if image uploaded correct method will provide image catalog UUID.
951 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
954 raise vimconn
.vimconnException("Image path can't be None.")
956 if not os
.path
.isfile(path
):
957 raise vimconn
.vimconnException("Can't read file. File not found.")
959 if not os
.access(path
, os
.R_OK
):
960 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
962 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
964 dirpath
, filename
= os
.path
.split(path
)
965 flname
, file_extension
= os
.path
.splitext(path
)
966 if file_extension
!= '.ovf':
967 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
968 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
969 catalog_name
= os
.path
.splitext(filename
)[0]
970 self
.logger
.debug("File name {} Catalog Name {} file path {}".format(filename
, catalog_name
, path
))
972 catalogs
= vca
.get_catalogs()
973 if len(catalogs
) == 0:
974 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
975 result
= self
.create_vimcatalog(vca
, catalog_name
)
977 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
978 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_name
,
979 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
981 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
982 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
984 for catalog
in catalogs
:
985 # search for existing catalog if we find same name we return ID
987 if catalog
.name
== catalog_name
:
988 self
.logger
.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name
,
992 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
994 # if we didn't find existing catalog we create a new one and upload image.
995 self
.logger
.debug("Creating new catalog entry".format(catalog_name
))
996 result
= self
.create_vimcatalog(vca
, catalog_name
)
998 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
1000 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_name
,
1001 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1003 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1005 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1007 def get_vappid(self
, vdc
=None, vapp_name
=None):
1008 """ Method takes vdc object and vApp name and returns vapp uuid or None
1011 vdc: The VDC object.
1012 vapp_name: is application vappp name identifier
1015 The return vApp name otherwise None
1017 if vdc
is None or vapp_name
is None:
1019 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1021 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1022 vdc
.ResourceEntities
.ResourceEntity
)
1024 return refs
[0].href
.split("vapp")[1][1:]
1025 except Exception as e
:
1026 self
.logger
.exception(e
)
1030 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1031 """ Method Method returns True or False if vapp deployed in vCloud director
1034 vca: Connector to VCA
1035 vdc: The VDC object.
1036 vappid: vappid is application identifier
1039 The return True if vApp deployed
1044 refs
= filter(lambda ref
:
1045 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1046 vdc
.ResourceEntities
.ResourceEntity
)
1048 vappid
= ref
.href
.split("vapp")[1][1:]
1049 # find vapp with respected vapp uuid
1050 if vappid
== vapp_uuid
:
1052 except Exception as e
:
1053 self
.logger
.exception(e
)
1057 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1058 """Method returns vApp name from vCD and lookup done by vapp_id.
1061 vca: Connector to VCA
1062 vdc: The VDC object.
1063 vapp_uuid: vappid is application identifier
1066 The return vApp name otherwise None
1070 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1071 vdc
.ResourceEntities
.ResourceEntity
)
1073 # we care only about UUID the rest doesn't matter
1074 vappid
= ref
.href
.split("vapp")[1][1:]
1075 if vappid
== vapp_uuid
:
1076 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1078 tree
= XmlElementTree
.fromstring(response
.content
)
1079 return tree
.attrib
['name']
1080 except Exception as e
:
1081 self
.logger
.exception(e
)
1085 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1087 """Adds a VM instance to VIM
1089 start: indicates if VM must start or boot in pause mode. Ignored
1090 image_id,flavor_id: image and flavor uuid
1091 net_list: list of interfaces, each one is a dictionary with:
1093 net_id: network uuid to connect
1094 vpci: virtual vcpi to assign
1095 model: interface model, virtio, e2000, ...
1097 use: 'data', 'bridge', 'mgmt'
1098 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1099 vim_id: filled/added by this function
1100 cloud_config: can be a text script to be passed directly to cloud-init,
1101 or an object to inject users and ssh keys with format:
1102 key-pairs: [] list of keys to install to the default user
1103 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1104 #TODO ip, security groups
1105 Returns >=0, the instance identifier
1109 self
.logger
.info("Creating new instance for entry".format(name
))
1110 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1111 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1112 vca
= self
.connect()
1114 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1116 #new vm name = vmname + tenant_id + uuid
1117 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1118 full_name
= ''.join(new_vm_name
)
1120 # if vm already deployed we return existing uuid
1121 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1122 # if vapp_uuid is not None:
1125 # we check for presence of VDC, Catalog entry and Flavor.
1126 vdc
= vca
.get_vdc(self
.tenant_name
)
1128 raise vimconn
.vimconnNotFoundException(
1129 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1130 catalogs
= vca
.get_catalogs()
1131 if catalogs
is None:
1132 raise vimconn
.vimconnNotFoundException(
1133 "new_vminstance(): Failed create vApp {}: ""(Failed retrieve catalog information)".format(name
))
1137 if flavor_id
is not None:
1138 flavor
= flavorlist
[flavor_id
]
1140 raise vimconn
.vimconnNotFoundException(
1141 "new_vminstance(): Failed create vApp {}: (Failed retrieve flavor information)".format(name
))
1144 vm_cpus
= flavor
['vcpus']
1145 vm_memory
= flavor
['ram']
1147 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1149 # image upload creates template name as catalog name space Template.
1150 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
) + ' Template'
1155 # client must provide at least one entry in net_list if not we report error
1156 primary_net_name
= None
1157 if net_list
is not None and len(net_list
) > 0:
1158 primary_net
= net_list
[0]
1159 if primary_net
is None:
1160 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1163 primary_net_id
= primary_net
['net_id']
1164 primary_net_name
= self
.get_network_name_by_id(primary_net_id
)
1165 network_mode
= primary_net
['use']
1167 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1169 # use: 'data', 'bridge', 'mgmt'
1170 # create vApp. Set vcpu and ram based on flavor id.
1171 vapptask
= vca
.create_vapp(self
.tenant_name
, full_name
, templateName
,
1172 self
.get_catalogbyid(image_id
, catalogs
),
1173 network_name
=primary_net_name
, # can be None if net_list None
1174 network_mode
='bridged',
1176 vm_cpus
=vm_cpus
, # can be None if flavor is None
1177 vm_memory
=vm_memory
) # can be None if flavor is None
1179 if vapptask
is None or vapptask
is False:
1180 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(full_name
))
1181 if type(vapptask
) is VappTask
:
1182 vca
.block_until_completed(vapptask
)
1184 # we should have now vapp in undeployed state.
1185 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), full_name
)
1187 raise vimconn
.vimconnUnexpectedResponse(
1188 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(full_name
))
1193 for net
in net_list
:
1194 # openmano uses network id in UUID format.
1195 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1196 interface_net_id
= net
['net_id']
1197 interface_net_name
= self
.get_network_name_by_id(interface_net_id
)
1198 interface_network_mode
= net
['use']
1200 if primary_net_name
is not None:
1201 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1203 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1204 if type(task
) is GenericTask
:
1205 vca
.block_until_completed(task
)
1206 # connect network to VM
1207 # TODO figure out mapping between openmano representation to vCloud director.
1208 # one idea use first nic as management DHCP all remaining in bridge mode
1209 task
= vapp
.connect_vms(nets
[0].name
, connection_index
=nicIndex
,
1210 connections_primary_index
=nicIndex
,
1211 ip_allocation_mode
='DHCP')
1212 if type(task
) is GenericTask
:
1213 vca
.block_until_completed(task
)
1216 # it might be a case if specific mandatory entry in dict is empty
1217 self
.logger
.debug("Key error {}".format(KeyError.message
))
1218 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1220 # deploy and power on vm
1221 task
= vapp
.poweron()
1222 if type(task
) is TaskType
:
1223 vca
.block_until_completed(task
)
1224 deploytask
= vapp
.deploy(powerOn
='True')
1225 if type(task
) is TaskType
:
1226 vca
.block_until_completed(deploytask
)
1228 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1229 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), full_name
)
1230 if vapp_uuid
is not None:
1233 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1237 ## based on current discussion
1241 # created: '2016-09-08T11:51:58'
1242 # description: simple-instance.linux1.1
1243 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1244 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1245 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1250 def get_vminstance(self
, vim_vm_uuid
=None):
1251 """Returns the VM instance information from VIM"""
1253 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1254 vca
= self
.connect()
1256 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1258 vdc
= vca
.get_vdc(self
.tenant_name
)
1260 raise vimconn
.vimconnConnectionException(
1261 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1263 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1264 if not vm_info_dict
:
1265 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1266 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1268 status_key
= vm_info_dict
['status']
1271 vm_dict
= {'created': vm_info_dict
['created'],
1272 'description': vm_info_dict
['name'],
1273 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1274 'hostId': vm_info_dict
['vmuuid'],
1276 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1278 if 'interfaces' in vm_info_dict
:
1279 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1281 vm_dict
['interfaces'] = []
1283 vm_dict
= {'created': '',
1285 'status': vcdStatusCode2manoFormat
[int(-1)],
1286 'hostId': vm_info_dict
['vmuuid'],
1287 'error_msg': "Inconsistency state",
1288 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1292 def delete_vminstance(self
, vm__vim_uuid
):
1293 """Method poweroff and remove VM instance from vcloud director network.
1296 vm__vim_uuid: VM UUID
1299 Returns the instance identifier
1302 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1303 vca
= self
.connect()
1305 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1307 vdc
= vca
.get_vdc(self
.tenant_name
)
1309 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1311 raise vimconn
.vimconnException(
1312 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1315 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1316 if vapp_name
is None:
1317 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1318 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1320 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1322 # Delete vApp and wait for status change if task executed and vApp is None.
1323 # We successfully delete vApp from vCloud
1324 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1325 # poweroff vapp / undeploy and delete
1326 power_off_task
= vapp
.poweroff()
1327 if type(power_off_task
) is GenericTask
:
1328 vca
.block_until_completed(power_off_task
)
1330 if not power_off_task
:
1331 self
.logger
.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid
))
1334 if vapp
.me
.deployed
:
1335 undeploy_task
= vapp
.undeploy()
1336 if type(undeploy_task
) is GenericTask
:
1338 while retry
<= DELETE_INSTANCE_RETRY
:
1339 result
= vca
.block_until_completed(undeploy_task
)
1347 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1348 if vapp
is not None:
1349 delete_task
= vapp
.delete()
1351 while retry
<= DELETE_INSTANCE_RETRY
:
1352 task
= vapp
.delete()
1353 if type(task
) is GenericTask
:
1354 vca
.block_until_completed(delete_task
)
1356 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1360 self
.logger
.debug(traceback
.format_exc())
1361 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1363 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1366 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1368 def refresh_vms_status(self
, vm_list
):
1369 """Get the status of the virtual machines and their interfaces/ports
1370 Params: the list of VM identifiers
1371 Returns a dictionary with:
1372 vm_id: #VIM id of this Virtual Machine
1373 status: #Mandatory. Text with one of:
1374 # DELETED (not found at vim)
1375 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1376 # OTHER (Vim reported other status not understood)
1377 # ERROR (VIM indicates an ERROR status)
1378 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1379 # CREATING (on building process), ERROR
1380 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1382 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1383 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1385 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1386 mac_address: #Text format XX:XX:XX:XX:XX:XX
1387 vim_net_id: #network id where this interface is connected
1388 vim_interface_id: #interface/port VIM id
1389 ip_address: #null, or text with IPv4, IPv6 address
1392 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1393 vca
= self
.connect()
1395 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1397 vdc
= vca
.get_vdc(self
.tenant_name
)
1399 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1402 for vmuuid
in vm_list
:
1403 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1404 if vmname
is not None:
1406 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1407 vm_info
= the_vapp
.get_vms_details()
1408 vm_status
= vm_info
[0]['status']
1410 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1411 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1412 'vim_info': yaml
.safe_dump(the_vapp
.get_vms_details()), 'interfaces': []}
1416 vm_app_networks
= the_vapp
.get_vms_network_info()
1417 for vapp_network
in vm_app_networks
:
1418 for vm_network
in vapp_network
:
1419 if vm_network
['name'] == vmname
:
1420 interface
= {"mac_address": vm_network
['mac'],
1421 "vim_net_id": self
.get_network_name_by_id(vm_network
['network_name']),
1422 "vim_interface_id": vm_network
['network_name'],
1423 'ip_address': vm_network
['ip']}
1424 # interface['vim_info'] = yaml.safe_dump(vm_network)
1425 vm_dict
["interfaces"].append(interface
)
1426 # add a vm to vm dict
1427 vms_dict
.setdefault(vmuuid
, vm_dict
)
1429 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1430 self
.logger
.debug(traceback
.format_exc())
1434 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1435 """Send and action over a VM instance from VIM
1436 Returns the vm_id if the action was successfully sent to the VIM"""
1438 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1439 if vm__vim_uuid
is None or action_dict
is None:
1440 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1442 vca
= self
.connect()
1444 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1446 vdc
= vca
.get_vdc(self
.tenant_name
)
1448 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1450 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1451 if vapp_name
is None:
1452 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1453 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1455 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1458 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1459 # TODO fix all status
1460 if "start" in action_dict
:
1461 if action_dict
["start"] == "rebuild":
1462 the_vapp
.deploy(powerOn
=True)
1464 vm_info
= the_vapp
.get_vms_details()
1465 vm_status
= vm_info
[0]['status']
1466 if vm_status
== "Suspended":
1468 elif vm_status
.status
== "Powered off":
1470 elif "pause" in action_dict
:
1473 elif "resume" in action_dict
:
1476 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1478 elif "forceOff" in action_dict
:
1480 elif "terminate" in action_dict
:
1482 # elif "createImage" in action_dict:
1483 # server.create_image()
1489 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1491 Get a console for the virtual machine
1493 vm_id: uuid of the VM
1494 console_type, can be:
1495 "novnc" (by default), "xvpvnc" for VNC types,
1496 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1497 Returns dict with the console parameters:
1498 protocol: ssh, ftp, http, https, ...
1499 server: usually ip address
1500 port: the http, ssh, ... port
1501 suffix: extra text, e.g. the http path and query string
1503 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1505 # NOT USED METHODS in current version
1507 def host_vim2gui(self
, host
, server_dict
):
1508 """Transform host dictionary from VIM format to GUI format,
1509 and append to the server_dict
1511 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1513 def get_hosts_info(self
):
1514 """Get the information of deployed hosts
1515 Returns the hosts content"""
1516 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1518 def get_hosts(self
, vim_tenant
):
1519 """Get the hosts and deployed instances
1520 Returns the hosts content"""
1521 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1523 def get_processor_rankings(self
):
1524 """Get the processor rankings in the VIM database"""
1525 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1527 def new_host(self
, host_data
):
1528 """Adds a new host to VIM"""
1529 '''Returns status code of the VIM response'''
1530 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1532 def new_external_port(self
, port_data
):
1533 """Adds a external port to VIM"""
1534 '''Returns the port identifier'''
1535 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1537 def new_external_network(self
, net_name
, net_type
):
1538 """Adds a external network to VIM (shared)"""
1539 '''Returns the network identifier'''
1540 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1542 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1543 """Connects a external port to a network"""
1544 '''Returns status code of the VIM response'''
1545 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1547 def new_vminstancefromJSON(self
, vm_data
):
1548 """Adds a VM instance to VIM"""
1549 '''Returns the instance identifier'''
1550 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1552 def get_network_name_by_id(self
, network_name
=None):
1553 """Method gets vcloud director network named based on supplied uuid.
1556 network_name: network_id
1559 The return network name.
1562 vca
= self
.connect()
1564 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1566 if network_name
is None:
1570 org_network_dict
= self
.get_org(self
.org_uuid
)['networks']
1571 for net_uuid
in org_network_dict
:
1572 if org_network_dict
[net_uuid
] == network_name
:
1575 self
.logger
.debug("Exception in get_network_name_by_id")
1576 self
.logger
.debug(traceback
.format_exc())
1580 def list_org_action(self
):
1582 Method leverages vCloud director and query for available organization for particular user
1585 vca - is active VCA connection.
1586 vdc_name - is a vdc name that will be used to query vms action
1589 The return XML respond
1592 vca
= self
.connect()
1594 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1596 url_list
= [vca
.host
, '/api/org']
1597 vm_list_rest_call
= ''.join(url_list
)
1599 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1600 response
= Http
.get(url
=vm_list_rest_call
,
1601 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1604 if response
.status_code
== requests
.codes
.ok
:
1605 return response
.content
1609 def get_org_action(self
, org_uuid
=None):
1611 Method leverages vCloud director and retrieve available object fdr organization.
1614 vca - is active VCA connection.
1615 vdc_name - is a vdc name that will be used to query vms action
1618 The return XML respond
1621 vca
= self
.connect()
1623 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1625 if org_uuid
is None:
1628 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1629 vm_list_rest_call
= ''.join(url_list
)
1631 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1632 response
= Http
.get(url
=vm_list_rest_call
,
1633 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1636 if response
.status_code
== requests
.codes
.ok
:
1637 return response
.content
1641 def get_org(self
, org_uuid
=None):
1643 Method retrieves available organization in vCloud Director
1646 org_uuid - is a organization uuid.
1649 The return dictionary with following key
1650 "network" - for network list under the org
1651 "catalogs" - for network list under the org
1652 "vdcs" - for vdc list under org
1656 vca
= self
.connect()
1658 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1660 if org_uuid
is None:
1663 content
= self
.get_org_action(org_uuid
=org_uuid
)
1668 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1669 for child
in vm_list_xmlroot
:
1670 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1671 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1672 org_dict
['vdcs'] = vdc_list
1673 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1674 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1675 org_dict
['networks'] = network_list
1676 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1677 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1678 org_dict
['catalogs'] = catalog_list
1684 def get_org_list(self
):
1686 Method retrieves available organization in vCloud Director
1689 vca - is active VCA connection.
1692 The return dictionary and key for each entry VDC UUID
1696 vca
= self
.connect()
1698 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1700 content
= self
.list_org_action()
1702 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1703 for vm_xml
in vm_list_xmlroot
:
1704 if vm_xml
.tag
.split("}")[1] == 'Org':
1705 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1706 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1712 def vms_view_action(self
, vdc_name
=None):
1713 """ Method leverages vCloud director vms query call
1716 vca - is active VCA connection.
1717 vdc_name - is a vdc name that will be used to query vms action
1720 The return XML respond
1722 vca
= self
.connect()
1723 if vdc_name
is None:
1726 url_list
= [vca
.host
, '/api/vms/query']
1727 vm_list_rest_call
= ''.join(url_list
)
1729 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1730 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1731 vca
.vcloud_session
.organization
.Link
)
1733 response
= Http
.get(url
=vm_list_rest_call
,
1734 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1737 if response
.status_code
== requests
.codes
.ok
:
1738 return response
.content
1742 def get_vapp_list(self
, vdc_name
=None):
1744 Method retrieves vApp list deployed vCloud director and returns a dictionary
1745 contains a list of all vapp deployed for queried VDC.
1746 The key for a dictionary is vApp UUID
1750 vca - is active VCA connection.
1751 vdc_name - is a vdc name that will be used to query vms action
1754 The return dictionary and key for each entry vapp UUID
1758 if vdc_name
is None:
1761 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1763 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1764 for vm_xml
in vm_list_xmlroot
:
1765 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1766 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1767 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1768 if 'vappTemplate-' in rawuuid
[0]:
1769 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1770 # vm and use raw UUID as key
1771 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1777 def get_vm_list(self
, vdc_name
=None):
1779 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1780 contains a list of all VM's deployed for queried VDC.
1781 The key for a dictionary is VM UUID
1785 vca - is active VCA connection.
1786 vdc_name - is a vdc name that will be used to query vms action
1789 The return dictionary and key for each entry vapp UUID
1793 if vdc_name
is None:
1796 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1798 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1799 for vm_xml
in vm_list_xmlroot
:
1800 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1801 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1802 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1803 if 'vm-' in rawuuid
[0]:
1804 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1805 # vm and use raw UUID as key
1806 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1812 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1814 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1815 contains a list of all VM's deployed for queried VDC.
1816 The key for a dictionary is VM UUID
1820 vca - is active VCA connection.
1821 vdc_name - is a vdc name that will be used to query vms action
1824 The return dictionary and key for each entry vapp UUID
1827 vca
= self
.connect()
1829 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1831 if vdc_name
is None:
1834 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1836 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1837 for vm_xml
in vm_list_xmlroot
:
1838 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1839 # lookup done by UUID
1841 if vapp_name
in vm_xml
.attrib
['container']:
1842 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1843 if 'vm-' in rawuuid
[0]:
1844 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1846 # lookup done by Name
1848 if vapp_name
in vm_xml
.attrib
['name']:
1849 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1850 if 'vm-' in rawuuid
[0]:
1851 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1858 def get_network_action(self
, network_uuid
=None):
1860 Method leverages vCloud director and query network based on network uuid
1863 vca - is active VCA connection.
1864 network_uuid - is a network uuid
1867 The return XML respond
1870 vca
= self
.connect()
1872 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1874 if network_uuid
is None:
1877 url_list
= [vca
.host
, '/api/network/', network_uuid
]
1878 vm_list_rest_call
= ''.join(url_list
)
1880 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1881 response
= Http
.get(url
=vm_list_rest_call
,
1882 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1885 if response
.status_code
== requests
.codes
.ok
:
1886 return response
.content
1890 def get_vcd_network(self
, network_uuid
=None):
1892 Method retrieves available network from vCloud Director
1895 network_uuid - is VCD network UUID
1897 Each element serialized as key : value pair
1899 Following keys available for access. network_configuration['Gateway'}
1903 <IsInherited>true</IsInherited>
1904 <Gateway>172.16.252.100</Gateway>
1905 <Netmask>255.255.255.0</Netmask>
1906 <Dns1>172.16.254.201</Dns1>
1907 <Dns2>172.16.254.202</Dns2>
1908 <DnsSuffix>vmwarelab.edu</DnsSuffix>
1909 <IsEnabled>true</IsEnabled>
1912 <StartAddress>172.16.252.1</StartAddress>
1913 <EndAddress>172.16.252.99</EndAddress>
1918 <FenceMode>bridged</FenceMode>
1921 The return dictionary and key for each entry vapp UUID
1924 network_configuration
= {}
1925 if network_uuid
is None:
1928 content
= self
.get_network_action(network_uuid
=network_uuid
)
1930 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1932 network_configuration
['status'] = vm_list_xmlroot
.get("status")
1933 network_configuration
['name'] = vm_list_xmlroot
.get("name")
1934 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
1936 for child
in vm_list_xmlroot
:
1937 if child
.tag
.split("}")[1] == 'IsShared':
1938 network_configuration
['isShared'] = child
.text
.strip()
1939 if child
.tag
.split("}")[1] == 'Configuration':
1940 for configuration
in child
.iter():
1941 tagKey
= configuration
.tag
.split("}")[1].strip()
1943 network_configuration
[tagKey
] = configuration
.text
.strip()
1944 return network_configuration
1948 return network_configuration
1950 def delete_network_action(self
, network_uuid
=None):
1952 Method delete given network from vCloud director
1955 network_uuid - is a network uuid that client wish to delete
1958 The return None or XML respond or false
1961 vca
= self
.connect_as_admin()
1963 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1964 if network_uuid
is None:
1967 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
1968 vm_list_rest_call
= ''.join(url_list
)
1970 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1971 response
= Http
.delete(url
=vm_list_rest_call
,
1972 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1976 if response
.status_code
== 202:
1981 def create_network(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
1983 Method create network in vCloud director
1986 network_name - is network name to be created.
1987 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1988 It optional attribute. by default if no parent network indicate the first available will be used.
1991 The return network uuid or return None
1994 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
1995 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
1996 parent_network_uuid
=parent_network_uuid
,
1999 self
.logger
.debug("Failed create network {}.".format(network_name
))
2003 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2004 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2005 if len(vcd_uuid
) == 4:
2006 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2009 self
.logger
.debug("Failed create network {}".format(network_name
))
2012 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2014 Method create network in vCloud director
2017 network_name - is network name to be created.
2018 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2019 It optional attribute. by default if no parent network indicate the first available will be used.
2022 The return network uuid or return None
2025 vca
= self
.connect_as_admin()
2027 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2028 if network_name
is None:
2031 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2032 vm_list_rest_call
= ''.join(url_list
)
2033 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2034 response
= Http
.get(url
=vm_list_rest_call
,
2035 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2039 provider_network
= None
2040 available_networks
= None
2041 add_vdc_rest_url
= None
2043 if response
.status_code
!= requests
.codes
.ok
:
2044 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2045 response
.status_code
))
2049 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2050 for child
in vm_list_xmlroot
:
2051 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2052 provider_network
= child
.attrib
.get('href')
2053 # application/vnd.vmware.admin.providervdc+xml
2054 if child
.tag
.split("}")[1] == 'Link':
2055 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2056 and child
.attrib
.get('rel') == 'add':
2057 add_vdc_rest_url
= child
.attrib
.get('href')
2059 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2060 self
.logger
.debug("Respond body {}".format(response
.content
))
2063 # find pvdc provided available network
2064 response
= Http
.get(url
=provider_network
,
2065 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2068 if response
.status_code
!= requests
.codes
.ok
:
2069 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2070 response
.status_code
))
2073 # available_networks.split("/")[-1]
2075 if parent_network_uuid
is None:
2077 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2078 for child
in vm_list_xmlroot
.iter():
2079 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2080 for networks
in child
.iter():
2081 # application/vnd.vmware.admin.network+xml
2082 if networks
.attrib
.get('href') is not None:
2083 available_networks
= networks
.attrib
.get('href')
2088 # either use client provided UUID or search for a first available
2089 # if both are not defined we return none
2090 if parent_network_uuid
is not None:
2091 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2092 add_vdc_rest_url
= ''.join(url_list
)
2094 # return response.content
2095 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2096 <Description>Openmano created</Description>
2098 <ParentNetwork href="{1:s}"/>
2099 <FenceMode>{2:s}</FenceMode>
2101 <IsShared>{3:s}</IsShared>
2102 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2104 headers
= vca
.vcloud_session
.get_vcloud_headers()
2105 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2106 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2108 # if we all ok we respond with content otherwise by default None
2109 if response
.status_code
== 201:
2110 return response
.content
2113 def get_provider_rest(self
, vca
=None):
2115 Method gets provider vdc view from vcloud director
2118 network_name - is network name to be created.
2119 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2120 It optional attribute. by default if no parent network indicate the first available will be used.
2123 The return xml content of respond or None
2126 url_list
= [vca
.host
, '/api/admin']
2127 response
= Http
.get(url
=''.join(url_list
),
2128 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2132 if response
.status_code
== requests
.codes
.ok
:
2133 return response
.content
2136 def create_vdc(self
, vdc_name
=None):
2140 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2141 if xml_content
is not None:
2143 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2144 for child
in task_resp_xmlroot
:
2145 if child
.tag
.split("}")[1] == 'Owner':
2146 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2147 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2150 self
.logger
.debug("Respond body {}".format(xml_content
))
2154 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2156 Method create vdc in vCloud director based on VDC template.
2157 it uses pre-defined template that must be named openmano
2160 vdc_name - name of a new vdc.
2163 The return xml content of respond or None
2166 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2167 vca
= self
.connect()
2169 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2170 if vdc_name
is None:
2173 url_list
= [vca
.host
, '/api/vdcTemplates']
2174 vm_list_rest_call
= ''.join(url_list
)
2175 response
= Http
.get(url
=vm_list_rest_call
,
2176 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2180 # container url to a template
2181 vdc_template_ref
= None
2183 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2184 for child
in vm_list_xmlroot
:
2185 # application/vnd.vmware.admin.providervdc+xml
2186 # we need find a template from witch we instantiate VDC
2187 if child
.tag
.split("}")[1] == 'VdcTemplate':
2188 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2189 'name') == 'openmano':
2190 vdc_template_ref
= child
.attrib
.get('href')
2192 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2193 self
.logger
.debug("Respond body {}".format(response
.content
))
2196 # if we didn't found required pre defined template we return None
2197 if vdc_template_ref
is None:
2202 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2203 vm_list_rest_call
= ''.join(url_list
)
2204 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2205 <Source href="{1:s}"></Source>
2206 <Description>opnemano</Description>
2207 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2208 headers
= vca
.vcloud_session
.get_vcloud_headers()
2209 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2210 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2212 # if we all ok we respond with content otherwise by default None
2213 if response
.status_code
>= 200 and response
.status_code
< 300:
2214 return response
.content
2217 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2218 self
.logger
.debug("Respond body {}".format(response
.content
))
2222 def create_vdc_rest(self
, vdc_name
=None):
2224 Method create network in vCloud director
2227 network_name - is network name to be created.
2228 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2229 It optional attribute. by default if no parent network indicate the first available will be used.
2232 The return network uuid or return None
2235 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2237 vca
= self
.connect_as_admin()
2239 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2240 if vdc_name
is None:
2243 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2244 vm_list_rest_call
= ''.join(url_list
)
2245 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2246 response
= Http
.get(url
=vm_list_rest_call
,
2247 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2251 provider_vdc_ref
= None
2252 add_vdc_rest_url
= None
2253 available_networks
= None
2255 if response
.status_code
!= requests
.codes
.ok
:
2256 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2257 response
.status_code
))
2261 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2262 for child
in vm_list_xmlroot
:
2263 # application/vnd.vmware.admin.providervdc+xml
2264 if child
.tag
.split("}")[1] == 'Link':
2265 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2266 and child
.attrib
.get('rel') == 'add':
2267 add_vdc_rest_url
= child
.attrib
.get('href')
2269 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2270 self
.logger
.debug("Respond body {}".format(response
.content
))
2273 response
= self
.get_provider_rest(vca
=vca
)
2275 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2276 for child
in vm_list_xmlroot
:
2277 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2278 for sub_child
in child
:
2279 provider_vdc_ref
= sub_child
.attrib
.get('href')
2281 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2282 self
.logger
.debug("Respond body {}".format(response
))
2285 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2286 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2287 <AllocationModel>ReservationPool</AllocationModel>
2288 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2289 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2290 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2291 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2292 <ProviderVdcReference
2293 name="Main Provider"
2295 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2299 headers
= vca
.vcloud_session
.get_vcloud_headers()
2300 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2301 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2304 # if we all ok we respond with content otherwise by default None
2305 if response
.status_code
== 201:
2306 return response
.content
2309 def get_vapp_details_rest(self
, vapp_uuid
=None):
2311 Method retrieve vapp detail from vCloud director
2314 vapp_uuid - is vapp identifier.
2317 The return network uuid or return None
2322 vca
= self
.connect()
2324 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2325 if vapp_uuid
is None:
2328 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2329 get_vapp_restcall
= ''.join(url_list
)
2330 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2331 response
= Http
.get(url
=get_vapp_restcall
,
2332 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2336 if response
.status_code
!= requests
.codes
.ok
:
2337 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2338 response
.status_code
))
2339 return parsed_respond
2342 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2343 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2345 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2346 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2347 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2349 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2350 if created_section
is not None:
2351 parsed_respond
['created'] = created_section
.text
2353 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2354 if network_section
is not None and 'networkName' in network_section
.attrib
:
2355 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2357 ipscopes_section
= \
2358 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2360 if ipscopes_section
is not None:
2361 for ipscope
in ipscopes_section
:
2362 for scope
in ipscope
:
2363 tag_key
= scope
.tag
.split("}")[1]
2364 if tag_key
== 'IpRanges':
2365 ip_ranges
= scope
.getchildren()
2366 for ipblock
in ip_ranges
:
2367 for block
in ipblock
:
2368 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2370 parsed_respond
[tag_key
] = scope
.text
2372 # parse children section for other attrib
2373 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2374 if children_section
is not None:
2375 parsed_respond
['name'] = children_section
.attrib
['name']
2376 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2377 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2378 parsed_respond
['status'] = children_section
.attrib
['status']
2379 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2380 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2382 for adapters
in network_adapter
:
2383 adapter_key
= adapters
.tag
.split("}")[1]
2384 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2385 parsed_respond
['primarynetwork'] = adapters
.text
2386 if adapter_key
== 'NetworkConnection':
2388 if 'network' in adapters
.attrib
:
2389 vnic
['network'] = adapters
.attrib
['network']
2390 for adapter
in adapters
:
2391 setting_key
= adapter
.tag
.split("}")[1]
2392 vnic
[setting_key
] = adapter
.text
2393 nic_list
.append(vnic
)
2395 for link
in children_section
:
2396 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
2397 if link
.attrib
['rel'] == 'screen:acquireTicket':
2398 parsed_respond
['acquireTicket'] = link
.attrib
2399 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2400 parsed_respond
['acquireMksTicket'] = link
.attrib
2402 parsed_respond
['interfaces'] = nic_list
2406 return parsed_respond
2408 def acuire_console(self
, vm_uuid
=None):
2410 vca
= self
.connect()
2412 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2416 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2417 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
2418 console_dict
= vm_dict
['acquireTicket']
2419 console_rest_call
= console_dict
['href']
2421 response
= Http
.post(url
=console_rest_call
,
2422 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2426 if response
.status_code
== requests
.codes
.ok
:
2427 return response
.content