d4b83e5486399bf28219aaf9e8c8ae49a4f0025d
[osm/RO.git] / osm_ro / vimconn_vmware.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2016-2017 VMware Inc.
5 # This file is part of ETSI OSM
6 # All Rights Reserved.
7 #
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
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
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
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: osslegalrouting@vmware.com
22 ##
23
24 """
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
26 mbayramov@vmware.com
27 """
28 from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
29
30 import vimconn
31 import os
32 import traceback
33 import itertools
34 import requests
35 import ssl
36 import atexit
37
38 from pyVmomi import vim, vmodl
39 from pyVim.connect import SmartConnect, Disconnect
40
41 from xml.etree import ElementTree as XmlElementTree
42 from lxml import etree as lxmlElementTree
43
44 import yaml
45 from pyvcloud.vcd.client import BasicLoginCredentials,Client,VcdTaskException
46 from pyvcloud.vcd.vdc import VDC
47 from pyvcloud.vcd.org import Org
48 import re
49 from pyvcloud.vcd.vapp import VApp
50 from xml.sax.saxutils import escape
51 import logging
52 import json
53 import time
54 import uuid
55 import httplib
56 #For python3
57 #import http.client
58 import hashlib
59 import socket
60 import struct
61 import netaddr
62 import random
63
64 # global variable for vcd connector type
65 STANDALONE = 'standalone'
66
67 # key for flavor dicts
68 FLAVOR_RAM_KEY = 'ram'
69 FLAVOR_VCPUS_KEY = 'vcpus'
70 FLAVOR_DISK_KEY = 'disk'
71 DEFAULT_IP_PROFILE = {'dhcp_count':50,
72 'dhcp_enabled':True,
73 'ip_version':"IPv4"
74 }
75 # global variable for wait time
76 INTERVAL_TIME = 5
77 MAX_WAIT_TIME = 1800
78
79 API_VERSION = '31.0'
80
81 __author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare, Prakash Kasar"
82 __date__ = "$09-Mar-2018 11:09:29$"
83 __version__ = '0.2'
84
85 # -1: "Could not be created",
86 # 0: "Unresolved",
87 # 1: "Resolved",
88 # 2: "Deployed",
89 # 3: "Suspended",
90 # 4: "Powered on",
91 # 5: "Waiting for user input",
92 # 6: "Unknown state",
93 # 7: "Unrecognized state",
94 # 8: "Powered off",
95 # 9: "Inconsistent state",
96 # 10: "Children do not all have the same status",
97 # 11: "Upload initiated, OVF descriptor pending",
98 # 12: "Upload initiated, copying contents",
99 # 13: "Upload initiated , disk contents pending",
100 # 14: "Upload has been quarantined",
101 # 15: "Upload quarantine period has expired"
102
103 # mapping vCD status to MANO
104 vcdStatusCode2manoFormat = {4: 'ACTIVE',
105 7: 'PAUSED',
106 3: 'SUSPENDED',
107 8: 'INACTIVE',
108 12: 'BUILD',
109 -1: 'ERROR',
110 14: 'DELETED'}
111
112 #
113 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
114 'ERROR': 'ERROR', 'DELETED': 'DELETED'
115 }
116
117 class vimconnector(vimconn.vimconnector):
118 # dict used to store flavor in memory
119 flavorlist = {}
120
121 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
122 url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}):
123 """
124 Constructor create vmware connector to vCloud director.
125
126 By default construct doesn't validate connection state. So client can create object with None arguments.
127 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
128
129 a) It initialize organization UUID
130 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
131
132 Args:
133 uuid - is organization uuid.
134 name - is organization name that must be presented in vCloud director.
135 tenant_id - is VDC uuid it must be presented in vCloud director
136 tenant_name - is VDC name.
137 url - is hostname or ip address of vCloud director
138 url_admin - same as above.
139 user - is user that administrator for organization. Caller must make sure that
140 username has right privileges.
141
142 password - is password for a user.
143
144 VMware connector also requires PVDC administrative privileges and separate account.
145 This variables must be passed via config argument dict contains keys
146
147 dict['admin_username']
148 dict['admin_password']
149 config - Provide NSX and vCenter information
150
151 Returns:
152 Nothing.
153 """
154
155 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
156 url_admin, user, passwd, log_level, config)
157
158 self.logger = logging.getLogger('openmano.vim.vmware')
159 self.logger.setLevel(10)
160 self.persistent_info = persistent_info
161
162 self.name = name
163 self.id = uuid
164 self.url = url
165 self.url_admin = url_admin
166 self.tenant_id = tenant_id
167 self.tenant_name = tenant_name
168 self.user = user
169 self.passwd = passwd
170 self.config = config
171 self.admin_password = None
172 self.admin_user = None
173 self.org_name = ""
174 self.nsx_manager = None
175 self.nsx_user = None
176 self.nsx_password = None
177 self.availability_zone = None
178
179 # Disable warnings from self-signed certificates.
180 requests.packages.urllib3.disable_warnings()
181
182 if tenant_name is not None:
183 orgnameandtenant = tenant_name.split(":")
184 if len(orgnameandtenant) == 2:
185 self.tenant_name = orgnameandtenant[1]
186 self.org_name = orgnameandtenant[0]
187 else:
188 self.tenant_name = tenant_name
189 if "orgname" in config:
190 self.org_name = config['orgname']
191
192 if log_level:
193 self.logger.setLevel(getattr(logging, log_level))
194
195 try:
196 self.admin_user = config['admin_username']
197 self.admin_password = config['admin_password']
198 except KeyError:
199 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
200
201 try:
202 self.nsx_manager = config['nsx_manager']
203 self.nsx_user = config['nsx_user']
204 self.nsx_password = config['nsx_password']
205 except KeyError:
206 raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
207
208 self.vcenter_ip = config.get("vcenter_ip", None)
209 self.vcenter_port = config.get("vcenter_port", None)
210 self.vcenter_user = config.get("vcenter_user", None)
211 self.vcenter_password = config.get("vcenter_password", None)
212
213 #Set availability zone for Affinity rules
214 self.availability_zone = self.set_availability_zones()
215
216 # ############# Stub code for SRIOV #################
217 # try:
218 # self.dvs_name = config['dv_switch_name']
219 # except KeyError:
220 # raise vimconn.vimconnException(message="Error: distributed virtaul switch name is empty in Config")
221 #
222 # self.vlanID_range = config.get("vlanID_range", None)
223
224 self.org_uuid = None
225 self.client = None
226
227 if not url:
228 raise vimconn.vimconnException('url param can not be NoneType')
229
230 if not self.url_admin: # try to use normal url
231 self.url_admin = self.url
232
233 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
234 self.tenant_id, self.tenant_name))
235 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
236 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
237
238 # initialize organization
239 if self.user is not None and self.passwd is not None and self.url:
240 self.init_organization()
241
242 def __getitem__(self, index):
243 if index == 'name':
244 return self.name
245 if index == 'tenant_id':
246 return self.tenant_id
247 if index == 'tenant_name':
248 return self.tenant_name
249 elif index == 'id':
250 return self.id
251 elif index == 'org_name':
252 return self.org_name
253 elif index == 'org_uuid':
254 return self.org_uuid
255 elif index == 'user':
256 return self.user
257 elif index == 'passwd':
258 return self.passwd
259 elif index == 'url':
260 return self.url
261 elif index == 'url_admin':
262 return self.url_admin
263 elif index == "config":
264 return self.config
265 else:
266 raise KeyError("Invalid key '%s'" % str(index))
267
268 def __setitem__(self, index, value):
269 if index == 'name':
270 self.name = value
271 if index == 'tenant_id':
272 self.tenant_id = value
273 if index == 'tenant_name':
274 self.tenant_name = value
275 elif index == 'id':
276 self.id = value
277 elif index == 'org_name':
278 self.org_name = value
279 elif index == 'org_uuid':
280 self.org_uuid = value
281 elif index == 'user':
282 self.user = value
283 elif index == 'passwd':
284 self.passwd = value
285 elif index == 'url':
286 self.url = value
287 elif index == 'url_admin':
288 self.url_admin = value
289 else:
290 raise KeyError("Invalid key '%s'" % str(index))
291
292 def connect_as_admin(self):
293 """ Method connect as pvdc admin user to vCloud director.
294 There are certain action that can be done only by provider vdc admin user.
295 Organization creation / provider network creation etc.
296
297 Returns:
298 The return client object that latter can be used to connect to vcloud director as admin for provider vdc
299 """
300 self.logger.debug("Logging into vCD {} as admin.".format(self.org_name))
301
302 try:
303 host = self.url
304 org = 'System'
305 client_as_admin = Client(host, verify_ssl_certs=False)
306 client_as_admin.set_highest_supported_version()
307 client_as_admin.set_credentials(BasicLoginCredentials(self.admin_user, org, self.admin_password))
308 except Exception as e:
309 raise vimconn.vimconnException(
310 "Can't connect to a vCloud director as: {} with exception {}".format(self.admin_user, e))
311
312 return client_as_admin
313
314 def connect(self):
315 """ Method connect as normal user to vCloud director.
316
317 Returns:
318 The return client object that latter can be used to connect to vCloud director as admin for VDC
319 """
320 try:
321 self.logger.debug("Logging into vCD {} as {} to datacenter {}.".format(self.org_name,
322 self.user,
323 self.org_name))
324 host = self.url
325 client = Client(host, verify_ssl_certs=False)
326 client.set_highest_supported_version()
327 client.set_credentials(BasicLoginCredentials(self.user, self.org_name, self.passwd))
328 except:
329 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
330 "{} as user: {}".format(self.org_name, self.user))
331
332 return client
333
334 def init_organization(self):
335 """ Method initialize organization UUID and VDC parameters.
336
337 At bare minimum client must provide organization name that present in vCloud director and VDC.
338
339 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
340 The Org - UUID will be initialized at the run time if data center present in vCloud director.
341
342 Returns:
343 The return vca object that letter can be used to connect to vcloud direct as admin
344 """
345 client = self.connect()
346 if not client:
347 raise vimconn.vimconnConnectionException("Failed to connect vCD.")
348
349 self.client = client
350 try:
351 if self.org_uuid is None:
352 org_list = client.get_org_list()
353 for org in org_list.Org:
354 # we set org UUID at the init phase but we can do it only when we have valid credential.
355 if org.get('name') == self.org_name:
356 self.org_uuid = org.get('href').split('/')[-1]
357 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
358 break
359 else:
360 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
361
362 # if well good we require for org details
363 org_details_dict = self.get_org(org_uuid=self.org_uuid)
364
365 # we have two case if we want to initialize VDC ID or VDC name at run time
366 # tenant_name provided but no tenant id
367 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
368 vdcs_dict = org_details_dict['vdcs']
369 for vdc in vdcs_dict:
370 if vdcs_dict[vdc] == self.tenant_name:
371 self.tenant_id = vdc
372 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
373 self.org_name))
374 break
375 else:
376 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
377 # case two we have tenant_id but we don't have tenant name so we find and set it.
378 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
379 vdcs_dict = org_details_dict['vdcs']
380 for vdc in vdcs_dict:
381 if vdc == self.tenant_id:
382 self.tenant_name = vdcs_dict[vdc]
383 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
384 self.org_name))
385 break
386 else:
387 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
388 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
389 except:
390 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
391 self.logger.debug(traceback.format_exc())
392 self.org_uuid = None
393
394 def new_tenant(self, tenant_name=None, tenant_description=None):
395 """ Method adds a new tenant to VIM with this name.
396 This action requires access to create VDC action in vCloud director.
397
398 Args:
399 tenant_name is tenant_name to be created.
400 tenant_description not used for this call
401
402 Return:
403 returns the tenant identifier in UUID format.
404 If action is failed method will throw vimconn.vimconnException method
405 """
406 vdc_task = self.create_vdc(vdc_name=tenant_name)
407 if vdc_task is not None:
408 vdc_uuid, value = vdc_task.popitem()
409 self.logger.info("Created new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
410 return vdc_uuid
411 else:
412 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
413
414 def delete_tenant(self, tenant_id=None):
415 """ Delete a tenant from VIM
416 Args:
417 tenant_id is tenant_id to be deleted.
418
419 Return:
420 returns the tenant identifier in UUID format.
421 If action is failed method will throw exception
422 """
423 vca = self.connect_as_admin()
424 if not vca:
425 raise vimconn.vimconnConnectionException("Failed to connect vCD")
426
427 if tenant_id is not None:
428 if vca._session:
429 #Get OrgVDC
430 url_list = [self.url, '/api/vdc/', tenant_id]
431 orgvdc_herf = ''.join(url_list)
432
433 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
434 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
435 response = self.perform_request(req_type='GET',
436 url=orgvdc_herf,
437 headers=headers)
438
439 if response.status_code != requests.codes.ok:
440 self.logger.debug("delete_tenant():GET REST API call {} failed. "\
441 "Return status code {}".format(orgvdc_herf,
442 response.status_code))
443 raise vimconn.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id))
444
445 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
446 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
447 #For python3
448 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
449 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
450 vdc_remove_href = lxmlroot_respond.find("xmlns:Link[@rel='remove']",namespaces).attrib['href']
451 vdc_remove_href = vdc_remove_href + '?recursive=true&force=true'
452
453 response = self.perform_request(req_type='DELETE',
454 url=vdc_remove_href,
455 headers=headers)
456
457 if response.status_code == 202:
458 time.sleep(5)
459 return tenant_id
460 else:
461 self.logger.debug("delete_tenant(): DELETE REST API call {} failed. "\
462 "Return status code {}".format(vdc_remove_href,
463 response.status_code))
464 raise vimconn.vimconnException("Fail to delete tenant with ID {}".format(tenant_id))
465 else:
466 self.logger.debug("delete_tenant():Incorrect tenant ID {}".format(tenant_id))
467 raise vimconn.vimconnNotFoundException("Fail to get tenant {}".format(tenant_id))
468
469
470 def get_tenant_list(self, filter_dict={}):
471 """Obtain tenants of VIM
472 filter_dict can contain the following keys:
473 name: filter by tenant name
474 id: filter by tenant uuid/id
475 <other VIM specific>
476 Returns the tenant list of dictionaries:
477 [{'name':'<name>, 'id':'<id>, ...}, ...]
478
479 """
480 org_dict = self.get_org(self.org_uuid)
481 vdcs_dict = org_dict['vdcs']
482
483 vdclist = []
484 try:
485 for k in vdcs_dict:
486 entry = {'name': vdcs_dict[k], 'id': k}
487 # if caller didn't specify dictionary we return all tenants.
488 if filter_dict is not None and filter_dict:
489 filtered_entry = entry.copy()
490 filtered_dict = set(entry.keys()) - set(filter_dict)
491 for unwanted_key in filtered_dict: del entry[unwanted_key]
492 if filter_dict == entry:
493 vdclist.append(filtered_entry)
494 else:
495 vdclist.append(entry)
496 except:
497 self.logger.debug("Error in get_tenant_list()")
498 self.logger.debug(traceback.format_exc())
499 raise vimconn.vimconnException("Incorrect state. {}")
500
501 return vdclist
502
503 def new_network(self, net_name, net_type, ip_profile=None, shared=False, vlan=None):
504 """Adds a tenant network to VIM
505 Params:
506 'net_name': name of the network
507 'net_type': one of:
508 'bridge': overlay isolated network
509 'data': underlay E-LAN network for Passthrough and SRIOV interfaces
510 'ptp': underlay E-LINE network for Passthrough and SRIOV interfaces.
511 'ip_profile': is a dict containing the IP parameters of the network
512 'ip_version': can be "IPv4" or "IPv6" (Currently only IPv4 is implemented)
513 'subnet_address': ip_prefix_schema, that is X.X.X.X/Y
514 'gateway_address': (Optional) ip_schema, that is X.X.X.X
515 'dns_address': (Optional) comma separated list of ip_schema, e.g. X.X.X.X[,X,X,X,X]
516 'dhcp_enabled': True or False
517 'dhcp_start_address': ip_schema, first IP to grant
518 'dhcp_count': number of IPs to grant.
519 'shared': if this network can be seen/use by other tenants/organization
520 'vlan': in case of a data or ptp net_type, the intended vlan tag to be used for the network
521 Returns a tuple with the network identifier and created_items, or raises an exception on error
522 created_items can be None or a dictionary where this method can include key-values that will be passed to
523 the method delete_network. Can be used to store created segments, created l2gw connections, etc.
524 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
525 as not present.
526 """
527
528 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
529 .format(net_name, net_type, ip_profile, shared))
530
531 created_items = {}
532 isshared = 'false'
533 if shared:
534 isshared = 'true'
535
536 # ############# Stub code for SRIOV #################
537 # if net_type == "data" or net_type == "ptp":
538 # if self.config.get('dv_switch_name') == None:
539 # raise vimconn.vimconnConflictException("You must provide 'dv_switch_name' at config value")
540 # network_uuid = self.create_dvPort_group(net_name)
541
542 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
543 ip_profile=ip_profile, isshared=isshared)
544 if network_uuid is not None:
545 return network_uuid, created_items
546 else:
547 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
548
549 def get_vcd_network_list(self):
550 """ Method available organization for a logged in tenant
551
552 Returns:
553 The return vca object that letter can be used to connect to vcloud direct as admin
554 """
555
556 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
557
558 if not self.tenant_name:
559 raise vimconn.vimconnConnectionException("Tenant name is empty.")
560
561 org, vdc = self.get_vdc_details()
562 if vdc is None:
563 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
564
565 vdc_uuid = vdc.get('id').split(":")[3]
566 if self.client._session:
567 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
568 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
569 response = self.perform_request(req_type='GET',
570 url=vdc.get('href'),
571 headers=headers)
572 if response.status_code != 200:
573 self.logger.error("Failed to get vdc content")
574 raise vimconn.vimconnNotFoundException("Failed to get vdc content")
575 else:
576 content = XmlElementTree.fromstring(response.content)
577
578 network_list = []
579 try:
580 for item in content:
581 if item.tag.split('}')[-1] == 'AvailableNetworks':
582 for net in item:
583 response = self.perform_request(req_type='GET',
584 url=net.get('href'),
585 headers=headers)
586
587 if response.status_code != 200:
588 self.logger.error("Failed to get network content")
589 raise vimconn.vimconnNotFoundException("Failed to get network content")
590 else:
591 net_details = XmlElementTree.fromstring(response.content)
592
593 filter_dict = {}
594 net_uuid = net_details.get('id').split(":")
595 if len(net_uuid) != 4:
596 continue
597 else:
598 net_uuid = net_uuid[3]
599 # create dict entry
600 self.logger.debug("get_vcd_network_list(): Adding network {} "
601 "to a list vcd id {} network {}".format(net_uuid,
602 vdc_uuid,
603 net_details.get('name')))
604 filter_dict["name"] = net_details.get('name')
605 filter_dict["id"] = net_uuid
606 if [i.text for i in net_details if i.tag.split('}')[-1] == 'IsShared'][0] == 'true':
607 shared = True
608 else:
609 shared = False
610 filter_dict["shared"] = shared
611 filter_dict["tenant_id"] = vdc_uuid
612 if int(net_details.get('status')) == 1:
613 filter_dict["admin_state_up"] = True
614 else:
615 filter_dict["admin_state_up"] = False
616 filter_dict["status"] = "ACTIVE"
617 filter_dict["type"] = "bridge"
618 network_list.append(filter_dict)
619 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
620 except:
621 self.logger.debug("Error in get_vcd_network_list", exc_info=True)
622 pass
623
624 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
625 return network_list
626
627 def get_network_list(self, filter_dict={}):
628 """Obtain tenant networks of VIM
629 Filter_dict can be:
630 name: network name OR/AND
631 id: network uuid OR/AND
632 shared: boolean OR/AND
633 tenant_id: tenant OR/AND
634 admin_state_up: boolean
635 status: 'ACTIVE'
636
637 [{key : value , key : value}]
638
639 Returns the network list of dictionaries:
640 [{<the fields at Filter_dict plus some VIM specific>}, ...]
641 List can be empty
642 """
643
644 self.logger.debug("get_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
645
646 if not self.tenant_name:
647 raise vimconn.vimconnConnectionException("Tenant name is empty.")
648
649 org, vdc = self.get_vdc_details()
650 if vdc is None:
651 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
652
653 try:
654 vdcid = vdc.get('id').split(":")[3]
655
656 if self.client._session:
657 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
658 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
659 response = self.perform_request(req_type='GET',
660 url=vdc.get('href'),
661 headers=headers)
662 if response.status_code != 200:
663 self.logger.error("Failed to get vdc content")
664 raise vimconn.vimconnNotFoundException("Failed to get vdc content")
665 else:
666 content = XmlElementTree.fromstring(response.content)
667
668 network_list = []
669 for item in content:
670 if item.tag.split('}')[-1] == 'AvailableNetworks':
671 for net in item:
672 response = self.perform_request(req_type='GET',
673 url=net.get('href'),
674 headers=headers)
675
676 if response.status_code != 200:
677 self.logger.error("Failed to get network content")
678 raise vimconn.vimconnNotFoundException("Failed to get network content")
679 else:
680 net_details = XmlElementTree.fromstring(response.content)
681
682 filter_entry = {}
683 net_uuid = net_details.get('id').split(":")
684 if len(net_uuid) != 4:
685 continue
686 else:
687 net_uuid = net_uuid[3]
688 # create dict entry
689 self.logger.debug("get_network_list(): Adding net {}"
690 " to a list vcd id {} network {}".format(net_uuid,
691 vdcid,
692 net_details.get('name')))
693 filter_entry["name"] = net_details.get('name')
694 filter_entry["id"] = net_uuid
695 if [i.text for i in net_details if i.tag.split('}')[-1] == 'IsShared'][0] == 'true':
696 shared = True
697 else:
698 shared = False
699 filter_entry["shared"] = shared
700 filter_entry["tenant_id"] = vdcid
701 if int(net_details.get('status')) == 1:
702 filter_entry["admin_state_up"] = True
703 else:
704 filter_entry["admin_state_up"] = False
705 filter_entry["status"] = "ACTIVE"
706 filter_entry["type"] = "bridge"
707 filtered_entry = filter_entry.copy()
708
709 if filter_dict is not None and filter_dict:
710 # we remove all the key : value we don't care and match only
711 # respected field
712 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
713 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
714 if filter_dict == filter_entry:
715 network_list.append(filtered_entry)
716 else:
717 network_list.append(filtered_entry)
718 except Exception as e:
719 self.logger.debug("Error in get_network_list",exc_info=True)
720 if isinstance(e, vimconn.vimconnException):
721 raise
722 else:
723 raise vimconn.vimconnNotFoundException("Failed : Networks list not found {} ".format(e))
724
725 self.logger.debug("Returning {}".format(network_list))
726 return network_list
727
728 def get_network(self, net_id):
729 """Method obtains network details of net_id VIM network
730 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
731
732 try:
733 org, vdc = self.get_vdc_details()
734 vdc_id = vdc.get('id').split(":")[3]
735 if self.client._session:
736 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
737 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
738 response = self.perform_request(req_type='GET',
739 url=vdc.get('href'),
740 headers=headers)
741 if response.status_code != 200:
742 self.logger.error("Failed to get vdc content")
743 raise vimconn.vimconnNotFoundException("Failed to get vdc content")
744 else:
745 content = XmlElementTree.fromstring(response.content)
746
747 filter_dict = {}
748
749 for item in content:
750 if item.tag.split('}')[-1] == 'AvailableNetworks':
751 for net in item:
752 response = self.perform_request(req_type='GET',
753 url=net.get('href'),
754 headers=headers)
755
756 if response.status_code != 200:
757 self.logger.error("Failed to get network content")
758 raise vimconn.vimconnNotFoundException("Failed to get network content")
759 else:
760 net_details = XmlElementTree.fromstring(response.content)
761
762 vdc_network_id = net_details.get('id').split(":")
763 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
764 filter_dict["name"] = net_details.get('name')
765 filter_dict["id"] = vdc_network_id[3]
766 if [i.text for i in net_details if i.tag.split('}')[-1] == 'IsShared'][0] == 'true':
767 shared = True
768 else:
769 shared = False
770 filter_dict["shared"] = shared
771 filter_dict["tenant_id"] = vdc_id
772 if int(net_details.get('status')) == 1:
773 filter_dict["admin_state_up"] = True
774 else:
775 filter_dict["admin_state_up"] = False
776 filter_dict["status"] = "ACTIVE"
777 filter_dict["type"] = "bridge"
778 self.logger.debug("Returning {}".format(filter_dict))
779 return filter_dict
780 else:
781 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
782 except Exception as e:
783 self.logger.debug("Error in get_network")
784 self.logger.debug(traceback.format_exc())
785 if isinstance(e, vimconn.vimconnException):
786 raise
787 else:
788 raise vimconn.vimconnNotFoundException("Failed : Network not found {} ".format(e))
789
790 return filter_dict
791
792 def delete_network(self, net_id, created_items=None):
793 """
794 Removes a tenant network from VIM and its associated elements
795 :param net_id: VIM identifier of the network, provided by method new_network
796 :param created_items: dictionary with extra items to be deleted. provided by method new_network
797 Returns the network identifier or raises an exception upon error or when network is not found
798 """
799
800 # ############# Stub code for SRIOV #################
801 # dvport_group = self.get_dvport_group(net_id)
802 # if dvport_group:
803 # #delete portgroup
804 # status = self.destroy_dvport_group(net_id)
805 # if status:
806 # # Remove vlanID from persistent info
807 # if net_id in self.persistent_info["used_vlanIDs"]:
808 # del self.persistent_info["used_vlanIDs"][net_id]
809 #
810 # return net_id
811
812 vcd_network = self.get_vcd_network(network_uuid=net_id)
813 if vcd_network is not None and vcd_network:
814 if self.delete_network_action(network_uuid=net_id):
815 return net_id
816 else:
817 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
818
819 def refresh_nets_status(self, net_list):
820 """Get the status of the networks
821 Params: the list of network identifiers
822 Returns a dictionary with:
823 net_id: #VIM id of this network
824 status: #Mandatory. Text with one of:
825 # DELETED (not found at vim)
826 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
827 # OTHER (Vim reported other status not understood)
828 # ERROR (VIM indicates an ERROR status)
829 # ACTIVE, INACTIVE, DOWN (admin down),
830 # BUILD (on building process)
831 #
832 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
833 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
834
835 """
836
837 dict_entry = {}
838 try:
839 for net in net_list:
840 errormsg = ''
841 vcd_network = self.get_vcd_network(network_uuid=net)
842 if vcd_network is not None and vcd_network:
843 if vcd_network['status'] == '1':
844 status = 'ACTIVE'
845 else:
846 status = 'DOWN'
847 else:
848 status = 'DELETED'
849 errormsg = 'Network not found.'
850
851 dict_entry[net] = {'status': status, 'error_msg': errormsg,
852 'vim_info': yaml.safe_dump(vcd_network)}
853 except:
854 self.logger.debug("Error in refresh_nets_status")
855 self.logger.debug(traceback.format_exc())
856
857 return dict_entry
858
859 def get_flavor(self, flavor_id):
860 """Obtain flavor details from the VIM
861 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
862 """
863 if flavor_id not in vimconnector.flavorlist:
864 raise vimconn.vimconnNotFoundException("Flavor not found.")
865 return vimconnector.flavorlist[flavor_id]
866
867 def new_flavor(self, flavor_data):
868 """Adds a tenant flavor to VIM
869 flavor_data contains a dictionary with information, keys:
870 name: flavor name
871 ram: memory (cloud type) in MBytes
872 vpcus: cpus (cloud type)
873 extended: EPA parameters
874 - numas: #items requested in same NUMA
875 memory: number of 1G huge pages memory
876 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
877 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
878 - name: interface name
879 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
880 bandwidth: X Gbps; requested guarantee bandwidth
881 vpci: requested virtual PCI address
882 disk: disk size
883 is_public:
884 #TODO to concrete
885 Returns the flavor identifier"""
886
887 # generate a new uuid put to internal dict and return it.
888 self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
889 new_flavor=flavor_data
890 ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
891 cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
892 disk = flavor_data.get(FLAVOR_DISK_KEY, 0)
893
894 if not isinstance(ram, int):
895 raise vimconn.vimconnException("Non-integer value for ram")
896 elif not isinstance(cpu, int):
897 raise vimconn.vimconnException("Non-integer value for cpu")
898 elif not isinstance(disk, int):
899 raise vimconn.vimconnException("Non-integer value for disk")
900
901 extended_flv = flavor_data.get("extended")
902 if extended_flv:
903 numas=extended_flv.get("numas")
904 if numas:
905 for numa in numas:
906 #overwrite ram and vcpus
907 if 'memory' in numa:
908 ram = numa['memory']*1024
909 if 'paired-threads' in numa:
910 cpu = numa['paired-threads']*2
911 elif 'cores' in numa:
912 cpu = numa['cores']
913 elif 'threads' in numa:
914 cpu = numa['threads']
915
916 new_flavor[FLAVOR_RAM_KEY] = ram
917 new_flavor[FLAVOR_VCPUS_KEY] = cpu
918 new_flavor[FLAVOR_DISK_KEY] = disk
919 # generate a new uuid put to internal dict and return it.
920 flavor_id = uuid.uuid4()
921 vimconnector.flavorlist[str(flavor_id)] = new_flavor
922 self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
923
924 return str(flavor_id)
925
926 def delete_flavor(self, flavor_id):
927 """Deletes a tenant flavor from VIM identify by its id
928
929 Returns the used id or raise an exception
930 """
931 if flavor_id not in vimconnector.flavorlist:
932 raise vimconn.vimconnNotFoundException("Flavor not found.")
933
934 vimconnector.flavorlist.pop(flavor_id, None)
935 return flavor_id
936
937 def new_image(self, image_dict):
938 """
939 Adds a tenant image to VIM
940 Returns:
941 200, image-id if the image is created
942 <0, message if there is an error
943 """
944
945 return self.get_image_id_from_path(image_dict['location'])
946
947 def delete_image(self, image_id):
948 """
949 Deletes a tenant image from VIM
950 Args:
951 image_id is ID of Image to be deleted
952 Return:
953 returns the image identifier in UUID format or raises an exception on error
954 """
955 conn = self.connect_as_admin()
956 if not conn:
957 raise vimconn.vimconnConnectionException("Failed to connect vCD")
958 # Get Catalog details
959 url_list = [self.url, '/api/catalog/', image_id]
960 catalog_herf = ''.join(url_list)
961
962 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
963 'x-vcloud-authorization': conn._session.headers['x-vcloud-authorization']}
964
965 response = self.perform_request(req_type='GET',
966 url=catalog_herf,
967 headers=headers)
968
969 if response.status_code != requests.codes.ok:
970 self.logger.debug("delete_image():GET REST API call {} failed. "\
971 "Return status code {}".format(catalog_herf,
972 response.status_code))
973 raise vimconn.vimconnNotFoundException("Fail to get image {}".format(image_id))
974
975 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
976 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
977 #For python3
978 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
979 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
980
981 catalogItems_section = lxmlroot_respond.find("xmlns:CatalogItems",namespaces)
982 catalogItems = catalogItems_section.iterfind("xmlns:CatalogItem",namespaces)
983 for catalogItem in catalogItems:
984 catalogItem_href = catalogItem.attrib['href']
985
986 response = self.perform_request(req_type='GET',
987 url=catalogItem_href,
988 headers=headers)
989
990 if response.status_code != requests.codes.ok:
991 self.logger.debug("delete_image():GET REST API call {} failed. "\
992 "Return status code {}".format(catalog_herf,
993 response.status_code))
994 raise vimconn.vimconnNotFoundException("Fail to get catalogItem {} for catalog {}".format(
995 catalogItem,
996 image_id))
997
998 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
999 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
1000 #For python3
1001 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
1002 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
1003 catalogitem_remove_href = lxmlroot_respond.find("xmlns:Link[@rel='remove']",namespaces).attrib['href']
1004
1005 #Remove catalogItem
1006 response = self.perform_request(req_type='DELETE',
1007 url=catalogitem_remove_href,
1008 headers=headers)
1009 if response.status_code == requests.codes.no_content:
1010 self.logger.debug("Deleted Catalog item {}".format(catalogItem))
1011 else:
1012 raise vimconn.vimconnException("Fail to delete Catalog Item {}".format(catalogItem))
1013
1014 #Remove catalog
1015 url_list = [self.url, '/api/admin/catalog/', image_id]
1016 catalog_remove_herf = ''.join(url_list)
1017 response = self.perform_request(req_type='DELETE',
1018 url=catalog_remove_herf,
1019 headers=headers)
1020
1021 if response.status_code == requests.codes.no_content:
1022 self.logger.debug("Deleted Catalog {}".format(image_id))
1023 return image_id
1024 else:
1025 raise vimconn.vimconnException("Fail to delete Catalog {}".format(image_id))
1026
1027
1028 def catalog_exists(self, catalog_name, catalogs):
1029 """
1030
1031 :param catalog_name:
1032 :param catalogs:
1033 :return:
1034 """
1035 for catalog in catalogs:
1036 if catalog['name'] == catalog_name:
1037 return True
1038 return False
1039
1040 def create_vimcatalog(self, vca=None, catalog_name=None):
1041 """ Create new catalog entry in vCloud director.
1042
1043 Args
1044 vca: vCloud director.
1045 catalog_name catalog that client wish to create. Note no validation done for a name.
1046 Client must make sure that provide valid string representation.
1047
1048 Return (bool) True if catalog created.
1049
1050 """
1051 try:
1052 result = vca.create_catalog(catalog_name, catalog_name)
1053 if result is not None:
1054 return True
1055 catalogs = vca.list_catalogs()
1056 except:
1057 return False
1058 return self.catalog_exists(catalog_name, catalogs)
1059
1060 # noinspection PyIncorrectDocstring
1061 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
1062 description='', progress=False, chunk_bytes=128 * 1024):
1063 """
1064 Uploads a OVF file to a vCloud catalog
1065
1066 :param chunk_bytes:
1067 :param progress:
1068 :param description:
1069 :param image_name:
1070 :param vca:
1071 :param catalog_name: (str): The name of the catalog to upload the media.
1072 :param media_file_name: (str): The name of the local media file to upload.
1073 :return: (bool) True if the media file was successfully uploaded, false otherwise.
1074 """
1075 os.path.isfile(media_file_name)
1076 statinfo = os.stat(media_file_name)
1077
1078 # find a catalog entry where we upload OVF.
1079 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
1080 # status change.
1081 # if VCD can parse OVF we upload VMDK file
1082 try:
1083 for catalog in vca.list_catalogs():
1084 if catalog_name != catalog['name']:
1085 continue
1086 catalog_href = "{}/api/catalog/{}/action/upload".format(self.url, catalog['id'])
1087 data = """
1088 <UploadVAppTemplateParams name="{}" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>{} vApp Template</Description></UploadVAppTemplateParams>
1089 """.format(catalog_name, description)
1090
1091 if self.client:
1092 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
1093 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
1094 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
1095
1096 response = self.perform_request(req_type='POST',
1097 url=catalog_href,
1098 headers=headers,
1099 data=data)
1100
1101 if response.status_code == requests.codes.created:
1102 catalogItem = XmlElementTree.fromstring(response.content)
1103 entity = [child for child in catalogItem if
1104 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
1105 href = entity.get('href')
1106 template = href
1107
1108 response = self.perform_request(req_type='GET',
1109 url=href,
1110 headers=headers)
1111
1112 if response.status_code == requests.codes.ok:
1113 headers['Content-Type'] = 'Content-Type text/xml'
1114 result = re.search('rel="upload:default"\shref="(.*?\/descriptor.ovf)"',response.content)
1115 if result:
1116 transfer_href = result.group(1)
1117
1118 response = self.perform_request(req_type='PUT',
1119 url=transfer_href,
1120 headers=headers,
1121 data=open(media_file_name, 'rb'))
1122 if response.status_code != requests.codes.ok:
1123 self.logger.debug(
1124 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
1125 media_file_name))
1126 return False
1127
1128 # TODO fix this with aync block
1129 time.sleep(5)
1130
1131 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
1132
1133 # uploading VMDK file
1134 # check status of OVF upload and upload remaining files.
1135 response = self.perform_request(req_type='GET',
1136 url=template,
1137 headers=headers)
1138
1139 if response.status_code == requests.codes.ok:
1140 result = re.search('rel="upload:default"\s*href="(.*?vmdk)"',response.content)
1141 if result:
1142 link_href = result.group(1)
1143 # we skip ovf since it already uploaded.
1144 if 'ovf' in link_href:
1145 continue
1146 # The OVF file and VMDK must be in a same directory
1147 head, tail = os.path.split(media_file_name)
1148 file_vmdk = head + '/' + link_href.split("/")[-1]
1149 if not os.path.isfile(file_vmdk):
1150 return False
1151 statinfo = os.stat(file_vmdk)
1152 if statinfo.st_size == 0:
1153 return False
1154 hrefvmdk = link_href
1155
1156 if progress:
1157 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
1158 FileTransferSpeed()]
1159 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
1160
1161 bytes_transferred = 0
1162 f = open(file_vmdk, 'rb')
1163 while bytes_transferred < statinfo.st_size:
1164 my_bytes = f.read(chunk_bytes)
1165 if len(my_bytes) <= chunk_bytes:
1166 headers['Content-Range'] = 'bytes %s-%s/%s' % (
1167 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
1168 headers['Content-Length'] = str(len(my_bytes))
1169 response = requests.put(url=hrefvmdk,
1170 headers=headers,
1171 data=my_bytes,
1172 verify=False)
1173 if response.status_code == requests.codes.ok:
1174 bytes_transferred += len(my_bytes)
1175 if progress:
1176 progress_bar.update(bytes_transferred)
1177 else:
1178 self.logger.debug(
1179 'file upload failed with error: [%s] %s' % (response.status_code,
1180 response.content))
1181
1182 f.close()
1183 return False
1184 f.close()
1185 if progress:
1186 progress_bar.finish()
1187 time.sleep(10)
1188 return True
1189 else:
1190 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
1191 format(catalog_name, media_file_name))
1192 return False
1193 except Exception as exp:
1194 self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1195 .format(catalog_name,media_file_name, exp))
1196 raise vimconn.vimconnException(
1197 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
1198 .format(catalog_name,media_file_name, exp))
1199
1200 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
1201 return False
1202
1203 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
1204 """Upload media file"""
1205 # TODO add named parameters for readability
1206
1207 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
1208 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
1209
1210 def validate_uuid4(self, uuid_string=None):
1211 """ Method validate correct format of UUID.
1212
1213 Return: true if string represent valid uuid
1214 """
1215 try:
1216 val = uuid.UUID(uuid_string, version=4)
1217 except ValueError:
1218 return False
1219 return True
1220
1221 def get_catalogid(self, catalog_name=None, catalogs=None):
1222 """ Method check catalog and return catalog ID in UUID format.
1223
1224 Args
1225 catalog_name: catalog name as string
1226 catalogs: list of catalogs.
1227
1228 Return: catalogs uuid
1229 """
1230
1231 for catalog in catalogs:
1232 if catalog['name'] == catalog_name:
1233 catalog_id = catalog['id']
1234 return catalog_id
1235 return None
1236
1237 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
1238 """ Method check catalog and return catalog name lookup done by catalog UUID.
1239
1240 Args
1241 catalog_name: catalog name as string
1242 catalogs: list of catalogs.
1243
1244 Return: catalogs name or None
1245 """
1246
1247 if not self.validate_uuid4(uuid_string=catalog_uuid):
1248 return None
1249
1250 for catalog in catalogs:
1251 catalog_id = catalog.get('id')
1252 if catalog_id == catalog_uuid:
1253 return catalog.get('name')
1254 return None
1255
1256 def get_catalog_obj(self, catalog_uuid=None, catalogs=None):
1257 """ Method check catalog and return catalog name lookup done by catalog UUID.
1258
1259 Args
1260 catalog_name: catalog name as string
1261 catalogs: list of catalogs.
1262
1263 Return: catalogs name or None
1264 """
1265
1266 if not self.validate_uuid4(uuid_string=catalog_uuid):
1267 return None
1268
1269 for catalog in catalogs:
1270 catalog_id = catalog.get('id')
1271 if catalog_id == catalog_uuid:
1272 return catalog
1273 return None
1274
1275 def get_image_id_from_path(self, path=None, progress=False):
1276 """ Method upload OVF image to vCloud director.
1277
1278 Each OVF image represented as single catalog entry in vcloud director.
1279 The method check for existing catalog entry. The check done by file name without file extension.
1280
1281 if given catalog name already present method will respond with existing catalog uuid otherwise
1282 it will create new catalog entry and upload OVF file to newly created catalog.
1283
1284 If method can't create catalog entry or upload a file it will throw exception.
1285
1286 Method accept boolean flag progress that will output progress bar. It useful method
1287 for standalone upload use case. In case to test large file upload.
1288
1289 Args
1290 path: - valid path to OVF file.
1291 progress - boolean progress bar show progress bar.
1292
1293 Return: if image uploaded correct method will provide image catalog UUID.
1294 """
1295
1296 if not path:
1297 raise vimconn.vimconnException("Image path can't be None.")
1298
1299 if not os.path.isfile(path):
1300 raise vimconn.vimconnException("Can't read file. File not found.")
1301
1302 if not os.access(path, os.R_OK):
1303 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1304
1305 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1306
1307 dirpath, filename = os.path.split(path)
1308 flname, file_extension = os.path.splitext(path)
1309 if file_extension != '.ovf':
1310 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1311 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1312
1313 catalog_name = os.path.splitext(filename)[0]
1314 catalog_md5_name = hashlib.md5(path).hexdigest()
1315 self.logger.debug("File name {} Catalog Name {} file path {} "
1316 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1317
1318 try:
1319 org,vdc = self.get_vdc_details()
1320 catalogs = org.list_catalogs()
1321 except Exception as exp:
1322 self.logger.debug("Failed get catalogs() with Exception {} ".format(exp))
1323 raise vimconn.vimconnException("Failed get catalogs() with Exception {} ".format(exp))
1324
1325 if len(catalogs) == 0:
1326 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1327 result = self.create_vimcatalog(org, catalog_md5_name)
1328 if not result:
1329 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1330
1331 result = self.upload_vimimage(vca=org, catalog_name=catalog_md5_name,
1332 media_name=filename, medial_file_name=path, progress=progress)
1333 if not result:
1334 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1335 return self.get_catalogid(catalog_name, catalogs)
1336 else:
1337 for catalog in catalogs:
1338 # search for existing catalog if we find same name we return ID
1339 # TODO optimize this
1340 if catalog['name'] == catalog_md5_name:
1341 self.logger.debug("Found existing catalog entry for {} "
1342 "catalog id {}".format(catalog_name,
1343 self.get_catalogid(catalog_md5_name, catalogs)))
1344 return self.get_catalogid(catalog_md5_name, catalogs)
1345
1346 # if we didn't find existing catalog we create a new one and upload image.
1347 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1348 result = self.create_vimcatalog(org, catalog_md5_name)
1349 if not result:
1350 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1351
1352 result = self.upload_vimimage(vca=org, catalog_name=catalog_md5_name,
1353 media_name=filename, medial_file_name=path, progress=progress)
1354 if not result:
1355 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1356
1357 return self.get_catalogid(catalog_md5_name, org.list_catalogs())
1358
1359 def get_image_list(self, filter_dict={}):
1360 '''Obtain tenant images from VIM
1361 Filter_dict can be:
1362 name: image name
1363 id: image uuid
1364 checksum: image checksum
1365 location: image path
1366 Returns the image list of dictionaries:
1367 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1368 List can be empty
1369 '''
1370
1371 try:
1372 org, vdc = self.get_vdc_details()
1373 image_list = []
1374 catalogs = org.list_catalogs()
1375 if len(catalogs) == 0:
1376 return image_list
1377 else:
1378 for catalog in catalogs:
1379 catalog_uuid = catalog.get('id')
1380 name = catalog.get('name')
1381 filtered_dict = {}
1382 if filter_dict.get("name") and filter_dict["name"] != name:
1383 continue
1384 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1385 continue
1386 filtered_dict ["name"] = name
1387 filtered_dict ["id"] = catalog_uuid
1388 image_list.append(filtered_dict)
1389
1390 self.logger.debug("List of already created catalog items: {}".format(image_list))
1391 return image_list
1392 except Exception as exp:
1393 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
1394
1395 def get_vappid(self, vdc=None, vapp_name=None):
1396 """ Method takes vdc object and vApp name and returns vapp uuid or None
1397
1398 Args:
1399 vdc: The VDC object.
1400 vapp_name: is application vappp name identifier
1401
1402 Returns:
1403 The return vApp name otherwise None
1404 """
1405 if vdc is None or vapp_name is None:
1406 return None
1407 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1408 try:
1409 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1410 vdc.ResourceEntities.ResourceEntity)
1411 #For python3
1412 #refs = [ref for ref in vdc.ResourceEntities.ResourceEntity\
1413 # if ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1414 if len(refs) == 1:
1415 return refs[0].href.split("vapp")[1][1:]
1416 except Exception as e:
1417 self.logger.exception(e)
1418 return False
1419 return None
1420
1421 def check_vapp(self, vdc=None, vapp_uuid=None):
1422 """ Method Method returns True or False if vapp deployed in vCloud director
1423
1424 Args:
1425 vca: Connector to VCA
1426 vdc: The VDC object.
1427 vappid: vappid is application identifier
1428
1429 Returns:
1430 The return True if vApp deployed
1431 :param vdc:
1432 :param vapp_uuid:
1433 """
1434 try:
1435 refs = filter(lambda ref:
1436 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1437 vdc.ResourceEntities.ResourceEntity)
1438 #For python3
1439 #refs = [ref for ref in vdc.ResourceEntities.ResourceEntity\
1440 # if ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml']
1441 for ref in refs:
1442 vappid = ref.href.split("vapp")[1][1:]
1443 # find vapp with respected vapp uuid
1444 if vappid == vapp_uuid:
1445 return True
1446 except Exception as e:
1447 self.logger.exception(e)
1448 return False
1449 return False
1450
1451 def get_namebyvappid(self, vapp_uuid=None):
1452 """Method returns vApp name from vCD and lookup done by vapp_id.
1453
1454 Args:
1455 vapp_uuid: vappid is application identifier
1456
1457 Returns:
1458 The return vApp name otherwise None
1459 """
1460 try:
1461 if self.client and vapp_uuid:
1462 vapp_call = "{}/api/vApp/vapp-{}".format(self.url, vapp_uuid)
1463 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
1464 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
1465
1466 response = self.perform_request(req_type='GET',
1467 url=vapp_call,
1468 headers=headers)
1469 #Retry login if session expired & retry sending request
1470 if response.status_code == 403:
1471 response = self.retry_rest('GET', vapp_call)
1472
1473 tree = XmlElementTree.fromstring(response.content)
1474 return tree.attrib['name']
1475 except Exception as e:
1476 self.logger.exception(e)
1477 return None
1478 return None
1479
1480 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list=[],
1481 cloud_config=None, disk_list=None, availability_zone_index=None, availability_zone_list=None):
1482 """Adds a VM instance to VIM
1483 Params:
1484 'start': (boolean) indicates if VM must start or created in pause mode.
1485 'image_id','flavor_id': image and flavor VIM id to use for the VM
1486 'net_list': list of interfaces, each one is a dictionary with:
1487 'name': (optional) name for the interface.
1488 'net_id': VIM network id where this interface must be connect to. Mandatory for type==virtual
1489 'vpci': (optional) virtual vPCI address to assign at the VM. Can be ignored depending on VIM capabilities
1490 'model': (optional and only have sense for type==virtual) interface model: virtio, e1000, ...
1491 'mac_address': (optional) mac address to assign to this interface
1492 #TODO: CHECK if an optional 'vlan' parameter is needed for VIMs when type if VF and net_id is not provided,
1493 the VLAN tag to be used. In case net_id is provided, the internal network vlan is used for tagging VF
1494 'type': (mandatory) can be one of:
1495 'virtual', in this case always connected to a network of type 'net_type=bridge'
1496 'PCI-PASSTHROUGH' or 'PF' (passthrough): depending on VIM capabilities it can be connected to a data/ptp network ot it
1497 can created unconnected
1498 'SR-IOV' or 'VF' (SRIOV with VLAN tag): same as PF for network connectivity.
1499 'VFnotShared'(SRIOV without VLAN tag) same as PF for network connectivity. VF where no other VFs
1500 are allocated on the same physical NIC
1501 'bw': (optional) only for PF/VF/VFnotShared. Minimal Bandwidth required for the interface in GBPS
1502 'port_security': (optional) If False it must avoid any traffic filtering at this interface. If missing
1503 or True, it must apply the default VIM behaviour
1504 After execution the method will add the key:
1505 'vim_id': must be filled/added by this method with the VIM identifier generated by the VIM for this
1506 interface. 'net_list' is modified
1507 'cloud_config': (optional) dictionary with:
1508 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
1509 'users': (optional) list of users to be inserted, each item is a dict with:
1510 'name': (mandatory) user name,
1511 'key-pairs': (optional) list of strings with the public key to be inserted to the user
1512 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
1513 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
1514 'config-files': (optional). List of files to be transferred. Each item is a dict with:
1515 'dest': (mandatory) string with the destination absolute path
1516 'encoding': (optional, by default text). Can be one of:
1517 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
1518 'content' (mandatory): string with the content of the file
1519 'permissions': (optional) string with file permissions, typically octal notation '0644'
1520 'owner': (optional) file owner, string with the format 'owner:group'
1521 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk)
1522 'disk_list': (optional) list with additional disks to the VM. Each item is a dict with:
1523 'image_id': (optional). VIM id of an existing image. If not provided an empty disk must be mounted
1524 'size': (mandatory) string with the size of the disk in GB
1525 availability_zone_index: Index of availability_zone_list to use for this this VM. None if not AV required
1526 availability_zone_list: list of availability zones given by user in the VNFD descriptor. Ignore if
1527 availability_zone_index is None
1528 Returns a tuple with the instance identifier and created_items or raises an exception on error
1529 created_items can be None or a dictionary where this method can include key-values that will be passed to
1530 the method delete_vminstance and action_vminstance. Can be used to store created ports, volumes, etc.
1531 Format is vimconnector dependent, but do not use nested dictionaries and a value of None should be the same
1532 as not present.
1533 """
1534 self.logger.info("Creating new instance for entry {}".format(name))
1535 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {} "\
1536 "availability_zone_index {} availability_zone_list {}"\
1537 .format(description, start, image_id, flavor_id, net_list, cloud_config, disk_list,\
1538 availability_zone_index, availability_zone_list))
1539
1540 #new vm name = vmname + tenant_id + uuid
1541 new_vm_name = [name, '-', str(uuid.uuid4())]
1542 vmname_andid = ''.join(new_vm_name)
1543
1544 for net in net_list:
1545 if net['type'] == "PCI-PASSTHROUGH":
1546 raise vimconn.vimconnNotSupportedException(
1547 "Current vCD version does not support type : {}".format(net['type']))
1548
1549 if len(net_list) > 10:
1550 raise vimconn.vimconnNotSupportedException(
1551 "The VM hardware versions 7 and above support upto 10 NICs only")
1552
1553 # if vm already deployed we return existing uuid
1554 # we check for presence of VDC, Catalog entry and Flavor.
1555 org, vdc = self.get_vdc_details()
1556 if vdc is None:
1557 raise vimconn.vimconnNotFoundException(
1558 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1559 catalogs = org.list_catalogs()
1560 if catalogs is None:
1561 #Retry once, if failed by refreshing token
1562 self.get_token()
1563 org = Org(self.client, resource=self.client.get_org())
1564 catalogs = org.list_catalogs()
1565 if catalogs is None:
1566 raise vimconn.vimconnNotFoundException(
1567 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1568
1569 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1570 if catalog_hash_name:
1571 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1572 else:
1573 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1574 "(Failed retrieve catalog information {})".format(name, image_id))
1575
1576 # Set vCPU and Memory based on flavor.
1577 vm_cpus = None
1578 vm_memory = None
1579 vm_disk = None
1580 numas = None
1581
1582 if flavor_id is not None:
1583 if flavor_id not in vimconnector.flavorlist:
1584 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1585 "Failed retrieve flavor information "
1586 "flavor id {}".format(name, flavor_id))
1587 else:
1588 try:
1589 flavor = vimconnector.flavorlist[flavor_id]
1590 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1591 vm_memory = flavor[FLAVOR_RAM_KEY]
1592 vm_disk = flavor[FLAVOR_DISK_KEY]
1593 extended = flavor.get("extended", None)
1594 if extended:
1595 numas=extended.get("numas", None)
1596
1597 except Exception as exp:
1598 raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
1599
1600 # image upload creates template name as catalog name space Template.
1601 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1602 power_on = 'false'
1603 if start:
1604 power_on = 'true'
1605
1606 # client must provide at least one entry in net_list if not we report error
1607 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1608 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1609 primary_net = None
1610 primary_netname = None
1611 primary_net_href = None
1612 network_mode = 'bridged'
1613 if net_list is not None and len(net_list) > 0:
1614 for net in net_list:
1615 if 'use' in net and net['use'] == 'mgmt' and not primary_net:
1616 primary_net = net
1617 if primary_net is None:
1618 primary_net = net_list[0]
1619
1620 try:
1621 primary_net_id = primary_net['net_id']
1622 url_list = [self.url, '/api/network/', primary_net_id]
1623 primary_net_href = ''.join(url_list)
1624 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1625 if 'name' in network_dict:
1626 primary_netname = network_dict['name']
1627
1628 except KeyError:
1629 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1630 else:
1631 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1632
1633 # use: 'data', 'bridge', 'mgmt'
1634 # create vApp. Set vcpu and ram based on flavor id.
1635 try:
1636 vdc_obj = VDC(self.client, resource=org.get_vdc(self.tenant_name))
1637 if not vdc_obj:
1638 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed to get VDC object")
1639
1640 for retry in (1,2):
1641 items = org.get_catalog_item(catalog_hash_name, catalog_hash_name)
1642 catalog_items = [items.attrib]
1643
1644 if len(catalog_items) == 1:
1645 if self.client:
1646 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
1647 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
1648
1649 response = self.perform_request(req_type='GET',
1650 url=catalog_items[0].get('href'),
1651 headers=headers)
1652 catalogItem = XmlElementTree.fromstring(response.content)
1653 entity = [child for child in catalogItem if child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
1654 vapp_tempalte_href = entity.get("href")
1655
1656 response = self.perform_request(req_type='GET',
1657 url=vapp_tempalte_href,
1658 headers=headers)
1659 if response.status_code != requests.codes.ok:
1660 self.logger.debug("REST API call {} failed. Return status code {}".format(vapp_tempalte_href,
1661 response.status_code))
1662 else:
1663 result = (response.content).replace("\n"," ")
1664
1665 src = re.search('<Vm goldMaster="false"\sstatus="\d+"\sname="(.*?)"\s'
1666 'id="(\w+:\w+:vm:.*?)"\shref="(.*?)"\s'
1667 'type="application/vnd\.vmware\.vcloud\.vm\+xml',result)
1668 if src:
1669 vm_name = src.group(1)
1670 vm_id = src.group(2)
1671 vm_href = src.group(3)
1672
1673 cpus = re.search('<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>',result).group(1)
1674 memory_mb = re.search('<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>',result).group(1)
1675 cores = re.search('<vmw:CoresPerSocket ovf:required.*?>(\d+)</vmw:CoresPerSocket>',result).group(1)
1676
1677 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml'
1678 vdc_id = vdc.get('id').split(':')[-1]
1679 instantiate_vapp_href = "{}/api/vdc/{}/action/instantiateVAppTemplate".format(self.url,
1680 vdc_id)
1681 data = """<?xml version="1.0" encoding="UTF-8"?>
1682 <InstantiateVAppTemplateParams
1683 xmlns="http://www.vmware.com/vcloud/v1.5"
1684 name="{}"
1685 deploy="false"
1686 powerOn="false"
1687 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1688 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1">
1689 <Description>Vapp instantiation</Description>
1690 <InstantiationParams>
1691 <NetworkConfigSection>
1692 <ovf:Info>Configuration parameters for logical networks</ovf:Info>
1693 <NetworkConfig networkName="{}">
1694 <Configuration>
1695 <ParentNetwork href="{}" />
1696 <FenceMode>bridged</FenceMode>
1697 </Configuration>
1698 </NetworkConfig>
1699 </NetworkConfigSection>
1700 <LeaseSettingsSection
1701 type="application/vnd.vmware.vcloud.leaseSettingsSection+xml">
1702 <ovf:Info>Lease Settings</ovf:Info>
1703 <StorageLeaseInSeconds>172800</StorageLeaseInSeconds>
1704 <StorageLeaseExpiration>2014-04-25T08:08:16.438-07:00</StorageLeaseExpiration>
1705 </LeaseSettingsSection>
1706 </InstantiationParams>
1707 <Source href="{}"/>
1708 <SourcedItem>
1709 <Source href="{}" id="{}" name="{}"
1710 type="application/vnd.vmware.vcloud.vm+xml"/>
1711 <VmGeneralParams>
1712 <NeedsCustomization>false</NeedsCustomization>
1713 </VmGeneralParams>
1714 <InstantiationParams>
1715 <NetworkConnectionSection>
1716 <ovf:Info>Specifies the available VM network connections</ovf:Info>
1717 <NetworkConnection network="{}">
1718 <NetworkConnectionIndex>0</NetworkConnectionIndex>
1719 <IsConnected>true</IsConnected>
1720 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
1721 </NetworkConnection>
1722 </NetworkConnectionSection><ovf:VirtualHardwareSection>
1723 <ovf:Info>Virtual hardware requirements</ovf:Info>
1724 <ovf:Item xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
1725 xmlns:vmw="http://www.vmware.com/schema/ovf">
1726 <rasd:AllocationUnits>hertz * 10^6</rasd:AllocationUnits>
1727 <rasd:Description>Number of Virtual CPUs</rasd:Description>
1728 <rasd:ElementName xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="str">{cpu} virtual CPU(s)</rasd:ElementName>
1729 <rasd:InstanceID>4</rasd:InstanceID>
1730 <rasd:Reservation>0</rasd:Reservation>
1731 <rasd:ResourceType>3</rasd:ResourceType>
1732 <rasd:VirtualQuantity xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="int">{cpu}</rasd:VirtualQuantity>
1733 <rasd:Weight>0</rasd:Weight>
1734 <vmw:CoresPerSocket ovf:required="false">{core}</vmw:CoresPerSocket>
1735 </ovf:Item><ovf:Item xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData">
1736 <rasd:AllocationUnits>byte * 2^20</rasd:AllocationUnits>
1737 <rasd:Description>Memory Size</rasd:Description>
1738 <rasd:ElementName xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="str">{memory} MB of memory</rasd:ElementName>
1739 <rasd:InstanceID>5</rasd:InstanceID>
1740 <rasd:Reservation>0</rasd:Reservation>
1741 <rasd:ResourceType>4</rasd:ResourceType>
1742 <rasd:VirtualQuantity xmlns:py="http://codespeak.net/lxml/objectify/pytype" py:pytype="int">{memory}</rasd:VirtualQuantity>
1743 <rasd:Weight>0</rasd:Weight>
1744 </ovf:Item>
1745 </ovf:VirtualHardwareSection>
1746 </InstantiationParams>
1747 </SourcedItem>
1748 <AllEULAsAccepted>false</AllEULAsAccepted>
1749 </InstantiateVAppTemplateParams>""".format(vmname_andid,
1750 primary_netname,
1751 primary_net_href,
1752 vapp_tempalte_href,
1753 vm_href,
1754 vm_id,
1755 vm_name,
1756 primary_netname,
1757 cpu=cpus,
1758 core=cores,
1759 memory=memory_mb)
1760
1761 response = self.perform_request(req_type='POST',
1762 url=instantiate_vapp_href,
1763 headers=headers,
1764 data=data)
1765
1766 if response.status_code != 201:
1767 self.logger.error("REST call {} failed reason : {}"\
1768 "status code : {}".format(instantiate_vapp_href,
1769 response.content,
1770 response.status_code))
1771 raise vimconn.vimconnException("new_vminstance(): Failed to create"\
1772 "vAapp {}".format(vmname_andid))
1773 else:
1774 vapptask = self.get_task_from_response(response.content)
1775
1776 if vapptask is None and retry==1:
1777 self.get_token() # Retry getting token
1778 continue
1779 else:
1780 break
1781
1782 if vapptask is None or vapptask is False:
1783 raise vimconn.vimconnUnexpectedResponse(
1784 "new_vminstance(): failed to create vApp {}".format(vmname_andid))
1785
1786 # wait for task to complete
1787 result = self.client.get_task_monitor().wait_for_success(task=vapptask)
1788
1789 if result.get('status') == 'success':
1790 self.logger.debug("new_vminstance(): Sucessfully created Vapp {}".format(vmname_andid))
1791 else:
1792 raise vimconn.vimconnUnexpectedResponse(
1793 "new_vminstance(): failed to create vApp {}".format(vmname_andid))
1794
1795 except Exception as exp:
1796 raise vimconn.vimconnUnexpectedResponse(
1797 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid, exp))
1798
1799 # we should have now vapp in undeployed state.
1800 try:
1801 vdc_obj = VDC(self.client, href=vdc.get('href'))
1802 vapp_resource = vdc_obj.get_vapp(vmname_andid)
1803 vapp_uuid = vapp_resource.get('id').split(':')[-1]
1804 vapp = VApp(self.client, resource=vapp_resource)
1805
1806 except Exception as exp:
1807 raise vimconn.vimconnUnexpectedResponse(
1808 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1809 .format(vmname_andid, exp))
1810
1811 if vapp_uuid is None:
1812 raise vimconn.vimconnUnexpectedResponse(
1813 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1814 vmname_andid))
1815
1816 #Add PCI passthrough/SRIOV configrations
1817 vm_obj = None
1818 pci_devices_info = []
1819 reserve_memory = False
1820
1821 for net in net_list:
1822 if net["type"] == "PF" or net["type"] == "PCI-PASSTHROUGH":
1823 pci_devices_info.append(net)
1824 elif (net["type"] == "VF" or net["type"] == "SR-IOV" or net["type"] == "VFnotShared") and 'net_id'in net:
1825 reserve_memory = True
1826
1827 #Add PCI
1828 if len(pci_devices_info) > 0:
1829 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1830 vmname_andid ))
1831 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1832 pci_devices_info,
1833 vmname_andid)
1834 if PCI_devices_status:
1835 self.logger.info("Added PCI devives {} to VM {}".format(
1836 pci_devices_info,
1837 vmname_andid)
1838 )
1839 reserve_memory = True
1840 else:
1841 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1842 pci_devices_info,
1843 vmname_andid)
1844 )
1845
1846 # Modify vm disk
1847 if vm_disk:
1848 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1849 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1850 if result :
1851 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1852
1853 #Add new or existing disks to vApp
1854 if disk_list:
1855 added_existing_disk = False
1856 for disk in disk_list:
1857 if 'device_type' in disk and disk['device_type'] == 'cdrom':
1858 image_id = disk['image_id']
1859 # Adding CD-ROM to VM
1860 # will revisit code once specification ready to support this feature
1861 self.insert_media_to_vm(vapp, image_id)
1862 elif "image_id" in disk and disk["image_id"] is not None:
1863 self.logger.debug("Adding existing disk from image {} to vm {} ".format(
1864 disk["image_id"] , vapp_uuid))
1865 self.add_existing_disk(catalogs=catalogs,
1866 image_id=disk["image_id"],
1867 size = disk["size"],
1868 template_name=templateName,
1869 vapp_uuid=vapp_uuid
1870 )
1871 added_existing_disk = True
1872 else:
1873 #Wait till added existing disk gets reflected into vCD database/API
1874 if added_existing_disk:
1875 time.sleep(5)
1876 added_existing_disk = False
1877 self.add_new_disk(vapp_uuid, disk['size'])
1878
1879 if numas:
1880 # Assigning numa affinity setting
1881 for numa in numas:
1882 if 'paired-threads-id' in numa:
1883 paired_threads_id = numa['paired-threads-id']
1884 self.set_numa_affinity(vapp_uuid, paired_threads_id)
1885
1886 # add NICs & connect to networks in netlist
1887 try:
1888 vdc_obj = VDC(self.client, href=vdc.get('href'))
1889 vapp_resource = vdc_obj.get_vapp(vmname_andid)
1890 vapp = VApp(self.client, resource=vapp_resource)
1891 vapp_id = vapp_resource.get('id').split(':')[-1]
1892
1893 self.logger.info("Removing primary NIC: ")
1894 # First remove all NICs so that NIC properties can be adjusted as needed
1895 self.remove_primary_network_adapter_from_all_vms(vapp)
1896
1897 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1898 primary_nic_index = 0
1899 nicIndex = 0
1900 for net in net_list:
1901 # openmano uses network id in UUID format.
1902 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1903 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1904 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1905
1906 if 'net_id' not in net:
1907 continue
1908
1909 #Using net_id as a vim_id i.e. vim interface id, as do not have saperate vim interface id
1910 #Same will be returned in refresh_vms_status() as vim_interface_id
1911 net['vim_id'] = net['net_id'] # Provide the same VIM identifier as the VIM network
1912
1913 interface_net_id = net['net_id']
1914 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1915 interface_network_mode = net['use']
1916
1917 if interface_network_mode == 'mgmt':
1918 primary_nic_index = nicIndex
1919
1920 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1921 - DHCP (The IP address is obtained from a DHCP service.)
1922 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1923 - NONE (No IP addressing mode specified.)"""
1924
1925 if primary_netname is not None:
1926 self.logger.debug("new_vminstance(): Filtering by net name {}".format(interface_net_name))
1927 nets = filter(lambda n: n.get('name') == interface_net_name, self.get_network_list())
1928 #For python3
1929 #nets = [n for n in self.get_network_list() if n.get('name') == interface_net_name]
1930 if len(nets) == 1:
1931 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].get('name')))
1932
1933 if interface_net_name != primary_netname:
1934 # connect network to VM - with all DHCP by default
1935 self.logger.info("new_vminstance(): Attaching net {} to vapp".format(interface_net_name))
1936 self.connect_vapp_to_org_vdc_network(vapp_id, nets[0].get('name'))
1937
1938 type_list = ('PF', 'PCI-PASSTHROUGH', 'VFnotShared')
1939 nic_type = 'VMXNET3'
1940 if 'type' in net and net['type'] not in type_list:
1941 # fetching nic type from vnf
1942 if 'model' in net:
1943 if net['model'] is not None:
1944 if net['model'].lower() == 'paravirt' or net['model'].lower() == 'virtio':
1945 nic_type = 'VMXNET3'
1946 else:
1947 nic_type = net['model']
1948
1949 self.logger.info("new_vminstance(): adding network adapter "\
1950 "to a network {}".format(nets[0].get('name')))
1951 self.add_network_adapter_to_vms(vapp, nets[0].get('name'),
1952 primary_nic_index,
1953 nicIndex,
1954 net,
1955 nic_type=nic_type)
1956 else:
1957 self.logger.info("new_vminstance(): adding network adapter "\
1958 "to a network {}".format(nets[0].get('name')))
1959 if net['type'] in ['SR-IOV', 'VF']:
1960 nic_type = net['type']
1961 self.add_network_adapter_to_vms(vapp, nets[0].get('name'),
1962 primary_nic_index,
1963 nicIndex,
1964 net,
1965 nic_type=nic_type)
1966 nicIndex += 1
1967
1968 # cloud-init for ssh-key injection
1969 if cloud_config:
1970 self.cloud_init(vapp,cloud_config)
1971
1972 # If VM has PCI devices or SRIOV reserve memory for VM
1973 if reserve_memory:
1974 self.reserve_memory_for_all_vms(vapp, memory_mb)
1975
1976 self.logger.debug("new_vminstance(): starting power on vApp {} ".format(vmname_andid))
1977
1978 poweron_task = self.power_on_vapp(vapp_id, vmname_andid)
1979 result = self.client.get_task_monitor().wait_for_success(task=poweron_task)
1980 if result.get('status') == 'success':
1981 self.logger.info("new_vminstance(): Successfully power on "\
1982 "vApp {}".format(vmname_andid))
1983 else:
1984 self.logger.error("new_vminstance(): failed to power on vApp "\
1985 "{}".format(vmname_andid))
1986
1987 except Exception as exp :
1988 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1989 self.logger.error("new_vminstance(): Failed create new vm instance {} with exception {}"
1990 .format(name, exp))
1991 raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {} with exception {}"
1992 .format(name, exp))
1993
1994 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1995 wait_time = 0
1996 vapp_uuid = None
1997 while wait_time <= MAX_WAIT_TIME:
1998 try:
1999 vapp_resource = vdc_obj.get_vapp(vmname_andid)
2000 vapp = VApp(self.client, resource=vapp_resource)
2001 except Exception as exp:
2002 raise vimconn.vimconnUnexpectedResponse(
2003 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
2004 .format(vmname_andid, exp))
2005
2006 #if vapp and vapp.me.deployed:
2007 if vapp and vapp_resource.get('deployed') == 'true':
2008 vapp_uuid = vapp_resource.get('id').split(':')[-1]
2009 break
2010 else:
2011 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
2012 time.sleep(INTERVAL_TIME)
2013
2014 wait_time +=INTERVAL_TIME
2015
2016 #SET Affinity Rule for VM
2017 #Pre-requisites: User has created Hosh Groups in vCenter with respective Hosts to be used
2018 #While creating VIM account user has to pass the Host Group names in availability_zone list
2019 #"availability_zone" is a part of VIM "config" parameters
2020 #For example, in VIM config: "availability_zone":["HG_170","HG_174","HG_175"]
2021 #Host groups are referred as availability zones
2022 #With following procedure, deployed VM will be added into a VM group.
2023 #Then A VM to Host Affinity rule will be created using the VM group & Host group.
2024 if(availability_zone_list):
2025 self.logger.debug("Existing Host Groups in VIM {}".format(self.config.get('availability_zone')))
2026 #Admin access required for creating Affinity rules
2027 client = self.connect_as_admin()
2028 if not client:
2029 raise vimconn.vimconnConnectionException("Failed to connect vCD as admin")
2030 else:
2031 self.client = client
2032 if self.client:
2033 headers = {'Accept':'application/*+xml;version=27.0',
2034 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
2035 #Step1: Get provider vdc details from organization
2036 pvdc_href = self.get_pvdc_for_org(self.tenant_name, headers)
2037 if pvdc_href is not None:
2038 #Step2: Found required pvdc, now get resource pool information
2039 respool_href = self.get_resource_pool_details(pvdc_href, headers)
2040 if respool_href is None:
2041 #Raise error if respool_href not found
2042 msg = "new_vminstance():Error in finding resource pool details in pvdc {}"\
2043 .format(pvdc_href)
2044 self.log_message(msg)
2045
2046 #Step3: Verify requested availability zone(hostGroup) is present in vCD
2047 # get availability Zone
2048 vm_az = self.get_vm_availability_zone(availability_zone_index, availability_zone_list)
2049 # check if provided av zone(hostGroup) is present in vCD VIM
2050 status = self.check_availibility_zone(vm_az, respool_href, headers)
2051 if status is False:
2052 msg = "new_vminstance(): Error in finding availability zone(Host Group): {} in "\
2053 "resource pool {} status: {}".format(vm_az,respool_href,status)
2054 self.log_message(msg)
2055 else:
2056 self.logger.debug ("new_vminstance(): Availability zone {} found in VIM".format(vm_az))
2057
2058 #Step4: Find VM group references to create vm group
2059 vmgrp_href = self.find_vmgroup_reference(respool_href, headers)
2060 if vmgrp_href == None:
2061 msg = "new_vminstance(): No reference to VmGroup found in resource pool"
2062 self.log_message(msg)
2063
2064 #Step5: Create a VmGroup with name az_VmGroup
2065 vmgrp_name = vm_az + "_" + name #Formed VM Group name = Host Group name + VM name
2066 status = self.create_vmgroup(vmgrp_name, vmgrp_href, headers)
2067 if status is not True:
2068 msg = "new_vminstance(): Error in creating VM group {}".format(vmgrp_name)
2069 self.log_message(msg)
2070
2071 #VM Group url to add vms to vm group
2072 vmgrpname_url = self.url + "/api/admin/extension/vmGroup/name/"+ vmgrp_name
2073
2074 #Step6: Add VM to VM Group
2075 #Find VM uuid from vapp_uuid
2076 vm_details = self.get_vapp_details_rest(vapp_uuid)
2077 vm_uuid = vm_details['vmuuid']
2078
2079 status = self.add_vm_to_vmgroup(vm_uuid, vmgrpname_url, vmgrp_name, headers)
2080 if status is not True:
2081 msg = "new_vminstance(): Error in adding VM to VM group {}".format(vmgrp_name)
2082 self.log_message(msg)
2083
2084 #Step7: Create VM to Host affinity rule
2085 addrule_href = self.get_add_rule_reference (respool_href, headers)
2086 if addrule_href is None:
2087 msg = "new_vminstance(): Error in finding href to add rule in resource pool: {}"\
2088 .format(respool_href)
2089 self.log_message(msg)
2090
2091 status = self.create_vm_to_host_affinity_rule(addrule_href, vmgrp_name, vm_az, "Affinity", headers)
2092 if status is False:
2093 msg = "new_vminstance(): Error in creating affinity rule for VM {} in Host group {}"\
2094 .format(name, vm_az)
2095 self.log_message(msg)
2096 else:
2097 self.logger.debug("new_vminstance(): Affinity rule created successfully. Added {} in Host group {}"\
2098 .format(name, vm_az))
2099 #Reset token to a normal user to perform other operations
2100 self.get_token()
2101
2102 if vapp_uuid is not None:
2103 return vapp_uuid, None
2104 else:
2105 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
2106
2107
2108 def get_vcd_availibility_zones(self,respool_href, headers):
2109 """ Method to find presence of av zone is VIM resource pool
2110
2111 Args:
2112 respool_href - resource pool href
2113 headers - header information
2114
2115 Returns:
2116 vcd_az - list of azone present in vCD
2117 """
2118 vcd_az = []
2119 url=respool_href
2120 resp = self.perform_request(req_type='GET',url=respool_href, headers=headers)
2121
2122 if resp.status_code != requests.codes.ok:
2123 self.logger.debug ("REST API call {} failed. Return status code {}".format(url, resp.status_code))
2124 else:
2125 #Get the href to hostGroups and find provided hostGroup is present in it
2126 resp_xml = XmlElementTree.fromstring(resp.content)
2127 for child in resp_xml:
2128 if 'VMWProviderVdcResourcePool' in child.tag:
2129 for schild in child:
2130 if 'Link' in schild.tag:
2131 if schild.attrib.get('type') == "application/vnd.vmware.admin.vmwHostGroupsType+xml":
2132 hostGroup = schild.attrib.get('href')
2133 hg_resp = self.perform_request(req_type='GET',url=hostGroup, headers=headers)
2134 if hg_resp.status_code != requests.codes.ok:
2135 self.logger.debug ("REST API call {} failed. Return status code {}".format(hostGroup, hg_resp.status_code))
2136 else:
2137 hg_resp_xml = XmlElementTree.fromstring(hg_resp.content)
2138 for hostGroup in hg_resp_xml:
2139 if 'HostGroup' in hostGroup.tag:
2140 #append host group name to the list
2141 vcd_az.append(hostGroup.attrib.get("name"))
2142 return vcd_az
2143
2144
2145 def set_availability_zones(self):
2146 """
2147 Set vim availability zone
2148 """
2149
2150 vim_availability_zones = None
2151 availability_zone = None
2152 if 'availability_zone' in self.config:
2153 vim_availability_zones = self.config.get('availability_zone')
2154 if isinstance(vim_availability_zones, str):
2155 availability_zone = [vim_availability_zones]
2156 elif isinstance(vim_availability_zones, list):
2157 availability_zone = vim_availability_zones
2158 else:
2159 return availability_zone
2160
2161 return availability_zone
2162
2163
2164 def get_vm_availability_zone(self, availability_zone_index, availability_zone_list):
2165 """
2166 Return the availability zone to be used by the created VM.
2167 returns: The VIM availability zone to be used or None
2168 """
2169 if availability_zone_index is None:
2170 if not self.config.get('availability_zone'):
2171 return None
2172 elif isinstance(self.config.get('availability_zone'), str):
2173 return self.config['availability_zone']
2174 else:
2175 return self.config['availability_zone'][0]
2176
2177 vim_availability_zones = self.availability_zone
2178
2179 # check if VIM offer enough availability zones describe in the VNFD
2180 if vim_availability_zones and len(availability_zone_list) <= len(vim_availability_zones):
2181 # check if all the names of NFV AV match VIM AV names
2182 match_by_index = False
2183 for av in availability_zone_list:
2184 if av not in vim_availability_zones:
2185 match_by_index = True
2186 break
2187 if match_by_index:
2188 self.logger.debug("Required Availability zone or Host Group not found in VIM config")
2189 self.logger.debug("Input Availability zone list: {}".format(availability_zone_list))
2190 self.logger.debug("VIM configured Availability zones: {}".format(vim_availability_zones))
2191 self.logger.debug("VIM Availability zones will be used by index")
2192 return vim_availability_zones[availability_zone_index]
2193 else:
2194 return availability_zone_list[availability_zone_index]
2195 else:
2196 raise vimconn.vimconnConflictException("No enough availability zones at VIM for this deployment")
2197
2198
2199 def create_vm_to_host_affinity_rule(self, addrule_href, vmgrpname, hostgrpname, polarity, headers):
2200 """ Method to create VM to Host Affinity rule in vCD
2201
2202 Args:
2203 addrule_href - href to make a POST request
2204 vmgrpname - name of the VM group created
2205 hostgrpnmae - name of the host group created earlier
2206 polarity - Affinity or Anti-affinity (default: Affinity)
2207 headers - headers to make REST call
2208
2209 Returns:
2210 True- if rule is created
2211 False- Failed to create rule due to some error
2212
2213 """
2214 task_status = False
2215 rule_name = polarity + "_" + vmgrpname
2216 payload = """<?xml version="1.0" encoding="UTF-8"?>
2217 <vmext:VMWVmHostAffinityRule
2218 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
2219 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
2220 type="application/vnd.vmware.admin.vmwVmHostAffinityRule+xml">
2221 <vcloud:Name>{}</vcloud:Name>
2222 <vcloud:IsEnabled>true</vcloud:IsEnabled>
2223 <vcloud:IsMandatory>true</vcloud:IsMandatory>
2224 <vcloud:Polarity>{}</vcloud:Polarity>
2225 <vmext:HostGroupName>{}</vmext:HostGroupName>
2226 <vmext:VmGroupName>{}</vmext:VmGroupName>
2227 </vmext:VMWVmHostAffinityRule>""".format(rule_name, polarity, hostgrpname, vmgrpname)
2228
2229 resp = self.perform_request(req_type='POST',url=addrule_href, headers=headers, data=payload)
2230
2231 if resp.status_code != requests.codes.accepted:
2232 self.logger.debug ("REST API call {} failed. Return status code {}".format(addrule_href, resp.status_code))
2233 task_status = False
2234 return task_status
2235 else:
2236 affinity_task = self.get_task_from_response(resp.content)
2237 self.logger.debug ("affinity_task: {}".format(affinity_task))
2238 if affinity_task is None or affinity_task is False:
2239 raise vimconn.vimconnUnexpectedResponse("failed to find affinity task")
2240 # wait for task to complete
2241 result = self.client.get_task_monitor().wait_for_success(task=affinity_task)
2242 if result.get('status') == 'success':
2243 self.logger.debug("Successfully created affinity rule {}".format(rule_name))
2244 return True
2245 else:
2246 raise vimconn.vimconnUnexpectedResponse(
2247 "failed to create affinity rule {}".format(rule_name))
2248
2249
2250 def get_add_rule_reference (self, respool_href, headers):
2251 """ This method finds href to add vm to host affinity rule to vCD
2252
2253 Args:
2254 respool_href- href to resource pool
2255 headers- header information to make REST call
2256
2257 Returns:
2258 None - if no valid href to add rule found or
2259 addrule_href - href to add vm to host affinity rule of resource pool
2260 """
2261 addrule_href = None
2262 resp = self.perform_request(req_type='GET',url=respool_href, headers=headers)
2263
2264 if resp.status_code != requests.codes.ok:
2265 self.logger.debug ("REST API call {} failed. Return status code {}".format(respool_href, resp.status_code))
2266 else:
2267
2268 resp_xml = XmlElementTree.fromstring(resp.content)
2269 for child in resp_xml:
2270 if 'VMWProviderVdcResourcePool' in child.tag:
2271 for schild in child:
2272 if 'Link' in schild.tag:
2273 if schild.attrib.get('type') == "application/vnd.vmware.admin.vmwVmHostAffinityRule+xml" and \
2274 schild.attrib.get('rel') == "add":
2275 addrule_href = schild.attrib.get('href')
2276 break
2277
2278 return addrule_href
2279
2280
2281 def add_vm_to_vmgroup(self, vm_uuid, vmGroupNameURL, vmGroup_name, headers):
2282 """ Method to add deployed VM to newly created VM Group.
2283 This is required to create VM to Host affinity in vCD
2284
2285 Args:
2286 vm_uuid- newly created vm uuid
2287 vmGroupNameURL- URL to VM Group name
2288 vmGroup_name- Name of VM group created
2289 headers- Headers for REST request
2290
2291 Returns:
2292 True- if VM added to VM group successfully
2293 False- if any error encounter
2294 """
2295
2296 addvm_resp = self.perform_request(req_type='GET',url=vmGroupNameURL, headers=headers)#, data=payload)
2297
2298 if addvm_resp.status_code != requests.codes.ok:
2299 self.logger.debug ("REST API call to get VM Group Name url {} failed. Return status code {}"\
2300 .format(vmGroupNameURL, addvm_resp.status_code))
2301 return False
2302 else:
2303 resp_xml = XmlElementTree.fromstring(addvm_resp.content)
2304 for child in resp_xml:
2305 if child.tag.split('}')[1] == 'Link':
2306 if child.attrib.get("rel") == "addVms":
2307 addvmtogrpURL = child.attrib.get("href")
2308
2309 #Get vm details
2310 url_list = [self.url, '/api/vApp/vm-',vm_uuid]
2311 vmdetailsURL = ''.join(url_list)
2312
2313 resp = self.perform_request(req_type='GET',url=vmdetailsURL, headers=headers)
2314
2315 if resp.status_code != requests.codes.ok:
2316 self.logger.debug ("REST API call {} failed. Return status code {}".format(vmdetailsURL, resp.status_code))
2317 return False
2318
2319 #Parse VM details
2320 resp_xml = XmlElementTree.fromstring(resp.content)
2321 if resp_xml.tag.split('}')[1] == "Vm":
2322 vm_id = resp_xml.attrib.get("id")
2323 vm_name = resp_xml.attrib.get("name")
2324 vm_href = resp_xml.attrib.get("href")
2325 #print vm_id, vm_name, vm_href
2326 #Add VM into VMgroup
2327 payload = """<?xml version="1.0" encoding="UTF-8"?>\
2328 <ns2:Vms xmlns:ns2="http://www.vmware.com/vcloud/v1.5" \
2329 xmlns="http://www.vmware.com/vcloud/versions" \
2330 xmlns:ns3="http://schemas.dmtf.org/ovf/envelope/1" \
2331 xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" \
2332 xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/common" \
2333 xmlns:ns6="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" \
2334 xmlns:ns7="http://www.vmware.com/schema/ovf" \
2335 xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" \
2336 xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">\
2337 <ns2:VmReference href="{}" id="{}" name="{}" \
2338 type="application/vnd.vmware.vcloud.vm+xml" />\
2339 </ns2:Vms>""".format(vm_href, vm_id, vm_name)
2340
2341 addvmtogrp_resp = self.perform_request(req_type='POST',url=addvmtogrpURL, headers=headers, data=payload)
2342
2343 if addvmtogrp_resp.status_code != requests.codes.accepted:
2344 self.logger.debug ("REST API call {} failed. Return status code {}".format(addvmtogrpURL, addvmtogrp_resp.status_code))
2345 return False
2346 else:
2347 self.logger.debug ("Done adding VM {} to VMgroup {}".format(vm_name, vmGroup_name))
2348 return True
2349
2350
2351 def create_vmgroup(self, vmgroup_name, vmgroup_href, headers):
2352 """Method to create a VM group in vCD
2353
2354 Args:
2355 vmgroup_name : Name of VM group to be created
2356 vmgroup_href : href for vmgroup
2357 headers- Headers for REST request
2358 """
2359 #POST to add URL with required data
2360 vmgroup_status = False
2361 payload = """<VMWVmGroup xmlns="http://www.vmware.com/vcloud/extension/v1.5" \
2362 xmlns:vcloud_v1.5="http://www.vmware.com/vcloud/v1.5" name="{}">\
2363 <vmCount>1</vmCount>\
2364 </VMWVmGroup>""".format(vmgroup_name)
2365 resp = self.perform_request(req_type='POST',url=vmgroup_href, headers=headers, data=payload)
2366
2367 if resp.status_code != requests.codes.accepted:
2368 self.logger.debug ("REST API call {} failed. Return status code {}".format(vmgroup_href, resp.status_code))
2369 return vmgroup_status
2370 else:
2371 vmgroup_task = self.get_task_from_response(resp.content)
2372 if vmgroup_task is None or vmgroup_task is False:
2373 raise vimconn.vimconnUnexpectedResponse(
2374 "create_vmgroup(): failed to create VM group {}".format(vmgroup_name))
2375
2376 # wait for task to complete
2377 result = self.client.get_task_monitor().wait_for_success(task=vmgroup_task)
2378
2379 if result.get('status') == 'success':
2380 self.logger.debug("create_vmgroup(): Successfully created VM group {}".format(vmgroup_name))
2381 #time.sleep(10)
2382 vmgroup_status = True
2383 return vmgroup_status
2384 else:
2385 raise vimconn.vimconnUnexpectedResponse(\
2386 "create_vmgroup(): failed to create VM group {}".format(vmgroup_name))
2387
2388
2389 def find_vmgroup_reference(self, url, headers):
2390 """ Method to create a new VMGroup which is required to add created VM
2391 Args:
2392 url- resource pool href
2393 headers- header information
2394
2395 Returns:
2396 returns href to VM group to create VM group
2397 """
2398 #Perform GET on resource pool to find 'add' link to create VMGroup
2399 #https://vcd-ip/api/admin/extension/providervdc/<providervdc id>/resourcePools
2400 vmgrp_href = None
2401 resp = self.perform_request(req_type='GET',url=url, headers=headers)
2402
2403 if resp.status_code != requests.codes.ok:
2404 self.logger.debug ("REST API call {} failed. Return status code {}".format(url, resp.status_code))
2405 else:
2406 #Get the href to add vmGroup to vCD
2407 resp_xml = XmlElementTree.fromstring(resp.content)
2408 for child in resp_xml:
2409 if 'VMWProviderVdcResourcePool' in child.tag:
2410 for schild in child:
2411 if 'Link' in schild.tag:
2412 #Find href with type VMGroup and rel with add
2413 if schild.attrib.get('type') == "application/vnd.vmware.admin.vmwVmGroupType+xml"\
2414 and schild.attrib.get('rel') == "add":
2415 vmgrp_href = schild.attrib.get('href')
2416 return vmgrp_href
2417
2418
2419 def check_availibility_zone(self, az, respool_href, headers):
2420 """ Method to verify requested av zone is present or not in provided
2421 resource pool
2422
2423 Args:
2424 az - name of hostgroup (availibility_zone)
2425 respool_href - Resource Pool href
2426 headers - Headers to make REST call
2427 Returns:
2428 az_found - True if availibility_zone is found else False
2429 """
2430 az_found = False
2431 headers['Accept']='application/*+xml;version=27.0'
2432 resp = self.perform_request(req_type='GET',url=respool_href, headers=headers)
2433
2434 if resp.status_code != requests.codes.ok:
2435 self.logger.debug ("REST API call {} failed. Return status code {}".format(respool_href, resp.status_code))
2436 else:
2437 #Get the href to hostGroups and find provided hostGroup is present in it
2438 resp_xml = XmlElementTree.fromstring(resp.content)
2439
2440 for child in resp_xml:
2441 if 'VMWProviderVdcResourcePool' in child.tag:
2442 for schild in child:
2443 if 'Link' in schild.tag:
2444 if schild.attrib.get('type') == "application/vnd.vmware.admin.vmwHostGroupsType+xml":
2445 hostGroup_href = schild.attrib.get('href')
2446 hg_resp = self.perform_request(req_type='GET',url=hostGroup_href, headers=headers)
2447 if hg_resp.status_code != requests.codes.ok:
2448 self.logger.debug ("REST API call {} failed. Return status code {}".format(hostGroup_href, hg_resp.status_code))
2449 else:
2450 hg_resp_xml = XmlElementTree.fromstring(hg_resp.content)
2451 for hostGroup in hg_resp_xml:
2452 if 'HostGroup' in hostGroup.tag:
2453 if hostGroup.attrib.get("name") == az:
2454 az_found = True
2455 break
2456 return az_found
2457
2458
2459 def get_pvdc_for_org(self, org_vdc, headers):
2460 """ This method gets provider vdc references from organisation
2461
2462 Args:
2463 org_vdc - name of the organisation VDC to find pvdc
2464 headers - headers to make REST call
2465
2466 Returns:
2467 None - if no pvdc href found else
2468 pvdc_href - href to pvdc
2469 """
2470
2471 #Get provider VDC references from vCD
2472 pvdc_href = None
2473 #url = '<vcd url>/api/admin/extension/providerVdcReferences'
2474 url_list = [self.url, '/api/admin/extension/providerVdcReferences']
2475 url = ''.join(url_list)
2476
2477 response = self.perform_request(req_type='GET',url=url, headers=headers)
2478 if response.status_code != requests.codes.ok:
2479 self.logger.debug ("REST API call {} failed. Return status code {}"\
2480 .format(url, response.status_code))
2481 else:
2482 xmlroot_response = XmlElementTree.fromstring(response.content)
2483 for child in xmlroot_response:
2484 if 'ProviderVdcReference' in child.tag:
2485 pvdc_href = child.attrib.get('href')
2486 #Get vdcReferences to find org
2487 pvdc_resp = self.perform_request(req_type='GET',url=pvdc_href, headers=headers)
2488 if pvdc_resp.status_code != requests.codes.ok:
2489 raise vimconn.vimconnException("REST API call {} failed. "\
2490 "Return status code {}"\
2491 .format(url, pvdc_resp.status_code))
2492
2493 pvdc_resp_xml = XmlElementTree.fromstring(pvdc_resp.content)
2494 for child in pvdc_resp_xml:
2495 if 'Link' in child.tag:
2496 if child.attrib.get('type') == "application/vnd.vmware.admin.vdcReferences+xml":
2497 vdc_href = child.attrib.get('href')
2498
2499 #Check if provided org is present in vdc
2500 vdc_resp = self.perform_request(req_type='GET',
2501 url=vdc_href,
2502 headers=headers)
2503 if vdc_resp.status_code != requests.codes.ok:
2504 raise vimconn.vimconnException("REST API call {} failed. "\
2505 "Return status code {}"\
2506 .format(url, vdc_resp.status_code))
2507 vdc_resp_xml = XmlElementTree.fromstring(vdc_resp.content)
2508 for child in vdc_resp_xml:
2509 if 'VdcReference' in child.tag:
2510 if child.attrib.get('name') == org_vdc:
2511 return pvdc_href
2512
2513
2514 def get_resource_pool_details(self, pvdc_href, headers):
2515 """ Method to get resource pool information.
2516 Host groups are property of resource group.
2517 To get host groups, we need to GET details of resource pool.
2518
2519 Args:
2520 pvdc_href: href to pvdc details
2521 headers: headers
2522
2523 Returns:
2524 respool_href - Returns href link reference to resource pool
2525 """
2526 respool_href = None
2527 resp = self.perform_request(req_type='GET',url=pvdc_href, headers=headers)
2528
2529 if resp.status_code != requests.codes.ok:
2530 self.logger.debug ("REST API call {} failed. Return status code {}"\
2531 .format(pvdc_href, resp.status_code))
2532 else:
2533 respool_resp_xml = XmlElementTree.fromstring(resp.content)
2534 for child in respool_resp_xml:
2535 if 'Link' in child.tag:
2536 if child.attrib.get('type') == "application/vnd.vmware.admin.vmwProviderVdcResourcePoolSet+xml":
2537 respool_href = child.attrib.get("href")
2538 break
2539 return respool_href
2540
2541
2542 def log_message(self, msg):
2543 """
2544 Method to log error messages related to Affinity rule creation
2545 in new_vminstance & raise Exception
2546 Args :
2547 msg - Error message to be logged
2548
2549 """
2550 #get token to connect vCD as a normal user
2551 self.get_token()
2552 self.logger.debug(msg)
2553 raise vimconn.vimconnException(msg)
2554
2555
2556 ##
2557 ##
2558 ## based on current discussion
2559 ##
2560 ##
2561 ## server:
2562 # created: '2016-09-08T11:51:58'
2563 # description: simple-instance.linux1.1
2564 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
2565 # hostId: e836c036-74e7-11e6-b249-0800273e724c
2566 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
2567 # status: ACTIVE
2568 # error_msg:
2569 # interfaces: …
2570 #
2571 def get_vminstance(self, vim_vm_uuid=None):
2572 """Returns the VM instance information from VIM"""
2573
2574 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
2575
2576 org, vdc = self.get_vdc_details()
2577 if vdc is None:
2578 raise vimconn.vimconnConnectionException(
2579 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
2580
2581 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
2582 if not vm_info_dict:
2583 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
2584 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
2585
2586 status_key = vm_info_dict['status']
2587 error = ''
2588 try:
2589 vm_dict = {'created': vm_info_dict['created'],
2590 'description': vm_info_dict['name'],
2591 'status': vcdStatusCode2manoFormat[int(status_key)],
2592 'hostId': vm_info_dict['vmuuid'],
2593 'error_msg': error,
2594 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
2595
2596 if 'interfaces' in vm_info_dict:
2597 vm_dict['interfaces'] = vm_info_dict['interfaces']
2598 else:
2599 vm_dict['interfaces'] = []
2600 except KeyError:
2601 vm_dict = {'created': '',
2602 'description': '',
2603 'status': vcdStatusCode2manoFormat[int(-1)],
2604 'hostId': vm_info_dict['vmuuid'],
2605 'error_msg': "Inconsistency state",
2606 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
2607
2608 return vm_dict
2609
2610 def delete_vminstance(self, vm__vim_uuid, created_items=None):
2611 """Method poweroff and remove VM instance from vcloud director network.
2612
2613 Args:
2614 vm__vim_uuid: VM UUID
2615
2616 Returns:
2617 Returns the instance identifier
2618 """
2619
2620 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
2621
2622 org, vdc = self.get_vdc_details()
2623 vdc_obj = VDC(self.client, href=vdc.get('href'))
2624 if vdc_obj is None:
2625 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
2626 self.tenant_name))
2627 raise vimconn.vimconnException(
2628 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
2629
2630 try:
2631 vapp_name = self.get_namebyvappid(vm__vim_uuid)
2632 if vapp_name is None:
2633 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2634 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
2635 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
2636 vapp_resource = vdc_obj.get_vapp(vapp_name)
2637 vapp = VApp(self.client, resource=vapp_resource)
2638
2639 # Delete vApp and wait for status change if task executed and vApp is None.
2640
2641 if vapp:
2642 if vapp_resource.get('deployed') == 'true':
2643 self.logger.info("Powering off vApp {}".format(vapp_name))
2644 #Power off vApp
2645 powered_off = False
2646 wait_time = 0
2647 while wait_time <= MAX_WAIT_TIME:
2648 power_off_task = vapp.power_off()
2649 result = self.client.get_task_monitor().wait_for_success(task=power_off_task)
2650
2651 if result.get('status') == 'success':
2652 powered_off = True
2653 break
2654 else:
2655 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
2656 time.sleep(INTERVAL_TIME)
2657
2658 wait_time +=INTERVAL_TIME
2659 if not powered_off:
2660 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
2661 else:
2662 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
2663
2664 #Undeploy vApp
2665 self.logger.info("Undeploy vApp {}".format(vapp_name))
2666 wait_time = 0
2667 undeployed = False
2668 while wait_time <= MAX_WAIT_TIME:
2669 vapp = VApp(self.client, resource=vapp_resource)
2670 if not vapp:
2671 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2672 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
2673 undeploy_task = vapp.undeploy()
2674
2675 result = self.client.get_task_monitor().wait_for_success(task=undeploy_task)
2676 if result.get('status') == 'success':
2677 undeployed = True
2678 break
2679 else:
2680 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
2681 time.sleep(INTERVAL_TIME)
2682
2683 wait_time +=INTERVAL_TIME
2684
2685 if not undeployed:
2686 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
2687
2688 # delete vapp
2689 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
2690
2691 if vapp is not None:
2692 wait_time = 0
2693 result = False
2694
2695 while wait_time <= MAX_WAIT_TIME:
2696 vapp = VApp(self.client, resource=vapp_resource)
2697 if not vapp:
2698 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2699 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
2700
2701 delete_task = vdc_obj.delete_vapp(vapp.name, force=True)
2702
2703 result = self.client.get_task_monitor().wait_for_success(task=delete_task)
2704 if result.get('status') == 'success':
2705 break
2706 else:
2707 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
2708 time.sleep(INTERVAL_TIME)
2709
2710 wait_time +=INTERVAL_TIME
2711
2712 if result is None:
2713 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
2714 else:
2715 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
2716 return vm__vim_uuid
2717 except:
2718 self.logger.debug(traceback.format_exc())
2719 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
2720
2721
2722 def refresh_vms_status(self, vm_list):
2723 """Get the status of the virtual machines and their interfaces/ports
2724 Params: the list of VM identifiers
2725 Returns a dictionary with:
2726 vm_id: #VIM id of this Virtual Machine
2727 status: #Mandatory. Text with one of:
2728 # DELETED (not found at vim)
2729 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
2730 # OTHER (Vim reported other status not understood)
2731 # ERROR (VIM indicates an ERROR status)
2732 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
2733 # CREATING (on building process), ERROR
2734 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
2735 #
2736 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
2737 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2738 interfaces:
2739 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
2740 mac_address: #Text format XX:XX:XX:XX:XX:XX
2741 vim_net_id: #network id where this interface is connected
2742 vim_interface_id: #interface/port VIM id
2743 ip_address: #null, or text with IPv4, IPv6 address
2744 """
2745
2746 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
2747
2748 org,vdc = self.get_vdc_details()
2749 if vdc is None:
2750 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
2751
2752 vms_dict = {}
2753 nsx_edge_list = []
2754 for vmuuid in vm_list:
2755 vapp_name = self.get_namebyvappid(vmuuid)
2756 if vapp_name is not None:
2757
2758 try:
2759 vm_pci_details = self.get_vm_pci_details(vmuuid)
2760 vdc_obj = VDC(self.client, href=vdc.get('href'))
2761 vapp_resource = vdc_obj.get_vapp(vapp_name)
2762 the_vapp = VApp(self.client, resource=vapp_resource)
2763
2764 vm_details = {}
2765 for vm in the_vapp.get_all_vms():
2766 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
2767 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
2768 response = self.perform_request(req_type='GET',
2769 url=vm.get('href'),
2770 headers=headers)
2771
2772 if response.status_code != 200:
2773 self.logger.error("refresh_vms_status : REST call {} failed reason : {}"\
2774 "status code : {}".format(vm.get('href'),
2775 response.content,
2776 response.status_code))
2777 raise vimconn.vimconnException("refresh_vms_status : Failed to get "\
2778 "VM details")
2779 xmlroot = XmlElementTree.fromstring(response.content)
2780
2781
2782 result = response.content.replace("\n"," ")
2783 hdd_match = re.search('vcloud:capacity="(\d+)"\svcloud:storageProfileOverrideVmDefault=',result)
2784 if hdd_match:
2785 hdd_mb = hdd_match.group(1)
2786 vm_details['hdd_mb'] = int(hdd_mb) if hdd_mb else None
2787 cpus_match = re.search('<rasd:Description>Number of Virtual CPUs</.*?>(\d+)</rasd:VirtualQuantity>',result)
2788 if cpus_match:
2789 cpus = cpus_match.group(1)
2790 vm_details['cpus'] = int(cpus) if cpus else None
2791 memory_mb = re.search('<rasd:Description>Memory Size</.*?>(\d+)</rasd:VirtualQuantity>',result).group(1)
2792 vm_details['memory_mb'] = int(memory_mb) if memory_mb else None
2793 vm_details['status'] = vcdStatusCode2manoFormat[int(xmlroot.get('status'))]
2794 vm_details['id'] = xmlroot.get('id')
2795 vm_details['name'] = xmlroot.get('name')
2796 vm_info = [vm_details]
2797 if vm_pci_details:
2798 vm_info[0].update(vm_pci_details)
2799
2800 vm_dict = {'status': vcdStatusCode2manoFormat[int(vapp_resource.get('status'))],
2801 'error_msg': vcdStatusCode2manoFormat[int(vapp_resource.get('status'))],
2802 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
2803
2804 # get networks
2805 vm_ip = None
2806 vm_mac = None
2807 networks = re.findall('<NetworkConnection needsCustomization=.*?</NetworkConnection>',result)
2808 for network in networks:
2809 mac_s = re.search('<MACAddress>(.*?)</MACAddress>',network)
2810 vm_mac = mac_s.group(1) if mac_s else None
2811 ip_s = re.search('<IpAddress>(.*?)</IpAddress>',network)
2812 vm_ip = ip_s.group(1) if ip_s else None
2813
2814 if vm_ip is None:
2815 if not nsx_edge_list:
2816 nsx_edge_list = self.get_edge_details()
2817 if nsx_edge_list is None:
2818 raise vimconn.vimconnException("refresh_vms_status:"\
2819 "Failed to get edge details from NSX Manager")
2820 if vm_mac is not None:
2821 vm_ip = self.get_ipaddr_from_NSXedge(nsx_edge_list, vm_mac)
2822
2823 net_s = re.search('network="(.*?)"',network)
2824 network_name = net_s.group(1) if net_s else None
2825
2826 vm_net_id = self.get_network_id_by_name(network_name)
2827 interface = {"mac_address": vm_mac,
2828 "vim_net_id": vm_net_id,
2829 "vim_interface_id": vm_net_id,
2830 "ip_address": vm_ip}
2831
2832 vm_dict["interfaces"].append(interface)
2833
2834 # add a vm to vm dict
2835 vms_dict.setdefault(vmuuid, vm_dict)
2836 self.logger.debug("refresh_vms_status : vm info {}".format(vm_dict))
2837 except Exception as exp:
2838 self.logger.debug("Error in response {}".format(exp))
2839 self.logger.debug(traceback.format_exc())
2840
2841 return vms_dict
2842
2843
2844 def get_edge_details(self):
2845 """Get the NSX edge list from NSX Manager
2846 Returns list of NSX edges
2847 """
2848 edge_list = []
2849 rheaders = {'Content-Type': 'application/xml'}
2850 nsx_api_url = '/api/4.0/edges'
2851
2852 self.logger.debug("Get edge details from NSX Manager {} {}".format(self.nsx_manager, nsx_api_url))
2853
2854 try:
2855 resp = requests.get(self.nsx_manager + nsx_api_url,
2856 auth = (self.nsx_user, self.nsx_password),
2857 verify = False, headers = rheaders)
2858 if resp.status_code == requests.codes.ok:
2859 paged_Edge_List = XmlElementTree.fromstring(resp.text)
2860 for edge_pages in paged_Edge_List:
2861 if edge_pages.tag == 'edgePage':
2862 for edge_summary in edge_pages:
2863 if edge_summary.tag == 'pagingInfo':
2864 for element in edge_summary:
2865 if element.tag == 'totalCount' and element.text == '0':
2866 raise vimconn.vimconnException("get_edge_details: No NSX edges details found: {}"
2867 .format(self.nsx_manager))
2868
2869 if edge_summary.tag == 'edgeSummary':
2870 for element in edge_summary:
2871 if element.tag == 'id':
2872 edge_list.append(element.text)
2873 else:
2874 raise vimconn.vimconnException("get_edge_details: No NSX edge details found: {}"
2875 .format(self.nsx_manager))
2876
2877 if not edge_list:
2878 raise vimconn.vimconnException("get_edge_details: "\
2879 "No NSX edge details found: {}"
2880 .format(self.nsx_manager))
2881 else:
2882 self.logger.debug("get_edge_details: Found NSX edges {}".format(edge_list))
2883 return edge_list
2884 else:
2885 self.logger.debug("get_edge_details: "
2886 "Failed to get NSX edge details from NSX Manager: {}"
2887 .format(resp.content))
2888 return None
2889
2890 except Exception as exp:
2891 self.logger.debug("get_edge_details: "\
2892 "Failed to get NSX edge details from NSX Manager: {}"
2893 .format(exp))
2894 raise vimconn.vimconnException("get_edge_details: "\
2895 "Failed to get NSX edge details from NSX Manager: {}"
2896 .format(exp))
2897
2898
2899 def get_ipaddr_from_NSXedge(self, nsx_edges, mac_address):
2900 """Get IP address details from NSX edges, using the MAC address
2901 PARAMS: nsx_edges : List of NSX edges
2902 mac_address : Find IP address corresponding to this MAC address
2903 Returns: IP address corrresponding to the provided MAC address
2904 """
2905
2906 ip_addr = None
2907 rheaders = {'Content-Type': 'application/xml'}
2908
2909 self.logger.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
2910
2911 try:
2912 for edge in nsx_edges:
2913 nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
2914
2915 resp = requests.get(self.nsx_manager + nsx_api_url,
2916 auth = (self.nsx_user, self.nsx_password),
2917 verify = False, headers = rheaders)
2918
2919 if resp.status_code == requests.codes.ok:
2920 dhcp_leases = XmlElementTree.fromstring(resp.text)
2921 for child in dhcp_leases:
2922 if child.tag == 'dhcpLeaseInfo':
2923 dhcpLeaseInfo = child
2924 for leaseInfo in dhcpLeaseInfo:
2925 for elem in leaseInfo:
2926 if (elem.tag)=='macAddress':
2927 edge_mac_addr = elem.text
2928 if (elem.tag)=='ipAddress':
2929 ip_addr = elem.text
2930 if edge_mac_addr is not None:
2931 if edge_mac_addr == mac_address:
2932 self.logger.debug("Found ip addr {} for mac {} at NSX edge {}"
2933 .format(ip_addr, mac_address,edge))
2934 return ip_addr
2935 else:
2936 self.logger.debug("get_ipaddr_from_NSXedge: "\
2937 "Error occurred while getting DHCP lease info from NSX Manager: {}"
2938 .format(resp.content))
2939
2940 self.logger.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
2941 return None
2942
2943 except XmlElementTree.ParseError as Err:
2944 self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
2945
2946
2947 def action_vminstance(self, vm__vim_uuid=None, action_dict=None, created_items={}):
2948 """Send and action over a VM instance from VIM
2949 Returns the vm_id if the action was successfully sent to the VIM"""
2950
2951 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
2952 if vm__vim_uuid is None or action_dict is None:
2953 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
2954
2955 org, vdc = self.get_vdc_details()
2956 if vdc is None:
2957 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
2958
2959 vapp_name = self.get_namebyvappid(vm__vim_uuid)
2960 if vapp_name is None:
2961 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2962 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2963 else:
2964 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
2965
2966 try:
2967 vdc_obj = VDC(self.client, href=vdc.get('href'))
2968 vapp_resource = vdc_obj.get_vapp(vapp_name)
2969 vapp = VApp(self.client, resource=vapp_resource)
2970 if "start" in action_dict:
2971 self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name))
2972 poweron_task = self.power_on_vapp(vm__vim_uuid, vapp_name)
2973 result = self.client.get_task_monitor().wait_for_success(task=poweron_task)
2974 self.instance_actions_result("start", result, vapp_name)
2975 elif "rebuild" in action_dict:
2976 self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name))
2977 rebuild_task = vapp.deploy(power_on=True)
2978 result = self.client.get_task_monitor().wait_for_success(task=rebuild_task)
2979 self.instance_actions_result("rebuild", result, vapp_name)
2980 elif "pause" in action_dict:
2981 self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name))
2982 pause_task = vapp.undeploy(action='suspend')
2983 result = self.client.get_task_monitor().wait_for_success(task=pause_task)
2984 self.instance_actions_result("pause", result, vapp_name)
2985 elif "resume" in action_dict:
2986 self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name))
2987 poweron_task = self.power_on_vapp(vm__vim_uuid, vapp_name)
2988 result = self.client.get_task_monitor().wait_for_success(task=poweron_task)
2989 self.instance_actions_result("resume", result, vapp_name)
2990 elif "shutoff" in action_dict or "shutdown" in action_dict:
2991 action_name , value = action_dict.items()[0]
2992 #For python3
2993 #action_name , value = list(action_dict.items())[0]
2994 self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name))
2995 shutdown_task = vapp.shutdown()
2996 result = self.client.get_task_monitor().wait_for_success(task=shutdown_task)
2997 if action_name == "shutdown":
2998 self.instance_actions_result("shutdown", result, vapp_name)
2999 else:
3000 self.instance_actions_result("shutoff", result, vapp_name)
3001 elif "forceOff" in action_dict:
3002 result = vapp.undeploy(action='powerOff')
3003 self.instance_actions_result("forceOff", result, vapp_name)
3004 elif "reboot" in action_dict:
3005 self.logger.info("action_vminstance: reboot vApp: {}".format(vapp_name))
3006 reboot_task = vapp.reboot()
3007 self.client.get_task_monitor().wait_for_success(task=reboot_task)
3008 else:
3009 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
3010 return vm__vim_uuid
3011 except Exception as exp :
3012 self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
3013 raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
3014
3015 def instance_actions_result(self, action, result, vapp_name):
3016 if result.get('status') == 'success':
3017 self.logger.info("action_vminstance: Sucessfully {} the vApp: {}".format(action, vapp_name))
3018 else:
3019 self.logger.error("action_vminstance: Failed to {} vApp: {}".format(action, vapp_name))
3020
3021 def get_vminstance_console(self, vm_id, console_type="novnc"):
3022 """
3023 Get a console for the virtual machine
3024 Params:
3025 vm_id: uuid of the VM
3026 console_type, can be:
3027 "novnc" (by default), "xvpvnc" for VNC types,
3028 "rdp-html5" for RDP types, "spice-html5" for SPICE types
3029 Returns dict with the console parameters:
3030 protocol: ssh, ftp, http, https, ...
3031 server: usually ip address
3032 port: the http, ssh, ... port
3033 suffix: extra text, e.g. the http path and query string
3034 """
3035 console_dict = {}
3036
3037 if console_type==None or console_type=='novnc':
3038
3039 url_rest_call = "{}/api/vApp/vm-{}/screen/action/acquireMksTicket".format(self.url, vm_id)
3040
3041 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3042 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3043 response = self.perform_request(req_type='POST',
3044 url=url_rest_call,
3045 headers=headers)
3046
3047 if response.status_code == 403:
3048 response = self.retry_rest('GET', url_rest_call)
3049
3050 if response.status_code != 200:
3051 self.logger.error("REST call {} failed reason : {}"\
3052 "status code : {}".format(url_rest_call,
3053 response.content,
3054 response.status_code))
3055 raise vimconn.vimconnException("get_vminstance_console : Failed to get "\
3056 "VM Mks ticket details")
3057 s = re.search("<Host>(.*?)</Host>",response.content)
3058 console_dict['server'] = s.group(1) if s else None
3059 s1 = re.search("<Port>(\d+)</Port>",response.content)
3060 console_dict['port'] = s1.group(1) if s1 else None
3061
3062
3063 url_rest_call = "{}/api/vApp/vm-{}/screen/action/acquireTicket".format(self.url, vm_id)
3064
3065 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3066 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3067 response = self.perform_request(req_type='POST',
3068 url=url_rest_call,
3069 headers=headers)
3070
3071 if response.status_code == 403:
3072 response = self.retry_rest('GET', url_rest_call)
3073
3074 if response.status_code != 200:
3075 self.logger.error("REST call {} failed reason : {}"\
3076 "status code : {}".format(url_rest_call,
3077 response.content,
3078 response.status_code))
3079 raise vimconn.vimconnException("get_vminstance_console : Failed to get "\
3080 "VM console details")
3081 s = re.search(">.*?/(vm-\d+.*)</",response.content)
3082 console_dict['suffix'] = s.group(1) if s else None
3083 console_dict['protocol'] = "https"
3084
3085 return console_dict
3086
3087 # NOT USED METHODS in current version
3088
3089 def host_vim2gui(self, host, server_dict):
3090 """Transform host dictionary from VIM format to GUI format,
3091 and append to the server_dict
3092 """
3093 raise vimconn.vimconnNotImplemented("Should have implemented this")
3094
3095 def get_hosts_info(self):
3096 """Get the information of deployed hosts
3097 Returns the hosts content"""
3098 raise vimconn.vimconnNotImplemented("Should have implemented this")
3099
3100 def get_hosts(self, vim_tenant):
3101 """Get the hosts and deployed instances
3102 Returns the hosts content"""
3103 raise vimconn.vimconnNotImplemented("Should have implemented this")
3104
3105 def get_processor_rankings(self):
3106 """Get the processor rankings in the VIM database"""
3107 raise vimconn.vimconnNotImplemented("Should have implemented this")
3108
3109 def new_host(self, host_data):
3110 """Adds a new host to VIM"""
3111 '''Returns status code of the VIM response'''
3112 raise vimconn.vimconnNotImplemented("Should have implemented this")
3113
3114 def new_external_port(self, port_data):
3115 """Adds a external port to VIM"""
3116 '''Returns the port identifier'''
3117 raise vimconn.vimconnNotImplemented("Should have implemented this")
3118
3119 def new_external_network(self, net_name, net_type):
3120 """Adds a external network to VIM (shared)"""
3121 '''Returns the network identifier'''
3122 raise vimconn.vimconnNotImplemented("Should have implemented this")
3123
3124 def connect_port_network(self, port_id, network_id, admin=False):
3125 """Connects a external port to a network"""
3126 '''Returns status code of the VIM response'''
3127 raise vimconn.vimconnNotImplemented("Should have implemented this")
3128
3129 def new_vminstancefromJSON(self, vm_data):
3130 """Adds a VM instance to VIM"""
3131 '''Returns the instance identifier'''
3132 raise vimconn.vimconnNotImplemented("Should have implemented this")
3133
3134 def get_network_name_by_id(self, network_uuid=None):
3135 """Method gets vcloud director network named based on supplied uuid.
3136
3137 Args:
3138 network_uuid: network_id
3139
3140 Returns:
3141 The return network name.
3142 """
3143
3144 if not network_uuid:
3145 return None
3146
3147 try:
3148 org_dict = self.get_org(self.org_uuid)
3149 if 'networks' in org_dict:
3150 org_network_dict = org_dict['networks']
3151 for net_uuid in org_network_dict:
3152 if net_uuid == network_uuid:
3153 return org_network_dict[net_uuid]
3154 except:
3155 self.logger.debug("Exception in get_network_name_by_id")
3156 self.logger.debug(traceback.format_exc())
3157
3158 return None
3159
3160 def get_network_id_by_name(self, network_name=None):
3161 """Method gets vcloud director network uuid based on supplied name.
3162
3163 Args:
3164 network_name: network_name
3165 Returns:
3166 The return network uuid.
3167 network_uuid: network_id
3168 """
3169
3170 if not network_name:
3171 self.logger.debug("get_network_id_by_name() : Network name is empty")
3172 return None
3173
3174 try:
3175 org_dict = self.get_org(self.org_uuid)
3176 if org_dict and 'networks' in org_dict:
3177 org_network_dict = org_dict['networks']
3178 for net_uuid,net_name in org_network_dict.iteritems():
3179 #For python3
3180 #for net_uuid,net_name in org_network_dict.items():
3181 if net_name == network_name:
3182 return net_uuid
3183
3184 except KeyError as exp:
3185 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
3186
3187 return None
3188
3189 def list_org_action(self):
3190 """
3191 Method leverages vCloud director and query for available organization for particular user
3192
3193 Args:
3194 vca - is active VCA connection.
3195 vdc_name - is a vdc name that will be used to query vms action
3196
3197 Returns:
3198 The return XML respond
3199 """
3200 url_list = [self.url, '/api/org']
3201 vm_list_rest_call = ''.join(url_list)
3202
3203 if self.client._session:
3204 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3205 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3206
3207 response = self.perform_request(req_type='GET',
3208 url=vm_list_rest_call,
3209 headers=headers)
3210
3211 if response.status_code == 403:
3212 response = self.retry_rest('GET', vm_list_rest_call)
3213
3214 if response.status_code == requests.codes.ok:
3215 return response.content
3216
3217 return None
3218
3219 def get_org_action(self, org_uuid=None):
3220 """
3221 Method leverages vCloud director and retrieve available object for organization.
3222
3223 Args:
3224 org_uuid - vCD organization uuid
3225 self.client - is active connection.
3226
3227 Returns:
3228 The return XML respond
3229 """
3230
3231 if org_uuid is None:
3232 return None
3233
3234 url_list = [self.url, '/api/org/', org_uuid]
3235 vm_list_rest_call = ''.join(url_list)
3236
3237 if self.client._session:
3238 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3239 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3240
3241 #response = requests.get(vm_list_rest_call, headers=headers, verify=False)
3242 response = self.perform_request(req_type='GET',
3243 url=vm_list_rest_call,
3244 headers=headers)
3245 if response.status_code == 403:
3246 response = self.retry_rest('GET', vm_list_rest_call)
3247
3248 if response.status_code == requests.codes.ok:
3249 return response.content
3250 return None
3251
3252 def get_org(self, org_uuid=None):
3253 """
3254 Method retrieves available organization in vCloud Director
3255
3256 Args:
3257 org_uuid - is a organization uuid.
3258
3259 Returns:
3260 The return dictionary with following key
3261 "network" - for network list under the org
3262 "catalogs" - for network list under the org
3263 "vdcs" - for vdc list under org
3264 """
3265
3266 org_dict = {}
3267
3268 if org_uuid is None:
3269 return org_dict
3270
3271 content = self.get_org_action(org_uuid=org_uuid)
3272 try:
3273 vdc_list = {}
3274 network_list = {}
3275 catalog_list = {}
3276 vm_list_xmlroot = XmlElementTree.fromstring(content)
3277 for child in vm_list_xmlroot:
3278 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
3279 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
3280 org_dict['vdcs'] = vdc_list
3281 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
3282 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
3283 org_dict['networks'] = network_list
3284 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
3285 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
3286 org_dict['catalogs'] = catalog_list
3287 except:
3288 pass
3289
3290 return org_dict
3291
3292 def get_org_list(self):
3293 """
3294 Method retrieves available organization in vCloud Director
3295
3296 Args:
3297 vca - is active VCA connection.
3298
3299 Returns:
3300 The return dictionary and key for each entry VDC UUID
3301 """
3302
3303 org_dict = {}
3304
3305 content = self.list_org_action()
3306 try:
3307 vm_list_xmlroot = XmlElementTree.fromstring(content)
3308 for vm_xml in vm_list_xmlroot:
3309 if vm_xml.tag.split("}")[1] == 'Org':
3310 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
3311 org_dict[org_uuid[0]] = vm_xml.attrib['name']
3312 except:
3313 pass
3314
3315 return org_dict
3316
3317 def vms_view_action(self, vdc_name=None):
3318 """ Method leverages vCloud director vms query call
3319
3320 Args:
3321 vca - is active VCA connection.
3322 vdc_name - is a vdc name that will be used to query vms action
3323
3324 Returns:
3325 The return XML respond
3326 """
3327 vca = self.connect()
3328 if vdc_name is None:
3329 return None
3330
3331 url_list = [vca.host, '/api/vms/query']
3332 vm_list_rest_call = ''.join(url_list)
3333
3334 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
3335 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
3336 vca.vcloud_session.organization.Link)
3337 #For python3
3338 #refs = [ref for ref in vca.vcloud_session.organization.Link if ref.name == vdc_name and\
3339 # ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml']
3340 if len(refs) == 1:
3341 response = Http.get(url=vm_list_rest_call,
3342 headers=vca.vcloud_session.get_vcloud_headers(),
3343 verify=vca.verify,
3344 logger=vca.logger)
3345 if response.status_code == requests.codes.ok:
3346 return response.content
3347
3348 return None
3349
3350 def get_vapp_list(self, vdc_name=None):
3351 """
3352 Method retrieves vApp list deployed vCloud director and returns a dictionary
3353 contains a list of all vapp deployed for queried VDC.
3354 The key for a dictionary is vApp UUID
3355
3356
3357 Args:
3358 vca - is active VCA connection.
3359 vdc_name - is a vdc name that will be used to query vms action
3360
3361 Returns:
3362 The return dictionary and key for each entry vapp UUID
3363 """
3364
3365 vapp_dict = {}
3366 if vdc_name is None:
3367 return vapp_dict
3368
3369 content = self.vms_view_action(vdc_name=vdc_name)
3370 try:
3371 vm_list_xmlroot = XmlElementTree.fromstring(content)
3372 for vm_xml in vm_list_xmlroot:
3373 if vm_xml.tag.split("}")[1] == 'VMRecord':
3374 if vm_xml.attrib['isVAppTemplate'] == 'true':
3375 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
3376 if 'vappTemplate-' in rawuuid[0]:
3377 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
3378 # vm and use raw UUID as key
3379 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
3380 except:
3381 pass
3382
3383 return vapp_dict
3384
3385 def get_vm_list(self, vdc_name=None):
3386 """
3387 Method retrieves VM's list deployed vCloud director. It returns a dictionary
3388 contains a list of all VM's deployed for queried VDC.
3389 The key for a dictionary is VM UUID
3390
3391
3392 Args:
3393 vca - is active VCA connection.
3394 vdc_name - is a vdc name that will be used to query vms action
3395
3396 Returns:
3397 The return dictionary and key for each entry vapp UUID
3398 """
3399 vm_dict = {}
3400
3401 if vdc_name is None:
3402 return vm_dict
3403
3404 content = self.vms_view_action(vdc_name=vdc_name)
3405 try:
3406 vm_list_xmlroot = XmlElementTree.fromstring(content)
3407 for vm_xml in vm_list_xmlroot:
3408 if vm_xml.tag.split("}")[1] == 'VMRecord':
3409 if vm_xml.attrib['isVAppTemplate'] == 'false':
3410 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
3411 if 'vm-' in rawuuid[0]:
3412 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
3413 # vm and use raw UUID as key
3414 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
3415 except:
3416 pass
3417
3418 return vm_dict
3419
3420 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
3421 """
3422 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
3423 contains a list of all VM's deployed for queried VDC.
3424 The key for a dictionary is VM UUID
3425
3426
3427 Args:
3428 vca - is active VCA connection.
3429 vdc_name - is a vdc name that will be used to query vms action
3430
3431 Returns:
3432 The return dictionary and key for each entry vapp UUID
3433 """
3434 vm_dict = {}
3435 vca = self.connect()
3436 if not vca:
3437 raise vimconn.vimconnConnectionException("self.connect() is failed")
3438
3439 if vdc_name is None:
3440 return vm_dict
3441
3442 content = self.vms_view_action(vdc_name=vdc_name)
3443 try:
3444 vm_list_xmlroot = XmlElementTree.fromstring(content)
3445 for vm_xml in vm_list_xmlroot:
3446 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
3447 # lookup done by UUID
3448 if isuuid:
3449 if vapp_name in vm_xml.attrib['container']:
3450 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
3451 if 'vm-' in rawuuid[0]:
3452 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
3453 break
3454 # lookup done by Name
3455 else:
3456 if vapp_name in vm_xml.attrib['name']:
3457 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
3458 if 'vm-' in rawuuid[0]:
3459 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
3460 break
3461 except:
3462 pass
3463
3464 return vm_dict
3465
3466 def get_network_action(self, network_uuid=None):
3467 """
3468 Method leverages vCloud director and query network based on network uuid
3469
3470 Args:
3471 vca - is active VCA connection.
3472 network_uuid - is a network uuid
3473
3474 Returns:
3475 The return XML respond
3476 """
3477
3478 if network_uuid is None:
3479 return None
3480
3481 url_list = [self.url, '/api/network/', network_uuid]
3482 vm_list_rest_call = ''.join(url_list)
3483
3484 if self.client._session:
3485 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3486 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3487
3488 response = self.perform_request(req_type='GET',
3489 url=vm_list_rest_call,
3490 headers=headers)
3491 #Retry login if session expired & retry sending request
3492 if response.status_code == 403:
3493 response = self.retry_rest('GET', vm_list_rest_call)
3494
3495 if response.status_code == requests.codes.ok:
3496 return response.content
3497
3498 return None
3499
3500 def get_vcd_network(self, network_uuid=None):
3501 """
3502 Method retrieves available network from vCloud Director
3503
3504 Args:
3505 network_uuid - is VCD network UUID
3506
3507 Each element serialized as key : value pair
3508
3509 Following keys available for access. network_configuration['Gateway'}
3510 <Configuration>
3511 <IpScopes>
3512 <IpScope>
3513 <IsInherited>true</IsInherited>
3514 <Gateway>172.16.252.100</Gateway>
3515 <Netmask>255.255.255.0</Netmask>
3516 <Dns1>172.16.254.201</Dns1>
3517 <Dns2>172.16.254.202</Dns2>
3518 <DnsSuffix>vmwarelab.edu</DnsSuffix>
3519 <IsEnabled>true</IsEnabled>
3520 <IpRanges>
3521 <IpRange>
3522 <StartAddress>172.16.252.1</StartAddress>
3523 <EndAddress>172.16.252.99</EndAddress>
3524 </IpRange>
3525 </IpRanges>
3526 </IpScope>
3527 </IpScopes>
3528 <FenceMode>bridged</FenceMode>
3529
3530 Returns:
3531 The return dictionary and key for each entry vapp UUID
3532 """
3533
3534 network_configuration = {}
3535 if network_uuid is None:
3536 return network_uuid
3537
3538 try:
3539 content = self.get_network_action(network_uuid=network_uuid)
3540 vm_list_xmlroot = XmlElementTree.fromstring(content)
3541
3542 network_configuration['status'] = vm_list_xmlroot.get("status")
3543 network_configuration['name'] = vm_list_xmlroot.get("name")
3544 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
3545
3546 for child in vm_list_xmlroot:
3547 if child.tag.split("}")[1] == 'IsShared':
3548 network_configuration['isShared'] = child.text.strip()
3549 if child.tag.split("}")[1] == 'Configuration':
3550 for configuration in child.iter():
3551 tagKey = configuration.tag.split("}")[1].strip()
3552 if tagKey != "":
3553 network_configuration[tagKey] = configuration.text.strip()
3554 return network_configuration
3555 except Exception as exp :
3556 self.logger.debug("get_vcd_network: Failed with Exception {}".format(exp))
3557 raise vimconn.vimconnException("get_vcd_network: Failed with Exception {}".format(exp))
3558
3559 return network_configuration
3560
3561 def delete_network_action(self, network_uuid=None):
3562 """
3563 Method delete given network from vCloud director
3564
3565 Args:
3566 network_uuid - is a network uuid that client wish to delete
3567
3568 Returns:
3569 The return None or XML respond or false
3570 """
3571 client = self.connect_as_admin()
3572 if not client:
3573 raise vimconn.vimconnConnectionException("Failed to connect vCD as admin")
3574 if network_uuid is None:
3575 return False
3576
3577 url_list = [self.url, '/api/admin/network/', network_uuid]
3578 vm_list_rest_call = ''.join(url_list)
3579
3580 if client._session:
3581 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3582 'x-vcloud-authorization': client._session.headers['x-vcloud-authorization']}
3583 response = self.perform_request(req_type='DELETE',
3584 url=vm_list_rest_call,
3585 headers=headers)
3586 if response.status_code == 202:
3587 return True
3588
3589 return False
3590
3591 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
3592 ip_profile=None, isshared='true'):
3593 """
3594 Method create network in vCloud director
3595
3596 Args:
3597 network_name - is network name to be created.
3598 net_type - can be 'bridge','data','ptp','mgmt'.
3599 ip_profile is a dict containing the IP parameters of the network
3600 isshared - is a boolean
3601 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3602 It optional attribute. by default if no parent network indicate the first available will be used.
3603
3604 Returns:
3605 The return network uuid or return None
3606 """
3607
3608 new_network_name = [network_name, '-', str(uuid.uuid4())]
3609 content = self.create_network_rest(network_name=''.join(new_network_name),
3610 ip_profile=ip_profile,
3611 net_type=net_type,
3612 parent_network_uuid=parent_network_uuid,
3613 isshared=isshared)
3614 if content is None:
3615 self.logger.debug("Failed create network {}.".format(network_name))
3616 return None
3617
3618 try:
3619 vm_list_xmlroot = XmlElementTree.fromstring(content)
3620 vcd_uuid = vm_list_xmlroot.get('id').split(":")
3621 if len(vcd_uuid) == 4:
3622 self.logger.info("Created new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
3623 return vcd_uuid[3]
3624 except:
3625 self.logger.debug("Failed create network {}".format(network_name))
3626 return None
3627
3628 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
3629 ip_profile=None, isshared='true'):
3630 """
3631 Method create network in vCloud director
3632
3633 Args:
3634 network_name - is network name to be created.
3635 net_type - can be 'bridge','data','ptp','mgmt'.
3636 ip_profile is a dict containing the IP parameters of the network
3637 isshared - is a boolean
3638 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3639 It optional attribute. by default if no parent network indicate the first available will be used.
3640
3641 Returns:
3642 The return network uuid or return None
3643 """
3644 client_as_admin = self.connect_as_admin()
3645 if not client_as_admin:
3646 raise vimconn.vimconnConnectionException("Failed to connect vCD.")
3647 if network_name is None:
3648 return None
3649
3650 url_list = [self.url, '/api/admin/vdc/', self.tenant_id]
3651 vm_list_rest_call = ''.join(url_list)
3652
3653 if client_as_admin._session:
3654 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3655 'x-vcloud-authorization': client_as_admin._session.headers['x-vcloud-authorization']}
3656
3657 response = self.perform_request(req_type='GET',
3658 url=vm_list_rest_call,
3659 headers=headers)
3660
3661 provider_network = None
3662 available_networks = None
3663 add_vdc_rest_url = None
3664
3665 if response.status_code != requests.codes.ok:
3666 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
3667 response.status_code))
3668 return None
3669 else:
3670 try:
3671 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
3672 for child in vm_list_xmlroot:
3673 if child.tag.split("}")[1] == 'ProviderVdcReference':
3674 provider_network = child.attrib.get('href')
3675 # application/vnd.vmware.admin.providervdc+xml
3676 if child.tag.split("}")[1] == 'Link':
3677 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
3678 and child.attrib.get('rel') == 'add':
3679 add_vdc_rest_url = child.attrib.get('href')
3680 except:
3681 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3682 self.logger.debug("Respond body {}".format(response.content))
3683 return None
3684
3685 # find pvdc provided available network
3686 response = self.perform_request(req_type='GET',
3687 url=provider_network,
3688 headers=headers)
3689 if response.status_code != requests.codes.ok:
3690 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
3691 response.status_code))
3692 return None
3693
3694 if parent_network_uuid is None:
3695 try:
3696 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
3697 for child in vm_list_xmlroot.iter():
3698 if child.tag.split("}")[1] == 'AvailableNetworks':
3699 for networks in child.iter():
3700 # application/vnd.vmware.admin.network+xml
3701 if networks.attrib.get('href') is not None:
3702 available_networks = networks.attrib.get('href')
3703 break
3704 except:
3705 return None
3706
3707 try:
3708 #Configure IP profile of the network
3709 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
3710
3711 if 'subnet_address' not in ip_profile or ip_profile['subnet_address'] is None:
3712 subnet_rand = random.randint(0, 255)
3713 ip_base = "192.168.{}.".format(subnet_rand)
3714 ip_profile['subnet_address'] = ip_base + "0/24"
3715 else:
3716 ip_base = ip_profile['subnet_address'].rsplit('.',1)[0] + '.'
3717
3718 if 'gateway_address' not in ip_profile or ip_profile['gateway_address'] is None:
3719 ip_profile['gateway_address']=ip_base + "1"
3720 if 'dhcp_count' not in ip_profile or ip_profile['dhcp_count'] is None:
3721 ip_profile['dhcp_count']=DEFAULT_IP_PROFILE['dhcp_count']
3722 if 'dhcp_enabled' not in ip_profile or ip_profile['dhcp_enabled'] is None:
3723 ip_profile['dhcp_enabled']=DEFAULT_IP_PROFILE['dhcp_enabled']
3724 if 'dhcp_start_address' not in ip_profile or ip_profile['dhcp_start_address'] is None:
3725 ip_profile['dhcp_start_address']=ip_base + "3"
3726 if 'ip_version' not in ip_profile or ip_profile['ip_version'] is None:
3727 ip_profile['ip_version']=DEFAULT_IP_PROFILE['ip_version']
3728 if 'dns_address' not in ip_profile or ip_profile['dns_address'] is None:
3729 ip_profile['dns_address']=ip_base + "2"
3730
3731 gateway_address=ip_profile['gateway_address']
3732 dhcp_count=int(ip_profile['dhcp_count'])
3733 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
3734
3735 if ip_profile['dhcp_enabled']==True:
3736 dhcp_enabled='true'
3737 else:
3738 dhcp_enabled='false'
3739 dhcp_start_address=ip_profile['dhcp_start_address']
3740
3741 #derive dhcp_end_address from dhcp_start_address & dhcp_count
3742 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
3743 end_ip_int += dhcp_count - 1
3744 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
3745
3746 ip_version=ip_profile['ip_version']
3747 dns_address=ip_profile['dns_address']
3748 except KeyError as exp:
3749 self.logger.debug("Create Network REST: Key error {}".format(exp))
3750 raise vimconn.vimconnException("Create Network REST: Key error{}".format(exp))
3751
3752 # either use client provided UUID or search for a first available
3753 # if both are not defined we return none
3754 if parent_network_uuid is not None:
3755 provider_network = None
3756 available_networks = None
3757 add_vdc_rest_url = None
3758
3759 url_list = [self.url, '/api/admin/vdc/', self.tenant_id, '/networks']
3760 add_vdc_rest_url = ''.join(url_list)
3761
3762 url_list = [self.url, '/api/admin/network/', parent_network_uuid]
3763 available_networks = ''.join(url_list)
3764
3765 #Creating all networks as Direct Org VDC type networks.
3766 #Unused in case of Underlay (data/ptp) network interface.
3767 fence_mode="isolated"
3768 is_inherited='false'
3769 dns_list = dns_address.split(";")
3770 dns1 = dns_list[0]
3771 dns2_text = ""
3772 if len(dns_list) >= 2:
3773 dns2_text = "\n <Dns2>{}</Dns2>\n".format(dns_list[1])
3774 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
3775 <Description>Openmano created</Description>
3776 <Configuration>
3777 <IpScopes>
3778 <IpScope>
3779 <IsInherited>{1:s}</IsInherited>
3780 <Gateway>{2:s}</Gateway>
3781 <Netmask>{3:s}</Netmask>
3782 <Dns1>{4:s}</Dns1>{5:s}
3783 <IsEnabled>{6:s}</IsEnabled>
3784 <IpRanges>
3785 <IpRange>
3786 <StartAddress>{7:s}</StartAddress>
3787 <EndAddress>{8:s}</EndAddress>
3788 </IpRange>
3789 </IpRanges>
3790 </IpScope>
3791 </IpScopes>
3792 <FenceMode>{9:s}</FenceMode>
3793 </Configuration>
3794 <IsShared>{10:s}</IsShared>
3795 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
3796 subnet_address, dns1, dns2_text, dhcp_enabled,
3797 dhcp_start_address, dhcp_end_address,
3798 fence_mode, isshared)
3799
3800 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
3801 try:
3802 response = self.perform_request(req_type='POST',
3803 url=add_vdc_rest_url,
3804 headers=headers,
3805 data=data)
3806
3807 if response.status_code != 201:
3808 self.logger.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
3809 .format(response.status_code,response.content))
3810 else:
3811 network_task = self.get_task_from_response(response.content)
3812 self.logger.debug("Create Network REST : Waiting for Network creation complete")
3813 time.sleep(5)
3814 result = self.client.get_task_monitor().wait_for_success(task=network_task)
3815 if result.get('status') == 'success':
3816 return response.content
3817 else:
3818 self.logger.debug("create_network_rest task failed. Network Create response : {}"
3819 .format(response.content))
3820 except Exception as exp:
3821 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
3822
3823 return None
3824
3825 def convert_cidr_to_netmask(self, cidr_ip=None):
3826 """
3827 Method sets convert CIDR netmask address to normal IP format
3828 Args:
3829 cidr_ip : CIDR IP address
3830 Returns:
3831 netmask : Converted netmask
3832 """
3833 if cidr_ip is not None:
3834 if '/' in cidr_ip:
3835 network, net_bits = cidr_ip.split('/')
3836 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
3837 else:
3838 netmask = cidr_ip
3839 return netmask
3840 return None
3841
3842 def get_provider_rest(self, vca=None):
3843 """
3844 Method gets provider vdc view from vcloud director
3845
3846 Args:
3847 network_name - is network name to be created.
3848 parent_network_uuid - is parent provider vdc network that will be used for mapping.
3849 It optional attribute. by default if no parent network indicate the first available will be used.
3850
3851 Returns:
3852 The return xml content of respond or None
3853 """
3854
3855 url_list = [self.url, '/api/admin']
3856 if vca:
3857 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3858 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3859 response = self.perform_request(req_type='GET',
3860 url=''.join(url_list),
3861 headers=headers)
3862
3863 if response.status_code == requests.codes.ok:
3864 return response.content
3865 return None
3866
3867 def create_vdc(self, vdc_name=None):
3868
3869 vdc_dict = {}
3870
3871 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
3872 if xml_content is not None:
3873 try:
3874 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
3875 for child in task_resp_xmlroot:
3876 if child.tag.split("}")[1] == 'Owner':
3877 vdc_id = child.attrib.get('href').split("/")[-1]
3878 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
3879 return vdc_dict
3880 except:
3881 self.logger.debug("Respond body {}".format(xml_content))
3882
3883 return None
3884
3885 def create_vdc_from_tmpl_rest(self, vdc_name=None):
3886 """
3887 Method create vdc in vCloud director based on VDC template.
3888 it uses pre-defined template.
3889
3890 Args:
3891 vdc_name - name of a new vdc.
3892
3893 Returns:
3894 The return xml content of respond or None
3895 """
3896 # pre-requesite atleast one vdc template should be available in vCD
3897 self.logger.info("Creating new vdc {}".format(vdc_name))
3898 vca = self.connect_as_admin()
3899 if not vca:
3900 raise vimconn.vimconnConnectionException("Failed to connect vCD")
3901 if vdc_name is None:
3902 return None
3903
3904 url_list = [self.url, '/api/vdcTemplates']
3905 vm_list_rest_call = ''.join(url_list)
3906
3907 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3908 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
3909 response = self.perform_request(req_type='GET',
3910 url=vm_list_rest_call,
3911 headers=headers)
3912
3913 # container url to a template
3914 vdc_template_ref = None
3915 try:
3916 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
3917 for child in vm_list_xmlroot:
3918 # application/vnd.vmware.admin.providervdc+xml
3919 # we need find a template from witch we instantiate VDC
3920 if child.tag.split("}")[1] == 'VdcTemplate':
3921 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml':
3922 vdc_template_ref = child.attrib.get('href')
3923 except:
3924 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3925 self.logger.debug("Respond body {}".format(response.content))
3926 return None
3927
3928 # if we didn't found required pre defined template we return None
3929 if vdc_template_ref is None:
3930 return None
3931
3932 try:
3933 # instantiate vdc
3934 url_list = [self.url, '/api/org/', self.org_uuid, '/action/instantiate']
3935 vm_list_rest_call = ''.join(url_list)
3936 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
3937 <Source href="{1:s}"></Source>
3938 <Description>opnemano</Description>
3939 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
3940
3941 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
3942
3943 response = self.perform_request(req_type='POST',
3944 url=vm_list_rest_call,
3945 headers=headers,
3946 data=data)
3947
3948 vdc_task = self.get_task_from_response(response.content)
3949 self.client.get_task_monitor().wait_for_success(task=vdc_task)
3950
3951 # if we all ok we respond with content otherwise by default None
3952 if response.status_code >= 200 and response.status_code < 300:
3953 return response.content
3954 return None
3955 except:
3956 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3957 self.logger.debug("Respond body {}".format(response.content))
3958
3959 return None
3960
3961 def create_vdc_rest(self, vdc_name=None):
3962 """
3963 Method create network in vCloud director
3964
3965 Args:
3966 vdc_name - vdc name to be created
3967 Returns:
3968 The return response
3969 """
3970
3971 self.logger.info("Creating new vdc {}".format(vdc_name))
3972
3973 vca = self.connect_as_admin()
3974 if not vca:
3975 raise vimconn.vimconnConnectionException("Failed to connect vCD")
3976 if vdc_name is None:
3977 return None
3978
3979 url_list = [self.url, '/api/admin/org/', self.org_uuid]
3980 vm_list_rest_call = ''.join(url_list)
3981
3982 if vca._session:
3983 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
3984 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
3985 response = self.perform_request(req_type='GET',
3986 url=vm_list_rest_call,
3987 headers=headers)
3988
3989 provider_vdc_ref = None
3990 add_vdc_rest_url = None
3991 available_networks = None
3992
3993 if response.status_code != requests.codes.ok:
3994 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
3995 response.status_code))
3996 return None
3997 else:
3998 try:
3999 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
4000 for child in vm_list_xmlroot:
4001 # application/vnd.vmware.admin.providervdc+xml
4002 if child.tag.split("}")[1] == 'Link':
4003 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
4004 and child.attrib.get('rel') == 'add':
4005 add_vdc_rest_url = child.attrib.get('href')
4006 except:
4007 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
4008 self.logger.debug("Respond body {}".format(response.content))
4009 return None
4010
4011 response = self.get_provider_rest(vca=vca)
4012 try:
4013 vm_list_xmlroot = XmlElementTree.fromstring(response)
4014 for child in vm_list_xmlroot:
4015 if child.tag.split("}")[1] == 'ProviderVdcReferences':
4016 for sub_child in child:
4017 provider_vdc_ref = sub_child.attrib.get('href')
4018 except:
4019 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
4020 self.logger.debug("Respond body {}".format(response))
4021 return None
4022
4023 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
4024 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
4025 <AllocationModel>ReservationPool</AllocationModel>
4026 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
4027 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
4028 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
4029 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
4030 <ProviderVdcReference
4031 name="Main Provider"
4032 href="{2:s}" />
4033 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
4034 escape(vdc_name),
4035 provider_vdc_ref)
4036
4037 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
4038
4039 response = self.perform_request(req_type='POST',
4040 url=add_vdc_rest_url,
4041 headers=headers,
4042 data=data)
4043
4044 # if we all ok we respond with content otherwise by default None
4045 if response.status_code == 201:
4046 return response.content
4047 return None
4048
4049 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
4050 """
4051 Method retrieve vapp detail from vCloud director
4052
4053 Args:
4054 vapp_uuid - is vapp identifier.
4055
4056 Returns:
4057 The return network uuid or return None
4058 """
4059
4060 parsed_respond = {}
4061 vca = None
4062
4063 if need_admin_access:
4064 vca = self.connect_as_admin()
4065 else:
4066 vca = self.client
4067
4068 if not vca:
4069 raise vimconn.vimconnConnectionException("Failed to connect vCD")
4070 if vapp_uuid is None:
4071 return None
4072
4073 url_list = [self.url, '/api/vApp/vapp-', vapp_uuid]
4074 get_vapp_restcall = ''.join(url_list)
4075
4076 if vca._session:
4077 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4078 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
4079 response = self.perform_request(req_type='GET',
4080 url=get_vapp_restcall,
4081 headers=headers)
4082
4083 if response.status_code == 403:
4084 if need_admin_access == False:
4085 response = self.retry_rest('GET', get_vapp_restcall)
4086
4087 if response.status_code != requests.codes.ok:
4088 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
4089 response.status_code))
4090 return parsed_respond
4091
4092 try:
4093 xmlroot_respond = XmlElementTree.fromstring(response.content)
4094 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
4095
4096 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
4097 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
4098 'vmw': 'http://www.vmware.com/schema/ovf',
4099 'vm': 'http://www.vmware.com/vcloud/v1.5',
4100 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
4101 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
4102 "xmlns":"http://www.vmware.com/vcloud/v1.5"
4103 }
4104
4105 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
4106 if created_section is not None:
4107 parsed_respond['created'] = created_section.text
4108
4109 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
4110 if network_section is not None and 'networkName' in network_section.attrib:
4111 parsed_respond['networkname'] = network_section.attrib['networkName']
4112
4113 ipscopes_section = \
4114 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
4115 namespaces)
4116 if ipscopes_section is not None:
4117 for ipscope in ipscopes_section:
4118 for scope in ipscope:
4119 tag_key = scope.tag.split("}")[1]
4120 if tag_key == 'IpRanges':
4121 ip_ranges = scope.getchildren()
4122 for ipblock in ip_ranges:
4123 for block in ipblock:
4124 parsed_respond[block.tag.split("}")[1]] = block.text
4125 else:
4126 parsed_respond[tag_key] = scope.text
4127
4128 # parse children section for other attrib
4129 children_section = xmlroot_respond.find('vm:Children/', namespaces)
4130 if children_section is not None:
4131 parsed_respond['name'] = children_section.attrib['name']
4132 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
4133 if "nestedHypervisorEnabled" in children_section.attrib else None
4134 parsed_respond['deployed'] = children_section.attrib['deployed']
4135 parsed_respond['status'] = children_section.attrib['status']
4136 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
4137 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
4138 nic_list = []
4139 for adapters in network_adapter:
4140 adapter_key = adapters.tag.split("}")[1]
4141 if adapter_key == 'PrimaryNetworkConnectionIndex':
4142 parsed_respond['primarynetwork'] = adapters.text
4143 if adapter_key == 'NetworkConnection':
4144 vnic = {}
4145 if 'network' in adapters.attrib:
4146 vnic['network'] = adapters.attrib['network']
4147 for adapter in adapters:
4148 setting_key = adapter.tag.split("}")[1]
4149 vnic[setting_key] = adapter.text
4150 nic_list.append(vnic)
4151
4152 for link in children_section:
4153 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
4154 if link.attrib['rel'] == 'screen:acquireTicket':
4155 parsed_respond['acquireTicket'] = link.attrib
4156 if link.attrib['rel'] == 'screen:acquireMksTicket':
4157 parsed_respond['acquireMksTicket'] = link.attrib
4158
4159 parsed_respond['interfaces'] = nic_list
4160 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
4161 if vCloud_extension_section is not None:
4162 vm_vcenter_info = {}
4163 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
4164 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
4165 if vmext is not None:
4166 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
4167 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
4168
4169 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
4170 vm_virtual_hardware_info = {}
4171 if virtual_hardware_section is not None:
4172 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
4173 if item.find("rasd:Description",namespaces).text == "Hard disk":
4174 disk_size = item.find("rasd:HostResource" ,namespaces
4175 ).attrib["{"+namespaces['vm']+"}capacity"]
4176
4177 vm_virtual_hardware_info["disk_size"]= disk_size
4178 break
4179
4180 for link in virtual_hardware_section:
4181 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
4182 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
4183 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
4184 break
4185
4186 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
4187 except Exception as exp :
4188 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
4189 return parsed_respond
4190
4191 def acquire_console(self, vm_uuid=None):
4192
4193 if vm_uuid is None:
4194 return None
4195 if self.client._session:
4196 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4197 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4198 vm_dict = self.get_vapp_details_rest(vapp_uuid=vm_uuid)
4199 console_dict = vm_dict['acquireTicket']
4200 console_rest_call = console_dict['href']
4201
4202 response = self.perform_request(req_type='POST',
4203 url=console_rest_call,
4204 headers=headers)
4205
4206 if response.status_code == 403:
4207 response = self.retry_rest('POST', console_rest_call)
4208
4209 if response.status_code == requests.codes.ok:
4210 return response.content
4211
4212 return None
4213
4214 def modify_vm_disk(self, vapp_uuid, flavor_disk):
4215 """
4216 Method retrieve vm disk details
4217
4218 Args:
4219 vapp_uuid - is vapp identifier.
4220 flavor_disk - disk size as specified in VNFD (flavor)
4221
4222 Returns:
4223 The return network uuid or return None
4224 """
4225 status = None
4226 try:
4227 #Flavor disk is in GB convert it into MB
4228 flavor_disk = int(flavor_disk) * 1024
4229 vm_details = self.get_vapp_details_rest(vapp_uuid)
4230 if vm_details:
4231 vm_name = vm_details["name"]
4232 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
4233
4234 if vm_details and "vm_virtual_hardware" in vm_details:
4235 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
4236 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
4237
4238 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
4239
4240 if flavor_disk > vm_disk:
4241 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
4242 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
4243 vm_disk, flavor_disk ))
4244 else:
4245 status = True
4246 self.logger.info("No need to modify disk of VM {}".format(vm_name))
4247
4248 return status
4249 except Exception as exp:
4250 self.logger.info("Error occurred while modifing disk size {}".format(exp))
4251
4252
4253 def modify_vm_disk_rest(self, disk_href , disk_size):
4254 """
4255 Method retrieve modify vm disk size
4256
4257 Args:
4258 disk_href - vCD API URL to GET and PUT disk data
4259 disk_size - disk size as specified in VNFD (flavor)
4260
4261 Returns:
4262 The return network uuid or return None
4263 """
4264 if disk_href is None or disk_size is None:
4265 return None
4266
4267 if self.client._session:
4268 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4269 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4270 response = self.perform_request(req_type='GET',
4271 url=disk_href,
4272 headers=headers)
4273
4274 if response.status_code == 403:
4275 response = self.retry_rest('GET', disk_href)
4276
4277 if response.status_code != requests.codes.ok:
4278 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
4279 response.status_code))
4280 return None
4281 try:
4282 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
4283 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
4284 #For python3
4285 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
4286 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4287
4288 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
4289 if item.find("rasd:Description",namespaces).text == "Hard disk":
4290 disk_item = item.find("rasd:HostResource" ,namespaces )
4291 if disk_item is not None:
4292 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
4293 break
4294
4295 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
4296 xml_declaration=True)
4297
4298 #Send PUT request to modify disk size
4299 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
4300
4301 response = self.perform_request(req_type='PUT',
4302 url=disk_href,
4303 headers=headers,
4304 data=data)
4305 if response.status_code == 403:
4306 add_headers = {'Content-Type': headers['Content-Type']}
4307 response = self.retry_rest('PUT', disk_href, add_headers, data)
4308
4309 if response.status_code != 202:
4310 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
4311 response.status_code))
4312 else:
4313 modify_disk_task = self.get_task_from_response(response.content)
4314 result = self.client.get_task_monitor().wait_for_success(task=modify_disk_task)
4315 if result.get('status') == 'success':
4316 return True
4317 else:
4318 return False
4319 return None
4320
4321 except Exception as exp :
4322 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
4323 return None
4324
4325 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
4326 """
4327 Method to attach pci devices to VM
4328
4329 Args:
4330 vapp_uuid - uuid of vApp/VM
4331 pci_devices - pci devices infromation as specified in VNFD (flavor)
4332
4333 Returns:
4334 The status of add pci device task , vm object and
4335 vcenter_conect object
4336 """
4337 vm_obj = None
4338 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
4339 vcenter_conect, content = self.get_vcenter_content()
4340 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
4341
4342 if vm_moref_id:
4343 try:
4344 no_of_pci_devices = len(pci_devices)
4345 if no_of_pci_devices > 0:
4346 #Get VM and its host
4347 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
4348 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
4349 if host_obj and vm_obj:
4350 #get PCI devies from host on which vapp is currently installed
4351 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
4352
4353 if avilable_pci_devices is None:
4354 #find other hosts with active pci devices
4355 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
4356 content,
4357 no_of_pci_devices
4358 )
4359
4360 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
4361 #Migrate vm to the host where PCI devices are availble
4362 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
4363 task = self.relocate_vm(new_host_obj, vm_obj)
4364 if task is not None:
4365 result = self.wait_for_vcenter_task(task, vcenter_conect)
4366 self.logger.info("Migrate VM status: {}".format(result))
4367 host_obj = new_host_obj
4368 else:
4369 self.logger.info("Fail to migrate VM : {}".format(result))
4370 raise vimconn.vimconnNotFoundException(
4371 "Fail to migrate VM : {} to host {}".format(
4372 vmname_andid,
4373 new_host_obj)
4374 )
4375
4376 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
4377 #Add PCI devices one by one
4378 for pci_device in avilable_pci_devices:
4379 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
4380 if task:
4381 status= self.wait_for_vcenter_task(task, vcenter_conect)
4382 if status:
4383 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
4384 else:
4385 self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
4386 return True, vm_obj, vcenter_conect
4387 else:
4388 self.logger.error("Currently there is no host with"\
4389 " {} number of avaialble PCI devices required for VM {}".format(
4390 no_of_pci_devices,
4391 vmname_andid)
4392 )
4393 raise vimconn.vimconnNotFoundException(
4394 "Currently there is no host with {} "\
4395 "number of avaialble PCI devices required for VM {}".format(
4396 no_of_pci_devices,
4397 vmname_andid))
4398 else:
4399 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
4400
4401 except vmodl.MethodFault as error:
4402 self.logger.error("Error occurred while adding PCI devices {} ",error)
4403 return None, vm_obj, vcenter_conect
4404
4405 def get_vm_obj(self, content, mob_id):
4406 """
4407 Method to get the vsphere VM object associated with a given morf ID
4408 Args:
4409 vapp_uuid - uuid of vApp/VM
4410 content - vCenter content object
4411 mob_id - mob_id of VM
4412
4413 Returns:
4414 VM and host object
4415 """
4416 vm_obj = None
4417 host_obj = None
4418 try :
4419 container = content.viewManager.CreateContainerView(content.rootFolder,
4420 [vim.VirtualMachine], True
4421 )
4422 for vm in container.view:
4423 mobID = vm._GetMoId()
4424 if mobID == mob_id:
4425 vm_obj = vm
4426 host_obj = vm_obj.runtime.host
4427 break
4428 except Exception as exp:
4429 self.logger.error("Error occurred while finding VM object : {}".format(exp))
4430 return host_obj, vm_obj
4431
4432 def get_pci_devices(self, host, need_devices):
4433 """
4434 Method to get the details of pci devices on given host
4435 Args:
4436 host - vSphere host object
4437 need_devices - number of pci devices needed on host
4438
4439 Returns:
4440 array of pci devices
4441 """
4442 all_devices = []
4443 all_device_ids = []
4444 used_devices_ids = []
4445
4446 try:
4447 if host:
4448 pciPassthruInfo = host.config.pciPassthruInfo
4449 pciDevies = host.hardware.pciDevice
4450
4451 for pci_status in pciPassthruInfo:
4452 if pci_status.passthruActive:
4453 for device in pciDevies:
4454 if device.id == pci_status.id:
4455 all_device_ids.append(device.id)
4456 all_devices.append(device)
4457
4458 #check if devices are in use
4459 avalible_devices = all_devices
4460 for vm in host.vm:
4461 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
4462 vm_devices = vm.config.hardware.device
4463 for device in vm_devices:
4464 if type(device) is vim.vm.device.VirtualPCIPassthrough:
4465 if device.backing.id in all_device_ids:
4466 for use_device in avalible_devices:
4467 if use_device.id == device.backing.id:
4468 avalible_devices.remove(use_device)
4469 used_devices_ids.append(device.backing.id)
4470 self.logger.debug("Device {} from devices {}"\
4471 "is in use".format(device.backing.id,
4472 device)
4473 )
4474 if len(avalible_devices) < need_devices:
4475 self.logger.debug("Host {} don't have {} number of active devices".format(host,
4476 need_devices))
4477 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
4478 avalible_devices))
4479 return None
4480 else:
4481 required_devices = avalible_devices[:need_devices]
4482 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
4483 len(avalible_devices),
4484 host,
4485 need_devices))
4486 self.logger.info("Retruning {} devices as {}".format(need_devices,
4487 required_devices ))
4488 return required_devices
4489
4490 except Exception as exp:
4491 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
4492
4493 return None
4494
4495 def get_host_and_PCIdevices(self, content, need_devices):
4496 """
4497 Method to get the details of pci devices infromation on all hosts
4498
4499 Args:
4500 content - vSphere host object
4501 need_devices - number of pci devices needed on host
4502
4503 Returns:
4504 array of pci devices and host object
4505 """
4506 host_obj = None
4507 pci_device_objs = None
4508 try:
4509 if content:
4510 container = content.viewManager.CreateContainerView(content.rootFolder,
4511 [vim.HostSystem], True)
4512 for host in container.view:
4513 devices = self.get_pci_devices(host, need_devices)
4514 if devices:
4515 host_obj = host
4516 pci_device_objs = devices
4517 break
4518 except Exception as exp:
4519 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
4520
4521 return host_obj,pci_device_objs
4522
4523 def relocate_vm(self, dest_host, vm) :
4524 """
4525 Method to get the relocate VM to new host
4526
4527 Args:
4528 dest_host - vSphere host object
4529 vm - vSphere VM object
4530
4531 Returns:
4532 task object
4533 """
4534 task = None
4535 try:
4536 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
4537 task = vm.Relocate(relocate_spec)
4538 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
4539 except Exception as exp:
4540 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
4541 dest_host, vm, exp))
4542 return task
4543
4544 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
4545 """
4546 Waits and provides updates on a vSphere task
4547 """
4548 while task.info.state == vim.TaskInfo.State.running:
4549 time.sleep(2)
4550
4551 if task.info.state == vim.TaskInfo.State.success:
4552 if task.info.result is not None and not hideResult:
4553 self.logger.info('{} completed successfully, result: {}'.format(
4554 actionName,
4555 task.info.result))
4556 else:
4557 self.logger.info('Task {} completed successfully.'.format(actionName))
4558 else:
4559 self.logger.error('{} did not complete successfully: {} '.format(
4560 actionName,
4561 task.info.error)
4562 )
4563
4564 return task.info.result
4565
4566 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
4567 """
4568 Method to add pci device in given VM
4569
4570 Args:
4571 host_object - vSphere host object
4572 vm_object - vSphere VM object
4573 host_pci_dev - host_pci_dev must be one of the devices from the
4574 host_object.hardware.pciDevice list
4575 which is configured as a PCI passthrough device
4576
4577 Returns:
4578 task object
4579 """
4580 task = None
4581 if vm_object and host_object and host_pci_dev:
4582 try :
4583 #Add PCI device to VM
4584 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
4585 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
4586
4587 if host_pci_dev.id not in systemid_by_pciid:
4588 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
4589 return None
4590
4591 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
4592 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
4593 id=host_pci_dev.id,
4594 systemId=systemid_by_pciid[host_pci_dev.id],
4595 vendorId=host_pci_dev.vendorId,
4596 deviceName=host_pci_dev.deviceName)
4597
4598 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
4599
4600 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
4601 new_device_config.operation = "add"
4602 vmConfigSpec = vim.vm.ConfigSpec()
4603 vmConfigSpec.deviceChange = [new_device_config]
4604
4605 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
4606 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
4607 host_pci_dev, vm_object, host_object)
4608 )
4609 except Exception as exp:
4610 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
4611 host_pci_dev,
4612 vm_object,
4613 exp))
4614 return task
4615
4616 def get_vm_vcenter_info(self):
4617 """
4618 Method to get details of vCenter and vm
4619
4620 Args:
4621 vapp_uuid - uuid of vApp or VM
4622
4623 Returns:
4624 Moref Id of VM and deails of vCenter
4625 """
4626 vm_vcenter_info = {}
4627
4628 if self.vcenter_ip is not None:
4629 vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
4630 else:
4631 raise vimconn.vimconnException(message="vCenter IP is not provided."\
4632 " Please provide vCenter IP while attaching datacenter to tenant in --config")
4633 if self.vcenter_port is not None:
4634 vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
4635 else:
4636 raise vimconn.vimconnException(message="vCenter port is not provided."\
4637 " Please provide vCenter port while attaching datacenter to tenant in --config")
4638 if self.vcenter_user is not None:
4639 vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
4640 else:
4641 raise vimconn.vimconnException(message="vCenter user is not provided."\
4642 " Please provide vCenter user while attaching datacenter to tenant in --config")
4643
4644 if self.vcenter_password is not None:
4645 vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
4646 else:
4647 raise vimconn.vimconnException(message="vCenter user password is not provided."\
4648 " Please provide vCenter user password while attaching datacenter to tenant in --config")
4649
4650 return vm_vcenter_info
4651
4652
4653 def get_vm_pci_details(self, vmuuid):
4654 """
4655 Method to get VM PCI device details from vCenter
4656
4657 Args:
4658 vm_obj - vSphere VM object
4659
4660 Returns:
4661 dict of PCI devives attached to VM
4662
4663 """
4664 vm_pci_devices_info = {}
4665 try:
4666 vcenter_conect, content = self.get_vcenter_content()
4667 vm_moref_id = self.get_vm_moref_id(vmuuid)
4668 if vm_moref_id:
4669 #Get VM and its host
4670 if content:
4671 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
4672 if host_obj and vm_obj:
4673 vm_pci_devices_info["host_name"]= host_obj.name
4674 vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
4675 for device in vm_obj.config.hardware.device:
4676 if type(device) == vim.vm.device.VirtualPCIPassthrough:
4677 device_details={'devide_id':device.backing.id,
4678 'pciSlotNumber':device.slotInfo.pciSlotNumber,
4679 }
4680 vm_pci_devices_info[device.deviceInfo.label] = device_details
4681 else:
4682 self.logger.error("Can not connect to vCenter while getting "\
4683 "PCI devices infromationn")
4684 return vm_pci_devices_info
4685 except Exception as exp:
4686 self.logger.error("Error occurred while getting VM infromationn"\
4687 " for VM : {}".format(exp))
4688 raise vimconn.vimconnException(message=exp)
4689
4690
4691 def reserve_memory_for_all_vms(self, vapp, memory_mb):
4692 """
4693 Method to reserve memory for all VMs
4694 Args :
4695 vapp - VApp
4696 memory_mb - Memory in MB
4697 Returns:
4698 None
4699 """
4700
4701 self.logger.info("Reserve memory for all VMs")
4702 for vms in vapp.get_all_vms():
4703 vm_id = vms.get('id').split(':')[-1]
4704
4705 url_rest_call = "{}/api/vApp/vm-{}/virtualHardwareSection/memory".format(self.url, vm_id)
4706
4707 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4708 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4709 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItem+xml'
4710 response = self.perform_request(req_type='GET',
4711 url=url_rest_call,
4712 headers=headers)
4713
4714 if response.status_code == 403:
4715 response = self.retry_rest('GET', url_rest_call)
4716
4717 if response.status_code != 200:
4718 self.logger.error("REST call {} failed reason : {}"\
4719 "status code : {}".format(url_rest_call,
4720 response.content,
4721 response.status_code))
4722 raise vimconn.vimconnException("reserve_memory_for_all_vms : Failed to get "\
4723 "memory")
4724
4725 bytexml = bytes(bytearray(response.content, encoding='utf-8'))
4726 contentelem = lxmlElementTree.XML(bytexml)
4727 namespaces = {prefix:uri for prefix,uri in contentelem.nsmap.iteritems() if prefix}
4728 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4729
4730 # Find the reservation element in the response
4731 memelem_list = contentelem.findall(".//rasd:Reservation", namespaces)
4732 for memelem in memelem_list:
4733 memelem.text = str(memory_mb)
4734
4735 newdata = lxmlElementTree.tostring(contentelem, pretty_print=True)
4736
4737 response = self.perform_request(req_type='PUT',
4738 url=url_rest_call,
4739 headers=headers,
4740 data=newdata)
4741
4742 if response.status_code == 403:
4743 add_headers = {'Content-Type': headers['Content-Type']}
4744 response = self.retry_rest('PUT', url_rest_call, add_headers, newdata)
4745
4746 if response.status_code != 202:
4747 self.logger.error("REST call {} failed reason : {}"\
4748 "status code : {} ".format(url_rest_call,
4749 response.content,
4750 response.status_code))
4751 raise vimconn.vimconnException("reserve_memory_for_all_vms : Failed to update "\
4752 "virtual hardware memory section")
4753 else:
4754 mem_task = self.get_task_from_response(response.content)
4755 result = self.client.get_task_monitor().wait_for_success(task=mem_task)
4756 if result.get('status') == 'success':
4757 self.logger.info("reserve_memory_for_all_vms(): VM {} succeeded "\
4758 .format(vm_id))
4759 else:
4760 self.logger.error("reserve_memory_for_all_vms(): VM {} failed "\
4761 .format(vm_id))
4762
4763 def connect_vapp_to_org_vdc_network(self, vapp_id, net_name):
4764 """
4765 Configure VApp network config with org vdc network
4766 Args :
4767 vapp - VApp
4768 Returns:
4769 None
4770 """
4771
4772 self.logger.info("Connecting vapp {} to org vdc network {}".
4773 format(vapp_id, net_name))
4774
4775 url_rest_call = "{}/api/vApp/vapp-{}/networkConfigSection/".format(self.url, vapp_id)
4776
4777 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4778 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4779 response = self.perform_request(req_type='GET',
4780 url=url_rest_call,
4781 headers=headers)
4782
4783 if response.status_code == 403:
4784 response = self.retry_rest('GET', url_rest_call)
4785
4786 if response.status_code != 200:
4787 self.logger.error("REST call {} failed reason : {}"\
4788 "status code : {}".format(url_rest_call,
4789 response.content,
4790 response.status_code))
4791 raise vimconn.vimconnException("connect_vapp_to_org_vdc_network : Failed to get "\
4792 "network config section")
4793
4794 data = response.content
4795 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConfigSection+xml'
4796 net_id = self.get_network_id_by_name(net_name)
4797 if not net_id:
4798 raise vimconn.vimconnException("connect_vapp_to_org_vdc_network : Failed to find "\
4799 "existing network")
4800
4801 bytexml = bytes(bytearray(data, encoding='utf-8'))
4802 newelem = lxmlElementTree.XML(bytexml)
4803 namespaces = {prefix: uri for prefix, uri in newelem.nsmap.iteritems() if prefix}
4804 namespaces["xmlns"] = "http://www.vmware.com/vcloud/v1.5"
4805 nwcfglist = newelem.findall(".//xmlns:NetworkConfig", namespaces)
4806
4807 newstr = """<NetworkConfig networkName="{}">
4808 <Configuration>
4809 <ParentNetwork href="{}/api/network/{}"/>
4810 <FenceMode>bridged</FenceMode>
4811 </Configuration>
4812 </NetworkConfig>
4813 """.format(net_name, self.url, net_id)
4814 newcfgelem = lxmlElementTree.fromstring(newstr)
4815 if nwcfglist:
4816 nwcfglist[0].addnext(newcfgelem)
4817
4818 newdata = lxmlElementTree.tostring(newelem, pretty_print=True)
4819
4820 response = self.perform_request(req_type='PUT',
4821 url=url_rest_call,
4822 headers=headers,
4823 data=newdata)
4824
4825 if response.status_code == 403:
4826 add_headers = {'Content-Type': headers['Content-Type']}
4827 response = self.retry_rest('PUT', url_rest_call, add_headers, newdata)
4828
4829 if response.status_code != 202:
4830 self.logger.error("REST call {} failed reason : {}"\
4831 "status code : {} ".format(url_rest_call,
4832 response.content,
4833 response.status_code))
4834 raise vimconn.vimconnException("connect_vapp_to_org_vdc_network : Failed to update "\
4835 "network config section")
4836 else:
4837 vapp_task = self.get_task_from_response(response.content)
4838 result = self.client.get_task_monitor().wait_for_success(task=vapp_task)
4839 if result.get('status') == 'success':
4840 self.logger.info("connect_vapp_to_org_vdc_network(): Vapp {} connected to "\
4841 "network {}".format(vapp_id, net_name))
4842 else:
4843 self.logger.error("connect_vapp_to_org_vdc_network(): Vapp {} failed to "\
4844 "connect to network {}".format(vapp_id, net_name))
4845
4846 def remove_primary_network_adapter_from_all_vms(self, vapp):
4847 """
4848 Method to remove network adapter type to vm
4849 Args :
4850 vapp - VApp
4851 Returns:
4852 None
4853 """
4854
4855 self.logger.info("Removing network adapter from all VMs")
4856 for vms in vapp.get_all_vms():
4857 vm_id = vms.get('id').split(':')[-1]
4858
4859 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.url, vm_id)
4860
4861 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4862 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4863 response = self.perform_request(req_type='GET',
4864 url=url_rest_call,
4865 headers=headers)
4866
4867 if response.status_code == 403:
4868 response = self.retry_rest('GET', url_rest_call)
4869
4870 if response.status_code != 200:
4871 self.logger.error("REST call {} failed reason : {}"\
4872 "status code : {}".format(url_rest_call,
4873 response.content,
4874 response.status_code))
4875 raise vimconn.vimconnException("remove_primary_network_adapter : Failed to get "\
4876 "network connection section")
4877
4878 data = response.content
4879 data = data.split('<Link rel="edit"')[0]
4880
4881 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
4882
4883 newdata = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
4884 <NetworkConnectionSection xmlns="http://www.vmware.com/vcloud/v1.5"
4885 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
4886 xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData"
4887 xmlns:common="http://schemas.dmtf.org/wbem/wscim/1/common"
4888 xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
4889 xmlns:vmw="http://www.vmware.com/schema/ovf"
4890 xmlns:ovfenv="http://schemas.dmtf.org/ovf/environment/1"
4891 xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5"
4892 xmlns:ns9="http://www.vmware.com/vcloud/versions"
4893 href="{url}" type="application/vnd.vmware.vcloud.networkConnectionSection+xml" ovf:required="false">
4894 <ovf:Info>Specifies the available VM network connections</ovf:Info>
4895 <PrimaryNetworkConnectionIndex>0</PrimaryNetworkConnectionIndex>
4896 <Link rel="edit" href="{url}" type="application/vnd.vmware.vcloud.networkConnectionSection+xml"/>
4897 </NetworkConnectionSection>""".format(url=url_rest_call)
4898 response = self.perform_request(req_type='PUT',
4899 url=url_rest_call,
4900 headers=headers,
4901 data=newdata)
4902
4903 if response.status_code == 403:
4904 add_headers = {'Content-Type': headers['Content-Type']}
4905 response = self.retry_rest('PUT', url_rest_call, add_headers, newdata)
4906
4907 if response.status_code != 202:
4908 self.logger.error("REST call {} failed reason : {}"\
4909 "status code : {} ".format(url_rest_call,
4910 response.content,
4911 response.status_code))
4912 raise vimconn.vimconnException("remove_primary_network_adapter : Failed to update "\
4913 "network connection section")
4914 else:
4915 nic_task = self.get_task_from_response(response.content)
4916 result = self.client.get_task_monitor().wait_for_success(task=nic_task)
4917 if result.get('status') == 'success':
4918 self.logger.info("remove_primary_network_adapter(): VM {} conneced to "\
4919 "default NIC type".format(vm_id))
4920 else:
4921 self.logger.error("remove_primary_network_adapter(): VM {} failed to "\
4922 "connect NIC type".format(vm_id))
4923
4924 def add_network_adapter_to_vms(self, vapp, network_name, primary_nic_index, nicIndex, net, nic_type=None):
4925 """
4926 Method to add network adapter type to vm
4927 Args :
4928 network_name - name of network
4929 primary_nic_index - int value for primary nic index
4930 nicIndex - int value for nic index
4931 nic_type - specify model name to which add to vm
4932 Returns:
4933 None
4934 """
4935
4936 self.logger.info("Add network adapter to VM: network_name {} nicIndex {} nic_type {}".\
4937 format(network_name, nicIndex, nic_type))
4938 try:
4939 ip_address = None
4940 floating_ip = False
4941 mac_address = None
4942 if 'floating_ip' in net: floating_ip = net['floating_ip']
4943
4944 # Stub for ip_address feature
4945 if 'ip_address' in net: ip_address = net['ip_address']
4946
4947 if 'mac_address' in net: mac_address = net['mac_address']
4948
4949 if floating_ip:
4950 allocation_mode = "POOL"
4951 elif ip_address:
4952 allocation_mode = "MANUAL"
4953 else:
4954 allocation_mode = "DHCP"
4955
4956 if not nic_type:
4957 for vms in vapp.get_all_vms():
4958 vm_id = vms.get('id').split(':')[-1]
4959
4960 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.url, vm_id)
4961
4962 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
4963 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
4964 response = self.perform_request(req_type='GET',
4965 url=url_rest_call,
4966 headers=headers)
4967
4968 if response.status_code == 403:
4969 response = self.retry_rest('GET', url_rest_call)
4970
4971 if response.status_code != 200:
4972 self.logger.error("REST call {} failed reason : {}"\
4973 "status code : {}".format(url_rest_call,
4974 response.content,
4975 response.status_code))
4976 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
4977 "network connection section")
4978
4979 data = response.content
4980 data = data.split('<Link rel="edit"')[0]
4981 if '<PrimaryNetworkConnectionIndex>' not in data:
4982 self.logger.debug("add_network_adapter PrimaryNIC not in data")
4983 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
4984 <NetworkConnection network="{}">
4985 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
4986 <IsConnected>true</IsConnected>
4987 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
4988 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
4989 allocation_mode)
4990 # Stub for ip_address feature
4991 if ip_address:
4992 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
4993 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
4994
4995 if mac_address:
4996 mac_tag = '<MACAddress>{}</MACAddress>'.format(mac_address)
4997 item = item.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag))
4998
4999 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n</NetworkConnectionSection>'.format(item))
5000 else:
5001 self.logger.debug("add_network_adapter PrimaryNIC in data")
5002 new_item = """<NetworkConnection network="{}">
5003 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
5004 <IsConnected>true</IsConnected>
5005 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
5006 </NetworkConnection>""".format(network_name, nicIndex,
5007 allocation_mode)
5008 # Stub for ip_address feature
5009 if ip_address:
5010 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
5011 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
5012
5013 if mac_address:
5014 mac_tag = '<MACAddress>{}</MACAddress>'.format(mac_address)
5015 new_item = new_item.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag))
5016
5017 data = data + new_item + '</NetworkConnectionSection>'
5018
5019 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
5020
5021 response = self.perform_request(req_type='PUT',
5022 url=url_rest_call,
5023 headers=headers,
5024 data=data)
5025
5026 if response.status_code == 403:
5027 add_headers = {'Content-Type': headers['Content-Type']}
5028 response = self.retry_rest('PUT', url_rest_call, add_headers, data)
5029
5030 if response.status_code != 202:
5031 self.logger.error("REST call {} failed reason : {}"\
5032 "status code : {} ".format(url_rest_call,
5033 response.content,
5034 response.status_code))
5035 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
5036 "network connection section")
5037 else:
5038 nic_task = self.get_task_from_response(response.content)
5039 result = self.client.get_task_monitor().wait_for_success(task=nic_task)
5040 if result.get('status') == 'success':
5041 self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\
5042 "default NIC type".format(vm_id))
5043 else:
5044 self.logger.error("add_network_adapter_to_vms(): VM {} failed to "\
5045 "connect NIC type".format(vm_id))
5046 else:
5047 for vms in vapp.get_all_vms():
5048 vm_id = vms.get('id').split(':')[-1]
5049
5050 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(self.url, vm_id)
5051
5052 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
5053 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
5054 response = self.perform_request(req_type='GET',
5055 url=url_rest_call,
5056 headers=headers)
5057
5058 if response.status_code == 403:
5059 response = self.retry_rest('GET', url_rest_call)
5060
5061 if response.status_code != 200:
5062 self.logger.error("REST call {} failed reason : {}"\
5063 "status code : {}".format(url_rest_call,
5064 response.content,
5065 response.status_code))
5066 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
5067 "network connection section")
5068 data = response.content
5069 data = data.split('<Link rel="edit"')[0]
5070 vcd_netadapter_type = nic_type
5071 if nic_type in ['SR-IOV', 'VF']:
5072 vcd_netadapter_type = "SRIOVETHERNETCARD"
5073
5074 if '<PrimaryNetworkConnectionIndex>' not in data:
5075 self.logger.debug("add_network_adapter PrimaryNIC not in data nic_type {}".format(nic_type))
5076 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
5077 <NetworkConnection network="{}">
5078 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
5079 <IsConnected>true</IsConnected>
5080 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
5081 <NetworkAdapterType>{}</NetworkAdapterType>
5082 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
5083 allocation_mode, vcd_netadapter_type)
5084 # Stub for ip_address feature
5085 if ip_address:
5086 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
5087 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
5088
5089 if mac_address:
5090 mac_tag = '<MACAddress>{}</MACAddress>'.format(mac_address)
5091 item = item.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag))
5092
5093 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n</NetworkConnectionSection>'.format(item))
5094 else:
5095 self.logger.debug("add_network_adapter PrimaryNIC in data nic_type {}".format(nic_type))
5096 new_item = """<NetworkConnection network="{}">
5097 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
5098 <IsConnected>true</IsConnected>
5099 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
5100 <NetworkAdapterType>{}</NetworkAdapterType>
5101 </NetworkConnection>""".format(network_name, nicIndex,
5102 allocation_mode, vcd_netadapter_type)
5103 # Stub for ip_address feature
5104 if ip_address:
5105 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
5106 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
5107
5108 if mac_address:
5109 mac_tag = '<MACAddress>{}</MACAddress>'.format(mac_address)
5110 new_item = new_item.replace('</IsConnected>\n','</IsConnected>\n{}\n'.format(mac_tag))
5111
5112 data = data + new_item + '</NetworkConnectionSection>'
5113
5114 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
5115
5116 response = self.perform_request(req_type='PUT',
5117 url=url_rest_call,
5118 headers=headers,
5119 data=data)
5120
5121 if response.status_code == 403:
5122 add_headers = {'Content-Type': headers['Content-Type']}
5123 response = self.retry_rest('PUT', url_rest_call, add_headers, data)
5124
5125 if response.status_code != 202:
5126 self.logger.error("REST call {} failed reason : {}"\
5127 "status code : {}".format(url_rest_call,
5128 response.content,
5129 response.status_code))
5130 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
5131 "network connection section")
5132 else:
5133 nic_task = self.get_task_from_response(response.content)
5134 result = self.client.get_task_monitor().wait_for_success(task=nic_task)
5135 if result.get('status') == 'success':
5136 self.logger.info("add_network_adapter_to_vms(): VM {} "\
5137 "conneced to NIC type {}".format(vm_id, nic_type))
5138 else:
5139 self.logger.error("add_network_adapter_to_vms(): VM {} "\
5140 "failed to connect NIC type {}".format(vm_id, nic_type))
5141 except Exception as exp:
5142 self.logger.error("add_network_adapter_to_vms() : exception occurred "\
5143 "while adding Network adapter")
5144 raise vimconn.vimconnException(message=exp)
5145
5146
5147 def set_numa_affinity(self, vmuuid, paired_threads_id):
5148 """
5149 Method to assign numa affinity in vm configuration parammeters
5150 Args :
5151 vmuuid - vm uuid
5152 paired_threads_id - one or more virtual processor
5153 numbers
5154 Returns:
5155 return if True
5156 """
5157 try:
5158 vcenter_conect, content = self.get_vcenter_content()
5159 vm_moref_id = self.get_vm_moref_id(vmuuid)
5160
5161 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
5162 if vm_obj:
5163 config_spec = vim.vm.ConfigSpec()
5164 config_spec.extraConfig = []
5165 opt = vim.option.OptionValue()
5166 opt.key = 'numa.nodeAffinity'
5167 opt.value = str(paired_threads_id)
5168 config_spec.extraConfig.append(opt)
5169 task = vm_obj.ReconfigVM_Task(config_spec)
5170 if task:
5171 result = self.wait_for_vcenter_task(task, vcenter_conect)
5172 extra_config = vm_obj.config.extraConfig
5173 flag = False
5174 for opts in extra_config:
5175 if 'numa.nodeAffinity' in opts.key:
5176 flag = True
5177 self.logger.info("set_numa_affinity: Sucessfully assign numa affinity "\
5178 "value {} for vm {}".format(opt.value, vm_obj))
5179 if flag:
5180 return
5181 else:
5182 self.logger.error("set_numa_affinity: Failed to assign numa affinity")
5183 except Exception as exp:
5184 self.logger.error("set_numa_affinity : exception occurred while setting numa affinity "\
5185 "for VM {} : {}".format(vm_obj, vm_moref_id))
5186 raise vimconn.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
5187 "affinity".format(exp))
5188
5189
5190 def cloud_init(self, vapp, cloud_config):
5191 """
5192 Method to inject ssh-key
5193 vapp - vapp object
5194 cloud_config a dictionary with:
5195 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
5196 'users': (optional) list of users to be inserted, each item is a dict with:
5197 'name': (mandatory) user name,
5198 'key-pairs': (optional) list of strings with the public key to be inserted to the user
5199 'user-data': (optional) can be a string with the text script to be passed directly to cloud-init,
5200 or a list of strings, each one contains a script to be passed, usually with a MIMEmultipart file
5201 'config-files': (optional). List of files to be transferred. Each item is a dict with:
5202 'dest': (mandatory) string with the destination absolute path
5203 'encoding': (optional, by default text). Can be one of:
5204 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
5205 'content' (mandatory): string with the content of the file
5206 'permissions': (optional) string with file permissions, typically octal notation '0644'
5207 'owner': (optional) file owner, string with the format 'owner:group'
5208 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
5209 """
5210 try:
5211 if not isinstance(cloud_config, dict):
5212 raise Exception("cloud_init : parameter cloud_config is not a dictionary")
5213 else:
5214 key_pairs = []
5215 userdata = []
5216 if "key-pairs" in cloud_config:
5217 key_pairs = cloud_config["key-pairs"]
5218
5219 if "users" in cloud_config:
5220 userdata = cloud_config["users"]
5221
5222 self.logger.debug("cloud_init : Guest os customization started..")
5223 customize_script = self.format_script(key_pairs=key_pairs, users_list=userdata)
5224 customize_script = customize_script.replace("&","&amp;")
5225 self.guest_customization(vapp, customize_script)
5226
5227 except Exception as exp:
5228 self.logger.error("cloud_init : exception occurred while injecting "\
5229 "ssh-key")
5230 raise vimconn.vimconnException("cloud_init : Error {} failed to inject "\
5231 "ssh-key".format(exp))
5232
5233 def format_script(self, key_pairs=[], users_list=[]):
5234 bash_script = """#!/bin/sh
5235 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
5236 if [ "$1" = "precustomization" ];then
5237 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
5238 """
5239
5240 keys = "\n".join(key_pairs)
5241 if keys:
5242 keys_data = """
5243 if [ ! -d /root/.ssh ];then
5244 mkdir /root/.ssh
5245 chown root:root /root/.ssh
5246 chmod 700 /root/.ssh
5247 touch /root/.ssh/authorized_keys
5248 chown root:root /root/.ssh/authorized_keys
5249 chmod 600 /root/.ssh/authorized_keys
5250 # make centos with selinux happy
5251 which restorecon && restorecon -Rv /root/.ssh
5252 else
5253 touch /root/.ssh/authorized_keys
5254 chown root:root /root/.ssh/authorized_keys
5255 chmod 600 /root/.ssh/authorized_keys
5256 fi
5257 echo '{key}' >> /root/.ssh/authorized_keys
5258 """.format(key=keys)
5259
5260 bash_script+= keys_data
5261
5262 for user in users_list:
5263 if 'name' in user: user_name = user['name']
5264 if 'key-pairs' in user:
5265 user_keys = "\n".join(user['key-pairs'])
5266 else:
5267 user_keys = None
5268
5269 add_user_name = """
5270 useradd -d /home/{user_name} -m -g users -s /bin/bash {user_name}
5271 """.format(user_name=user_name)
5272
5273 bash_script+= add_user_name
5274
5275 if user_keys:
5276 user_keys_data = """
5277 mkdir /home/{user_name}/.ssh
5278 chown {user_name}:{user_name} /home/{user_name}/.ssh
5279 chmod 700 /home/{user_name}/.ssh
5280 touch /home/{user_name}/.ssh/authorized_keys
5281 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
5282 chmod 600 /home/{user_name}/.ssh/authorized_keys
5283 # make centos with selinux happy
5284 which restorecon && restorecon -Rv /home/{user_name}/.ssh
5285 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
5286 """.format(user_name=user_name,user_key=user_keys)
5287
5288 bash_script+= user_keys_data
5289
5290 return bash_script+"\n\tfi"
5291
5292 def guest_customization(self, vapp, customize_script):
5293 """
5294 Method to customize guest os
5295 vapp - Vapp object
5296 customize_script - Customize script to be run at first boot of VM.
5297 """
5298 for vm in vapp.get_all_vms():
5299 vm_id = vm.get('id').split(':')[-1]
5300 vm_name = vm.get('name')
5301 vm_name = vm_name.replace('_','-')
5302
5303 vm_customization_url = "{}/api/vApp/vm-{}/guestCustomizationSection/".format(self.url, vm_id)
5304 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
5305 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
5306
5307 headers['Content-Type'] = "application/vnd.vmware.vcloud.guestCustomizationSection+xml"
5308
5309 data = """<GuestCustomizationSection
5310 xmlns="http://www.vmware.com/vcloud/v1.5"
5311 xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"
5312 ovf:required="false" href="{}" type="application/vnd.vmware.vcloud.guestCustomizationSection+xml">
5313 <ovf:Info>Specifies Guest OS Customization Settings</ovf:Info>
5314 <Enabled>true</Enabled>
5315 <ChangeSid>false</ChangeSid>
5316 <VirtualMachineId>{}</VirtualMachineId>
5317 <JoinDomainEnabled>false</JoinDomainEnabled>
5318 <UseOrgSettings>false</UseOrgSettings>
5319 <AdminPasswordEnabled>false</AdminPasswordEnabled>
5320 <AdminPasswordAuto>true</AdminPasswordAuto>
5321 <AdminAutoLogonEnabled>false</AdminAutoLogonEnabled>
5322 <AdminAutoLogonCount>0</AdminAutoLogonCount>
5323 <ResetPasswordRequired>false</ResetPasswordRequired>
5324 <CustomizationScript>{}</CustomizationScript>
5325 <ComputerName>{}</ComputerName>
5326 <Link href="{}" type="application/vnd.vmware.vcloud.guestCustomizationSection+xml" rel="edit"/>
5327 </GuestCustomizationSection>
5328 """.format(vm_customization_url,
5329 vm_id,
5330 customize_script,
5331 vm_name,
5332 vm_customization_url)
5333
5334 response = self.perform_request(req_type='PUT',
5335 url=vm_customization_url,
5336 headers=headers,
5337 data=data)
5338 if response.status_code == 202:
5339 guest_task = self.get_task_from_response(response.content)
5340 self.client.get_task_monitor().wait_for_success(task=guest_task)
5341 self.logger.info("guest_customization : customized guest os task "\
5342 "completed for VM {}".format(vm_name))
5343 else:
5344 self.logger.error("guest_customization : task for customized guest os"\
5345 "failed for VM {}".format(vm_name))
5346 raise vimconn.vimconnException("guest_customization : failed to perform"\
5347 "guest os customization on VM {}".format(vm_name))
5348
5349 def add_new_disk(self, vapp_uuid, disk_size):
5350 """
5351 Method to create an empty vm disk
5352
5353 Args:
5354 vapp_uuid - is vapp identifier.
5355 disk_size - size of disk to be created in GB
5356
5357 Returns:
5358 None
5359 """
5360 status = False
5361 vm_details = None
5362 try:
5363 #Disk size in GB, convert it into MB
5364 if disk_size is not None:
5365 disk_size_mb = int(disk_size) * 1024
5366 vm_details = self.get_vapp_details_rest(vapp_uuid)
5367
5368 if vm_details and "vm_virtual_hardware" in vm_details:
5369 self.logger.info("Adding disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
5370 disk_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
5371 status = self.add_new_disk_rest(disk_href, disk_size_mb)
5372
5373 except Exception as exp:
5374 msg = "Error occurred while creating new disk {}.".format(exp)
5375 self.rollback_newvm(vapp_uuid, msg)
5376
5377 if status:
5378 self.logger.info("Added new disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
5379 else:
5380 #If failed to add disk, delete VM
5381 msg = "add_new_disk: Failed to add new disk to {}".format(vm_details["name"])
5382 self.rollback_newvm(vapp_uuid, msg)
5383
5384
5385 def add_new_disk_rest(self, disk_href, disk_size_mb):
5386 """
5387 Retrives vApp Disks section & add new empty disk
5388
5389 Args:
5390 disk_href: Disk section href to addd disk
5391 disk_size_mb: Disk size in MB
5392
5393 Returns: Status of add new disk task
5394 """
5395 status = False
5396 if self.client._session:
5397 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
5398 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
5399 response = self.perform_request(req_type='GET',
5400 url=disk_href,
5401 headers=headers)
5402
5403 if response.status_code == 403:
5404 response = self.retry_rest('GET', disk_href)
5405
5406 if response.status_code != requests.codes.ok:
5407 self.logger.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}"
5408 .format(disk_href, response.status_code))
5409 return status
5410 try:
5411 #Find but type & max of instance IDs assigned to disks
5412 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
5413 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
5414 #For python3
5415 #namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.items() if prefix}
5416 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
5417 instance_id = 0
5418 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
5419 if item.find("rasd:Description",namespaces).text == "Hard disk":
5420 inst_id = int(item.find("rasd:InstanceID" ,namespaces).text)
5421 if inst_id > instance_id:
5422 instance_id = inst_id
5423 disk_item = item.find("rasd:HostResource" ,namespaces)
5424 bus_subtype = disk_item.attrib["{"+namespaces['xmlns']+"}busSubType"]
5425 bus_type = disk_item.attrib["{"+namespaces['xmlns']+"}busType"]
5426
5427 instance_id = instance_id + 1
5428 new_item = """<Item>
5429 <rasd:Description>Hard disk</rasd:Description>
5430 <rasd:ElementName>New disk</rasd:ElementName>
5431 <rasd:HostResource
5432 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
5433 vcloud:capacity="{}"
5434 vcloud:busSubType="{}"
5435 vcloud:busType="{}"></rasd:HostResource>
5436 <rasd:InstanceID>{}</rasd:InstanceID>
5437 <rasd:ResourceType>17</rasd:ResourceType>
5438 </Item>""".format(disk_size_mb, bus_subtype, bus_type, instance_id)
5439
5440 new_data = response.content
5441 #Add new item at the bottom
5442 new_data = new_data.replace('</Item>\n</RasdItemsList>', '</Item>\n{}\n</RasdItemsList>'.format(new_item))
5443
5444 # Send PUT request to modify virtual hardware section with new disk
5445 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
5446
5447 response = self.perform_request(req_type='PUT',
5448 url=disk_href,
5449 data=new_data,
5450 headers=headers)
5451
5452 if response.status_code == 403:
5453 add_headers = {'Content-Type': headers['Content-Type']}
5454 response = self.retry_rest('PUT', disk_href, add_headers, new_data)
5455
5456 if response.status_code != 202:
5457 self.logger.error("PUT REST API call {} failed. Return status code {}. Response Content:{}"
5458 .format(disk_href, response.status_code, response.content))
5459 else:
5460 add_disk_task = self.get_task_from_response(response.content)
5461 result = self.client.get_task_monitor().wait_for_success(task=add_disk_task)
5462 if result.get('status') == 'success':
5463 status = True
5464 else:
5465 self.logger.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb))
5466
5467 except Exception as exp:
5468 self.logger.error("Error occurred calling rest api for creating new disk {}".format(exp))
5469
5470 return status
5471
5472
5473 def add_existing_disk(self, catalogs=None, image_id=None, size=None, template_name=None, vapp_uuid=None):
5474 """
5475 Method to add existing disk to vm
5476 Args :
5477 catalogs - List of VDC catalogs
5478 image_id - Catalog ID
5479 template_name - Name of template in catalog
5480 vapp_uuid - UUID of vApp
5481 Returns:
5482 None
5483 """
5484 disk_info = None
5485 vcenter_conect, content = self.get_vcenter_content()
5486 #find moref-id of vm in image
5487 catalog_vm_info = self.get_vapp_template_details(catalogs=catalogs,
5488 image_id=image_id,
5489 )
5490
5491 if catalog_vm_info and "vm_vcenter_info" in catalog_vm_info:
5492 if "vm_moref_id" in catalog_vm_info["vm_vcenter_info"]:
5493 catalog_vm_moref_id = catalog_vm_info["vm_vcenter_info"].get("vm_moref_id", None)
5494 if catalog_vm_moref_id:
5495 self.logger.info("Moref_id of VM in catalog : {}" .format(catalog_vm_moref_id))
5496 host, catalog_vm_obj = self.get_vm_obj(content, catalog_vm_moref_id)
5497 if catalog_vm_obj:
5498 #find existing disk
5499 disk_info = self.find_disk(catalog_vm_obj)
5500 else:
5501 exp_msg = "No VM with image id {} found".format(image_id)
5502 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
5503 else:
5504 exp_msg = "No Image found with image ID {} ".format(image_id)
5505 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
5506
5507 if disk_info:
5508 self.logger.info("Existing disk_info : {}".format(disk_info))
5509 #get VM
5510 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
5511 host, vm_obj = self.get_vm_obj(content, vm_moref_id)
5512 if vm_obj:
5513 status = self.add_disk(vcenter_conect=vcenter_conect,
5514 vm=vm_obj,
5515 disk_info=disk_info,
5516 size=size,
5517 vapp_uuid=vapp_uuid
5518 )
5519 if status:
5520 self.logger.info("Disk from image id {} added to {}".format(image_id,
5521 vm_obj.config.name)
5522 )
5523 else:
5524 msg = "No disk found with image id {} to add in VM {}".format(
5525 image_id,
5526 vm_obj.config.name)
5527 self.rollback_newvm(vapp_uuid, msg, exp_type="NotFound")
5528
5529
5530 def find_disk(self, vm_obj):
5531 """
5532 Method to find details of existing disk in VM
5533 Args :
5534 vm_obj - vCenter object of VM
5535 image_id - Catalog ID
5536 Returns:
5537 disk_info : dict of disk details
5538 """
5539 disk_info = {}
5540 if vm_obj:
5541 try:
5542 devices = vm_obj.config.hardware.device
5543 for device in devices:
5544 if type(device) is vim.vm.device.VirtualDisk:
5545 if isinstance(device.backing,vim.vm.device.VirtualDisk.FlatVer2BackingInfo) and hasattr(device.backing, 'fileName'):
5546 disk_info["full_path"] = device.backing.fileName
5547 disk_info["datastore"] = device.backing.datastore
5548 disk_info["capacityKB"] = device.capacityInKB
5549 break
5550 except Exception as exp:
5551 self.logger.error("find_disk() : exception occurred while "\
5552 "getting existing disk details :{}".format(exp))
5553 return disk_info
5554
5555
5556 def add_disk(self, vcenter_conect=None, vm=None, size=None, vapp_uuid=None, disk_info={}):
5557 """
5558 Method to add existing disk in VM
5559 Args :
5560 vcenter_conect - vCenter content object
5561 vm - vCenter vm object
5562 disk_info : dict of disk details
5563 Returns:
5564 status : status of add disk task
5565 """
5566 datastore = disk_info["datastore"] if "datastore" in disk_info else None
5567 fullpath = disk_info["full_path"] if "full_path" in disk_info else None
5568 capacityKB = disk_info["capacityKB"] if "capacityKB" in disk_info else None
5569 if size is not None:
5570 #Convert size from GB to KB
5571 sizeKB = int(size) * 1024 * 1024
5572 #compare size of existing disk and user given size.Assign whicherver is greater
5573 self.logger.info("Add Existing disk : sizeKB {} , capacityKB {}".format(
5574 sizeKB, capacityKB))
5575 if sizeKB > capacityKB:
5576 capacityKB = sizeKB
5577
5578 if datastore and fullpath and capacityKB:
5579 try:
5580 spec = vim.vm.ConfigSpec()
5581 # get all disks on a VM, set unit_number to the next available
5582 unit_number = 0
5583 for dev in vm.config.hardware.device:
5584 if hasattr(dev.backing, 'fileName'):
5585 unit_number = int(dev.unitNumber) + 1
5586 # unit_number 7 reserved for scsi controller
5587 if unit_number == 7:
5588 unit_number += 1
5589 if isinstance(dev, vim.vm.device.VirtualDisk):
5590 #vim.vm.device.VirtualSCSIController
5591 controller_key = dev.controllerKey
5592
5593 self.logger.info("Add Existing disk : unit number {} , controller key {}".format(
5594 unit_number, controller_key))
5595 # add disk here
5596 dev_changes = []
5597 disk_spec = vim.vm.device.VirtualDeviceSpec()
5598 disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
5599 disk_spec.device = vim.vm.device.VirtualDisk()
5600 disk_spec.device.backing = \
5601 vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
5602 disk_spec.device.backing.thinProvisioned = True
5603 disk_spec.device.backing.diskMode = 'persistent'
5604 disk_spec.device.backing.datastore = datastore
5605 disk_spec.device.backing.fileName = fullpath
5606
5607 disk_spec.device.unitNumber = unit_number
5608 disk_spec.device.capacityInKB = capacityKB
5609 disk_spec.device.controllerKey = controller_key
5610 dev_changes.append(disk_spec)
5611 spec.deviceChange = dev_changes
5612 task = vm.ReconfigVM_Task(spec=spec)
5613 status = self.wait_for_vcenter_task(task, vcenter_conect)
5614 return status
5615 except Exception as exp:
5616 exp_msg = "add_disk() : exception {} occurred while adding disk "\
5617 "{} to vm {}".format(exp,
5618 fullpath,
5619 vm.config.name)
5620 self.rollback_newvm(vapp_uuid, exp_msg)
5621 else:
5622 msg = "add_disk() : Can not add disk to VM with disk info {} ".format(disk_info)
5623 self.rollback_newvm(vapp_uuid, msg)
5624
5625
5626 def get_vcenter_content(self):
5627 """
5628 Get the vsphere content object
5629 """
5630 try:
5631 vm_vcenter_info = self.get_vm_vcenter_info()
5632 except Exception as exp:
5633 self.logger.error("Error occurred while getting vCenter infromationn"\
5634 " for VM : {}".format(exp))
5635 raise vimconn.vimconnException(message=exp)
5636
5637 context = None
5638 if hasattr(ssl, '_create_unverified_context'):
5639 context = ssl._create_unverified_context()
5640
5641 vcenter_conect = SmartConnect(
5642 host=vm_vcenter_info["vm_vcenter_ip"],
5643 user=vm_vcenter_info["vm_vcenter_user"],
5644 pwd=vm_vcenter_info["vm_vcenter_password"],
5645 port=int(vm_vcenter_info["vm_vcenter_port"]),
5646 sslContext=context
5647 )
5648 atexit.register(Disconnect, vcenter_conect)
5649 content = vcenter_conect.RetrieveContent()
5650 return vcenter_conect, content
5651
5652
5653 def get_vm_moref_id(self, vapp_uuid):
5654 """
5655 Get the moref_id of given VM
5656 """
5657 try:
5658 if vapp_uuid:
5659 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
5660 if vm_details and "vm_vcenter_info" in vm_details:
5661 vm_moref_id = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
5662 return vm_moref_id
5663
5664 except Exception as exp:
5665 self.logger.error("Error occurred while getting VM moref ID "\
5666 " for VM : {}".format(exp))
5667 return None
5668
5669
5670 def get_vapp_template_details(self, catalogs=None, image_id=None , template_name=None):
5671 """
5672 Method to get vApp template details
5673 Args :
5674 catalogs - list of VDC catalogs
5675 image_id - Catalog ID to find
5676 template_name : template name in catalog
5677 Returns:
5678 parsed_respond : dict of vApp tempalte details
5679 """
5680 parsed_response = {}
5681
5682 vca = self.connect_as_admin()
5683 if not vca:
5684 raise vimconn.vimconnConnectionException("Failed to connect vCD")
5685
5686 try:
5687 org, vdc = self.get_vdc_details()
5688 catalog = self.get_catalog_obj(image_id, catalogs)
5689 if catalog:
5690 items = org.get_catalog_item(catalog.get('name'), catalog.get('name'))
5691 catalog_items = [items.attrib]
5692
5693 if len(catalog_items) == 1:
5694 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
5695 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
5696
5697 response = self.perform_request(req_type='GET',
5698 url=catalog_items[0].get('href'),
5699 headers=headers)
5700 catalogItem = XmlElementTree.fromstring(response.content)
5701 entity = [child for child in catalogItem if child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
5702 vapp_tempalte_href = entity.get("href")
5703 #get vapp details and parse moref id
5704
5705 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
5706 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
5707 'vmw': 'http://www.vmware.com/schema/ovf',
5708 'vm': 'http://www.vmware.com/vcloud/v1.5',
5709 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
5710 'vmext':"http://www.vmware.com/vcloud/extension/v1.5",
5711 'xmlns':"http://www.vmware.com/vcloud/v1.5"
5712 }
5713
5714 if vca._session:
5715 response = self.perform_request(req_type='GET',
5716 url=vapp_tempalte_href,
5717 headers=headers)
5718
5719 if response.status_code != requests.codes.ok:
5720 self.logger.debug("REST API call {} failed. Return status code {}".format(
5721 vapp_tempalte_href, response.status_code))
5722
5723 else:
5724 xmlroot_respond = XmlElementTree.fromstring(response.content)
5725 children_section = xmlroot_respond.find('vm:Children/', namespaces)
5726 if children_section is not None:
5727 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
5728 if vCloud_extension_section is not None:
5729 vm_vcenter_info = {}
5730 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
5731 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
5732 if vmext is not None:
5733 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
5734 parsed_response["vm_vcenter_info"]= vm_vcenter_info
5735
5736 except Exception as exp :
5737 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
5738
5739 return parsed_response
5740
5741
5742 def rollback_newvm(self, vapp_uuid, msg , exp_type="Genric"):
5743 """
5744 Method to delete vApp
5745 Args :
5746 vapp_uuid - vApp UUID
5747 msg - Error message to be logged
5748 exp_type : Exception type
5749 Returns:
5750 None
5751 """
5752 if vapp_uuid:
5753 status = self.delete_vminstance(vapp_uuid)
5754 else:
5755 msg = "No vApp ID"
5756 self.logger.error(msg)
5757 if exp_type == "Genric":
5758 raise vimconn.vimconnException(msg)
5759 elif exp_type == "NotFound":
5760 raise vimconn.vimconnNotFoundException(message=msg)
5761
5762 def add_sriov(self, vapp_uuid, sriov_nets, vmname_andid):
5763 """
5764 Method to attach SRIOV adapters to VM
5765
5766 Args:
5767 vapp_uuid - uuid of vApp/VM
5768 sriov_nets - SRIOV devices infromation as specified in VNFD (flavor)
5769 vmname_andid - vmname
5770
5771 Returns:
5772 The status of add SRIOV adapter task , vm object and
5773 vcenter_conect object
5774 """
5775 vm_obj = None
5776 vcenter_conect, content = self.get_vcenter_content()
5777 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
5778
5779 if vm_moref_id:
5780 try:
5781 no_of_sriov_devices = len(sriov_nets)
5782 if no_of_sriov_devices > 0:
5783 #Get VM and its host
5784 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
5785 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
5786 if host_obj and vm_obj:
5787 #get SRIOV devies from host on which vapp is currently installed
5788 avilable_sriov_devices = self.get_sriov_devices(host_obj,
5789 no_of_sriov_devices,
5790 )
5791
5792 if len(avilable_sriov_devices) == 0:
5793 #find other hosts with active pci devices
5794 new_host_obj , avilable_sriov_devices = self.get_host_and_sriov_devices(
5795 content,
5796 no_of_sriov_devices,
5797 )
5798
5799 if new_host_obj is not None and len(avilable_sriov_devices)> 0:
5800 #Migrate vm to the host where SRIOV devices are available
5801 self.logger.info("Relocate VM {} on new host {}".format(vm_obj,
5802 new_host_obj))
5803 task = self.relocate_vm(new_host_obj, vm_obj)
5804 if task is not None:
5805 result = self.wait_for_vcenter_task(task, vcenter_conect)
5806 self.logger.info("Migrate VM status: {}".format(result))
5807 host_obj = new_host_obj
5808 else:
5809 self.logger.info("Fail to migrate VM : {}".format(result))
5810 raise vimconn.vimconnNotFoundException(
5811 "Fail to migrate VM : {} to host {}".format(
5812 vmname_andid,
5813 new_host_obj)
5814 )
5815
5816 if host_obj is not None and avilable_sriov_devices is not None and len(avilable_sriov_devices)> 0:
5817 #Add SRIOV devices one by one
5818 for sriov_net in sriov_nets:
5819 network_name = sriov_net.get('net_id')
5820 dvs_portgr_name = self.create_dvPort_group(network_name)
5821 if sriov_net.get('type') == "VF" or sriov_net.get('type') == "SR-IOV":
5822 #add vlan ID ,Modify portgroup for vlan ID
5823 self.configure_vlanID(content, vcenter_conect, network_name)
5824
5825 task = self.add_sriov_to_vm(content,
5826 vm_obj,
5827 host_obj,
5828 network_name,
5829 avilable_sriov_devices[0]
5830 )
5831 if task:
5832 status= self.wait_for_vcenter_task(task, vcenter_conect)
5833 if status:
5834 self.logger.info("Added SRIOV {} to VM {}".format(
5835 no_of_sriov_devices,
5836 str(vm_obj)))
5837 else:
5838 self.logger.error("Fail to add SRIOV {} to VM {}".format(
5839 no_of_sriov_devices,
5840 str(vm_obj)))
5841 raise vimconn.vimconnUnexpectedResponse(
5842 "Fail to add SRIOV adapter in VM ".format(str(vm_obj))
5843 )
5844 return True, vm_obj, vcenter_conect
5845 else:
5846 self.logger.error("Currently there is no host with"\
5847 " {} number of avaialble SRIOV "\
5848 "VFs required for VM {}".format(
5849 no_of_sriov_devices,
5850 vmname_andid)
5851 )
5852 raise vimconn.vimconnNotFoundException(
5853 "Currently there is no host with {} "\
5854 "number of avaialble SRIOV devices required for VM {}".format(
5855 no_of_sriov_devices,
5856 vmname_andid))
5857 else:
5858 self.logger.debug("No infromation about SRIOV devices {} ",sriov_nets)
5859
5860 except vmodl.MethodFault as error:
5861 self.logger.error("Error occurred while adding SRIOV {} ",error)
5862 return None, vm_obj, vcenter_conect
5863
5864
5865 def get_sriov_devices(self,host, no_of_vfs):
5866 """
5867 Method to get the details of SRIOV devices on given host
5868 Args:
5869 host - vSphere host object
5870 no_of_vfs - number of VFs needed on host
5871
5872 Returns:
5873 array of SRIOV devices
5874 """
5875 sriovInfo=[]
5876 if host:
5877 for device in host.config.pciPassthruInfo:
5878 if isinstance(device,vim.host.SriovInfo) and device.sriovActive:
5879 if device.numVirtualFunction >= no_of_vfs:
5880 sriovInfo.append(device)
5881 break
5882 return sriovInfo
5883
5884
5885 def get_host_and_sriov_devices(self, content, no_of_vfs):
5886 """
5887 Method to get the details of SRIOV devices infromation on all hosts
5888
5889 Args:
5890 content - vSphere host object
5891 no_of_vfs - number of pci VFs needed on host
5892
5893 Returns:
5894 array of SRIOV devices and host object
5895 """
5896 host_obj = None
5897 sriov_device_objs = None
5898 try:
5899 if content:
5900 container = content.viewManager.CreateContainerView(content.rootFolder,
5901 [vim.HostSystem], True)
5902 for host in container.view:
5903 devices = self.get_sriov_devices(host, no_of_vfs)
5904 if devices:
5905 host_obj = host
5906 sriov_device_objs = devices
5907 break
5908 except Exception as exp:
5909 self.logger.error("Error {} occurred while finding SRIOV devices on host: {}".format(exp, host_obj))
5910
5911 return host_obj,sriov_device_objs
5912
5913
5914 def add_sriov_to_vm(self,content, vm_obj, host_obj, network_name, sriov_device):
5915 """
5916 Method to add SRIOV adapter to vm
5917
5918 Args:
5919 host_obj - vSphere host object
5920 vm_obj - vSphere vm object
5921 content - vCenter content object
5922 network_name - name of distributed virtaul portgroup
5923 sriov_device - SRIOV device info
5924
5925 Returns:
5926 task object
5927 """
5928 devices = []
5929 vnic_label = "sriov nic"
5930 try:
5931 dvs_portgr = self.get_dvport_group(network_name)
5932 network_name = dvs_portgr.name
5933 nic = vim.vm.device.VirtualDeviceSpec()
5934 # VM device
5935 nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
5936 nic.device = vim.vm.device.VirtualSriovEthernetCard()
5937 nic.device.addressType = 'assigned'
5938 #nic.device.key = 13016
5939 nic.device.deviceInfo = vim.Description()
5940 nic.device.deviceInfo.label = vnic_label
5941 nic.device.deviceInfo.summary = network_name
5942 nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
5943
5944 nic.device.backing.network = self.get_obj(content, [vim.Network], network_name)
5945 nic.device.backing.deviceName = network_name
5946 nic.device.backing.useAutoDetect = False
5947 nic.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
5948 nic.device.connectable.startConnected = True
5949 nic.device.connectable.allowGuestControl = True
5950
5951 nic.device.sriovBacking = vim.vm.device.VirtualSriovEthernetCard.SriovBackingInfo()
5952 nic.device.sriovBacking.physicalFunctionBacking = vim.vm.device.VirtualPCIPassthrough.DeviceBackingInfo()
5953 nic.device.sriovBacking.physicalFunctionBacking.id = sriov_device.id
5954
5955 devices.append(nic)
5956 vmconf = vim.vm.ConfigSpec(deviceChange=devices)
5957 task = vm_obj.ReconfigVM_Task(vmconf)
5958 return task
5959 except Exception as exp:
5960 self.logger.error("Error {} occurred while adding SRIOV adapter in VM: {}".format(exp, vm_obj))
5961 return None
5962
5963
5964 def create_dvPort_group(self, network_name):
5965 """
5966 Method to create disributed virtual portgroup
5967
5968 Args:
5969 network_name - name of network/portgroup
5970
5971 Returns:
5972 portgroup key
5973 """
5974 try:
5975 new_network_name = [network_name, '-', str(uuid.uuid4())]
5976 network_name=''.join(new_network_name)
5977 vcenter_conect, content = self.get_vcenter_content()
5978
5979 dv_switch = self.get_obj(content, [vim.DistributedVirtualSwitch], self.dvs_name)
5980 if dv_switch:
5981 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
5982 dv_pg_spec.name = network_name
5983
5984 dv_pg_spec.type = vim.dvs.DistributedVirtualPortgroup.PortgroupType.earlyBinding
5985 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
5986 dv_pg_spec.defaultPortConfig.securityPolicy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy()
5987 dv_pg_spec.defaultPortConfig.securityPolicy.allowPromiscuous = vim.BoolPolicy(value=False)
5988 dv_pg_spec.defaultPortConfig.securityPolicy.forgedTransmits = vim.BoolPolicy(value=False)
5989 dv_pg_spec.defaultPortConfig.securityPolicy.macChanges = vim.BoolPolicy(value=False)
5990
5991 task = dv_switch.AddDVPortgroup_Task([dv_pg_spec])
5992 self.wait_for_vcenter_task(task, vcenter_conect)
5993
5994 dvPort_group = self.get_obj(content, [vim.dvs.DistributedVirtualPortgroup], network_name)
5995 if dvPort_group:
5996 self.logger.info("Created disributed virtaul port group: {}".format(dvPort_group))
5997 return dvPort_group.key
5998 else:
5999 self.logger.debug("No disributed virtual switch found with name {}".format(network_name))
6000
6001 except Exception as exp:
6002 self.logger.error("Error occurred while creating disributed virtaul port group {}"\
6003 " : {}".format(network_name, exp))
6004 return None
6005
6006 def reconfig_portgroup(self, content, dvPort_group_name , config_info={}):
6007 """
6008 Method to reconfigure disributed virtual portgroup
6009
6010 Args:
6011 dvPort_group_name - name of disributed virtual portgroup
6012 content - vCenter content object
6013 config_info - disributed virtual portgroup configuration
6014
6015 Returns:
6016 task object
6017 """
6018 try:
6019 dvPort_group = self.get_dvport_group(dvPort_group_name)
6020 if dvPort_group:
6021 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
6022 dv_pg_spec.configVersion = dvPort_group.config.configVersion
6023 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
6024 if "vlanID" in config_info:
6025 dv_pg_spec.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec()
6026 dv_pg_spec.defaultPortConfig.vlan.vlanId = config_info.get('vlanID')
6027
6028 task = dvPort_group.ReconfigureDVPortgroup_Task(spec=dv_pg_spec)
6029 return task
6030 else:
6031 return None
6032 except Exception as exp:
6033 self.logger.error("Error occurred while reconfiguraing disributed virtaul port group {}"\
6034 " : {}".format(dvPort_group_name, exp))
6035 return None
6036
6037
6038 def destroy_dvport_group(self , dvPort_group_name):
6039 """
6040 Method to destroy disributed virtual portgroup
6041
6042 Args:
6043 network_name - name of network/portgroup
6044
6045 Returns:
6046 True if portgroup successfully got deleted else false
6047 """
6048 vcenter_conect, content = self.get_vcenter_content()
6049 try:
6050 status = None
6051 dvPort_group = self.get_dvport_group(dvPort_group_name)
6052 if dvPort_group:
6053 task = dvPort_group.Destroy_Task()
6054 status = self.wait_for_vcenter_task(task, vcenter_conect)
6055 return status
6056 except vmodl.MethodFault as exp:
6057 self.logger.error("Caught vmodl fault {} while deleting disributed virtaul port group {}".format(
6058 exp, dvPort_group_name))
6059 return None
6060
6061
6062 def get_dvport_group(self, dvPort_group_name):
6063 """
6064 Method to get disributed virtual portgroup
6065
6066 Args:
6067 network_name - name of network/portgroup
6068
6069 Returns:
6070 portgroup object
6071 """
6072 vcenter_conect, content = self.get_vcenter_content()
6073 dvPort_group = None
6074 try:
6075 container = content.viewManager.CreateContainerView(content.rootFolder, [vim.dvs.DistributedVirtualPortgroup], True)
6076 for item in container.view:
6077 if item.key == dvPort_group_name:
6078 dvPort_group = item
6079 break
6080 return dvPort_group
6081 except vmodl.MethodFault as exp:
6082 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
6083 exp, dvPort_group_name))
6084 return None
6085
6086 def get_vlanID_from_dvs_portgr(self, dvPort_group_name):
6087 """
6088 Method to get disributed virtual portgroup vlanID
6089
6090 Args:
6091 network_name - name of network/portgroup
6092
6093 Returns:
6094 vlan ID
6095 """
6096 vlanId = None
6097 try:
6098 dvPort_group = self.get_dvport_group(dvPort_group_name)
6099 if dvPort_group:
6100 vlanId = dvPort_group.config.defaultPortConfig.vlan.vlanId
6101 except vmodl.MethodFault as exp:
6102 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
6103 exp, dvPort_group_name))
6104 return vlanId
6105
6106
6107 def configure_vlanID(self, content, vcenter_conect, dvPort_group_name):
6108 """
6109 Method to configure vlanID in disributed virtual portgroup vlanID
6110
6111 Args:
6112 network_name - name of network/portgroup
6113
6114 Returns:
6115 None
6116 """
6117 vlanID = self.get_vlanID_from_dvs_portgr(dvPort_group_name)
6118 if vlanID == 0:
6119 #configure vlanID
6120 vlanID = self.genrate_vlanID(dvPort_group_name)
6121 config = {"vlanID":vlanID}
6122 task = self.reconfig_portgroup(content, dvPort_group_name,
6123 config_info=config)
6124 if task:
6125 status= self.wait_for_vcenter_task(task, vcenter_conect)
6126 if status:
6127 self.logger.info("Reconfigured Port group {} for vlan ID {}".format(
6128 dvPort_group_name,vlanID))
6129 else:
6130 self.logger.error("Fail reconfigure portgroup {} for vlanID{}".format(
6131 dvPort_group_name, vlanID))
6132
6133
6134 def genrate_vlanID(self, network_name):
6135 """
6136 Method to get unused vlanID
6137 Args:
6138 network_name - name of network/portgroup
6139 Returns:
6140 vlanID
6141 """
6142 vlan_id = None
6143 used_ids = []
6144 if self.config.get('vlanID_range') == None:
6145 raise vimconn.vimconnConflictException("You must provide a 'vlanID_range' "\
6146 "at config value before creating sriov network with vlan tag")
6147 if "used_vlanIDs" not in self.persistent_info:
6148 self.persistent_info["used_vlanIDs"] = {}
6149 else:
6150 used_ids = self.persistent_info["used_vlanIDs"].values()
6151 #For python3
6152 #used_ids = list(self.persistent_info["used_vlanIDs"].values())
6153
6154 for vlanID_range in self.config.get('vlanID_range'):
6155 start_vlanid , end_vlanid = vlanID_range.split("-")
6156 if start_vlanid > end_vlanid:
6157 raise vimconn.vimconnConflictException("Invalid vlan ID range {}".format(
6158 vlanID_range))
6159
6160 for id in xrange(int(start_vlanid), int(end_vlanid) + 1):
6161 #For python3
6162 #for id in range(int(start_vlanid), int(end_vlanid) + 1):
6163 if id not in used_ids:
6164 vlan_id = id
6165 self.persistent_info["used_vlanIDs"][network_name] = vlan_id
6166 return vlan_id
6167 if vlan_id is None:
6168 raise vimconn.vimconnConflictException("All Vlan IDs are in use")
6169
6170
6171 def get_obj(self, content, vimtype, name):
6172 """
6173 Get the vsphere object associated with a given text name
6174 """
6175 obj = None
6176 container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
6177 for item in container.view:
6178 if item.name == name:
6179 obj = item
6180 break
6181 return obj
6182
6183
6184 def insert_media_to_vm(self, vapp, image_id):
6185 """
6186 Method to insert media CD-ROM (ISO image) from catalog to vm.
6187 vapp - vapp object to get vm id
6188 Image_id - image id for cdrom to be inerted to vm
6189 """
6190 # create connection object
6191 vca = self.connect()
6192 try:
6193 # fetching catalog details
6194 rest_url = "{}/api/catalog/{}".format(self.url, image_id)
6195 if vca._session:
6196 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
6197 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
6198 response = self.perform_request(req_type='GET',
6199 url=rest_url,
6200 headers=headers)
6201
6202 if response.status_code != 200:
6203 self.logger.error("REST call {} failed reason : {}"\
6204 "status code : {}".format(url_rest_call,
6205 response.content,
6206 response.status_code))
6207 raise vimconn.vimconnException("insert_media_to_vm(): Failed to get "\
6208 "catalog details")
6209 # searching iso name and id
6210 iso_name,media_id = self.get_media_details(vca, response.content)
6211
6212 if iso_name and media_id:
6213 data ="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
6214 <ns6:MediaInsertOrEjectParams
6215 xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns6="http://www.vmware.com/vcloud/v1.5" xmlns:ns7="http://www.vmware.com/schema/ovf" xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
6216 <ns6:Media
6217 type="application/vnd.vmware.vcloud.media+xml"
6218 name="{}.iso"
6219 id="urn:vcloud:media:{}"
6220 href="https://{}/api/media/{}"/>
6221 </ns6:MediaInsertOrEjectParams>""".format(iso_name, media_id,
6222 self.url,media_id)
6223
6224 for vms in vapp.get_all_vms():
6225 vm_id = vms.get('id').split(':')[-1]
6226
6227 headers['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
6228 rest_url = "{}/api/vApp/vm-{}/media/action/insertMedia".format(self.url,vm_id)
6229
6230 response = self.perform_request(req_type='POST',
6231 url=rest_url,
6232 data=data,
6233 headers=headers)
6234
6235 if response.status_code != 202:
6236 self.logger.error("Failed to insert CD-ROM to vm")
6237 raise vimconn.vimconnException("insert_media_to_vm() : Failed to insert"\
6238 "ISO image to vm")
6239 else:
6240 task = self.get_task_from_response(response.content)
6241 result = self.client.get_task_monitor().wait_for_success(task=task)
6242 if result.get('status') == 'success':
6243 self.logger.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
6244 " image to vm {}".format(vm_id))
6245
6246 except Exception as exp:
6247 self.logger.error("insert_media_to_vm() : exception occurred "\
6248 "while inserting media CD-ROM")
6249 raise vimconn.vimconnException(message=exp)
6250
6251
6252 def get_media_details(self, vca, content):
6253 """
6254 Method to get catalog item details
6255 vca - connection object
6256 content - Catalog details
6257 Return - Media name, media id
6258 """
6259 cataloghref_list = []
6260 try:
6261 if content:
6262 vm_list_xmlroot = XmlElementTree.fromstring(content)
6263 for child in vm_list_xmlroot.iter():
6264 if 'CatalogItem' in child.tag:
6265 cataloghref_list.append(child.attrib.get('href'))
6266 if cataloghref_list is not None:
6267 for href in cataloghref_list:
6268 if href:
6269 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
6270 'x-vcloud-authorization': vca._session.headers['x-vcloud-authorization']}
6271 response = self.perform_request(req_type='GET',
6272 url=href,
6273 headers=headers)
6274 if response.status_code != 200:
6275 self.logger.error("REST call {} failed reason : {}"\
6276 "status code : {}".format(href,
6277 response.content,
6278 response.status_code))
6279 raise vimconn.vimconnException("get_media_details : Failed to get "\
6280 "catalogitem details")
6281 list_xmlroot = XmlElementTree.fromstring(response.content)
6282 for child in list_xmlroot.iter():
6283 if 'Entity' in child.tag:
6284 if 'media' in child.attrib.get('href'):
6285 name = child.attrib.get('name')
6286 media_id = child.attrib.get('href').split('/').pop()
6287 return name,media_id
6288 else:
6289 self.logger.debug("Media name and id not found")
6290 return False,False
6291 except Exception as exp:
6292 self.logger.error("get_media_details : exception occurred "\
6293 "getting media details")
6294 raise vimconn.vimconnException(message=exp)
6295
6296
6297 def retry_rest(self, method, url, add_headers=None, data=None):
6298 """ Method to get Token & retry respective REST request
6299 Args:
6300 api - REST API - Can be one of 'GET' or 'PUT' or 'POST'
6301 url - request url to be used
6302 add_headers - Additional headers (optional)
6303 data - Request payload data to be passed in request
6304 Returns:
6305 response - Response of request
6306 """
6307 response = None
6308
6309 #Get token
6310 self.get_token()
6311
6312 if self.client._session:
6313 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
6314 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
6315
6316 if add_headers:
6317 headers.update(add_headers)
6318
6319 if method == 'GET':
6320 response = self.perform_request(req_type='GET',
6321 url=url,
6322 headers=headers)
6323 elif method == 'PUT':
6324 response = self.perform_request(req_type='PUT',
6325 url=url,
6326 headers=headers,
6327 data=data)
6328 elif method == 'POST':
6329 response = self.perform_request(req_type='POST',
6330 url=url,
6331 headers=headers,
6332 data=data)
6333 elif method == 'DELETE':
6334 response = self.perform_request(req_type='DELETE',
6335 url=url,
6336 headers=headers)
6337 return response
6338
6339
6340 def get_token(self):
6341 """ Generate a new token if expired
6342
6343 Returns:
6344 The return client object that letter can be used to connect to vCloud director as admin for VDC
6345 """
6346 try:
6347 self.logger.debug("Generate token for vca {} as {} to datacenter {}.".format(self.org_name,
6348 self.user,
6349 self.org_name))
6350 host = self.url
6351 client = Client(host, verify_ssl_certs=False)
6352 client.set_credentials(BasicLoginCredentials(self.user, self.org_name, self.passwd))
6353 # connection object
6354 self.client = client
6355
6356 except:
6357 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
6358 "{} as user: {}".format(self.org_name, self.user))
6359
6360 if not client:
6361 raise vimconn.vimconnConnectionException("Failed while reconnecting vCD")
6362
6363
6364 def get_vdc_details(self):
6365 """ Get VDC details using pyVcloud Lib
6366
6367 Returns org and vdc object
6368 """
6369 vdc = None
6370 try:
6371 org = Org(self.client, resource=self.client.get_org())
6372 vdc = org.get_vdc(self.tenant_name)
6373 except Exception as e:
6374 # pyvcloud not giving a specific exception, Refresh nevertheless
6375 self.logger.debug("Received exception {}, refreshing token ".format(str(e)))
6376
6377 #Retry once, if failed by refreshing token
6378 if vdc is None:
6379 self.get_token()
6380 org = Org(self.client, resource=self.client.get_org())
6381 vdc = org.get_vdc(self.tenant_name)
6382
6383 return org, vdc
6384
6385
6386 def perform_request(self, req_type, url, headers=None, data=None):
6387 """Perform the POST/PUT/GET/DELETE request."""
6388
6389 #Log REST request details
6390 self.log_request(req_type, url=url, headers=headers, data=data)
6391 # perform request and return its result
6392 if req_type == 'GET':
6393 response = requests.get(url=url,
6394 headers=headers,
6395 verify=False)
6396 elif req_type == 'PUT':
6397 response = requests.put(url=url,
6398 headers=headers,
6399 data=data,
6400 verify=False)
6401 elif req_type == 'POST':
6402 response = requests.post(url=url,
6403 headers=headers,
6404 data=data,
6405 verify=False)
6406 elif req_type == 'DELETE':
6407 response = requests.delete(url=url,
6408 headers=headers,
6409 verify=False)
6410 #Log the REST response
6411 self.log_response(response)
6412
6413 return response
6414
6415
6416 def log_request(self, req_type, url=None, headers=None, data=None):
6417 """Logs REST request details"""
6418
6419 if req_type is not None:
6420 self.logger.debug("Request type: {}".format(req_type))
6421
6422 if url is not None:
6423 self.logger.debug("Request url: {}".format(url))
6424
6425 if headers is not None:
6426 for header in headers:
6427 self.logger.debug("Request header: {}: {}".format(header, headers[header]))
6428
6429 if data is not None:
6430 self.logger.debug("Request data: {}".format(data))
6431
6432
6433 def log_response(self, response):
6434 """Logs REST response details"""
6435
6436 self.logger.debug("Response status code: {} ".format(response.status_code))
6437
6438
6439 def get_task_from_response(self, content):
6440 """
6441 content - API response content(response.content)
6442 return task object
6443 """
6444 xmlroot = XmlElementTree.fromstring(content)
6445 if xmlroot.tag.split('}')[1] == "Task":
6446 return xmlroot
6447 else:
6448 for ele in xmlroot:
6449 if ele.tag.split("}")[1] == "Tasks":
6450 task = ele[0]
6451 break
6452 return task
6453
6454
6455 def power_on_vapp(self,vapp_id, vapp_name):
6456 """
6457 vapp_id - vApp uuid
6458 vapp_name - vAapp name
6459 return - Task object
6460 """
6461 headers = {'Accept':'application/*+xml;version=' + API_VERSION,
6462 'x-vcloud-authorization': self.client._session.headers['x-vcloud-authorization']}
6463
6464 poweron_href = "{}/api/vApp/vapp-{}/power/action/powerOn".format(self.url,
6465 vapp_id)
6466 response = self.perform_request(req_type='POST',
6467 url=poweron_href,
6468 headers=headers)
6469
6470 if response.status_code != 202:
6471 self.logger.error("REST call {} failed reason : {}"\
6472 "status code : {} ".format(poweron_href,
6473 response.content,
6474 response.status_code))
6475 raise vimconn.vimconnException("power_on_vapp() : Failed to power on "\
6476 "vApp {}".format(vapp_name))
6477 else:
6478 poweron_task = self.get_task_from_response(response.content)
6479 return poweron_task
6480
6481