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 TypeError, '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 org_details_dict
.has_key('vdcs'):
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 org_details_dict
.has_key('vdcs'):
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 not flavorlist
.has_key(flavor_id
):
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 not flavorlist
.has_key(flavor_id
):
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
):
705 raise vimconn
.vimconnNotImplemented("Should have implemented this")
707 def catalog_exists(self
, catalog_name
, catalogs
):
714 for catalog
in catalogs
:
715 if catalog
.name
== catalog_name
:
719 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
720 """ Create new catalog entry in vCloud director.
723 vca: vCloud director.
724 catalog_name catalog that client wish to create. Note no validation done for a name.
725 Client must make sure that provide valid string representation.
727 Return (bool) True if catalog created.
731 task
= vca
.create_catalog(catalog_name
, catalog_name
)
732 result
= vca
.block_until_completed(task
)
735 catalogs
= vca
.get_catalogs()
738 return self
.catalog_exists(catalog_name
, catalogs
)
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
745 :param catalog_name: (str): The name of the catalog to upload the media.
746 :param item_name: (str): The name of the media file in the catalog.
747 :param media_file_name: (str): The name of the local media file to upload.
748 :return: (bool) True if the media file was successfully uploaded, false otherwise.
750 os
.path
.isfile(media_file_name
)
751 statinfo
= os
.stat(media_file_name
)
754 # find a catalog entry where we upload OVF.
755 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
757 # if VCD can parse OVF we upload VMDK file
758 for catalog
in vca
.get_catalogs():
759 if catalog_name
!= catalog
.name
:
761 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
762 link
.get_rel() == 'add', catalog
.get_Link())
763 assert len(link
) == 1
765 <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>
766 """ % (escape(image_name
), escape(description
))
767 headers
= vca
.vcloud_session
.get_vcloud_headers()
768 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
769 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
770 if response
.status_code
== requests
.codes
.created
:
771 catalogItem
= XmlElementTree
.fromstring(response
.content
)
772 entity
= [child
for child
in catalogItem
if
773 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
774 href
= entity
.get('href')
776 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
777 verify
=vca
.verify
, logger
=self
.logger
)
779 if response
.status_code
== requests
.codes
.ok
:
780 media
= mediaType
.parseString(response
.content
, True)
781 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
782 media
.get_Files().get_File()[0].get_Link())[0]
783 headers
= vca
.vcloud_session
.get_vcloud_headers()
784 headers
['Content-Type'] = 'Content-Type text/xml'
785 response
= Http
.put(link
.get_href(),
786 data
=open(media_file_name
, 'rb'),
788 verify
=vca
.verify
, logger
=self
.logger
)
789 if response
.status_code
!= requests
.codes
.ok
:
791 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
795 # TODO fix this with aync block
798 self
.logger
.debug("Failed create vApp template for catalog name {} and image {}".
799 format(catalog_name
, media_file_name
))
801 # uploading VMDK file
802 # check status of OVF upload and upload remaining files.
803 response
= Http
.get(template
,
804 headers
=vca
.vcloud_session
.get_vcloud_headers(),
808 if response
.status_code
== requests
.codes
.ok
:
809 media
= mediaType
.parseString(response
.content
, True)
810 number_of_files
= len(media
.get_Files().get_File())
811 for index
in xrange(0, number_of_files
):
812 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
813 media
.get_Files().get_File()[index
].get_Link())
814 for link
in links_list
:
815 # we skip ovf since it already uploaded.
816 if 'ovf' in link
.get_href():
818 # The OVF file and VMDK must be in a same directory
819 head
, tail
= os
.path
.split(media_file_name
)
820 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
821 if not os
.path
.isfile(file_vmdk
):
823 statinfo
= os
.stat(file_vmdk
)
824 if statinfo
.st_size
== 0:
826 hrefvmdk
= link
.get_href()
829 print("Uploading file: {}".format(file_vmdk
))
831 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
833 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
835 bytes_transferred
= 0
836 f
= open(file_vmdk
, 'rb')
837 while bytes_transferred
< statinfo
.st_size
:
838 my_bytes
= f
.read(chunk_bytes
)
839 if len(my_bytes
) <= chunk_bytes
:
840 headers
= vca
.vcloud_session
.get_vcloud_headers()
841 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
842 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
843 headers
['Content-Length'] = str(len(my_bytes
))
844 response
= Http
.put(hrefvmdk
,
850 if response
.status_code
== requests
.codes
.ok
:
851 bytes_transferred
+= len(my_bytes
)
853 progress_bar
.update(bytes_transferred
)
856 'file upload failed with error: [%s] %s' % (response
.status_code
,
863 progress_bar
.finish()
866 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
867 format(catalog_name
, media_file_name
))
870 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
873 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
874 """Upload media file"""
875 # TODO add named parameters for readability
877 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
878 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
880 def validate_uuid4(self
, uuid_string
=None):
881 """ Method validate correct format of UUID.
883 Return: true if string represent valid uuid
886 val
= uuid
.UUID(uuid_string
, version
=4)
891 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
892 """ Method check catalog and return catalog ID in UUID format.
895 catalog_name: catalog name as string
896 catalogs: list of catalogs.
898 Return: catalogs uuid
901 for catalog
in catalogs
:
902 if catalog
.name
== catalog_name
:
903 catalog_id
= catalog
.get_id().split(":")
907 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
908 """ Method check catalog and return catalog name lookup done by catalog UUID.
911 catalog_name: catalog name as string
912 catalogs: list of catalogs.
914 Return: catalogs name or None
917 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
920 for catalog
in catalogs
:
921 catalog_id
= catalog
.get_id().split(":")[3]
922 if catalog_id
== catalog_uuid
:
926 def get_image_id_from_path(self
, path
=None, progress
=False):
927 """ Method upload OVF image to vCloud director.
929 Each OVF image represented as single catalog entry in vcloud director.
930 The method check for existing catalog entry. The check done by file name without file extension.
932 if given catalog name already present method will respond with existing catalog uuid otherwise
933 it will create new catalog entry and upload OVF file to newly created catalog.
935 If method can't create catalog entry or upload a file it will throw exception.
937 Method accept boolean flag progress that will output progress bar. It useful method
938 for standalone upload use case. In case to test large file upload.
941 path: - valid path to OVF file.
942 progress - boolean progress bar show progress bar.
944 Return: if image uploaded correct method will provide image catalog UUID.
948 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
951 raise vimconn
.vimconnException("Image path can't be None.")
953 if not os
.path
.isfile(path
):
954 raise vimconn
.vimconnException("Can't read file. File not found.")
956 if not os
.access(path
, os
.R_OK
):
957 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
959 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
961 dirpath
, filename
= os
.path
.split(path
)
962 flname
, file_extension
= os
.path
.splitext(path
)
963 if file_extension
!= '.ovf':
964 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
965 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
966 catalog_name
= os
.path
.splitext(filename
)[0]
967 self
.logger
.debug("File name {} Catalog Name {} file path {}".format(filename
, catalog_name
, path
))
969 catalogs
= vca
.get_catalogs()
970 if len(catalogs
) == 0:
971 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
972 result
= self
.create_vimcatalog(vca
, catalog_name
)
974 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
975 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_name
,
976 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
978 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
979 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
981 for catalog
in catalogs
:
982 # search for existing catalog if we find same name we return ID
984 if catalog
.name
== catalog_name
:
985 self
.logger
.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name
,
989 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
991 # if we didn't find existing catalog we create a new one and upload image.
992 self
.logger
.debug("Creating new catalog entry".format(catalog_name
))
993 result
= self
.create_vimcatalog(vca
, catalog_name
)
995 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_name
))
997 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_name
,
998 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1000 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1002 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1004 def get_vappid(self
, vdc
=None, vapp_name
=None):
1005 """ Method takes vdc object and vApp name and returns vapp uuid or None
1008 vca: Connector to VCA
1009 vdc: The VDC object.
1010 vapp_name: is application vappp name identifier
1013 The return vApp name otherwise None
1015 if vdc
is None or vapp_name
is None:
1017 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1019 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1020 vdc
.ResourceEntities
.ResourceEntity
)
1022 return refs
[0].href
.split("vapp")[1][1:]
1023 except Exception as e
:
1024 self
.logger
.exception(e
)
1028 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1029 """ Method Method returns True or False if vapp deployed in vCloud director
1032 vca: Connector to VCA
1033 vdc: The VDC object.
1034 vappid: vappid is application identifier
1037 The return True if vApp deployed
1040 refs
= filter(lambda ref
:
1041 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1042 vdc
.ResourceEntities
.ResourceEntity
)
1044 vappid
= ref
.href
.split("vapp")[1][1:]
1045 # find vapp with respected vapp uuid
1046 if vappid
== vapp_uuid
:
1048 except Exception as e
:
1049 self
.logger
.exception(e
)
1053 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1054 """Method returns vApp name from vCD and lookup done by vapp_id.
1057 vca: Connector to VCA
1058 vdc: The VDC object.
1059 vapp_uuid: vappid is application identifier
1062 The return vApp name otherwise None
1066 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1067 vdc
.ResourceEntities
.ResourceEntity
)
1069 # we care only about UUID the rest doesn't matter
1070 vappid
= ref
.href
.split("vapp")[1][1:]
1071 if vappid
== vapp_uuid
:
1072 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1074 tree
= XmlElementTree
.fromstring(response
.content
)
1075 return tree
.attrib
['name']
1076 except Exception as e
:
1077 self
.logger
.exception(e
)
1081 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1083 """Adds a VM instance to VIM
1085 start: indicates if VM must start or boot in pause mode. Ignored
1086 image_id,flavor_id: image and flavor uuid
1087 net_list: list of interfaces, each one is a dictionary with:
1089 net_id: network uuid to connect
1090 vpci: virtual vcpi to assign
1091 model: interface model, virtio, e2000, ...
1093 use: 'data', 'bridge', 'mgmt'
1094 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1095 vim_id: filled/added by this function
1096 cloud_config: can be a text script to be passed directly to cloud-init,
1097 or an object to inject users and ssh keys with format:
1098 key-pairs: [] list of keys to install to the default user
1099 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1100 #TODO ip, security groups
1101 Returns >=0, the instance identifier
1105 self
.logger
.info("Creating new instance for entry".format(name
))
1106 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
1107 format(description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1108 vca
= self
.connect()
1110 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1112 # if vm already deployed we return existing uuid
1113 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), name
)
1114 if vapp_uuid
is not None:
1117 # we check for presence of VDC, Catalog entry and Flavor.
1118 vdc
= vca
.get_vdc(self
.tenant_name
)
1120 raise vimconn
.vimconnNotFoundException(
1121 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1122 catalogs
= vca
.get_catalogs()
1123 if catalogs
is None:
1124 raise vimconn
.vimconnNotFoundException(
1125 "new_vminstance(): Failed create vApp {}: Failed create vApp {}: "
1126 "(Failed retrieve catalog information)".format(name
))
1130 if flavor_id
is not None:
1131 flavor
= flavorlist
[flavor_id
]
1133 raise vimconn
.vimconnNotFoundException(
1134 "new_vminstance(): Failed create vApp {}: (Failed retrieve flavor information)".format(name
))
1137 vm_cpus
= flavor
['vcpus']
1138 vm_memory
= flavor
['ram']
1140 raise vimconn
.vimconnException("Corrupted flavor. {}".format(flavor_id
))
1142 # image upload creates template name as catalog name space Template.
1143 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
) + ' Template'
1148 # client must provide at least one entry in net_list if not we report error
1150 primary_net_name
= None
1151 if net_list
is not None and len(net_list
) > 0:
1152 primary_net
= net_list
[0]
1153 if primary_net
is None:
1154 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1158 primary_net_id
= primary_net
['net_id']
1159 primary_net_name
= self
.get_network_name_by_id(primary_net_id
)
1160 network_mode
= primary_net
['use']
1162 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1164 # use: 'data', 'bridge', 'mgmt'
1165 # create vApp. Set vcpu and ram based on flavor id.
1166 vapptask
= vca
.create_vapp(self
.tenant_name
, name
, templateName
,
1167 self
.get_catalogbyid(image_id
, catalogs
),
1168 network_name
=primary_net_name
, # can be None if net_list None
1169 network_mode
='bridged',
1171 vm_cpus
=vm_cpus
, # can be None if flavor is None
1172 vm_memory
=vm_memory
) # can be None if flavor is None
1174 if vapptask
is None or vapptask
is False:
1175 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(name
))
1176 if type(vapptask
) is VappTask
:
1177 vca
.block_until_completed(vapptask
)
1179 # we should have now vapp in undeployed state.
1180 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), name
)
1182 raise vimconn
.vimconnUnexpectedResponse(
1183 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(name
))
1188 for net
in net_list
:
1189 # openmano uses network id in UUID format.
1190 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1191 interface_net_id
= net
['net_id']
1192 interface_net_name
= self
.get_network_name_by_id(interface_net_id
)
1193 interface_network_mode
= net
['use']
1195 if primary_net_name
is not None:
1196 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1198 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1199 if type(task
) is GenericTask
:
1200 vca
.block_until_completed(task
)
1201 # connect network to VM
1202 # TODO figure out mapping between openmano representation to vCloud director.
1203 # one idea use first nic as management DHCP all remaining in bridge mode
1204 task
= vapp
.connect_vms(nets
[0].name
, connection_index
=nicIndex
,
1205 connections_primary_index
=nicIndex
,
1206 ip_allocation_mode
='DHCP')
1207 if type(task
) is GenericTask
:
1208 vca
.block_until_completed(task
)
1211 # it might be a case if specific mandatory entry in dict is empty
1212 self
.logger
.debug("Key error {}".format(KeyError.message
))
1213 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1215 # deploy and power on vm
1216 task
= vapp
.poweron()
1217 if type(task
) is TaskType
:
1218 vca
.block_until_completed(task
)
1219 deploytask
= vapp
.deploy(powerOn
='True')
1220 if type(task
) is TaskType
:
1221 vca
.block_until_completed(deploytask
)
1223 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1224 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), name
)
1225 if vapp_uuid
is not None:
1228 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1232 ## based on current discussion
1236 # created: '2016-09-08T11:51:58'
1237 # description: simple-instance.linux1.1
1238 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1239 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1240 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1245 def get_vminstance(self
, vim_vm_uuid
=None):
1246 '''Returns the VM instance information from VIM'''
1248 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1249 vca
= self
.connect()
1251 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1253 vdc
= vca
.get_vdc(self
.tenant_name
)
1255 raise vimconn
.vimconnConnectionException(
1256 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1258 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1259 if not vm_info_dict
:
1260 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1261 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1263 status_key
= vm_info_dict
['status']
1266 vm_dict
= {'created': vm_info_dict
['created'],
1267 'description': vm_info_dict
['name'],
1268 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1269 'hostId': vm_info_dict
['vmuuid'],
1271 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1273 if vm_info_dict
.has_key('interfaces'):
1274 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1276 vm_dict
['interfaces'] = []
1278 vm_dict
= {'created': '',
1280 'status': vcdStatusCode2manoFormat
[int(-1)],
1281 'hostId': vm_info_dict
['vmuuid'],
1282 'error_msg': "Inconsistency state",
1283 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1287 def delete_vminstance(self
, vm__vim_uuid
):
1288 """Method poweroff and remove VM instance from vcloud director network.
1291 vm__vim_uuid: VM UUID
1294 Returns the instance identifier
1297 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1298 vca
= self
.connect()
1300 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1302 vdc
= vca
.get_vdc(self
.tenant_name
)
1304 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1306 raise vimconn
.vimconnException(
1307 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1310 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1311 if vapp_name
is None:
1312 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1313 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1315 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1317 # Delete vApp and wait for status change if task executed and vApp is None.
1318 # We successfully delete vApp from vCloud
1319 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1320 # poweroff vapp / undeploy and delete
1321 power_off_task
= vapp
.poweroff()
1322 if type(power_off_task
) is GenericTask
:
1323 vca
.block_until_completed(power_off_task
)
1325 if not power_off_task
:
1326 self
.logger
.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid
))
1329 if vapp
.me
.deployed
:
1330 undeploy_task
= vapp
.undeploy()
1331 if type(undeploy_task
) is GenericTask
:
1333 while retry
<= DELETE_INSTANCE_RETRY
:
1334 result
= vca
.block_until_completed(undeploy_task
)
1342 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1343 if vapp
is not None:
1344 delete_task
= vapp
.delete()
1346 while retry
<= DELETE_INSTANCE_RETRY
:
1347 task
= vapp
.delete()
1348 if type(task
) is GenericTask
:
1349 vca
.block_until_completed(delete_task
)
1351 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1355 self
.logger
.debug(traceback
.format_exc())
1356 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1358 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1361 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1363 def refresh_vms_status(self
, vm_list
):
1364 """Get the status of the virtual machines and their interfaces/ports
1365 Params: the list of VM identifiers
1366 Returns a dictionary with:
1367 vm_id: #VIM id of this Virtual Machine
1368 status: #Mandatory. Text with one of:
1369 # DELETED (not found at vim)
1370 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1371 # OTHER (Vim reported other status not understood)
1372 # ERROR (VIM indicates an ERROR status)
1373 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1374 # CREATING (on building process), ERROR
1375 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1377 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1378 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1380 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1381 mac_address: #Text format XX:XX:XX:XX:XX:XX
1382 vim_net_id: #network id where this interface is connected
1383 vim_interface_id: #interface/port VIM id
1384 ip_address: #null, or text with IPv4, IPv6 address
1387 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1388 vca
= self
.connect()
1390 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1392 vdc
= vca
.get_vdc(self
.tenant_name
)
1394 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1397 for vmuuid
in vm_list
:
1398 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1399 if vmname
is not None:
1401 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1402 vm_info
= the_vapp
.get_vms_details()
1403 vm_status
= vm_info
[0]['status']
1405 vm_dict
= {'status': None, 'error_msg': None, 'vim_info': None, 'interfaces': []}
1406 vm_dict
['status'] = vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()]
1407 vm_dict
['error_msg'] = vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()]
1408 vm_dict
['vim_info'] = yaml
.safe_dump(the_vapp
.get_vms_details())
1412 vm_app_networks
= the_vapp
.get_vms_network_info()
1413 for vapp_network
in vm_app_networks
:
1414 for vm_network
in vapp_network
:
1415 if vm_network
['name'] == vmname
:
1417 # interface['vim_info'] = yaml.safe_dump(vm_network)
1418 interface
["mac_address"] = vm_network
['mac']
1419 interface
["vim_net_id"] = self
.get_network_name_by_id(vm_network
['network_name'])
1420 interface
["vim_interface_id"] = vm_network
['network_name']
1421 interface
['ip_address'] = vm_network
['ip']
1422 vm_dict
["interfaces"].append(interface
)
1423 # add a vm to vm dict
1424 vms_dict
.setdefault(vmuuid
, vm_dict
)
1426 self
.logger
.debug("Error in respond {}".format(KeyError.message
))
1427 self
.logger
.debug(traceback
.format_exc())
1431 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1432 """Send and action over a VM instance from VIM
1433 Returns the vm_id if the action was successfully sent to the VIM"""
1435 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1436 if vm__vim_uuid
is None or action_dict
is None:
1437 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1439 vca
= self
.connect()
1441 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1443 vdc
= vca
.get_vdc(self
.tenant_name
)
1445 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1447 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1448 if vapp_name
is None:
1449 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1450 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1452 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1455 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1456 # TODO fix all status
1457 if "start" in action_dict
:
1458 if action_dict
["start"] == "rebuild":
1459 the_vapp
.deploy(powerOn
=True)
1461 vm_info
= the_vapp
.get_vms_details()
1462 vm_status
= vm_info
[0]['status']
1463 if vm_status
== "Suspended":
1465 elif vm_status
.status
== "Powered off":
1467 elif "pause" in action_dict
:
1470 elif "resume" in action_dict
:
1473 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1475 elif "forceOff" in action_dict
:
1477 elif "terminate" in action_dict
:
1479 # elif "createImage" in action_dict:
1480 # server.create_image()
1486 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1488 Get a console for the virtual machine
1490 vm_id: uuid of the VM
1491 console_type, can be:
1492 "novnc" (by default), "xvpvnc" for VNC types,
1493 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1494 Returns dict with the console parameters:
1495 protocol: ssh, ftp, http, https, ...
1496 server: usually ip address
1497 port: the http, ssh, ... port
1498 suffix: extra text, e.g. the http path and query string
1500 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1502 # NOT USED METHODS in current version
1504 def host_vim2gui(self
, host
, server_dict
):
1505 '''Transform host dictionary from VIM format to GUI format,
1506 and append to the server_dict
1508 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1510 def get_hosts_info(self
):
1511 '''Get the information of deployed hosts
1512 Returns the hosts content'''
1513 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1515 def get_hosts(self
, vim_tenant
):
1516 '''Get the hosts and deployed instances
1517 Returns the hosts content'''
1518 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1520 def get_processor_rankings(self
):
1521 '''Get the processor rankings in the VIM database'''
1522 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1524 def new_host(self
, host_data
):
1525 '''Adds a new host to VIM'''
1526 '''Returns status code of the VIM response'''
1527 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1529 def new_external_port(self
, port_data
):
1530 '''Adds a external port to VIM'''
1531 '''Returns the port identifier'''
1532 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1534 def new_external_network(self
, net_name
, net_type
):
1535 '''Adds a external network to VIM (shared)'''
1536 '''Returns the network identifier'''
1537 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1539 def connect_port_network(self
, port_id
, network_id
, admin
=False):
1540 '''Connects a external port to a network'''
1541 '''Returns status code of the VIM response'''
1542 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1544 def new_vminstancefromJSON(self
, vm_data
):
1545 '''Adds a VM instance to VIM'''
1546 '''Returns the instance identifier'''
1547 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1549 def get_network_name_by_id(self
, network_name
=None):
1550 """Method gets vcloud director network named based on supplied uuid.
1553 network_name: network_id
1556 The return network name.
1559 vca
= self
.connect()
1561 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1563 if network_name
is None:
1567 org_network_dict
= self
.get_org(self
.org_uuid
)['networks']
1568 for net_uuid
in org_network_dict
:
1569 if org_network_dict
[net_uuid
] == network_name
:
1572 self
.logger
.debug("Exception in get_network_name_by_id")
1573 self
.logger
.debug(traceback
.format_exc())
1577 def list_org_action(self
):
1579 Method leverages vCloud director and query for available organization for particular user
1582 vca - is active VCA connection.
1583 vdc_name - is a vdc name that will be used to query vms action
1586 The return XML respond
1589 vca
= self
.connect()
1591 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1593 url_list
= [vca
.host
, '/api/org']
1594 vm_list_rest_call
= ''.join(url_list
)
1596 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1597 response
= Http
.get(url
=vm_list_rest_call
,
1598 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1601 if response
.status_code
== requests
.codes
.ok
:
1602 return response
.content
1606 def get_org_action(self
, org_uuid
=None):
1608 Method leverages vCloud director and retrieve available object fdr organization.
1611 vca - is active VCA connection.
1612 vdc_name - is a vdc name that will be used to query vms action
1615 The return XML respond
1618 vca
= self
.connect()
1620 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1622 if org_uuid
is None:
1625 url_list
= [vca
.host
, '/api/org/', org_uuid
]
1626 vm_list_rest_call
= ''.join(url_list
)
1628 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1629 response
= Http
.get(url
=vm_list_rest_call
,
1630 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1633 if response
.status_code
== requests
.codes
.ok
:
1634 return response
.content
1638 def get_org(self
, org_uuid
=None):
1640 Method retrieves available organization in vCloud Director
1643 org_uuid - is a organization uuid.
1646 The return dictionary with following key
1647 "network" - for network list under the org
1648 "catalogs" - for network list under the org
1649 "vdcs" - for vdc list under org
1653 vca
= self
.connect()
1655 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1657 if org_uuid
is None:
1660 content
= self
.get_org_action(org_uuid
=org_uuid
)
1665 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1666 for child
in vm_list_xmlroot
:
1667 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1668 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1669 org_dict
['vdcs'] = vdc_list
1670 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1671 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1672 org_dict
['networks'] = network_list
1673 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1674 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
1675 org_dict
['catalogs'] = catalog_list
1681 def get_org_list(self
):
1683 Method retrieves available organization in vCloud Director
1686 vca - is active VCA connection.
1689 The return dictionary and key for each entry VDC UUID
1693 vca
= self
.connect()
1695 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1697 content
= self
.list_org_action()
1699 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1700 for vm_xml
in vm_list_xmlroot
:
1701 if vm_xml
.tag
.split("}")[1] == 'Org':
1702 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1703 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
1709 def vms_view_action(self
, vdc_name
=None):
1710 """ Method leverages vCloud director vms query call
1713 vca - is active VCA connection.
1714 vdc_name - is a vdc name that will be used to query vms action
1717 The return XML respond
1719 vca
= self
.connect()
1720 if vdc_name
is None:
1723 url_list
= [vca
.host
, '/api/vms/query']
1724 vm_list_rest_call
= ''.join(url_list
)
1726 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1727 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
1728 vca
.vcloud_session
.organization
.Link
)
1730 response
= Http
.get(url
=vm_list_rest_call
,
1731 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1734 if response
.status_code
== requests
.codes
.ok
:
1735 return response
.content
1739 def get_vapp_list(self
, vdc_name
=None):
1741 Method retrieves vApp list deployed vCloud director and returns a dictionary
1742 contains a list of all vapp deployed for queried VDC.
1743 The key for a dictionary is vApp UUID
1747 vca - is active VCA connection.
1748 vdc_name - is a vdc name that will be used to query vms action
1751 The return dictionary and key for each entry vapp UUID
1755 if vdc_name
is None:
1758 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1760 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1761 for vm_xml
in vm_list_xmlroot
:
1762 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1763 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
1764 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
1765 if 'vappTemplate-' in rawuuid
[0]:
1766 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1767 # vm and use raw UUID as key
1768 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
1774 def get_vm_list(self
, vdc_name
=None):
1776 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1777 contains a list of all VM's deployed for queried VDC.
1778 The key for a dictionary is VM UUID
1782 vca - is active VCA connection.
1783 vdc_name - is a vdc name that will be used to query vms action
1786 The return dictionary and key for each entry vapp UUID
1790 if vdc_name
is None:
1793 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1795 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1796 for vm_xml
in vm_list_xmlroot
:
1797 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
1798 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
1799 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1800 if 'vm-' in rawuuid
[0]:
1801 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1802 # vm and use raw UUID as key
1803 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1809 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
1811 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
1812 contains a list of all VM's deployed for queried VDC.
1813 The key for a dictionary is VM UUID
1817 vca - is active VCA connection.
1818 vdc_name - is a vdc name that will be used to query vms action
1821 The return dictionary and key for each entry vapp UUID
1824 vca
= self
.connect()
1826 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1828 if vdc_name
is None:
1831 content
= self
.vms_view_action(vdc_name
=vdc_name
)
1833 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1834 for vm_xml
in vm_list_xmlroot
:
1835 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
1836 # lookup done by UUID
1838 if vapp_name
in vm_xml
.attrib
['container']:
1839 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1840 if 'vm-' in rawuuid
[0]:
1841 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1843 # lookup done by Name
1845 if vapp_name
in vm_xml
.attrib
['name']:
1846 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
1847 if 'vm-' in rawuuid
[0]:
1848 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
1855 def get_network_action(self
, network_uuid
=None):
1857 Method leverages vCloud director and query network based on network uuid
1860 vca - is active VCA connection.
1861 network_uuid - is a network uuid
1864 The return XML respond
1867 vca
= self
.connect()
1869 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1871 if network_uuid
is None:
1874 url_list
= [vca
.host
, '/api/network/', network_uuid
]
1875 vm_list_rest_call
= ''.join(url_list
)
1877 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1878 response
= Http
.get(url
=vm_list_rest_call
,
1879 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1882 if response
.status_code
== requests
.codes
.ok
:
1883 return response
.content
1887 def get_vcd_network(self
, network_uuid
=None):
1889 Method retrieves available network from vCloud Director
1892 network_uuid - is VCD network UUID
1894 Each element serialized as key : value pair
1896 Following keys available for access. network_configuration['Gateway'}
1900 <IsInherited>true</IsInherited>
1901 <Gateway>172.16.252.100</Gateway>
1902 <Netmask>255.255.255.0</Netmask>
1903 <Dns1>172.16.254.201</Dns1>
1904 <Dns2>172.16.254.202</Dns2>
1905 <DnsSuffix>vmwarelab.edu</DnsSuffix>
1906 <IsEnabled>true</IsEnabled>
1909 <StartAddress>172.16.252.1</StartAddress>
1910 <EndAddress>172.16.252.99</EndAddress>
1915 <FenceMode>bridged</FenceMode>
1918 The return dictionary and key for each entry vapp UUID
1921 network_configuration
= {}
1922 if network_uuid
is None:
1925 content
= self
.get_network_action(network_uuid
=network_uuid
)
1927 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
1929 network_configuration
['status'] = vm_list_xmlroot
.get("status")
1930 network_configuration
['name'] = vm_list_xmlroot
.get("name")
1931 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
1933 for child
in vm_list_xmlroot
:
1934 if child
.tag
.split("}")[1] == 'IsShared':
1935 network_configuration
['isShared'] = child
.text
.strip()
1936 if child
.tag
.split("}")[1] == 'Configuration':
1937 for configuration
in child
.iter():
1938 tagKey
= configuration
.tag
.split("}")[1].strip()
1940 network_configuration
[tagKey
] = configuration
.text
.strip()
1941 return network_configuration
1945 return network_configuration
1947 def delete_network_action(self
, network_uuid
=None):
1949 Method delete given network from vCloud director
1952 network_uuid - is a network uuid that client wish to delete
1955 The return None or XML respond or false
1958 vca
= self
.connect_as_admin()
1960 raise vimconn
.vimconnConnectionException("self.connect() is failed")
1961 if network_uuid
is None:
1964 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
1965 vm_list_rest_call
= ''.join(url_list
)
1967 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
1968 response
= Http
.delete(url
=vm_list_rest_call
,
1969 headers
=vca
.vcloud_session
.get_vcloud_headers(),
1973 if response
.status_code
== 202:
1978 def create_network(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 content
= self
.create_network_rest(network_name
=network_name
,
1992 parent_network_uuid
=parent_network_uuid
,
1995 self
.logger
.debug("Failed create network {}.".format(network_name
))
1999 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2000 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2001 if len(vcd_uuid
) == 4:
2002 self
.logger
.info("Create new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2005 self
.logger
.debug("Failed create network {}".format(network_name
))
2008 def create_network_rest(self
, network_name
=None, parent_network_uuid
=None, isshared
='true'):
2010 Method create network in vCloud director
2013 network_name - is network name to be created.
2014 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2015 It optional attribute. by default if no parent network indicate the first available will be used.
2018 The return network uuid or return None
2021 vca
= self
.connect_as_admin()
2023 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2024 if network_name
is None:
2027 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2028 vm_list_rest_call
= ''.join(url_list
)
2029 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2030 response
= Http
.get(url
=vm_list_rest_call
,
2031 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2035 provider_network
= None
2036 available_networks
= None
2037 add_vdc_rest_url
= None
2039 if response
.status_code
!= requests
.codes
.ok
:
2040 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2041 response
.status_code
))
2045 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2046 for child
in vm_list_xmlroot
:
2047 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2048 provider_network
= child
.attrib
.get('href')
2049 # application/vnd.vmware.admin.providervdc+xml
2050 if child
.tag
.split("}")[1] == 'Link':
2051 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2052 and child
.attrib
.get('rel') == 'add':
2053 add_vdc_rest_url
= child
.attrib
.get('href')
2055 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2056 self
.logger
.debug("Respond body {}".format(response
.content
))
2059 # find pvdc provided available network
2060 response
= Http
.get(url
=provider_network
,
2061 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2064 if response
.status_code
!= requests
.codes
.ok
:
2065 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2066 response
.status_code
))
2069 # available_networks.split("/")[-1]
2071 if parent_network_uuid
is None:
2073 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2074 for child
in vm_list_xmlroot
.iter():
2075 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2076 for networks
in child
.iter():
2077 # application/vnd.vmware.admin.network+xml
2078 if networks
.attrib
.get('href') is not None:
2079 available_networks
= networks
.attrib
.get('href')
2084 # either use client provided UUID or search for a first available
2085 # if both are not defined we return none
2086 if parent_network_uuid
is not None:
2087 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2088 add_vdc_rest_url
= ''.join(url_list
)
2090 # return response.content
2091 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2092 <Description>Openmano created</Description>
2094 <ParentNetwork href="{1:s}"/>
2095 <FenceMode>{2:s}</FenceMode>
2097 <IsShared>{3:s}</IsShared>
2098 </OrgVdcNetwork> """.format(escape(network_name
), available_networks
, "bridged", isshared
)
2100 headers
= vca
.vcloud_session
.get_vcloud_headers()
2101 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2102 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=vca
.logger
)
2104 # if we all ok we respond with content otherwise by default None
2105 if response
.status_code
== 201:
2106 return response
.content
2109 def get_provider_rest(self
, vca
=None):
2111 Method gets provider vdc view from vcloud director
2114 network_name - is network name to be created.
2115 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2116 It optional attribute. by default if no parent network indicate the first available will be used.
2119 The return xml content of respond or None
2122 url_list
= [vca
.host
, '/api/admin']
2123 response
= Http
.get(url
=''.join(url_list
),
2124 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2128 if response
.status_code
== requests
.codes
.ok
:
2129 return response
.content
2132 def create_vdc(self
, vdc_name
=None):
2136 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2137 if xml_content
is not None:
2139 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2140 for child
in task_resp_xmlroot
:
2141 if child
.tag
.split("}")[1] == 'Owner':
2142 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2143 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2146 self
.logger
.debug("Respond body {}".format(xml_content
))
2150 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2152 Method create vdc in vCloud director based on VDC template.
2153 it uses pre-defined template that must be named openmano
2156 vdc_name - name of a new vdc.
2159 The return xml content of respond or None
2162 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2163 vca
= self
.connect()
2165 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2166 if vdc_name
is None:
2169 url_list
= [vca
.host
, '/api/vdcTemplates']
2170 vm_list_rest_call
= ''.join(url_list
)
2171 response
= Http
.get(url
=vm_list_rest_call
,
2172 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2176 # container url to a template
2177 vdc_template_ref
= None
2179 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2180 for child
in vm_list_xmlroot
:
2181 # application/vnd.vmware.admin.providervdc+xml
2182 # we need find a template from witch we instantiate VDC
2183 if child
.tag
.split("}")[1] == 'VdcTemplate':
2184 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2185 'name') == 'openmano':
2186 vdc_template_ref
= child
.attrib
.get('href')
2188 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2189 self
.logger
.debug("Respond body {}".format(response
.content
))
2192 # if we didn't found required pre defined template we return None
2193 if vdc_template_ref
is None:
2198 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2199 vm_list_rest_call
= ''.join(url_list
)
2200 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2201 <Source href="{1:s}"></Source>
2202 <Description>opnemano</Description>
2203 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2204 headers
= vca
.vcloud_session
.get_vcloud_headers()
2205 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2206 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2208 # if we all ok we respond with content otherwise by default None
2209 if response
.status_code
>= 200 and response
.status_code
< 300:
2210 return response
.content
2213 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2214 self
.logger
.debug("Respond body {}".format(response
.content
))
2218 def create_vdc_rest(self
, vdc_name
=None):
2220 Method create network in vCloud director
2223 network_name - is network name to be created.
2224 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2225 It optional attribute. by default if no parent network indicate the first available will be used.
2228 The return network uuid or return None
2231 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2233 vca
= self
.connect_as_admin()
2235 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2236 if vdc_name
is None:
2239 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2240 vm_list_rest_call
= ''.join(url_list
)
2241 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2242 response
= Http
.get(url
=vm_list_rest_call
,
2243 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2247 provider_vdc_ref
= None
2248 add_vdc_rest_url
= None
2249 available_networks
= None
2251 if response
.status_code
!= requests
.codes
.ok
:
2252 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2253 response
.status_code
))
2257 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2258 for child
in vm_list_xmlroot
:
2259 # application/vnd.vmware.admin.providervdc+xml
2260 if child
.tag
.split("}")[1] == 'Link':
2261 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2262 and child
.attrib
.get('rel') == 'add':
2263 add_vdc_rest_url
= child
.attrib
.get('href')
2265 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2266 self
.logger
.debug("Respond body {}".format(response
.content
))
2269 response
= self
.get_provider_rest(vca
=vca
)
2271 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2272 for child
in vm_list_xmlroot
:
2273 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2274 for sub_child
in child
:
2275 provider_vdc_ref
= sub_child
.attrib
.get('href')
2277 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2278 self
.logger
.debug("Respond body {}".format(response
))
2281 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2282 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2283 <AllocationModel>ReservationPool</AllocationModel>
2284 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2285 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2286 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2287 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2288 <ProviderVdcReference
2289 name="Main Provider"
2291 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2295 headers
= vca
.vcloud_session
.get_vcloud_headers()
2296 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2297 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2300 # if we all ok we respond with content otherwise by default None
2301 if response
.status_code
== 201:
2302 return response
.content
2305 def get_vapp_details_rest(self
, vapp_uuid
=None):
2307 Method retrieve vapp detail from vCloud director
2310 vapp_uuid - is vapp identifier.
2313 The return network uuid or return None
2318 vca
= self
.connect()
2320 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2321 if vapp_uuid
is None:
2324 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2325 get_vapp_restcall
= ''.join(url_list
)
2326 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2327 response
= Http
.get(url
=get_vapp_restcall
,
2328 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2332 if response
.status_code
!= requests
.codes
.ok
:
2333 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2334 response
.status_code
))
2335 return parsed_respond
2338 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
2339 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
2341 namespaces_ovf
= {'ovf': 'http://schemas.dmtf.org/ovf/envelope/1'}
2342 namespace_vmm
= {'vmw': 'http://www.vmware.com/schema/ovf'}
2343 namespace_vm
= {'vm': 'http://www.vmware.com/vcloud/v1.5'}
2345 created_section
= xmlroot_respond
.find('vm:DateCreated', namespace_vm
)
2346 if created_section
is not None:
2347 parsed_respond
['created'] = created_section
.text
2349 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespace_vm
)
2350 if network_section
is not None and network_section
.attrib
.has_key('networkName'):
2351 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
2353 ipscopes_section
= \
2354 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2356 if ipscopes_section
is not None:
2357 for ipscope
in ipscopes_section
:
2358 for scope
in ipscope
:
2359 tag_key
= scope
.tag
.split("}")[1]
2360 if tag_key
== 'IpRanges':
2361 ip_ranges
= scope
.getchildren()
2362 for ipblock
in ip_ranges
:
2363 for block
in ipblock
:
2364 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
2366 parsed_respond
[tag_key
] = scope
.text
2368 # parse children section for other attrib
2369 children_section
= xmlroot_respond
.find('vm:Children/', namespace_vm
)
2370 if children_section
is not None:
2371 parsed_respond
['name'] = children_section
.attrib
['name']
2372 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled']
2373 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
2374 parsed_respond
['status'] = children_section
.attrib
['status']
2375 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
2376 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespace_vm
)
2378 for adapters
in network_adapter
:
2379 adapter_key
= adapters
.tag
.split("}")[1]
2380 if adapter_key
== 'PrimaryNetworkConnectionIndex':
2381 parsed_respond
['primarynetwork'] = adapters
.text
2382 if adapter_key
== 'NetworkConnection':
2384 if adapters
.attrib
.has_key('network'):
2385 vnic
['network'] = adapters
.attrib
['network']
2386 for adapter
in adapters
:
2387 setting_key
= adapter
.tag
.split("}")[1]
2388 vnic
[setting_key
] = adapter
.text
2389 nic_list
.append(vnic
)
2391 for link
in children_section
:
2392 if link
.tag
.split("}")[1] == 'Link' and link
.attrib
.has_key('rel'):
2393 if link
.attrib
['rel'] == 'screen:acquireTicket':
2394 parsed_respond
['acquireTicket'] = link
.attrib
2395 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
2396 parsed_respond
['acquireMksTicket'] = link
.attrib
2398 parsed_respond
['interfaces'] = nic_list
2402 return parsed_respond
2404 def acuireConsole(self
, vm_uuid
=None):
2406 vca
= self
.connect()
2408 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2412 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2413 vm_dict
= self
.get_vapp_details_rest(self
, vm_uuid
=vm_uuid
)
2414 console_dict
= vm_dict
['acquireTicket']
2415 console_rest_call
= console_dict
['href']
2417 response
= Http
.post(url
=console_rest_call
,
2418 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2422 if response
.status_code
!= requests
.codes
.ok
:
2425 return response
.content