10111c1fb6582f588120bb47b217f91e68370054
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
38 from pyVmomi
import vim
, vmodl
39 from pyVim
.connect
import SmartConnect
, Disconnect
41 from xml
.etree
import ElementTree
as XmlElementTree
42 from lxml
import etree
as lxmlElementTree
45 from pyvcloud
import Http
46 from pyvcloud
.vcloudair
import VCA
47 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
import sessionType
, organizationType
, \
48 vAppType
, organizationListType
, vdcType
, catalogType
, queryRecordViewType
, \
49 networkType
, vcloudType
, taskType
, diskType
, vmsType
, vdcTemplateListType
, mediaType
50 from xml
.sax
.saxutils
import escape
52 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TaskType
53 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.taskType
import TaskType
as GenericTask
54 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.vcloud
.vAppType
import TaskType
as VappTask
55 from pyvcloud
.schema
.vcd
.v1_5
.schemas
.admin
.vCloudEntities
import TasksInProgressType
68 # global variable for vcd connector type
69 STANDALONE
= 'standalone'
71 # key for flavor dicts
72 FLAVOR_RAM_KEY
= 'ram'
73 FLAVOR_VCPUS_KEY
= 'vcpus'
74 FLAVOR_DISK_KEY
= 'disk'
75 DEFAULT_IP_PROFILE
= {'dhcp_count':50,
79 # global variable for wait time
85 __author__
= "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
86 __date__
= "$12-Jan-2017 11:09:29$"
89 # -1: "Could not be created",
95 # 5: "Waiting for user input",
97 # 7: "Unrecognized state",
99 # 9: "Inconsistent state",
100 # 10: "Children do not all have the same status",
101 # 11: "Upload initiated, OVF descriptor pending",
102 # 12: "Upload initiated, copying contents",
103 # 13: "Upload initiated , disk contents pending",
104 # 14: "Upload has been quarantined",
105 # 15: "Upload quarantine period has expired"
107 # mapping vCD status to MANO
108 vcdStatusCode2manoFormat
= {4: 'ACTIVE',
117 netStatus2manoFormat
= {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
118 'ERROR': 'ERROR', 'DELETED': 'DELETED'
121 class vimconnector(vimconn
.vimconnector
):
122 # dict used to store flavor in memory
125 def __init__(self
, uuid
=None, name
=None, tenant_id
=None, tenant_name
=None,
126 url
=None, url_admin
=None, user
=None, passwd
=None, log_level
=None, config
={}, persistent_info
={}):
128 Constructor create vmware connector to vCloud director.
130 By default construct doesn't validate connection state. So client can create object with None arguments.
131 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
133 a) It initialize organization UUID
134 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
137 uuid - is organization uuid.
138 name - is organization name that must be presented in vCloud director.
139 tenant_id - is VDC uuid it must be presented in vCloud director
140 tenant_name - is VDC name.
141 url - is hostname or ip address of vCloud director
142 url_admin - same as above.
143 user - is user that administrator for organization. Caller must make sure that
144 username has right privileges.
146 password - is password for a user.
148 VMware connector also requires PVDC administrative privileges and separate account.
149 This variables must be passed via config argument dict contains keys
151 dict['admin_username']
152 dict['admin_password']
153 config - Provide NSX and vCenter information
159 vimconn
.vimconnector
.__init
__(self
, uuid
, name
, tenant_id
, tenant_name
, url
,
160 url_admin
, user
, passwd
, log_level
, config
)
162 self
.logger
= logging
.getLogger('openmano.vim.vmware')
163 self
.logger
.setLevel(10)
164 self
.persistent_info
= persistent_info
169 self
.url_admin
= url_admin
170 self
.tenant_id
= tenant_id
171 self
.tenant_name
= tenant_name
175 self
.admin_password
= None
176 self
.admin_user
= None
178 self
.nsx_manager
= None
180 self
.nsx_password
= None
181 self
.vcenter_ip
= None
182 self
.vcenter_port
= None
183 self
.vcenter_user
= None
184 self
.vcenter_password
= None
186 if tenant_name
is not None:
187 orgnameandtenant
= tenant_name
.split(":")
188 if len(orgnameandtenant
) == 2:
189 self
.tenant_name
= orgnameandtenant
[1]
190 self
.org_name
= orgnameandtenant
[0]
192 self
.tenant_name
= tenant_name
193 if "orgname" in config
:
194 self
.org_name
= config
['orgname']
197 self
.logger
.setLevel(getattr(logging
, log_level
))
200 self
.admin_user
= config
['admin_username']
201 self
.admin_password
= config
['admin_password']
203 raise vimconn
.vimconnException(message
="Error admin username or admin password is empty.")
206 self
.nsx_manager
= config
['nsx_manager']
207 self
.nsx_user
= config
['nsx_user']
208 self
.nsx_password
= config
['nsx_password']
210 raise vimconn
.vimconnException(message
="Error: nsx manager or nsx user or nsx password is empty in Config")
212 self
.vcenter_ip
= config
.get("vcenter_ip", None)
213 self
.vcenter_port
= config
.get("vcenter_port", None)
214 self
.vcenter_user
= config
.get("vcenter_user", None)
215 self
.vcenter_password
= config
.get("vcenter_password", None)
221 raise vimconn
.vimconnException('url param can not be NoneType')
223 if not self
.url_admin
: # try to use normal url
224 self
.url_admin
= self
.url
226 logging
.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self
.id, self
.org_name
,
227 self
.tenant_id
, self
.tenant_name
))
228 logging
.debug("vcd url {} vcd username: {} vcd password: {}".format(self
.url
, self
.user
, self
.passwd
))
229 logging
.debug("vcd admin username {} vcd admin passowrd {}".format(self
.admin_user
, self
.admin_password
))
231 # initialize organization
232 if self
.user
is not None and self
.passwd
is not None and self
.url
:
233 self
.init_organization()
235 def __getitem__(self
, index
):
238 if index
== 'tenant_id':
239 return self
.tenant_id
240 if index
== 'tenant_name':
241 return self
.tenant_name
244 elif index
== 'org_name':
246 elif index
== 'org_uuid':
248 elif index
== 'user':
250 elif index
== 'passwd':
254 elif index
== 'url_admin':
255 return self
.url_admin
256 elif index
== "config":
259 raise KeyError("Invalid key '%s'" % str(index
))
261 def __setitem__(self
, index
, value
):
264 if index
== 'tenant_id':
265 self
.tenant_id
= value
266 if index
== 'tenant_name':
267 self
.tenant_name
= value
270 elif index
== 'org_name':
271 self
.org_name
= value
272 elif index
== 'org_uuid':
273 self
.org_uuid
= value
274 elif index
== 'user':
276 elif index
== 'passwd':
280 elif index
== 'url_admin':
281 self
.url_admin
= value
283 raise KeyError("Invalid key '%s'" % str(index
))
285 def connect_as_admin(self
):
286 """ Method connect as pvdc admin user to vCloud director.
287 There are certain action that can be done only by provider vdc admin user.
288 Organization creation / provider network creation etc.
291 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
294 self
.logger
.debug("Logging in to a vca {} as admin.".format(self
.org_name
))
296 vca_admin
= VCA(host
=self
.url
,
297 username
=self
.admin_user
,
298 service_type
=STANDALONE
,
302 result
= vca_admin
.login(password
=self
.admin_password
, org
='System')
304 raise vimconn
.vimconnConnectionException(
305 "Can't connect to a vCloud director as: {}".format(self
.admin_user
))
306 result
= vca_admin
.login(token
=vca_admin
.token
, org
='System', org_url
=vca_admin
.vcloud_session
.org_url
)
309 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self
.admin_user
))
314 """ Method connect as normal user to vCloud director.
317 The return vca object that letter can be used to connect to vCloud director as admin for VDC
321 self
.logger
.debug("Logging in to a vca {} as {} to datacenter {}.".format(self
.org_name
,
324 vca
= VCA(host
=self
.url
,
326 service_type
=STANDALONE
,
331 result
= vca
.login(password
=self
.passwd
, org
=self
.org_name
)
333 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self
.user
))
334 result
= vca
.login(token
=vca
.token
, org
=self
.org_name
, org_url
=vca
.vcloud_session
.org_url
)
337 "Successfully logged to a vcloud direct org: {} as user: {}".format(self
.org_name
, self
.user
))
340 raise vimconn
.vimconnConnectionException("Can't connect to a vCloud director org: "
341 "{} as user: {}".format(self
.org_name
, self
.user
))
345 def init_organization(self
):
346 """ Method initialize organization UUID and VDC parameters.
348 At bare minimum client must provide organization name that present in vCloud director and VDC.
350 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
351 The Org - UUID will be initialized at the run time if data center present in vCloud director.
354 The return vca object that letter can be used to connect to vcloud direct as admin
357 if self
.org_uuid
is None:
358 org_dict
= self
.get_org_list()
360 # we set org UUID at the init phase but we can do it only when we have valid credential.
361 if org_dict
[org
] == self
.org_name
:
363 self
.logger
.debug("Setting organization UUID {}".format(self
.org_uuid
))
366 raise vimconn
.vimconnException("Vcloud director organization {} not found".format(self
.org_name
))
368 # if well good we require for org details
369 org_details_dict
= self
.get_org(org_uuid
=self
.org_uuid
)
371 # we have two case if we want to initialize VDC ID or VDC name at run time
372 # tenant_name provided but no tenant id
373 if self
.tenant_id
is None and self
.tenant_name
is not None and 'vdcs' in org_details_dict
:
374 vdcs_dict
= org_details_dict
['vdcs']
375 for vdc
in vdcs_dict
:
376 if vdcs_dict
[vdc
] == self
.tenant_name
:
378 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
382 raise vimconn
.vimconnException("Tenant name indicated but not present in vcloud director.")
383 # case two we have tenant_id but we don't have tenant name so we find and set it.
384 if self
.tenant_id
is not None and self
.tenant_name
is None and 'vdcs' in org_details_dict
:
385 vdcs_dict
= org_details_dict
['vdcs']
386 for vdc
in vdcs_dict
:
387 if vdc
== self
.tenant_id
:
388 self
.tenant_name
= vdcs_dict
[vdc
]
389 self
.logger
.debug("Setting vdc uuid {} for organization UUID {}".format(self
.tenant_id
,
393 raise vimconn
.vimconnException("Tenant id indicated but not present in vcloud director")
394 self
.logger
.debug("Setting organization uuid {}".format(self
.org_uuid
))
396 self
.logger
.debug("Failed initialize organization UUID for org {}".format(self
.org_name
))
397 self
.logger
.debug(traceback
.format_exc())
400 def new_tenant(self
, tenant_name
=None, tenant_description
=None):
401 """ Method adds a new tenant to VIM with this name.
402 This action requires access to create VDC action in vCloud director.
405 tenant_name is tenant_name to be created.
406 tenant_description not used for this call
409 returns the tenant identifier in UUID format.
410 If action is failed method will throw vimconn.vimconnException method
412 vdc_task
= self
.create_vdc(vdc_name
=tenant_name
)
413 if vdc_task
is not None:
414 vdc_uuid
, value
= vdc_task
.popitem()
415 self
.logger
.info("Crated new vdc {} and uuid: {}".format(tenant_name
, vdc_uuid
))
418 raise vimconn
.vimconnException("Failed create tenant {}".format(tenant_name
))
420 def delete_tenant(self
, tenant_id
=None):
421 """Delete a tenant from VIM"""
422 'Returns the tenant identifier'
423 raise vimconn
.vimconnNotImplemented("Should have implemented this")
425 def get_tenant_list(self
, filter_dict
={}):
426 """Obtain tenants of VIM
427 filter_dict can contain the following keys:
428 name: filter by tenant name
429 id: filter by tenant uuid/id
431 Returns the tenant list of dictionaries:
432 [{'name':'<name>, 'id':'<id>, ...}, ...]
435 org_dict
= self
.get_org(self
.org_uuid
)
436 vdcs_dict
= org_dict
['vdcs']
441 entry
= {'name': vdcs_dict
[k
], 'id': k
}
442 # if caller didn't specify dictionary we return all tenants.
443 if filter_dict
is not None and filter_dict
:
444 filtered_entry
= entry
.copy()
445 filtered_dict
= set(entry
.keys()) - set(filter_dict
)
446 for unwanted_key
in filtered_dict
: del entry
[unwanted_key
]
447 if filter_dict
== entry
:
448 vdclist
.append(filtered_entry
)
450 vdclist
.append(entry
)
452 self
.logger
.debug("Error in get_tenant_list()")
453 self
.logger
.debug(traceback
.format_exc())
454 raise vimconn
.vimconnException("Incorrect state. {}")
458 def new_network(self
, net_name
, net_type
, ip_profile
=None, shared
=False):
459 """Adds a tenant network to VIM
461 net_type can be 'bridge','data'.'ptp'.
462 ip_profile is a dict containing the IP parameters of the network
464 Returns the network identifier"""
466 self
.logger
.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
467 .format(net_name
, net_type
, ip_profile
, shared
))
473 network_uuid
= self
.create_network(network_name
=net_name
, net_type
=net_type
,
474 ip_profile
=ip_profile
, isshared
=isshared
)
475 if network_uuid
is not None:
478 raise vimconn
.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name
))
480 def get_vcd_network_list(self
):
481 """ Method available organization for a logged in tenant
484 The return vca object that letter can be used to connect to vcloud direct as admin
487 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
490 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
492 if not self
.tenant_name
:
493 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
495 vdc
= vca
.get_vdc(self
.tenant_name
)
497 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self
.tenant_name
))
499 vdc_uuid
= vdc
.get_id().split(":")[3]
500 networks
= vca
.get_networks(vdc
.get_name())
503 for network
in networks
:
505 netid
= network
.get_id().split(":")
509 filter_dict
["name"] = network
.get_name()
510 filter_dict
["id"] = netid
[3]
511 filter_dict
["shared"] = network
.get_IsShared()
512 filter_dict
["tenant_id"] = vdc_uuid
513 if network
.get_status() == 1:
514 filter_dict
["admin_state_up"] = True
516 filter_dict
["admin_state_up"] = False
517 filter_dict
["status"] = "ACTIVE"
518 filter_dict
["type"] = "bridge"
519 network_list
.append(filter_dict
)
520 self
.logger
.debug("get_vcd_network_list adding entry {}".format(filter_dict
))
522 self
.logger
.debug("Error in get_vcd_network_list")
523 self
.logger
.debug(traceback
.format_exc())
526 self
.logger
.debug("get_vcd_network_list returning {}".format(network_list
))
529 def get_network_list(self
, filter_dict
={}):
530 """Obtain tenant networks of VIM
532 name: network name OR/AND
533 id: network uuid OR/AND
534 shared: boolean OR/AND
535 tenant_id: tenant OR/AND
536 admin_state_up: boolean
539 [{key : value , key : value}]
541 Returns the network list of dictionaries:
542 [{<the fields at Filter_dict plus some VIM specific>}, ...]
546 self
.logger
.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self
.tenant_name
))
549 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
551 if not self
.tenant_name
:
552 raise vimconn
.vimconnConnectionException("Tenant name is empty.")
554 vdc
= vca
.get_vdc(self
.tenant_name
)
556 raise vimconn
.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self
.tenant_name
))
559 vdcid
= vdc
.get_id().split(":")[3]
560 networks
= vca
.get_networks(vdc
.get_name())
563 for network
in networks
:
565 net_uuid
= network
.get_id().split(":")
566 if len(net_uuid
) != 4:
569 net_uuid
= net_uuid
[3]
571 self
.logger
.debug("Adding {} to a list vcd id {} network {}".format(net_uuid
,
574 filter_entry
["name"] = network
.get_name()
575 filter_entry
["id"] = net_uuid
576 filter_entry
["shared"] = network
.get_IsShared()
577 filter_entry
["tenant_id"] = vdcid
578 if network
.get_status() == 1:
579 filter_entry
["admin_state_up"] = True
581 filter_entry
["admin_state_up"] = False
582 filter_entry
["status"] = "ACTIVE"
583 filter_entry
["type"] = "bridge"
584 filtered_entry
= filter_entry
.copy()
586 if filter_dict
is not None and filter_dict
:
587 # we remove all the key : value we don't care and match only
589 filtered_dict
= set(filter_entry
.keys()) - set(filter_dict
)
590 for unwanted_key
in filtered_dict
: del filter_entry
[unwanted_key
]
591 if filter_dict
== filter_entry
:
592 network_list
.append(filtered_entry
)
594 network_list
.append(filtered_entry
)
596 self
.logger
.debug("Error in get_vcd_network_list")
597 self
.logger
.debug(traceback
.format_exc())
599 self
.logger
.debug("Returning {}".format(network_list
))
602 def get_network(self
, net_id
):
603 """Method obtains network details of net_id VIM network
604 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
608 raise vimconn
.vimconnConnectionException("self.connect() is failed")
611 vdc
= vca
.get_vdc(self
.tenant_name
)
612 vdc_id
= vdc
.get_id().split(":")[3]
614 networks
= vca
.get_networks(vdc
.get_name())
617 for network
in networks
:
618 vdc_network_id
= network
.get_id().split(":")
619 if len(vdc_network_id
) == 4 and vdc_network_id
[3] == net_id
:
620 filter_dict
["name"] = network
.get_name()
621 filter_dict
["id"] = vdc_network_id
[3]
622 filter_dict
["shared"] = network
.get_IsShared()
623 filter_dict
["tenant_id"] = vdc_id
624 if network
.get_status() == 1:
625 filter_dict
["admin_state_up"] = True
627 filter_dict
["admin_state_up"] = False
628 filter_dict
["status"] = "ACTIVE"
629 filter_dict
["type"] = "bridge"
630 self
.logger
.debug("Returning {}".format(filter_dict
))
633 self
.logger
.debug("Error in get_network")
634 self
.logger
.debug(traceback
.format_exc())
638 def delete_network(self
, net_id
):
640 Method Deletes a tenant network from VIM, provide the network id.
642 Returns the network identifier or raise an exception
647 raise vimconn
.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self
.tenant_name
))
649 vcd_network
= self
.get_vcd_network(network_uuid
=net_id
)
650 if vcd_network
is not None and vcd_network
:
651 if self
.delete_network_action(network_uuid
=net_id
):
654 raise vimconn
.vimconnNotFoundException("Network {} not found".format(net_id
))
656 def refresh_nets_status(self
, net_list
):
657 """Get the status of the networks
658 Params: the list of network identifiers
659 Returns a dictionary with:
660 net_id: #VIM id of this network
661 status: #Mandatory. Text with one of:
662 # DELETED (not found at vim)
663 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
664 # OTHER (Vim reported other status not understood)
665 # ERROR (VIM indicates an ERROR status)
666 # ACTIVE, INACTIVE, DOWN (admin down),
667 # BUILD (on building process)
669 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
670 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
676 raise vimconn
.vimconnConnectionException("self.connect() is failed")
682 vcd_network
= self
.get_vcd_network(network_uuid
=net
)
683 if vcd_network
is not None and vcd_network
:
684 if vcd_network
['status'] == '1':
690 errormsg
= 'Network not found.'
692 dict_entry
[net
] = {'status': status
, 'error_msg': errormsg
,
693 'vim_info': yaml
.safe_dump(vcd_network
)}
695 self
.logger
.debug("Error in refresh_nets_status")
696 self
.logger
.debug(traceback
.format_exc())
700 def get_flavor(self
, flavor_id
):
701 """Obtain flavor details from the VIM
702 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
704 if flavor_id
not in vimconnector
.flavorlist
:
705 raise vimconn
.vimconnNotFoundException("Flavor not found.")
706 return vimconnector
.flavorlist
[flavor_id
]
708 def new_flavor(self
, flavor_data
):
709 """Adds a tenant flavor to VIM
710 flavor_data contains a dictionary with information, keys:
712 ram: memory (cloud type) in MBytes
713 vpcus: cpus (cloud type)
714 extended: EPA parameters
715 - numas: #items requested in same NUMA
716 memory: number of 1G huge pages memory
717 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
718 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
719 - name: interface name
720 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
721 bandwidth: X Gbps; requested guarantee bandwidth
722 vpci: requested virtual PCI address
726 Returns the flavor identifier"""
728 # generate a new uuid put to internal dict and return it.
729 self
.logger
.debug("Creating new flavor - flavor_data: {}".format(flavor_data
))
730 new_flavor
=flavor_data
731 ram
= flavor_data
.get(FLAVOR_RAM_KEY
, 1024)
732 cpu
= flavor_data
.get(FLAVOR_VCPUS_KEY
, 1)
733 disk
= flavor_data
.get(FLAVOR_DISK_KEY
, 1)
735 extended_flv
= flavor_data
.get("extended")
737 numas
=extended_flv
.get("numas")
740 #overwrite ram and vcpus
741 ram
= numa
['memory']*1024
742 if 'paired-threads' in numa
:
743 cpu
= numa
['paired-threads']*2
744 elif 'cores' in numa
:
746 elif 'threads' in numa
:
747 cpu
= numa
['threads']
749 new_flavor
[FLAVOR_RAM_KEY
] = ram
750 new_flavor
[FLAVOR_VCPUS_KEY
] = cpu
751 new_flavor
[FLAVOR_DISK_KEY
] = disk
752 # generate a new uuid put to internal dict and return it.
753 flavor_id
= uuid
.uuid4()
754 vimconnector
.flavorlist
[str(flavor_id
)] = new_flavor
755 self
.logger
.debug("Created flavor - {} : {}".format(flavor_id
, new_flavor
))
757 return str(flavor_id
)
759 def delete_flavor(self
, flavor_id
):
760 """Deletes a tenant flavor from VIM identify by its id
762 Returns the used id or raise an exception
764 if flavor_id
not in vimconnector
.flavorlist
:
765 raise vimconn
.vimconnNotFoundException("Flavor not found.")
767 vimconnector
.flavorlist
.pop(flavor_id
, None)
770 def new_image(self
, image_dict
):
772 Adds a tenant image to VIM
774 200, image-id if the image is created
775 <0, message if there is an error
778 return self
.get_image_id_from_path(image_dict
['location'])
780 def delete_image(self
, image_id
):
787 raise vimconn
.vimconnNotImplemented("Should have implemented this")
789 def catalog_exists(self
, catalog_name
, catalogs
):
796 for catalog
in catalogs
:
797 if catalog
.name
== catalog_name
:
801 def create_vimcatalog(self
, vca
=None, catalog_name
=None):
802 """ Create new catalog entry in vCloud director.
805 vca: vCloud director.
806 catalog_name catalog that client wish to create. Note no validation done for a name.
807 Client must make sure that provide valid string representation.
809 Return (bool) True if catalog created.
813 task
= vca
.create_catalog(catalog_name
, catalog_name
)
814 result
= vca
.block_until_completed(task
)
817 catalogs
= vca
.get_catalogs()
820 return self
.catalog_exists(catalog_name
, catalogs
)
822 # noinspection PyIncorrectDocstring
823 def upload_ovf(self
, vca
=None, catalog_name
=None, image_name
=None, media_file_name
=None,
824 description
='', progress
=False, chunk_bytes
=128 * 1024):
826 Uploads a OVF file to a vCloud catalog
833 :param catalog_name: (str): The name of the catalog to upload the media.
834 :param media_file_name: (str): The name of the local media file to upload.
835 :return: (bool) True if the media file was successfully uploaded, false otherwise.
837 os
.path
.isfile(media_file_name
)
838 statinfo
= os
.stat(media_file_name
)
840 # find a catalog entry where we upload OVF.
841 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
843 # if VCD can parse OVF we upload VMDK file
845 for catalog
in vca
.get_catalogs():
846 if catalog_name
!= catalog
.name
:
848 link
= filter(lambda link
: link
.get_type() == "application/vnd.vmware.vcloud.media+xml" and
849 link
.get_rel() == 'add', catalog
.get_Link())
850 assert len(link
) == 1
852 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
853 """ % (escape(catalog_name
), escape(description
))
854 headers
= vca
.vcloud_session
.get_vcloud_headers()
855 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
856 response
= Http
.post(link
[0].get_href(), headers
=headers
, data
=data
, verify
=vca
.verify
, logger
=self
.logger
)
857 if response
.status_code
== requests
.codes
.created
:
858 catalogItem
= XmlElementTree
.fromstring(response
.content
)
859 entity
= [child
for child
in catalogItem
if
860 child
.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
861 href
= entity
.get('href')
863 response
= Http
.get(href
, headers
=vca
.vcloud_session
.get_vcloud_headers(),
864 verify
=vca
.verify
, logger
=self
.logger
)
866 if response
.status_code
== requests
.codes
.ok
:
867 media
= mediaType
.parseString(response
.content
, True)
868 link
= filter(lambda link
: link
.get_rel() == 'upload:default',
869 media
.get_Files().get_File()[0].get_Link())[0]
870 headers
= vca
.vcloud_session
.get_vcloud_headers()
871 headers
['Content-Type'] = 'Content-Type text/xml'
872 response
= Http
.put(link
.get_href(),
873 data
=open(media_file_name
, 'rb'),
875 verify
=vca
.verify
, logger
=self
.logger
)
876 if response
.status_code
!= requests
.codes
.ok
:
878 "Failed create vApp template for catalog name {} and image {}".format(catalog_name
,
882 # TODO fix this with aync block
885 self
.logger
.debug("vApp template for catalog name {} and image {}".format(catalog_name
, media_file_name
))
887 # uploading VMDK file
888 # check status of OVF upload and upload remaining files.
889 response
= Http
.get(template
,
890 headers
=vca
.vcloud_session
.get_vcloud_headers(),
894 if response
.status_code
== requests
.codes
.ok
:
895 media
= mediaType
.parseString(response
.content
, True)
896 number_of_files
= len(media
.get_Files().get_File())
897 for index
in xrange(0, number_of_files
):
898 links_list
= filter(lambda link
: link
.get_rel() == 'upload:default',
899 media
.get_Files().get_File()[index
].get_Link())
900 for link
in links_list
:
901 # we skip ovf since it already uploaded.
902 if 'ovf' in link
.get_href():
904 # The OVF file and VMDK must be in a same directory
905 head
, tail
= os
.path
.split(media_file_name
)
906 file_vmdk
= head
+ '/' + link
.get_href().split("/")[-1]
907 if not os
.path
.isfile(file_vmdk
):
909 statinfo
= os
.stat(file_vmdk
)
910 if statinfo
.st_size
== 0:
912 hrefvmdk
= link
.get_href()
915 print("Uploading file: {}".format(file_vmdk
))
917 widgets
= ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
919 progress_bar
= ProgressBar(widgets
=widgets
, maxval
=statinfo
.st_size
).start()
921 bytes_transferred
= 0
922 f
= open(file_vmdk
, 'rb')
923 while bytes_transferred
< statinfo
.st_size
:
924 my_bytes
= f
.read(chunk_bytes
)
925 if len(my_bytes
) <= chunk_bytes
:
926 headers
= vca
.vcloud_session
.get_vcloud_headers()
927 headers
['Content-Range'] = 'bytes %s-%s/%s' % (
928 bytes_transferred
, len(my_bytes
) - 1, statinfo
.st_size
)
929 headers
['Content-Length'] = str(len(my_bytes
))
930 response
= Http
.put(hrefvmdk
,
936 if response
.status_code
== requests
.codes
.ok
:
937 bytes_transferred
+= len(my_bytes
)
939 progress_bar
.update(bytes_transferred
)
942 'file upload failed with error: [%s] %s' % (response
.status_code
,
949 progress_bar
.finish()
953 self
.logger
.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
954 format(catalog_name
, media_file_name
))
956 except Exception as exp
:
957 self
.logger
.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
958 .format(catalog_name
,media_file_name
, exp
))
959 raise vimconn
.vimconnException(
960 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
961 .format(catalog_name
,media_file_name
, exp
))
963 self
.logger
.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name
, media_file_name
))
966 def upload_vimimage(self
, vca
=None, catalog_name
=None, media_name
=None, medial_file_name
=None, progress
=False):
967 """Upload media file"""
968 # TODO add named parameters for readability
970 return self
.upload_ovf(vca
=vca
, catalog_name
=catalog_name
, image_name
=media_name
.split(".")[0],
971 media_file_name
=medial_file_name
, description
='medial_file_name', progress
=progress
)
973 def validate_uuid4(self
, uuid_string
=None):
974 """ Method validate correct format of UUID.
976 Return: true if string represent valid uuid
979 val
= uuid
.UUID(uuid_string
, version
=4)
984 def get_catalogid(self
, catalog_name
=None, catalogs
=None):
985 """ Method check catalog and return catalog ID in UUID format.
988 catalog_name: catalog name as string
989 catalogs: list of catalogs.
991 Return: catalogs uuid
994 for catalog
in catalogs
:
995 if catalog
.name
== catalog_name
:
996 catalog_id
= catalog
.get_id().split(":")
1000 def get_catalogbyid(self
, catalog_uuid
=None, catalogs
=None):
1001 """ Method check catalog and return catalog name lookup done by catalog UUID.
1004 catalog_name: catalog name as string
1005 catalogs: list of catalogs.
1007 Return: catalogs name or None
1010 if not self
.validate_uuid4(uuid_string
=catalog_uuid
):
1013 for catalog
in catalogs
:
1014 catalog_id
= catalog
.get_id().split(":")[3]
1015 if catalog_id
== catalog_uuid
:
1019 def get_image_id_from_path(self
, path
=None, progress
=False):
1020 """ Method upload OVF image to vCloud director.
1022 Each OVF image represented as single catalog entry in vcloud director.
1023 The method check for existing catalog entry. The check done by file name without file extension.
1025 if given catalog name already present method will respond with existing catalog uuid otherwise
1026 it will create new catalog entry and upload OVF file to newly created catalog.
1028 If method can't create catalog entry or upload a file it will throw exception.
1030 Method accept boolean flag progress that will output progress bar. It useful method
1031 for standalone upload use case. In case to test large file upload.
1034 path: - valid path to OVF file.
1035 progress - boolean progress bar show progress bar.
1037 Return: if image uploaded correct method will provide image catalog UUID.
1039 vca
= self
.connect()
1041 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1044 raise vimconn
.vimconnException("Image path can't be None.")
1046 if not os
.path
.isfile(path
):
1047 raise vimconn
.vimconnException("Can't read file. File not found.")
1049 if not os
.access(path
, os
.R_OK
):
1050 raise vimconn
.vimconnException("Can't read file. Check file permission to read.")
1052 self
.logger
.debug("get_image_id_from_path() client requesting {} ".format(path
))
1054 dirpath
, filename
= os
.path
.split(path
)
1055 flname
, file_extension
= os
.path
.splitext(path
)
1056 if file_extension
!= '.ovf':
1057 self
.logger
.debug("Wrong file extension {} connector support only OVF container.".format(file_extension
))
1058 raise vimconn
.vimconnException("Wrong container. vCloud director supports only OVF.")
1060 catalog_name
= os
.path
.splitext(filename
)[0]
1061 catalog_md5_name
= hashlib
.md5(path
).hexdigest()
1062 self
.logger
.debug("File name {} Catalog Name {} file path {} "
1063 "vdc catalog name {}".format(filename
, catalog_name
, path
, catalog_md5_name
))
1066 catalogs
= vca
.get_catalogs()
1067 except Exception as exp
:
1068 self
.logger
.debug("Failed get catalogs() with Exception {} ".format(exp
))
1069 raise vimconn
.vimconnException("Failed get catalogs() with Exception {} ".format(exp
))
1071 if len(catalogs
) == 0:
1072 self
.logger
.info("Creating a new catalog entry {} in vcloud director".format(catalog_name
))
1073 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1075 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1076 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1077 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1079 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name
))
1080 return self
.get_catalogid(catalog_name
, vca
.get_catalogs())
1082 for catalog
in catalogs
:
1083 # search for existing catalog if we find same name we return ID
1084 # TODO optimize this
1085 if catalog
.name
== catalog_md5_name
:
1086 self
.logger
.debug("Found existing catalog entry for {} "
1087 "catalog id {}".format(catalog_name
,
1088 self
.get_catalogid(catalog_md5_name
, catalogs
)))
1089 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1091 # if we didn't find existing catalog we create a new one and upload image.
1092 self
.logger
.debug("Creating new catalog entry {} - {}".format(catalog_name
, catalog_md5_name
))
1093 result
= self
.create_vimcatalog(vca
, catalog_md5_name
)
1095 raise vimconn
.vimconnException("Failed create new catalog {} ".format(catalog_md5_name
))
1097 result
= self
.upload_vimimage(vca
=vca
, catalog_name
=catalog_md5_name
,
1098 media_name
=filename
, medial_file_name
=path
, progress
=progress
)
1100 raise vimconn
.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name
))
1102 return self
.get_catalogid(catalog_md5_name
, vca
.get_catalogs())
1104 def get_image_list(self
, filter_dict
={}):
1105 '''Obtain tenant images from VIM
1109 checksum: image checksum
1110 location: image path
1111 Returns the image list of dictionaries:
1112 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1115 vca
= self
.connect()
1117 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1120 catalogs
= vca
.get_catalogs()
1121 if len(catalogs
) == 0:
1124 for catalog
in catalogs
:
1125 catalog_uuid
= catalog
.get_id().split(":")[3]
1128 if filter_dict
.get("name") and filter_dict
["name"] != name
:
1130 if filter_dict
.get("id") and filter_dict
["id"] != catalog_uuid
:
1132 filtered_dict
["name"] = name
1133 filtered_dict
["id"] = catalog_uuid
1134 image_list
.append(filtered_dict
)
1136 self
.logger
.debug("List of already created catalog items: {}".format(image_list
))
1138 except Exception as exp
:
1139 raise vimconn
.vimconnException("Exception occured while retriving catalog items {}".format(exp
))
1141 def get_vappid(self
, vdc
=None, vapp_name
=None):
1142 """ Method takes vdc object and vApp name and returns vapp uuid or None
1145 vdc: The VDC object.
1146 vapp_name: is application vappp name identifier
1149 The return vApp name otherwise None
1151 if vdc
is None or vapp_name
is None:
1153 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1155 refs
= filter(lambda ref
: ref
.name
== vapp_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1156 vdc
.ResourceEntities
.ResourceEntity
)
1158 return refs
[0].href
.split("vapp")[1][1:]
1159 except Exception as e
:
1160 self
.logger
.exception(e
)
1164 def check_vapp(self
, vdc
=None, vapp_uuid
=None):
1165 """ Method Method returns True or False if vapp deployed in vCloud director
1168 vca: Connector to VCA
1169 vdc: The VDC object.
1170 vappid: vappid is application identifier
1173 The return True if vApp deployed
1178 refs
= filter(lambda ref
:
1179 ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1180 vdc
.ResourceEntities
.ResourceEntity
)
1182 vappid
= ref
.href
.split("vapp")[1][1:]
1183 # find vapp with respected vapp uuid
1184 if vappid
== vapp_uuid
:
1186 except Exception as e
:
1187 self
.logger
.exception(e
)
1191 def get_namebyvappid(self
, vca
=None, vdc
=None, vapp_uuid
=None):
1192 """Method returns vApp name from vCD and lookup done by vapp_id.
1195 vca: Connector to VCA
1196 vdc: The VDC object.
1197 vapp_uuid: vappid is application identifier
1200 The return vApp name otherwise None
1204 refs
= filter(lambda ref
: ref
.type_
== 'application/vnd.vmware.vcloud.vApp+xml',
1205 vdc
.ResourceEntities
.ResourceEntity
)
1207 # we care only about UUID the rest doesn't matter
1208 vappid
= ref
.href
.split("vapp")[1][1:]
1209 if vappid
== vapp_uuid
:
1210 response
= Http
.get(ref
.href
, headers
=vca
.vcloud_session
.get_vcloud_headers(), verify
=vca
.verify
,
1212 tree
= XmlElementTree
.fromstring(response
.content
)
1213 return tree
.attrib
['name']
1214 except Exception as e
:
1215 self
.logger
.exception(e
)
1219 def new_vminstance(self
, name
=None, description
="", start
=False, image_id
=None, flavor_id
=None, net_list
={},
1220 cloud_config
=None, disk_list
=None):
1221 """Adds a VM instance to VIM
1223 start: indicates if VM must start or boot in pause mode. Ignored
1224 image_id,flavor_id: image and flavor uuid
1225 net_list: list of interfaces, each one is a dictionary with:
1227 net_id: network uuid to connect
1228 vpci: virtual vcpi to assign
1229 model: interface model, virtio, e2000, ...
1231 use: 'data', 'bridge', 'mgmt'
1232 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1233 vim_id: filled/added by this function
1234 cloud_config: can be a text script to be passed directly to cloud-init,
1235 or an object to inject users and ssh keys with format:
1236 key-pairs: [] list of keys to install to the default user
1237 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1238 #TODO ip, security groups
1239 Returns >=0, the instance identifier
1243 self
.logger
.info("Creating new instance for entry {}".format(name
))
1244 self
.logger
.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
1245 description
, start
, image_id
, flavor_id
, net_list
, cloud_config
))
1246 vca
= self
.connect()
1248 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1250 #new vm name = vmname + tenant_id + uuid
1251 new_vm_name
= [name
, '-', str(uuid
.uuid4())]
1252 vmname_andid
= ''.join(new_vm_name
)
1254 # if vm already deployed we return existing uuid
1255 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1256 # if vapp_uuid is not None:
1259 # we check for presence of VDC, Catalog entry and Flavor.
1260 vdc
= vca
.get_vdc(self
.tenant_name
)
1262 raise vimconn
.vimconnNotFoundException(
1263 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name
))
1264 catalogs
= vca
.get_catalogs()
1265 if catalogs
is None:
1266 raise vimconn
.vimconnNotFoundException(
1267 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name
))
1269 catalog_hash_name
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1270 if catalog_hash_name
:
1271 self
.logger
.info("Found catalog entry {} for image id {}".format(catalog_hash_name
, image_id
))
1273 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1274 "(Failed retrieve catalog information {})".format(name
, image_id
))
1277 # Set vCPU and Memory based on flavor.
1282 pci_devices_info
= []
1283 if flavor_id
is not None:
1284 if flavor_id
not in vimconnector
.flavorlist
:
1285 raise vimconn
.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1286 "Failed retrieve flavor information "
1287 "flavor id {}".format(name
, flavor_id
))
1290 flavor
= vimconnector
.flavorlist
[flavor_id
]
1291 vm_cpus
= flavor
[FLAVOR_VCPUS_KEY
]
1292 vm_memory
= flavor
[FLAVOR_RAM_KEY
]
1293 vm_disk
= flavor
[FLAVOR_DISK_KEY
]
1294 extended
= flavor
.get("extended", None)
1296 numas
=extended
.get("numas", None)
1299 for interface
in numa
.get("interfaces",() ):
1300 if interface
["dedicated"].strip()=="yes":
1301 pci_devices_info
.append(interface
)
1302 except Exception as exp
:
1303 raise vimconn
.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id
, exp
))
1305 # image upload creates template name as catalog name space Template.
1306 templateName
= self
.get_catalogbyid(catalog_uuid
=image_id
, catalogs
=catalogs
)
1311 # client must provide at least one entry in net_list if not we report error
1312 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1313 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1315 primary_netname
= None
1316 network_mode
= 'bridged'
1317 if net_list
is not None and len(net_list
) > 0:
1318 for net
in net_list
:
1319 if 'use' in net
and net
['use'] == 'mgmt':
1321 if primary_net
is None:
1322 primary_net
= net_list
[0]
1325 primary_net_id
= primary_net
['net_id']
1326 network_dict
= self
.get_vcd_network(network_uuid
=primary_net_id
)
1327 if 'name' in network_dict
:
1328 primary_netname
= network_dict
['name']
1331 raise vimconn
.vimconnException("Corrupted flavor. {}".format(primary_net
))
1333 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name
))
1335 # use: 'data', 'bridge', 'mgmt'
1336 # create vApp. Set vcpu and ram based on flavor id.
1338 vapptask
= vca
.create_vapp(self
.tenant_name
, vmname_andid
, templateName
,
1339 self
.get_catalogbyid(image_id
, catalogs
),
1340 network_name
=None, # None while creating vapp
1341 network_mode
=network_mode
,
1342 vm_name
=vmname_andid
,
1343 vm_cpus
=vm_cpus
, # can be None if flavor is None
1344 vm_memory
=vm_memory
) # can be None if flavor is None
1346 if vapptask
is None or vapptask
is False:
1347 raise vimconn
.vimconnUnexpectedResponse(
1348 "new_vminstance(): failed to create vApp {}".format(vmname_andid
))
1349 if type(vapptask
) is VappTask
:
1350 vca
.block_until_completed(vapptask
)
1352 except Exception as exp
:
1353 raise vimconn
.vimconnUnexpectedResponse(
1354 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid
, exp
))
1356 # we should have now vapp in undeployed state.
1358 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1359 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1360 except Exception as exp
:
1361 raise vimconn
.vimconnUnexpectedResponse(
1362 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1363 .format(vmname_andid
, exp
))
1366 raise vimconn
.vimconnUnexpectedResponse(
1367 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1370 #Add PCI passthrough configrations
1371 PCI_devices_status
= False
1374 if len(pci_devices_info
) > 0:
1375 self
.logger
.info("Need to add PCI devices {} into VM {}".format(pci_devices_info
,
1377 PCI_devices_status
, vm_obj
, vcenter_conect
= self
.add_pci_devices(vapp_uuid
,
1380 if PCI_devices_status
:
1381 self
.logger
.info("Added PCI devives {} to VM {}".format(
1386 self
.logger
.info("Fail to add PCI devives {} to VM {}".format(
1392 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1393 result
= self
.modify_vm_disk(vapp_uuid
, vm_disk
)
1395 self
.logger
.debug("Modified Disk size of VM {} ".format(vmname_andid
))
1398 # Assigning numa affinity setting
1400 if 'paired-threads-id' in numa
:
1401 paired_threads_id
= numa
['paired-threads-id']
1402 self
.set_numa_affinity(vapp_uuid
, paired_threads_id
)
1404 # add NICs & connect to networks in netlist
1406 self
.logger
.info("Request to connect VM to a network: {}".format(net_list
))
1408 primary_nic_index
= 0
1409 for net
in net_list
:
1410 # openmano uses network id in UUID format.
1411 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1412 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1413 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1415 if 'net_id' not in net
:
1418 interface_net_id
= net
['net_id']
1419 interface_net_name
= self
.get_network_name_by_id(network_uuid
=interface_net_id
)
1420 interface_network_mode
= net
['use']
1422 if interface_network_mode
== 'mgmt':
1423 primary_nic_index
= nicIndex
1425 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1426 - DHCP (The IP address is obtained from a DHCP service.)
1427 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1428 - NONE (No IP addressing mode specified.)"""
1430 if primary_netname
is not None:
1431 nets
= filter(lambda n
: n
.name
== interface_net_name
, vca
.get_networks(self
.tenant_name
))
1433 self
.logger
.info("new_vminstance(): Found requested network: {}".format(nets
[0].name
))
1434 task
= vapp
.connect_to_network(nets
[0].name
, nets
[0].href
)
1435 if type(task
) is GenericTask
:
1436 vca
.block_until_completed(task
)
1437 # connect network to VM - with all DHCP by default
1439 type_list
= ['PF','VF','VFnotShared']
1440 if 'type' in net
and net
['type'] not in type_list
:
1441 # fetching nic type from vnf
1443 nic_type
= net
['model']
1444 self
.logger
.info("new_vminstance(): adding network adapter "\
1445 "to a network {}".format(nets
[0].name
))
1446 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1452 self
.logger
.info("new_vminstance(): adding network adapter "\
1453 "to a network {}".format(nets
[0].name
))
1454 self
.add_network_adapter_to_vms(vapp
, nets
[0].name
,
1460 # cloud-init for ssh-key injection
1462 self
.cloud_init(vapp
,cloud_config
)
1464 # deploy and power on vm
1465 self
.logger
.debug("new_vminstance(): Deploying vApp {} ".format(name
))
1466 deploytask
= vapp
.deploy(powerOn
=False)
1467 if type(deploytask
) is GenericTask
:
1468 vca
.block_until_completed(deploytask
)
1470 # If VM has PCI devices reserve memory for VM
1471 if PCI_devices_status
and vm_obj
and vcenter_conect
:
1472 memReserve
= vm_obj
.config
.hardware
.memoryMB
1473 spec
= vim
.vm
.ConfigSpec()
1474 spec
.memoryAllocation
= vim
.ResourceAllocationInfo(reservation
=memReserve
)
1475 task
= vm_obj
.ReconfigVM_Task(spec
=spec
)
1477 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
1478 self
.logger
.info("Reserved memmoery {} MB for "\
1479 "VM VM status: {}".format(str(memReserve
),result
))
1481 self
.logger
.info("Fail to reserved memmoery {} to VM {}".format(
1482 str(memReserve
),str(vm_obj
)))
1484 self
.logger
.debug("new_vminstance(): power on vApp {} ".format(name
))
1485 poweron_task
= vapp
.poweron()
1486 if type(poweron_task
) is GenericTask
:
1487 vca
.block_until_completed(poweron_task
)
1489 except Exception as exp
:
1490 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1491 self
.logger
.debug("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1492 raise vimconn
.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name
, exp
))
1494 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1497 while wait_time
<= MAX_WAIT_TIME
:
1499 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1500 except Exception as exp
:
1501 raise vimconn
.vimconnUnexpectedResponse(
1502 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1503 .format(vmname_andid
, exp
))
1505 if vapp
and vapp
.me
.deployed
:
1506 vapp_uuid
= self
.get_vappid(vca
.get_vdc(self
.tenant_name
), vmname_andid
)
1509 self
.logger
.debug("new_vminstance(): Wait for vApp {} to deploy".format(name
))
1510 time
.sleep(INTERVAL_TIME
)
1512 wait_time
+=INTERVAL_TIME
1514 if vapp_uuid
is not None:
1517 raise vimconn
.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name
))
1521 ## based on current discussion
1525 # created: '2016-09-08T11:51:58'
1526 # description: simple-instance.linux1.1
1527 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1528 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1529 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1534 def get_vminstance(self
, vim_vm_uuid
=None):
1535 """Returns the VM instance information from VIM"""
1537 self
.logger
.debug("Client requesting vm instance {} ".format(vim_vm_uuid
))
1538 vca
= self
.connect()
1540 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1542 vdc
= vca
.get_vdc(self
.tenant_name
)
1544 raise vimconn
.vimconnConnectionException(
1545 "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1547 vm_info_dict
= self
.get_vapp_details_rest(vapp_uuid
=vim_vm_uuid
)
1548 if not vm_info_dict
:
1549 self
.logger
.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1550 raise vimconn
.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid
))
1552 status_key
= vm_info_dict
['status']
1555 vm_dict
= {'created': vm_info_dict
['created'],
1556 'description': vm_info_dict
['name'],
1557 'status': vcdStatusCode2manoFormat
[int(status_key
)],
1558 'hostId': vm_info_dict
['vmuuid'],
1560 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1562 if 'interfaces' in vm_info_dict
:
1563 vm_dict
['interfaces'] = vm_info_dict
['interfaces']
1565 vm_dict
['interfaces'] = []
1567 vm_dict
= {'created': '',
1569 'status': vcdStatusCode2manoFormat
[int(-1)],
1570 'hostId': vm_info_dict
['vmuuid'],
1571 'error_msg': "Inconsistency state",
1572 'vim_info': yaml
.safe_dump(vm_info_dict
), 'interfaces': []}
1576 def delete_vminstance(self
, vm__vim_uuid
):
1577 """Method poweroff and remove VM instance from vcloud director network.
1580 vm__vim_uuid: VM UUID
1583 Returns the instance identifier
1586 self
.logger
.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid
))
1587 vca
= self
.connect()
1589 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1591 vdc
= vca
.get_vdc(self
.tenant_name
)
1593 self
.logger
.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1595 raise vimconn
.vimconnException(
1596 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1599 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1600 if vapp_name
is None:
1601 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1602 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1604 self
.logger
.info("Deleting vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1606 # Delete vApp and wait for status change if task executed and vApp is None.
1607 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1610 if vapp
.me
.deployed
:
1611 self
.logger
.info("Powering off vApp {}".format(vapp_name
))
1615 while wait_time
<= MAX_WAIT_TIME
:
1616 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1618 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1619 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1621 power_off_task
= vapp
.poweroff()
1622 if type(power_off_task
) is GenericTask
:
1623 result
= vca
.block_until_completed(power_off_task
)
1628 self
.logger
.info("Wait for vApp {} to power off".format(vapp_name
))
1629 time
.sleep(INTERVAL_TIME
)
1631 wait_time
+=INTERVAL_TIME
1633 self
.logger
.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid
))
1635 self
.logger
.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid
))
1638 self
.logger
.info("Undeploy vApp {}".format(vapp_name
))
1641 while wait_time
<= MAX_WAIT_TIME
:
1642 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1644 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1645 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1646 undeploy_task
= vapp
.undeploy(action
='powerOff')
1648 if type(undeploy_task
) is GenericTask
:
1649 result
= vca
.block_until_completed(undeploy_task
)
1654 self
.logger
.debug("Wait for vApp {} to undeploy".format(vapp_name
))
1655 time
.sleep(INTERVAL_TIME
)
1657 wait_time
+=INTERVAL_TIME
1660 self
.logger
.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid
))
1663 self
.logger
.info("Start deletion of vApp {} ".format(vapp_name
))
1664 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1666 if vapp
is not None:
1670 while wait_time
<= MAX_WAIT_TIME
:
1671 vapp
= vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
)
1673 self
.logger
.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1674 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
)
1676 delete_task
= vapp
.delete()
1678 if type(delete_task
) is GenericTask
:
1679 vca
.block_until_completed(delete_task
)
1680 result
= vca
.block_until_completed(delete_task
)
1684 self
.logger
.debug("Wait for vApp {} to delete".format(vapp_name
))
1685 time
.sleep(INTERVAL_TIME
)
1687 wait_time
+=INTERVAL_TIME
1690 self
.logger
.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid
))
1693 self
.logger
.debug(traceback
.format_exc())
1694 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1696 if vca
.get_vapp(vca
.get_vdc(self
.tenant_name
), vapp_name
) is None:
1697 self
.logger
.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid
))
1700 raise vimconn
.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid
))
1702 def refresh_vms_status(self
, vm_list
):
1703 """Get the status of the virtual machines and their interfaces/ports
1704 Params: the list of VM identifiers
1705 Returns a dictionary with:
1706 vm_id: #VIM id of this Virtual Machine
1707 status: #Mandatory. Text with one of:
1708 # DELETED (not found at vim)
1709 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1710 # OTHER (Vim reported other status not understood)
1711 # ERROR (VIM indicates an ERROR status)
1712 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1713 # CREATING (on building process), ERROR
1714 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1716 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1717 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1719 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1720 mac_address: #Text format XX:XX:XX:XX:XX:XX
1721 vim_net_id: #network id where this interface is connected
1722 vim_interface_id: #interface/port VIM id
1723 ip_address: #null, or text with IPv4, IPv6 address
1726 self
.logger
.debug("Client requesting refresh vm status for {} ".format(vm_list
))
1728 vca
= self
.connect()
1730 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1732 vdc
= vca
.get_vdc(self
.tenant_name
)
1734 raise vimconn
.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
))
1738 for vmuuid
in vm_list
:
1739 vmname
= self
.get_namebyvappid(vca
, vdc
, vmuuid
)
1740 if vmname
is not None:
1743 the_vapp
= vca
.get_vapp(vdc
, vmname
)
1744 vm_info
= the_vapp
.get_vms_details()
1745 vm_status
= vm_info
[0]['status']
1746 vm_pci_details
= self
.get_vm_pci_details(vmuuid
)
1747 vm_info
[0].update(vm_pci_details
)
1749 vm_dict
= {'status': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1750 'error_msg': vcdStatusCode2manoFormat
[the_vapp
.me
.get_status()],
1751 'vim_info': yaml
.safe_dump(vm_info
), 'interfaces': []}
1754 vm_app_networks
= the_vapp
.get_vms_network_info()
1755 for vapp_network
in vm_app_networks
:
1756 for vm_network
in vapp_network
:
1757 if vm_network
['name'] == vmname
:
1758 #Assign IP Address based on MAC Address in NSX DHCP lease info
1759 if vm_network
['ip'] is None:
1760 if not nsx_edge_list
:
1761 nsx_edge_list
= self
.get_edge_details()
1762 if nsx_edge_list
is None:
1763 raise vimconn
.vimconnException("refresh_vms_status:"\
1764 "Failed to get edge details from NSX Manager")
1765 if vm_network
['mac'] is not None:
1766 vm_network
['ip'] = self
.get_ipaddr_from_NSXedge(nsx_edge_list
, vm_network
['mac'])
1768 vm_net_id
= self
.get_network_id_by_name(vm_network
['network_name'])
1769 interface
= {"mac_address": vm_network
['mac'],
1770 "vim_net_id": vm_net_id
,
1771 "vim_interface_id": vm_net_id
,
1772 'ip_address': vm_network
['ip']}
1773 # interface['vim_info'] = yaml.safe_dump(vm_network)
1774 vm_dict
["interfaces"].append(interface
)
1775 # add a vm to vm dict
1776 vms_dict
.setdefault(vmuuid
, vm_dict
)
1777 except Exception as exp
:
1778 self
.logger
.debug("Error in response {}".format(exp
))
1779 self
.logger
.debug(traceback
.format_exc())
1784 def get_edge_details(self
):
1785 """Get the NSX edge list from NSX Manager
1786 Returns list of NSX edges
1789 rheaders
= {'Content-Type': 'application/xml'}
1790 nsx_api_url
= '/api/4.0/edges'
1792 self
.logger
.debug("Get edge details from NSX Manager {} {}".format(self
.nsx_manager
, nsx_api_url
))
1795 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1796 auth
= (self
.nsx_user
, self
.nsx_password
),
1797 verify
= False, headers
= rheaders
)
1798 if resp
.status_code
== requests
.codes
.ok
:
1799 paged_Edge_List
= XmlElementTree
.fromstring(resp
.text
)
1800 for edge_pages
in paged_Edge_List
:
1801 if edge_pages
.tag
== 'edgePage':
1802 for edge_summary
in edge_pages
:
1803 if edge_summary
.tag
== 'pagingInfo':
1804 for element
in edge_summary
:
1805 if element
.tag
== 'totalCount' and element
.text
== '0':
1806 raise vimconn
.vimconnException("get_edge_details: No NSX edges details found: {}"
1807 .format(self
.nsx_manager
))
1809 if edge_summary
.tag
== 'edgeSummary':
1810 for element
in edge_summary
:
1811 if element
.tag
== 'id':
1812 edge_list
.append(element
.text
)
1814 raise vimconn
.vimconnException("get_edge_details: No NSX edge details found: {}"
1815 .format(self
.nsx_manager
))
1818 raise vimconn
.vimconnException("get_edge_details: "\
1819 "No NSX edge details found: {}"
1820 .format(self
.nsx_manager
))
1822 self
.logger
.debug("get_edge_details: Found NSX edges {}".format(edge_list
))
1825 self
.logger
.debug("get_edge_details: "
1826 "Failed to get NSX edge details from NSX Manager: {}"
1827 .format(resp
.content
))
1830 except Exception as exp
:
1831 self
.logger
.debug("get_edge_details: "\
1832 "Failed to get NSX edge details from NSX Manager: {}"
1834 raise vimconn
.vimconnException("get_edge_details: "\
1835 "Failed to get NSX edge details from NSX Manager: {}"
1839 def get_ipaddr_from_NSXedge(self
, nsx_edges
, mac_address
):
1840 """Get IP address details from NSX edges, using the MAC address
1841 PARAMS: nsx_edges : List of NSX edges
1842 mac_address : Find IP address corresponding to this MAC address
1843 Returns: IP address corrresponding to the provided MAC address
1847 rheaders
= {'Content-Type': 'application/xml'}
1849 self
.logger
.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
1852 for edge
in nsx_edges
:
1853 nsx_api_url
= '/api/4.0/edges/'+ edge
+'/dhcp/leaseInfo'
1855 resp
= requests
.get(self
.nsx_manager
+ nsx_api_url
,
1856 auth
= (self
.nsx_user
, self
.nsx_password
),
1857 verify
= False, headers
= rheaders
)
1859 if resp
.status_code
== requests
.codes
.ok
:
1860 dhcp_leases
= XmlElementTree
.fromstring(resp
.text
)
1861 for child
in dhcp_leases
:
1862 if child
.tag
== 'dhcpLeaseInfo':
1863 dhcpLeaseInfo
= child
1864 for leaseInfo
in dhcpLeaseInfo
:
1865 for elem
in leaseInfo
:
1866 if (elem
.tag
)=='macAddress':
1867 edge_mac_addr
= elem
.text
1868 if (elem
.tag
)=='ipAddress':
1870 if edge_mac_addr
is not None:
1871 if edge_mac_addr
== mac_address
:
1872 self
.logger
.debug("Found ip addr {} for mac {} at NSX edge {}"
1873 .format(ip_addr
, mac_address
,edge
))
1876 self
.logger
.debug("get_ipaddr_from_NSXedge: "\
1877 "Error occurred while getting DHCP lease info from NSX Manager: {}"
1878 .format(resp
.content
))
1880 self
.logger
.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
1883 except XmlElementTree
.ParseError
as Err
:
1884 self
.logger
.debug("ParseError in response from NSX Manager {}".format(Err
.message
), exc_info
=True)
1887 def action_vminstance(self
, vm__vim_uuid
=None, action_dict
=None):
1888 """Send and action over a VM instance from VIM
1889 Returns the vm_id if the action was successfully sent to the VIM"""
1891 self
.logger
.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid
, action_dict
))
1892 if vm__vim_uuid
is None or action_dict
is None:
1893 raise vimconn
.vimconnException("Invalid request. VM id or action is None.")
1895 vca
= self
.connect()
1897 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
1899 vdc
= vca
.get_vdc(self
.tenant_name
)
1901 return -1, "Failed to get a reference of VDC for a tenant {}".format(self
.tenant_name
)
1903 vapp_name
= self
.get_namebyvappid(vca
, vdc
, vm__vim_uuid
)
1904 if vapp_name
is None:
1905 self
.logger
.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1906 raise vimconn
.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid
))
1908 self
.logger
.info("Action_vminstance vApp {} and UUID {}".format(vapp_name
, vm__vim_uuid
))
1911 the_vapp
= vca
.get_vapp(vdc
, vapp_name
)
1912 # TODO fix all status
1913 if "start" in action_dict
:
1914 vm_info
= the_vapp
.get_vms_details()
1915 vm_status
= vm_info
[0]['status']
1916 self
.logger
.info("action_vminstance: Power on vApp: {}".format(vapp_name
))
1917 if vm_status
== "Suspended" or vm_status
== "Powered off":
1918 power_on_task
= the_vapp
.poweron()
1919 result
= vca
.block_until_completed(power_on_task
)
1920 self
.instance_actions_result("start", result
, vapp_name
)
1921 elif "rebuild" in action_dict
:
1922 self
.logger
.info("action_vminstance: Rebuild vApp: {}".format(vapp_name
))
1923 rebuild_task
= the_vapp
.deploy(powerOn
=True)
1924 result
= vca
.block_until_completed(rebuild_task
)
1925 self
.instance_actions_result("rebuild", result
, vapp_name
)
1926 elif "pause" in action_dict
:
1927 self
.logger
.info("action_vminstance: pause vApp: {}".format(vapp_name
))
1928 pause_task
= the_vapp
.undeploy(action
='suspend')
1929 result
= vca
.block_until_completed(pause_task
)
1930 self
.instance_actions_result("pause", result
, vapp_name
)
1931 elif "resume" in action_dict
:
1932 self
.logger
.info("action_vminstance: resume vApp: {}".format(vapp_name
))
1933 power_task
= the_vapp
.poweron()
1934 result
= vca
.block_until_completed(power_task
)
1935 self
.instance_actions_result("resume", result
, vapp_name
)
1936 elif "shutoff" in action_dict
or "shutdown" in action_dict
:
1937 action_name
, value
= action_dict
.items()[0]
1938 self
.logger
.info("action_vminstance: {} vApp: {}".format(action_name
, vapp_name
))
1939 power_off_task
= the_vapp
.undeploy(action
='powerOff')
1940 result
= vca
.block_until_completed(power_off_task
)
1941 if action_name
== "shutdown":
1942 self
.instance_actions_result("shutdown", result
, vapp_name
)
1944 self
.instance_actions_result("shutoff", result
, vapp_name
)
1945 elif "forceOff" in action_dict
:
1946 result
= the_vapp
.undeploy(action
='force')
1947 self
.instance_actions_result("forceOff", result
, vapp_name
)
1948 elif "reboot" in action_dict
:
1949 self
.logger
.info("action_vminstance: reboot vApp: {}".format(vapp_name
))
1950 reboot_task
= the_vapp
.reboot()
1952 raise vimconn
.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict
))
1954 except Exception as exp
:
1955 self
.logger
.debug("action_vminstance: Failed with Exception {}".format(exp
))
1956 raise vimconn
.vimconnException("action_vminstance: Failed with Exception {}".format(exp
))
1958 def instance_actions_result(self
, action
, result
, vapp_name
):
1960 self
.logger
.info("action_vminstance: Sucessfully {} the vApp: {}".format(action
, vapp_name
))
1962 self
.logger
.error("action_vminstance: Failed to {} vApp: {}".format(action
, vapp_name
))
1964 def get_vminstance_console(self
, vm_id
, console_type
="vnc"):
1966 Get a console for the virtual machine
1968 vm_id: uuid of the VM
1969 console_type, can be:
1970 "novnc" (by default), "xvpvnc" for VNC types,
1971 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1972 Returns dict with the console parameters:
1973 protocol: ssh, ftp, http, https, ...
1974 server: usually ip address
1975 port: the http, ssh, ... port
1976 suffix: extra text, e.g. the http path and query string
1978 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1980 # NOT USED METHODS in current version
1982 def host_vim2gui(self
, host
, server_dict
):
1983 """Transform host dictionary from VIM format to GUI format,
1984 and append to the server_dict
1986 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1988 def get_hosts_info(self
):
1989 """Get the information of deployed hosts
1990 Returns the hosts content"""
1991 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1993 def get_hosts(self
, vim_tenant
):
1994 """Get the hosts and deployed instances
1995 Returns the hosts content"""
1996 raise vimconn
.vimconnNotImplemented("Should have implemented this")
1998 def get_processor_rankings(self
):
1999 """Get the processor rankings in the VIM database"""
2000 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2002 def new_host(self
, host_data
):
2003 """Adds a new host to VIM"""
2004 '''Returns status code of the VIM response'''
2005 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2007 def new_external_port(self
, port_data
):
2008 """Adds a external port to VIM"""
2009 '''Returns the port identifier'''
2010 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2012 def new_external_network(self
, net_name
, net_type
):
2013 """Adds a external network to VIM (shared)"""
2014 '''Returns the network identifier'''
2015 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2017 def connect_port_network(self
, port_id
, network_id
, admin
=False):
2018 """Connects a external port to a network"""
2019 '''Returns status code of the VIM response'''
2020 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2022 def new_vminstancefromJSON(self
, vm_data
):
2023 """Adds a VM instance to VIM"""
2024 '''Returns the instance identifier'''
2025 raise vimconn
.vimconnNotImplemented("Should have implemented this")
2027 def get_network_name_by_id(self
, network_uuid
=None):
2028 """Method gets vcloud director network named based on supplied uuid.
2031 network_uuid: network_id
2034 The return network name.
2037 vca
= self
.connect()
2039 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2041 if not network_uuid
:
2045 org_dict
= self
.get_org(self
.org_uuid
)
2046 if 'networks' in org_dict
:
2047 org_network_dict
= org_dict
['networks']
2048 for net_uuid
in org_network_dict
:
2049 if net_uuid
== network_uuid
:
2050 return org_network_dict
[net_uuid
]
2052 self
.logger
.debug("Exception in get_network_name_by_id")
2053 self
.logger
.debug(traceback
.format_exc())
2057 def get_network_id_by_name(self
, network_name
=None):
2058 """Method gets vcloud director network uuid based on supplied name.
2061 network_name: network_name
2063 The return network uuid.
2064 network_uuid: network_id
2067 vca
= self
.connect()
2069 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2071 if not network_name
:
2072 self
.logger
.debug("get_network_id_by_name() : Network name is empty")
2076 org_dict
= self
.get_org(self
.org_uuid
)
2077 if org_dict
and 'networks' in org_dict
:
2078 org_network_dict
= org_dict
['networks']
2079 for net_uuid
,net_name
in org_network_dict
.iteritems():
2080 if net_name
== network_name
:
2083 except KeyError as exp
:
2084 self
.logger
.debug("get_network_id_by_name() : KeyError- {} ".format(exp
))
2088 def list_org_action(self
):
2090 Method leverages vCloud director and query for available organization for particular user
2093 vca - is active VCA connection.
2094 vdc_name - is a vdc name that will be used to query vms action
2097 The return XML respond
2100 vca
= self
.connect()
2102 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2104 url_list
= [vca
.host
, '/api/org']
2105 vm_list_rest_call
= ''.join(url_list
)
2107 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2108 response
= Http
.get(url
=vm_list_rest_call
,
2109 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2112 if response
.status_code
== requests
.codes
.ok
:
2113 return response
.content
2117 def get_org_action(self
, org_uuid
=None):
2119 Method leverages vCloud director and retrieve available object fdr organization.
2122 vca - is active VCA connection.
2123 vdc_name - is a vdc name that will be used to query vms action
2126 The return XML respond
2129 vca
= self
.connect()
2131 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2133 if org_uuid
is None:
2136 url_list
= [vca
.host
, '/api/org/', org_uuid
]
2137 vm_list_rest_call
= ''.join(url_list
)
2139 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2140 response
= Http
.get(url
=vm_list_rest_call
,
2141 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2144 if response
.status_code
== requests
.codes
.ok
:
2145 return response
.content
2149 def get_org(self
, org_uuid
=None):
2151 Method retrieves available organization in vCloud Director
2154 org_uuid - is a organization uuid.
2157 The return dictionary with following key
2158 "network" - for network list under the org
2159 "catalogs" - for network list under the org
2160 "vdcs" - for vdc list under org
2164 vca
= self
.connect()
2166 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2168 if org_uuid
is None:
2171 content
= self
.get_org_action(org_uuid
=org_uuid
)
2176 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2177 for child
in vm_list_xmlroot
:
2178 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2179 vdc_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2180 org_dict
['vdcs'] = vdc_list
2181 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2182 network_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2183 org_dict
['networks'] = network_list
2184 if child
.attrib
['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2185 catalog_list
[child
.attrib
['href'].split("/")[-1:][0]] = child
.attrib
['name']
2186 org_dict
['catalogs'] = catalog_list
2192 def get_org_list(self
):
2194 Method retrieves available organization in vCloud Director
2197 vca - is active VCA connection.
2200 The return dictionary and key for each entry VDC UUID
2204 vca
= self
.connect()
2206 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2208 content
= self
.list_org_action()
2210 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2211 for vm_xml
in vm_list_xmlroot
:
2212 if vm_xml
.tag
.split("}")[1] == 'Org':
2213 org_uuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2214 org_dict
[org_uuid
[0]] = vm_xml
.attrib
['name']
2220 def vms_view_action(self
, vdc_name
=None):
2221 """ Method leverages vCloud director vms query call
2224 vca - is active VCA connection.
2225 vdc_name - is a vdc name that will be used to query vms action
2228 The return XML respond
2230 vca
= self
.connect()
2231 if vdc_name
is None:
2234 url_list
= [vca
.host
, '/api/vms/query']
2235 vm_list_rest_call
= ''.join(url_list
)
2237 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2238 refs
= filter(lambda ref
: ref
.name
== vdc_name
and ref
.type_
== 'application/vnd.vmware.vcloud.vdc+xml',
2239 vca
.vcloud_session
.organization
.Link
)
2241 response
= Http
.get(url
=vm_list_rest_call
,
2242 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2245 if response
.status_code
== requests
.codes
.ok
:
2246 return response
.content
2250 def get_vapp_list(self
, vdc_name
=None):
2252 Method retrieves vApp list deployed vCloud director and returns a dictionary
2253 contains a list of all vapp deployed for queried VDC.
2254 The key for a dictionary is vApp UUID
2258 vca - is active VCA connection.
2259 vdc_name - is a vdc name that will be used to query vms action
2262 The return dictionary and key for each entry vapp UUID
2266 if vdc_name
is None:
2269 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2271 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2272 for vm_xml
in vm_list_xmlroot
:
2273 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2274 if vm_xml
.attrib
['isVAppTemplate'] == 'true':
2275 rawuuid
= vm_xml
.attrib
['container'].split('/')[-1:]
2276 if 'vappTemplate-' in rawuuid
[0]:
2277 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2278 # vm and use raw UUID as key
2279 vapp_dict
[rawuuid
[0][13:]] = vm_xml
.attrib
2285 def get_vm_list(self
, vdc_name
=None):
2287 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2288 contains a list of all VM's deployed for queried VDC.
2289 The key for a dictionary is VM UUID
2293 vca - is active VCA connection.
2294 vdc_name - is a vdc name that will be used to query vms action
2297 The return dictionary and key for each entry vapp UUID
2301 if vdc_name
is None:
2304 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2306 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2307 for vm_xml
in vm_list_xmlroot
:
2308 if vm_xml
.tag
.split("}")[1] == 'VMRecord':
2309 if vm_xml
.attrib
['isVAppTemplate'] == 'false':
2310 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2311 if 'vm-' in rawuuid
[0]:
2312 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2313 # vm and use raw UUID as key
2314 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2320 def get_vapp(self
, vdc_name
=None, vapp_name
=None, isuuid
=False):
2322 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2323 contains a list of all VM's deployed for queried VDC.
2324 The key for a dictionary is VM UUID
2328 vca - is active VCA connection.
2329 vdc_name - is a vdc name that will be used to query vms action
2332 The return dictionary and key for each entry vapp UUID
2335 vca
= self
.connect()
2337 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2339 if vdc_name
is None:
2342 content
= self
.vms_view_action(vdc_name
=vdc_name
)
2344 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2345 for vm_xml
in vm_list_xmlroot
:
2346 if vm_xml
.tag
.split("}")[1] == 'VMRecord' and vm_xml
.attrib
['isVAppTemplate'] == 'false':
2347 # lookup done by UUID
2349 if vapp_name
in vm_xml
.attrib
['container']:
2350 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2351 if 'vm-' in rawuuid
[0]:
2352 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2354 # lookup done by Name
2356 if vapp_name
in vm_xml
.attrib
['name']:
2357 rawuuid
= vm_xml
.attrib
['href'].split('/')[-1:]
2358 if 'vm-' in rawuuid
[0]:
2359 vm_dict
[rawuuid
[0][3:]] = vm_xml
.attrib
2366 def get_network_action(self
, network_uuid
=None):
2368 Method leverages vCloud director and query network based on network uuid
2371 vca - is active VCA connection.
2372 network_uuid - is a network uuid
2375 The return XML respond
2378 vca
= self
.connect()
2380 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2382 if network_uuid
is None:
2385 url_list
= [vca
.host
, '/api/network/', network_uuid
]
2386 vm_list_rest_call
= ''.join(url_list
)
2388 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2389 response
= Http
.get(url
=vm_list_rest_call
,
2390 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2393 if response
.status_code
== requests
.codes
.ok
:
2394 return response
.content
2398 def get_vcd_network(self
, network_uuid
=None):
2400 Method retrieves available network from vCloud Director
2403 network_uuid - is VCD network UUID
2405 Each element serialized as key : value pair
2407 Following keys available for access. network_configuration['Gateway'}
2411 <IsInherited>true</IsInherited>
2412 <Gateway>172.16.252.100</Gateway>
2413 <Netmask>255.255.255.0</Netmask>
2414 <Dns1>172.16.254.201</Dns1>
2415 <Dns2>172.16.254.202</Dns2>
2416 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2417 <IsEnabled>true</IsEnabled>
2420 <StartAddress>172.16.252.1</StartAddress>
2421 <EndAddress>172.16.252.99</EndAddress>
2426 <FenceMode>bridged</FenceMode>
2429 The return dictionary and key for each entry vapp UUID
2432 network_configuration
= {}
2433 if network_uuid
is None:
2437 content
= self
.get_network_action(network_uuid
=network_uuid
)
2438 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2440 network_configuration
['status'] = vm_list_xmlroot
.get("status")
2441 network_configuration
['name'] = vm_list_xmlroot
.get("name")
2442 network_configuration
['uuid'] = vm_list_xmlroot
.get("id").split(":")[3]
2444 for child
in vm_list_xmlroot
:
2445 if child
.tag
.split("}")[1] == 'IsShared':
2446 network_configuration
['isShared'] = child
.text
.strip()
2447 if child
.tag
.split("}")[1] == 'Configuration':
2448 for configuration
in child
.iter():
2449 tagKey
= configuration
.tag
.split("}")[1].strip()
2451 network_configuration
[tagKey
] = configuration
.text
.strip()
2452 return network_configuration
2453 except Exception as exp
:
2454 self
.logger
.debug("get_vcd_network: Failed with Exception {}".format(exp
))
2455 raise vimconn
.vimconnException("get_vcd_network: Failed with Exception {}".format(exp
))
2457 return network_configuration
2459 def delete_network_action(self
, network_uuid
=None):
2461 Method delete given network from vCloud director
2464 network_uuid - is a network uuid that client wish to delete
2467 The return None or XML respond or false
2470 vca
= self
.connect_as_admin()
2472 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2473 if network_uuid
is None:
2476 url_list
= [vca
.host
, '/api/admin/network/', network_uuid
]
2477 vm_list_rest_call
= ''.join(url_list
)
2479 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2480 response
= Http
.delete(url
=vm_list_rest_call
,
2481 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2485 if response
.status_code
== 202:
2490 def create_network(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2491 ip_profile
=None, isshared
='true'):
2493 Method create network in vCloud director
2496 network_name - is network name to be created.
2497 net_type - can be 'bridge','data','ptp','mgmt'.
2498 ip_profile is a dict containing the IP parameters of the network
2499 isshared - is a boolean
2500 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2501 It optional attribute. by default if no parent network indicate the first available will be used.
2504 The return network uuid or return None
2507 new_network_name
= [network_name
, '-', str(uuid
.uuid4())]
2508 content
= self
.create_network_rest(network_name
=''.join(new_network_name
),
2509 ip_profile
=ip_profile
,
2511 parent_network_uuid
=parent_network_uuid
,
2514 self
.logger
.debug("Failed create network {}.".format(network_name
))
2518 vm_list_xmlroot
= XmlElementTree
.fromstring(content
)
2519 vcd_uuid
= vm_list_xmlroot
.get('id').split(":")
2520 if len(vcd_uuid
) == 4:
2521 self
.logger
.info("Created new network name: {} uuid: {}".format(network_name
, vcd_uuid
[3]))
2524 self
.logger
.debug("Failed create network {}".format(network_name
))
2527 def create_network_rest(self
, network_name
=None, net_type
='bridge', parent_network_uuid
=None,
2528 ip_profile
=None, isshared
='true'):
2530 Method create network in vCloud director
2533 network_name - is network name to be created.
2534 net_type - can be 'bridge','data','ptp','mgmt'.
2535 ip_profile is a dict containing the IP parameters of the network
2536 isshared - is a boolean
2537 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2538 It optional attribute. by default if no parent network indicate the first available will be used.
2541 The return network uuid or return None
2544 vca
= self
.connect_as_admin()
2546 raise vimconn
.vimconnConnectionException("self.connect() is failed.")
2547 if network_name
is None:
2550 url_list
= [vca
.host
, '/api/admin/vdc/', self
.tenant_id
]
2551 vm_list_rest_call
= ''.join(url_list
)
2552 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2553 response
= Http
.get(url
=vm_list_rest_call
,
2554 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2558 provider_network
= None
2559 available_networks
= None
2560 add_vdc_rest_url
= None
2562 if response
.status_code
!= requests
.codes
.ok
:
2563 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2564 response
.status_code
))
2568 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2569 for child
in vm_list_xmlroot
:
2570 if child
.tag
.split("}")[1] == 'ProviderVdcReference':
2571 provider_network
= child
.attrib
.get('href')
2572 # application/vnd.vmware.admin.providervdc+xml
2573 if child
.tag
.split("}")[1] == 'Link':
2574 if child
.attrib
.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2575 and child
.attrib
.get('rel') == 'add':
2576 add_vdc_rest_url
= child
.attrib
.get('href')
2578 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2579 self
.logger
.debug("Respond body {}".format(response
.content
))
2582 # find pvdc provided available network
2583 response
= Http
.get(url
=provider_network
,
2584 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2587 if response
.status_code
!= requests
.codes
.ok
:
2588 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2589 response
.status_code
))
2592 # available_networks.split("/")[-1]
2594 if parent_network_uuid
is None:
2596 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2597 for child
in vm_list_xmlroot
.iter():
2598 if child
.tag
.split("}")[1] == 'AvailableNetworks':
2599 for networks
in child
.iter():
2600 # application/vnd.vmware.admin.network+xml
2601 if networks
.attrib
.get('href') is not None:
2602 available_networks
= networks
.attrib
.get('href')
2608 #Configure IP profile of the network
2609 ip_profile
= ip_profile
if ip_profile
is not None else DEFAULT_IP_PROFILE
2611 if 'subnet_address' not in ip_profile
or ip_profile
['subnet_address'] is None:
2612 subnet_rand
= random
.randint(0, 255)
2613 ip_base
= "192.168.{}.".format(subnet_rand
)
2614 ip_profile
['subnet_address'] = ip_base
+ "0/24"
2616 ip_base
= ip_profile
['subnet_address'].rsplit('.',1)[0] + '.'
2618 if 'gateway_address' not in ip_profile
or ip_profile
['gateway_address'] is None:
2619 ip_profile
['gateway_address']=ip_base
+ "1"
2620 if 'dhcp_count' not in ip_profile
or ip_profile
['dhcp_count'] is None:
2621 ip_profile
['dhcp_count']=DEFAULT_IP_PROFILE
['dhcp_count']
2622 if 'dhcp_enabled' not in ip_profile
or ip_profile
['dhcp_enabled'] is None:
2623 ip_profile
['dhcp_enabled']=DEFAULT_IP_PROFILE
['dhcp_enabled']
2624 if 'dhcp_start_address' not in ip_profile
or ip_profile
['dhcp_start_address'] is None:
2625 ip_profile
['dhcp_start_address']=ip_base
+ "3"
2626 if 'ip_version' not in ip_profile
or ip_profile
['ip_version'] is None:
2627 ip_profile
['ip_version']=DEFAULT_IP_PROFILE
['ip_version']
2628 if 'dns_address' not in ip_profile
or ip_profile
['dns_address'] is None:
2629 ip_profile
['dns_address']=ip_base
+ "2"
2631 gateway_address
=ip_profile
['gateway_address']
2632 dhcp_count
=int(ip_profile
['dhcp_count'])
2633 subnet_address
=self
.convert_cidr_to_netmask(ip_profile
['subnet_address'])
2635 if ip_profile
['dhcp_enabled']==True:
2638 dhcp_enabled
='false'
2639 dhcp_start_address
=ip_profile
['dhcp_start_address']
2641 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2642 end_ip_int
= int(netaddr
.IPAddress(dhcp_start_address
))
2643 end_ip_int
+= dhcp_count
- 1
2644 dhcp_end_address
= str(netaddr
.IPAddress(end_ip_int
))
2646 ip_version
=ip_profile
['ip_version']
2647 dns_address
=ip_profile
['dns_address']
2648 except KeyError as exp
:
2649 self
.logger
.debug("Create Network REST: Key error {}".format(exp
))
2650 raise vimconn
.vimconnException("Create Network REST: Key error{}".format(exp
))
2652 # either use client provided UUID or search for a first available
2653 # if both are not defined we return none
2654 if parent_network_uuid
is not None:
2655 url_list
= [vca
.host
, '/api/admin/network/', parent_network_uuid
]
2656 add_vdc_rest_url
= ''.join(url_list
)
2659 fence_mode
="isolated"
2661 is_inherited
='false'
2662 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2663 <Description>Openmano created</Description>
2667 <IsInherited>{1:s}</IsInherited>
2668 <Gateway>{2:s}</Gateway>
2669 <Netmask>{3:s}</Netmask>
2671 <IsEnabled>{5:s}</IsEnabled>
2674 <StartAddress>{6:s}</StartAddress>
2675 <EndAddress>{7:s}</EndAddress>
2680 <FenceMode>{8:s}</FenceMode>
2682 <IsShared>{9:s}</IsShared>
2683 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2684 subnet_address
, dns_address
, dhcp_enabled
,
2685 dhcp_start_address
, dhcp_end_address
, fence_mode
, isshared
)
2688 fence_mode
="bridged"
2689 is_inherited
='false'
2690 data
= """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2691 <Description>Openmano created</Description>
2695 <IsInherited>{1:s}</IsInherited>
2696 <Gateway>{2:s}</Gateway>
2697 <Netmask>{3:s}</Netmask>
2699 <IsEnabled>{5:s}</IsEnabled>
2702 <StartAddress>{6:s}</StartAddress>
2703 <EndAddress>{7:s}</EndAddress>
2708 <ParentNetwork href="{8:s}"/>
2709 <FenceMode>{9:s}</FenceMode>
2711 <IsShared>{10:s}</IsShared>
2712 </OrgVdcNetwork> """.format(escape(network_name
), is_inherited
, gateway_address
,
2713 subnet_address
, dns_address
, dhcp_enabled
,
2714 dhcp_start_address
, dhcp_end_address
, available_networks
,
2715 fence_mode
, isshared
)
2717 headers
= vca
.vcloud_session
.get_vcloud_headers()
2718 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2720 response
= Http
.post(url
=add_vdc_rest_url
,
2726 if response
.status_code
!= 201:
2727 self
.logger
.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2728 .format(response
.status_code
,response
.content
))
2730 network
= networkType
.parseString(response
.content
, True)
2731 create_nw_task
= network
.get_Tasks().get_Task()[0]
2733 # if we all ok we respond with content after network creation completes
2734 # otherwise by default return None
2735 if create_nw_task
is not None:
2736 self
.logger
.debug("Create Network REST : Waiting for Network creation complete")
2737 status
= vca
.block_until_completed(create_nw_task
)
2739 return response
.content
2741 self
.logger
.debug("create_network_rest task failed. Network Create response : {}"
2742 .format(response
.content
))
2743 except Exception as exp
:
2744 self
.logger
.debug("create_network_rest : Exception : {} ".format(exp
))
2748 def convert_cidr_to_netmask(self
, cidr_ip
=None):
2750 Method sets convert CIDR netmask address to normal IP format
2752 cidr_ip : CIDR IP address
2754 netmask : Converted netmask
2756 if cidr_ip
is not None:
2758 network
, net_bits
= cidr_ip
.split('/')
2759 netmask
= socket
.inet_ntoa(struct
.pack(">I", (0xffffffff << (32 - int(net_bits
))) & 0xffffffff))
2765 def get_provider_rest(self
, vca
=None):
2767 Method gets provider vdc view from vcloud director
2770 network_name - is network name to be created.
2771 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2772 It optional attribute. by default if no parent network indicate the first available will be used.
2775 The return xml content of respond or None
2778 url_list
= [vca
.host
, '/api/admin']
2779 response
= Http
.get(url
=''.join(url_list
),
2780 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2784 if response
.status_code
== requests
.codes
.ok
:
2785 return response
.content
2788 def create_vdc(self
, vdc_name
=None):
2792 xml_content
= self
.create_vdc_from_tmpl_rest(vdc_name
=vdc_name
)
2793 if xml_content
is not None:
2795 task_resp_xmlroot
= XmlElementTree
.fromstring(xml_content
)
2796 for child
in task_resp_xmlroot
:
2797 if child
.tag
.split("}")[1] == 'Owner':
2798 vdc_id
= child
.attrib
.get('href').split("/")[-1]
2799 vdc_dict
[vdc_id
] = task_resp_xmlroot
.get('href')
2802 self
.logger
.debug("Respond body {}".format(xml_content
))
2806 def create_vdc_from_tmpl_rest(self
, vdc_name
=None):
2808 Method create vdc in vCloud director based on VDC template.
2809 it uses pre-defined template that must be named openmano
2812 vdc_name - name of a new vdc.
2815 The return xml content of respond or None
2818 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2819 vca
= self
.connect()
2821 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2822 if vdc_name
is None:
2825 url_list
= [vca
.host
, '/api/vdcTemplates']
2826 vm_list_rest_call
= ''.join(url_list
)
2827 response
= Http
.get(url
=vm_list_rest_call
,
2828 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2832 # container url to a template
2833 vdc_template_ref
= None
2835 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2836 for child
in vm_list_xmlroot
:
2837 # application/vnd.vmware.admin.providervdc+xml
2838 # we need find a template from witch we instantiate VDC
2839 if child
.tag
.split("}")[1] == 'VdcTemplate':
2840 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child
.attrib
.get(
2841 'name') == 'openmano':
2842 vdc_template_ref
= child
.attrib
.get('href')
2844 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2845 self
.logger
.debug("Respond body {}".format(response
.content
))
2848 # if we didn't found required pre defined template we return None
2849 if vdc_template_ref
is None:
2854 url_list
= [vca
.host
, '/api/org/', self
.org_uuid
, '/action/instantiate']
2855 vm_list_rest_call
= ''.join(url_list
)
2856 data
= """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2857 <Source href="{1:s}"></Source>
2858 <Description>opnemano</Description>
2859 </InstantiateVdcTemplateParams>""".format(vdc_name
, vdc_template_ref
)
2860 headers
= vca
.vcloud_session
.get_vcloud_headers()
2861 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2862 response
= Http
.post(url
=vm_list_rest_call
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2864 # if we all ok we respond with content otherwise by default None
2865 if response
.status_code
>= 200 and response
.status_code
< 300:
2866 return response
.content
2869 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2870 self
.logger
.debug("Respond body {}".format(response
.content
))
2874 def create_vdc_rest(self
, vdc_name
=None):
2876 Method create network in vCloud director
2879 network_name - is network name to be created.
2880 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2881 It optional attribute. by default if no parent network indicate the first available will be used.
2884 The return network uuid or return None
2887 self
.logger
.info("Creating new vdc {}".format(vdc_name
))
2889 vca
= self
.connect_as_admin()
2891 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2892 if vdc_name
is None:
2895 url_list
= [vca
.host
, '/api/admin/org/', self
.org_uuid
]
2896 vm_list_rest_call
= ''.join(url_list
)
2897 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
2898 response
= Http
.get(url
=vm_list_rest_call
,
2899 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2903 provider_vdc_ref
= None
2904 add_vdc_rest_url
= None
2905 available_networks
= None
2907 if response
.status_code
!= requests
.codes
.ok
:
2908 self
.logger
.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call
,
2909 response
.status_code
))
2913 vm_list_xmlroot
= XmlElementTree
.fromstring(response
.content
)
2914 for child
in vm_list_xmlroot
:
2915 # application/vnd.vmware.admin.providervdc+xml
2916 if child
.tag
.split("}")[1] == 'Link':
2917 if child
.attrib
.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2918 and child
.attrib
.get('rel') == 'add':
2919 add_vdc_rest_url
= child
.attrib
.get('href')
2921 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2922 self
.logger
.debug("Respond body {}".format(response
.content
))
2925 response
= self
.get_provider_rest(vca
=vca
)
2927 vm_list_xmlroot
= XmlElementTree
.fromstring(response
)
2928 for child
in vm_list_xmlroot
:
2929 if child
.tag
.split("}")[1] == 'ProviderVdcReferences':
2930 for sub_child
in child
:
2931 provider_vdc_ref
= sub_child
.attrib
.get('href')
2933 self
.logger
.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call
))
2934 self
.logger
.debug("Respond body {}".format(response
))
2937 if add_vdc_rest_url
is not None and provider_vdc_ref
is not None:
2938 data
= """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2939 <AllocationModel>ReservationPool</AllocationModel>
2940 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2941 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2942 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2943 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2944 <ProviderVdcReference
2945 name="Main Provider"
2947 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name
),
2951 headers
= vca
.vcloud_session
.get_vcloud_headers()
2952 headers
['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2953 response
= Http
.post(url
=add_vdc_rest_url
, headers
=headers
, data
=data
, verify
=vca
.verify
,
2956 # if we all ok we respond with content otherwise by default None
2957 if response
.status_code
== 201:
2958 return response
.content
2961 def get_vapp_details_rest(self
, vapp_uuid
=None, need_admin_access
=False):
2963 Method retrieve vapp detail from vCloud director
2966 vapp_uuid - is vapp identifier.
2969 The return network uuid or return None
2975 if need_admin_access
:
2976 vca
= self
.connect_as_admin()
2978 vca
= self
.connect()
2981 raise vimconn
.vimconnConnectionException("self.connect() is failed")
2982 if vapp_uuid
is None:
2985 url_list
= [vca
.host
, '/api/vApp/vapp-', vapp_uuid
]
2986 get_vapp_restcall
= ''.join(url_list
)
2988 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
2989 response
= Http
.get(url
=get_vapp_restcall
,
2990 headers
=vca
.vcloud_session
.get_vcloud_headers(),
2994 if response
.status_code
!= requests
.codes
.ok
:
2995 self
.logger
.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall
,
2996 response
.status_code
))
2997 return parsed_respond
3000 xmlroot_respond
= XmlElementTree
.fromstring(response
.content
)
3001 parsed_respond
['ovfDescriptorUploaded'] = xmlroot_respond
.attrib
['ovfDescriptorUploaded']
3003 namespaces
= {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
3004 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
3005 'vmw': 'http://www.vmware.com/schema/ovf',
3006 'vm': 'http://www.vmware.com/vcloud/v1.5',
3007 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3008 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3009 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3012 created_section
= xmlroot_respond
.find('vm:DateCreated', namespaces
)
3013 if created_section
is not None:
3014 parsed_respond
['created'] = created_section
.text
3016 network_section
= xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces
)
3017 if network_section
is not None and 'networkName' in network_section
.attrib
:
3018 parsed_respond
['networkname'] = network_section
.attrib
['networkName']
3020 ipscopes_section
= \
3021 xmlroot_respond
.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3023 if ipscopes_section
is not None:
3024 for ipscope
in ipscopes_section
:
3025 for scope
in ipscope
:
3026 tag_key
= scope
.tag
.split("}")[1]
3027 if tag_key
== 'IpRanges':
3028 ip_ranges
= scope
.getchildren()
3029 for ipblock
in ip_ranges
:
3030 for block
in ipblock
:
3031 parsed_respond
[block
.tag
.split("}")[1]] = block
.text
3033 parsed_respond
[tag_key
] = scope
.text
3035 # parse children section for other attrib
3036 children_section
= xmlroot_respond
.find('vm:Children/', namespaces
)
3037 if children_section
is not None:
3038 parsed_respond
['name'] = children_section
.attrib
['name']
3039 parsed_respond
['nestedHypervisorEnabled'] = children_section
.attrib
['nestedHypervisorEnabled'] \
3040 if "nestedHypervisorEnabled" in children_section
.attrib
else None
3041 parsed_respond
['deployed'] = children_section
.attrib
['deployed']
3042 parsed_respond
['status'] = children_section
.attrib
['status']
3043 parsed_respond
['vmuuid'] = children_section
.attrib
['id'].split(":")[-1]
3044 network_adapter
= children_section
.find('vm:NetworkConnectionSection', namespaces
)
3046 for adapters
in network_adapter
:
3047 adapter_key
= adapters
.tag
.split("}")[1]
3048 if adapter_key
== 'PrimaryNetworkConnectionIndex':
3049 parsed_respond
['primarynetwork'] = adapters
.text
3050 if adapter_key
== 'NetworkConnection':
3052 if 'network' in adapters
.attrib
:
3053 vnic
['network'] = adapters
.attrib
['network']
3054 for adapter
in adapters
:
3055 setting_key
= adapter
.tag
.split("}")[1]
3056 vnic
[setting_key
] = adapter
.text
3057 nic_list
.append(vnic
)
3059 for link
in children_section
:
3060 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3061 if link
.attrib
['rel'] == 'screen:acquireTicket':
3062 parsed_respond
['acquireTicket'] = link
.attrib
3063 if link
.attrib
['rel'] == 'screen:acquireMksTicket':
3064 parsed_respond
['acquireMksTicket'] = link
.attrib
3066 parsed_respond
['interfaces'] = nic_list
3067 vCloud_extension_section
= children_section
.find('xmlns:VCloudExtension', namespaces
)
3068 if vCloud_extension_section
is not None:
3069 vm_vcenter_info
= {}
3070 vim_info
= vCloud_extension_section
.find('vmext:VmVimInfo', namespaces
)
3071 vmext
= vim_info
.find('vmext:VmVimObjectRef', namespaces
)
3072 if vmext
is not None:
3073 vm_vcenter_info
["vm_moref_id"] = vmext
.find('vmext:MoRef', namespaces
).text
3074 parsed_respond
["vm_vcenter_info"]= vm_vcenter_info
3076 virtual_hardware_section
= children_section
.find('ovf:VirtualHardwareSection', namespaces
)
3077 vm_virtual_hardware_info
= {}
3078 if virtual_hardware_section
is not None:
3079 for item
in virtual_hardware_section
.iterfind('ovf:Item',namespaces
):
3080 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3081 disk_size
= item
.find("rasd:HostResource" ,namespaces
3082 ).attrib
["{"+namespaces
['vm']+"}capacity"]
3084 vm_virtual_hardware_info
["disk_size"]= disk_size
3087 for link
in virtual_hardware_section
:
3088 if link
.tag
.split("}")[1] == 'Link' and 'rel' in link
.attrib
:
3089 if link
.attrib
['rel'] == 'edit' and link
.attrib
['href'].endswith("/disks"):
3090 vm_virtual_hardware_info
["disk_edit_href"] = link
.attrib
['href']
3093 parsed_respond
["vm_virtual_hardware"]= vm_virtual_hardware_info
3094 except Exception as exp
:
3095 self
.logger
.info("Error occurred calling rest api for getting vApp details {}".format(exp
))
3096 return parsed_respond
3098 def acuire_console(self
, vm_uuid
=None):
3100 vca
= self
.connect()
3102 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3106 if not (not vca
.vcloud_session
or not vca
.vcloud_session
.organization
):
3107 vm_dict
= self
.get_vapp_details_rest(self
, vapp_uuid
=vm_uuid
)
3108 console_dict
= vm_dict
['acquireTicket']
3109 console_rest_call
= console_dict
['href']
3111 response
= Http
.post(url
=console_rest_call
,
3112 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3116 if response
.status_code
== requests
.codes
.ok
:
3117 return response
.content
3121 def modify_vm_disk(self
, vapp_uuid
, flavor_disk
):
3123 Method retrieve vm disk details
3126 vapp_uuid - is vapp identifier.
3127 flavor_disk - disk size as specified in VNFD (flavor)
3130 The return network uuid or return None
3134 #Flavor disk is in GB convert it into MB
3135 flavor_disk
= int(flavor_disk
) * 1024
3136 vm_details
= self
.get_vapp_details_rest(vapp_uuid
)
3138 vm_name
= vm_details
["name"]
3139 self
.logger
.info("VM: {} flavor_disk :{}".format(vm_name
, flavor_disk
))
3141 if vm_details
and "vm_virtual_hardware" in vm_details
:
3142 vm_disk
= int(vm_details
["vm_virtual_hardware"]["disk_size"])
3143 disk_edit_href
= vm_details
["vm_virtual_hardware"]["disk_edit_href"]
3145 self
.logger
.info("VM: {} VM_disk :{}".format(vm_name
, vm_disk
))
3147 if flavor_disk
> vm_disk
:
3148 status
= self
.modify_vm_disk_rest(disk_edit_href
,flavor_disk
)
3149 self
.logger
.info("Modify disk of VM {} from {} to {} MB".format(vm_name
,
3150 vm_disk
, flavor_disk
))
3153 self
.logger
.info("No need to modify disk of VM {}".format(vm_name
))
3156 except Exception as exp
:
3157 self
.logger
.info("Error occurred while modifing disk size {}".format(exp
))
3160 def modify_vm_disk_rest(self
, disk_href
, disk_size
):
3162 Method retrieve modify vm disk size
3165 disk_href - vCD API URL to GET and PUT disk data
3166 disk_size - disk size as specified in VNFD (flavor)
3169 The return network uuid or return None
3171 vca
= self
.connect()
3173 raise vimconn
.vimconnConnectionException("self.connect() is failed")
3174 if disk_href
is None or disk_size
is None:
3177 if vca
.vcloud_session
and vca
.vcloud_session
.organization
:
3178 response
= Http
.get(url
=disk_href
,
3179 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3183 if response
.status_code
!= requests
.codes
.ok
:
3184 self
.logger
.debug("GET REST API call {} failed. Return status code {}".format(disk_href
,
3185 response
.status_code
))
3188 lxmlroot_respond
= lxmlElementTree
.fromstring(response
.content
)
3189 namespaces
= {prefix
:uri
for prefix
,uri
in lxmlroot_respond
.nsmap
.iteritems() if prefix
}
3190 namespaces
["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3192 for item
in lxmlroot_respond
.iterfind('xmlns:Item',namespaces
):
3193 if item
.find("rasd:Description",namespaces
).text
== "Hard disk":
3194 disk_item
= item
.find("rasd:HostResource" ,namespaces
)
3195 if disk_item
is not None:
3196 disk_item
.attrib
["{"+namespaces
['xmlns']+"}capacity"] = str(disk_size
)
3199 data
= lxmlElementTree
.tostring(lxmlroot_respond
, encoding
='utf8', method
='xml',
3200 xml_declaration
=True)
3202 #Send PUT request to modify disk size
3203 headers
= vca
.vcloud_session
.get_vcloud_headers()
3204 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3206 response
= Http
.put(url
=disk_href
,
3209 verify
=vca
.verify
, logger
=self
.logger
)
3211 if response
.status_code
!= 202:
3212 self
.logger
.debug("PUT REST API call {} failed. Return status code {}".format(disk_href
,
3213 response
.status_code
))
3215 modify_disk_task
= taskType
.parseString(response
.content
, True)
3216 if type(modify_disk_task
) is GenericTask
:
3217 status
= vca
.block_until_completed(modify_disk_task
)
3222 except Exception as exp
:
3223 self
.logger
.info("Error occurred calling rest api for modifing disk size {}".format(exp
))
3226 def add_pci_devices(self
, vapp_uuid
, pci_devices
, vmname_andid
):
3228 Method to attach pci devices to VM
3231 vapp_uuid - uuid of vApp/VM
3232 pci_devices - pci devices infromation as specified in VNFD (flavor)
3235 The status of add pci device task , vm object and
3236 vcenter_conect object
3239 vcenter_conect
= None
3240 self
.logger
.info("Add pci devices {} into vApp {}".format(pci_devices
, vapp_uuid
))
3242 vm_vcenter_info
= self
.get_vm_vcenter_info(vapp_uuid
)
3243 except Exception as exp
:
3244 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3245 " for VM : {}".format(exp
))
3246 raise vimconn
.vimconnException(message
=exp
)
3248 if vm_vcenter_info
["vm_moref_id"]:
3250 if hasattr(ssl
, '_create_unverified_context'):
3251 context
= ssl
._create
_unverified
_context
()
3253 no_of_pci_devices
= len(pci_devices
)
3254 if no_of_pci_devices
> 0:
3255 vcenter_conect
= SmartConnect(
3256 host
=vm_vcenter_info
["vm_vcenter_ip"],
3257 user
=vm_vcenter_info
["vm_vcenter_user"],
3258 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3259 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3261 atexit
.register(Disconnect
, vcenter_conect
)
3262 content
= vcenter_conect
.RetrieveContent()
3264 #Get VM and its host
3265 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3266 self
.logger
.info("VM {} is currently on host {}".format(vm_obj
, host_obj
))
3267 if host_obj
and vm_obj
:
3268 #get PCI devies from host on which vapp is currently installed
3269 avilable_pci_devices
= self
.get_pci_devices(host_obj
, no_of_pci_devices
)
3271 if avilable_pci_devices
is None:
3272 #find other hosts with active pci devices
3273 new_host_obj
, avilable_pci_devices
= self
.get_host_and_PCIdevices(
3278 if new_host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3279 #Migrate vm to the host where PCI devices are availble
3280 self
.logger
.info("Relocate VM {} on new host {}".format(vm_obj
, new_host_obj
))
3281 task
= self
.relocate_vm(new_host_obj
, vm_obj
)
3282 if task
is not None:
3283 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3284 self
.logger
.info("Migrate VM status: {}".format(result
))
3285 host_obj
= new_host_obj
3287 self
.logger
.info("Fail to migrate VM : {}".format(result
))
3288 raise vimconn
.vimconnNotFoundException(
3289 "Fail to migrate VM : {} to host {}".format(
3294 if host_obj
is not None and avilable_pci_devices
is not None and len(avilable_pci_devices
)> 0:
3295 #Add PCI devices one by one
3296 for pci_device
in avilable_pci_devices
:
3297 task
= self
.add_pci_to_vm(host_obj
, vm_obj
, pci_device
)
3299 status
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3301 self
.logger
.info("Added PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3303 self
.logger
.error("Fail to add PCI device {} to VM {}".format(pci_device
,str(vm_obj
)))
3304 return True, vm_obj
, vcenter_conect
3306 self
.logger
.error("Currently there is no host with"\
3307 " {} number of avaialble PCI devices required for VM {}".format(
3311 raise vimconn
.vimconnNotFoundException(
3312 "Currently there is no host with {} "\
3313 "number of avaialble PCI devices required for VM {}".format(
3317 self
.logger
.debug("No infromation about PCI devices {} ",pci_devices
)
3319 except vmodl
.MethodFault
as error
:
3320 self
.logger
.error("Error occurred while adding PCI devices {} ",error
)
3321 return None, vm_obj
, vcenter_conect
3323 def get_vm_obj(self
, content
, mob_id
):
3325 Method to get the vsphere VM object associated with a given morf ID
3327 vapp_uuid - uuid of vApp/VM
3328 content - vCenter content object
3329 mob_id - mob_id of VM
3337 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3338 [vim
.VirtualMachine
], True
3340 for vm
in container
.view
:
3341 mobID
= vm
._GetMoId
()
3344 host_obj
= vm_obj
.runtime
.host
3346 except Exception as exp
:
3347 self
.logger
.error("Error occurred while finding VM object : {}".format(exp
))
3348 return host_obj
, vm_obj
3350 def get_pci_devices(self
, host
, need_devices
):
3352 Method to get the details of pci devices on given host
3354 host - vSphere host object
3355 need_devices - number of pci devices needed on host
3358 array of pci devices
3362 used_devices_ids
= []
3366 pciPassthruInfo
= host
.config
.pciPassthruInfo
3367 pciDevies
= host
.hardware
.pciDevice
3369 for pci_status
in pciPassthruInfo
:
3370 if pci_status
.passthruActive
:
3371 for device
in pciDevies
:
3372 if device
.id == pci_status
.id:
3373 all_device_ids
.append(device
.id)
3374 all_devices
.append(device
)
3376 #check if devices are in use
3377 avalible_devices
= all_devices
3379 if vm
.runtime
.powerState
== vim
.VirtualMachinePowerState
.poweredOn
:
3380 vm_devices
= vm
.config
.hardware
.device
3381 for device
in vm_devices
:
3382 if type(device
) is vim
.vm
.device
.VirtualPCIPassthrough
:
3383 if device
.backing
.id in all_device_ids
:
3384 for use_device
in avalible_devices
:
3385 if use_device
.id == device
.backing
.id:
3386 avalible_devices
.remove(use_device
)
3387 used_devices_ids
.append(device
.backing
.id)
3388 self
.logger
.debug("Device {} from devices {}"\
3389 "is in use".format(device
.backing
.id,
3392 if len(avalible_devices
) < need_devices
:
3393 self
.logger
.debug("Host {} don't have {} number of active devices".format(host
,
3395 self
.logger
.debug("found only {} devives {}".format(len(avalible_devices
),
3399 required_devices
= avalible_devices
[:need_devices
]
3400 self
.logger
.info("Found {} PCI devivces on host {} but required only {}".format(
3401 len(avalible_devices
),
3404 self
.logger
.info("Retruning {} devices as {}".format(need_devices
,
3406 return required_devices
3408 except Exception as exp
:
3409 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host
))
3413 def get_host_and_PCIdevices(self
, content
, need_devices
):
3415 Method to get the details of pci devices infromation on all hosts
3418 content - vSphere host object
3419 need_devices - number of pci devices needed on host
3422 array of pci devices and host object
3425 pci_device_objs
= None
3428 container
= content
.viewManager
.CreateContainerView(content
.rootFolder
,
3429 [vim
.HostSystem
], True)
3430 for host
in container
.view
:
3431 devices
= self
.get_pci_devices(host
, need_devices
)
3434 pci_device_objs
= devices
3436 except Exception as exp
:
3437 self
.logger
.error("Error {} occurred while finding pci devices on host: {}".format(exp
, host_obj
))
3439 return host_obj
,pci_device_objs
3441 def relocate_vm(self
, dest_host
, vm
) :
3443 Method to get the relocate VM to new host
3446 dest_host - vSphere host object
3447 vm - vSphere VM object
3454 relocate_spec
= vim
.vm
.RelocateSpec(host
=dest_host
)
3455 task
= vm
.Relocate(relocate_spec
)
3456 self
.logger
.info("Migrating {} to destination host {}".format(vm
, dest_host
))
3457 except Exception as exp
:
3458 self
.logger
.error("Error occurred while relocate VM {} to new host {}: {}".format(
3459 dest_host
, vm
, exp
))
3462 def wait_for_vcenter_task(self
, task
, actionName
='job', hideResult
=False):
3464 Waits and provides updates on a vSphere task
3466 while task
.info
.state
== vim
.TaskInfo
.State
.running
:
3469 if task
.info
.state
== vim
.TaskInfo
.State
.success
:
3470 if task
.info
.result
is not None and not hideResult
:
3471 self
.logger
.info('{} completed successfully, result: {}'.format(
3475 self
.logger
.info('Task {} completed successfully.'.format(actionName
))
3477 self
.logger
.error('{} did not complete successfully: {} '.format(
3482 return task
.info
.result
3484 def add_pci_to_vm(self
,host_object
, vm_object
, host_pci_dev
):
3486 Method to add pci device in given VM
3489 host_object - vSphere host object
3490 vm_object - vSphere VM object
3491 host_pci_dev - host_pci_dev must be one of the devices from the
3492 host_object.hardware.pciDevice list
3493 which is configured as a PCI passthrough device
3499 if vm_object
and host_object
and host_pci_dev
:
3501 #Add PCI device to VM
3502 pci_passthroughs
= vm_object
.environmentBrowser
.QueryConfigTarget(host
=None).pciPassthrough
3503 systemid_by_pciid
= {item
.pciDevice
.id: item
.systemId
for item
in pci_passthroughs
}
3505 if host_pci_dev
.id not in systemid_by_pciid
:
3506 self
.logger
.error("Device {} is not a passthrough device ".format(host_pci_dev
))
3509 deviceId
= hex(host_pci_dev
.deviceId
% 2**16).lstrip('0x')
3510 backing
= vim
.VirtualPCIPassthroughDeviceBackingInfo(deviceId
=deviceId
,
3512 systemId
=systemid_by_pciid
[host_pci_dev
.id],
3513 vendorId
=host_pci_dev
.vendorId
,
3514 deviceName
=host_pci_dev
.deviceName
)
3516 hba_object
= vim
.VirtualPCIPassthrough(key
=-100, backing
=backing
)
3518 new_device_config
= vim
.VirtualDeviceConfigSpec(device
=hba_object
)
3519 new_device_config
.operation
= "add"
3520 vmConfigSpec
= vim
.vm
.ConfigSpec()
3521 vmConfigSpec
.deviceChange
= [new_device_config
]
3523 task
= vm_object
.ReconfigVM_Task(spec
=vmConfigSpec
)
3524 self
.logger
.info("Adding PCI device {} into VM {} from host {} ".format(
3525 host_pci_dev
, vm_object
, host_object
)
3527 except Exception as exp
:
3528 self
.logger
.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3534 def get_vm_vcenter_info(self
, vapp_uuid
):
3536 Method to get details of vCenter and vm
3539 vapp_uuid - uuid of vApp or VM
3542 Moref Id of VM and deails of vCenter
3544 vm_vcenter_info
= {}
3546 if self
.vcenter_ip
is not None:
3547 vm_vcenter_info
["vm_vcenter_ip"] = self
.vcenter_ip
3549 raise vimconn
.vimconnException(message
="vCenter IP is not provided."\
3550 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3551 if self
.vcenter_port
is not None:
3552 vm_vcenter_info
["vm_vcenter_port"] = self
.vcenter_port
3554 raise vimconn
.vimconnException(message
="vCenter port is not provided."\
3555 " Please provide vCenter port while attaching datacenter to tenant in --config")
3556 if self
.vcenter_user
is not None:
3557 vm_vcenter_info
["vm_vcenter_user"] = self
.vcenter_user
3559 raise vimconn
.vimconnException(message
="vCenter user is not provided."\
3560 " Please provide vCenter user while attaching datacenter to tenant in --config")
3562 if self
.vcenter_password
is not None:
3563 vm_vcenter_info
["vm_vcenter_password"] = self
.vcenter_password
3565 raise vimconn
.vimconnException(message
="vCenter user password is not provided."\
3566 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3568 vm_details
= self
.get_vapp_details_rest(vapp_uuid
, need_admin_access
=True)
3569 if vm_details
and "vm_vcenter_info" in vm_details
:
3570 vm_vcenter_info
["vm_moref_id"] = vm_details
["vm_vcenter_info"].get("vm_moref_id", None)
3572 return vm_vcenter_info
3574 except Exception as exp
:
3575 self
.logger
.error("Error occurred while getting vCenter infromationn"\
3576 " for VM : {}".format(exp
))
3579 def get_vm_pci_details(self
, vmuuid
):
3581 Method to get VM PCI device details from vCenter
3584 vm_obj - vSphere VM object
3587 dict of PCI devives attached to VM
3590 vm_pci_devices_info
= {}
3592 vm_vcenter_info
= self
.get_vm_vcenter_info(vmuuid
)
3593 if vm_vcenter_info
["vm_moref_id"]:
3595 if hasattr(ssl
, '_create_unverified_context'):
3596 context
= ssl
._create
_unverified
_context
()
3597 vcenter_conect
= SmartConnect(host
=vm_vcenter_info
["vm_vcenter_ip"],
3598 user
=vm_vcenter_info
["vm_vcenter_user"],
3599 pwd
=vm_vcenter_info
["vm_vcenter_password"],
3600 port
=int(vm_vcenter_info
["vm_vcenter_port"]),
3603 atexit
.register(Disconnect
, vcenter_conect
)
3604 content
= vcenter_conect
.RetrieveContent()
3606 #Get VM and its host
3608 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_vcenter_info
["vm_moref_id"])
3609 if host_obj
and vm_obj
:
3610 vm_pci_devices_info
["host_name"]= host_obj
.name
3611 vm_pci_devices_info
["host_ip"]= host_obj
.config
.network
.vnic
[0].spec
.ip
.ipAddress
3612 for device
in vm_obj
.config
.hardware
.device
:
3613 if type(device
) == vim
.vm
.device
.VirtualPCIPassthrough
:
3614 device_details
={'devide_id':device
.backing
.id,
3615 'pciSlotNumber':device
.slotInfo
.pciSlotNumber
,
3617 vm_pci_devices_info
[device
.deviceInfo
.label
] = device_details
3619 self
.logger
.error("Can not connect to vCenter while getting "\
3620 "PCI devices infromationn")
3621 return vm_pci_devices_info
3622 except Exception as exp
:
3623 self
.logger
.error("Error occurred while getting VM infromationn"\
3624 " for VM : {}".format(exp
))
3625 raise vimconn
.vimconnException(message
=exp
)
3627 def add_network_adapter_to_vms(self
, vapp
, network_name
, primary_nic_index
, nicIndex
, net
, nic_type
=None):
3629 Method to add network adapter type to vm
3631 network_name - name of network
3632 primary_nic_index - int value for primary nic index
3633 nicIndex - int value for nic index
3634 nic_type - specify model name to which add to vm
3638 vca
= self
.connect()
3640 raise vimconn
.vimconnConnectionException("Failed to connect vCloud director")
3644 if 'floating_ip' in net
: floating_ip
= net
['floating_ip']
3645 allocation_mode
= "POOL" if floating_ip
else "DHCP"
3648 for vms
in vapp
._get
_vms
():
3649 vm_id
= (vms
.id).split(':')[-1]
3651 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3653 response
= Http
.get(url
=url_rest_call
,
3654 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3657 if response
.status_code
!= 200:
3658 self
.logger
.error("REST call {} failed reason : {}"\
3659 "status code : {}".format(url_rest_call
,
3661 response
.status_code
))
3662 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3663 "network connection section")
3665 data
= response
.content
3666 if '<PrimaryNetworkConnectionIndex>' not in data
:
3667 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3668 <NetworkConnection network="{}">
3669 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3670 <IsConnected>true</IsConnected>
3671 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3672 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
3675 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3677 new_item
= """<NetworkConnection network="{}">
3678 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3679 <IsConnected>true</IsConnected>
3680 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3681 </NetworkConnection>""".format(network_name
, nicIndex
,
3684 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3686 headers
= vca
.vcloud_session
.get_vcloud_headers()
3687 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3688 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3691 if response
.status_code
!= 202:
3692 self
.logger
.error("REST call {} failed reason : {}"\
3693 "status code : {} ".format(url_rest_call
,
3695 response
.status_code
))
3696 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3697 "network connection section")
3699 nic_task
= taskType
.parseString(response
.content
, True)
3700 if isinstance(nic_task
, GenericTask
):
3701 vca
.block_until_completed(nic_task
)
3702 self
.logger
.info("add_network_adapter_to_vms(): VM {} conneced to "\
3703 "default NIC type".format(vm_id
))
3705 self
.logger
.error("add_network_adapter_to_vms(): VM {} failed to "\
3706 "connect NIC type".format(vm_id
))
3708 for vms
in vapp
._get
_vms
():
3709 vm_id
= (vms
.id).split(':')[-1]
3711 url_rest_call
= "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca
.host
, vm_id
)
3713 response
= Http
.get(url
=url_rest_call
,
3714 headers
=vca
.vcloud_session
.get_vcloud_headers(),
3717 if response
.status_code
!= 200:
3718 self
.logger
.error("REST call {} failed reason : {}"\
3719 "status code : {}".format(url_rest_call
,
3721 response
.status_code
))
3722 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to get "\
3723 "network connection section")
3724 data
= response
.content
3725 if '<PrimaryNetworkConnectionIndex>' not in data
:
3726 item
= """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3727 <NetworkConnection network="{}">
3728 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3729 <IsConnected>true</IsConnected>
3730 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3731 <NetworkAdapterType>{}</NetworkAdapterType>
3732 </NetworkConnection>""".format(primary_nic_index
, network_name
, nicIndex
,
3733 allocation_mode
, nic_type
)
3735 data
= data
.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item
))
3737 new_item
= """<NetworkConnection network="{}">
3738 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3739 <IsConnected>true</IsConnected>
3740 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3741 <NetworkAdapterType>{}</NetworkAdapterType>
3742 </NetworkConnection>""".format(network_name
, nicIndex
,
3743 allocation_mode
, nic_type
)
3745 data
= data
.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item
))
3747 headers
= vca
.vcloud_session
.get_vcloud_headers()
3748 headers
['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3749 response
= Http
.put(url
=url_rest_call
, headers
=headers
, data
=data
,
3753 if response
.status_code
!= 202:
3754 self
.logger
.error("REST call {} failed reason : {}"\
3755 "status code : {}".format(url_rest_call
,
3757 response
.status_code
))
3758 raise vimconn
.vimconnException("add_network_adapter_to_vms : Failed to update "\
3759 "network connection section")
3761 nic_task
= taskType
.parseString(response
.content
, True)
3762 if isinstance(nic_task
, GenericTask
):
3763 vca
.block_until_completed(nic_task
)
3764 self
.logger
.info("add_network_adapter_to_vms(): VM {} "\
3765 "conneced to NIC type {}".format(vm_id
, nic_type
))
3767 self
.logger
.error("add_network_adapter_to_vms(): VM {} "\
3768 "failed to connect NIC type {}".format(vm_id
, nic_type
))
3769 except Exception as exp
:
3770 self
.logger
.error("add_network_adapter_to_vms() : exception occurred "\
3771 "while adding Network adapter")
3772 raise vimconn
.vimconnException(message
=exp
)
3775 def set_numa_affinity(self
, vmuuid
, paired_threads_id
):
3777 Method to assign numa affinity in vm configuration parammeters
3780 paired_threads_id - one or more virtual processor
3786 vm_moref_id
, vm_vcenter_host
, vm_vcenter_username
, vm_vcenter_port
= self
.get_vcenter_info_rest(vmuuid
)
3787 if vm_moref_id
and vm_vcenter_host
and vm_vcenter_username
:
3789 if hasattr(ssl
, '_create_unverified_context'):
3790 context
= ssl
._create
_unverified
_context
()
3791 vcenter_conect
= SmartConnect(host
=vm_vcenter_host
, user
=vm_vcenter_username
,
3792 pwd
=self
.passwd
, port
=int(vm_vcenter_port
),
3794 atexit
.register(Disconnect
, vcenter_conect
)
3795 content
= vcenter_conect
.RetrieveContent()
3797 host_obj
, vm_obj
= self
.get_vm_obj(content
,vm_moref_id
)
3799 config_spec
= vim
.vm
.ConfigSpec()
3800 config_spec
.extraConfig
= []
3801 opt
= vim
.option
.OptionValue()
3802 opt
.key
= 'numa.nodeAffinity'
3803 opt
.value
= str(paired_threads_id
)
3804 config_spec
.extraConfig
.append(opt
)
3805 task
= vm_obj
.ReconfigVM_Task(config_spec
)
3807 result
= self
.wait_for_vcenter_task(task
, vcenter_conect
)
3808 extra_config
= vm_obj
.config
.extraConfig
3810 for opts
in extra_config
:
3811 if 'numa.nodeAffinity' in opts
.key
:
3813 self
.logger
.info("set_numa_affinity: Sucessfully assign numa affinity "\
3814 "value {} for vm {}".format(opt
.value
, vm_obj
))
3818 self
.logger
.error("set_numa_affinity: Failed to assign numa affinity")
3819 except Exception as exp
:
3820 self
.logger
.error("set_numa_affinity : exception occurred while setting numa affinity "\
3821 "for VM {} : {}".format(vm_obj
, vm_moref_id
))
3822 raise vimconn
.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
3823 "affinity".format(exp
))
3826 def cloud_init(self
, vapp
, cloud_config
):
3828 Method to inject ssh-key
3830 cloud_config a dictionary with:
3831 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
3832 'users': (optional) list of users to be inserted, each item is a dict with:
3833 'name': (mandatory) user name,
3834 'key-pairs': (optional) list of strings with the public key to be inserted to the user
3835 'user-data': (optional) string is a text script to be passed directly to cloud-init
3836 'config-files': (optional). List of files to be transferred. Each item is a dict with:
3837 'dest': (mandatory) string with the destination absolute path
3838 'encoding': (optional, by default text). Can be one of:
3839 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
3840 'content' (mandatory): string with the content of the file
3841 'permissions': (optional) string with file permissions, typically octal notation '0644'
3842 'owner': (optional) file owner, string with the format 'owner:group'
3843 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
3845 vca
= self
.connect()
3847 raise vimconn
.vimconnConnectionException("Failed to connect vCloud director")
3850 if isinstance(cloud_config
, dict):
3853 if "key-pairs" in cloud_config
:
3854 key_pairs
= cloud_config
["key-pairs"]
3856 if "users" in cloud_config
:
3857 userdata
= cloud_config
["users"]
3859 for key
in key_pairs
:
3860 for user
in userdata
:
3861 if 'name' in user
: user_name
= user
['name']
3862 if 'key-pairs' in user
and len(user
['key-pairs']) > 0:
3863 for user_key
in user
['key-pairs']:
3864 customize_script
= """
3866 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3867 if [ "$1" = "precustomization" ];then
3868 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3869 if [ ! -d /root/.ssh ];then
3871 chown root:root /root/.ssh
3872 chmod 700 /root/.ssh
3873 touch /root/.ssh/authorized_keys
3874 chown root:root /root/.ssh/authorized_keys
3875 chmod 600 /root/.ssh/authorized_keys
3876 # make centos with selinux happy
3877 which restorecon && restorecon -Rv /root/.ssh
3878 echo '{key}' >> /root/.ssh/authorized_keys
3880 touch /root/.ssh/authorized_keys
3881 chown root:root /root/.ssh/authorized_keys
3882 chmod 600 /root/.ssh/authorized_keys
3883 echo '{key}' >> /root/.ssh/authorized_keys
3885 if [ -d /home/{user_name} ];then
3886 if [ ! -d /home/{user_name}/.ssh ];then
3887 mkdir /home/{user_name}/.ssh
3888 chown {user_name}:{user_name} /home/{user_name}/.ssh
3889 chmod 700 /home/{user_name}/.ssh
3890 touch /home/{user_name}/.ssh/authorized_keys
3891 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3892 chmod 600 /home/{user_name}/.ssh/authorized_keys
3893 # make centos with selinux happy
3894 which restorecon && restorecon -Rv /home/{user_name}/.ssh
3895 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3897 touch /home/{user_name}/.ssh/authorized_keys
3898 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3899 chmod 600 /home/{user_name}/.ssh/authorized_keys
3900 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3903 fi""".format(key
=key
, user_name
=user_name
, user_key
=user_key
)
3905 for vm
in vapp
._get
_vms
():
3907 task
= vapp
.customize_guest_os(vm_name
, customization_script
=customize_script
)
3908 if isinstance(task
, GenericTask
):
3909 vca
.block_until_completed(task
)
3910 self
.logger
.info("cloud_init : customized guest os task "\
3911 "completed for VM {}".format(vm_name
))
3913 self
.logger
.error("cloud_init : task for customized guest os"\
3914 "failed for VM {}".format(vm_name
))
3915 except Exception as exp
:
3916 self
.logger
.error("cloud_init : exception occurred while injecting "\
3918 raise vimconn
.vimconnException("cloud_init : Error {} failed to inject "\
3919 "ssh-key".format(exp
))