Added validation in new_flavor method and unit tests for new_flavor,delete_flavor...
[osm/RO.git] / osm_ro / vimconn_vmware.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
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 with: nfvlabs@tid.es
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 import Http
46 from pyvcloud.vcloudair import VCA
47 from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
48 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
49 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
50 from xml.sax.saxutils import escape
51
52 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
53 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
54 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
55 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
56
57 import logging
58 import json
59 import time
60 import uuid
61 import httplib
62 import hashlib
63 import socket
64 import struct
65 import netaddr
66 import random
67
68 # global variable for vcd connector type
69 STANDALONE = 'standalone'
70
71 # key for flavor dicts
72 FLAVOR_RAM_KEY = 'ram'
73 FLAVOR_VCPUS_KEY = 'vcpus'
74 FLAVOR_DISK_KEY = 'disk'
75 DEFAULT_IP_PROFILE = {'dhcp_count':50,
76 'dhcp_enabled':True,
77 'ip_version':"IPv4"
78 }
79 # global variable for wait time
80 INTERVAL_TIME = 5
81 MAX_WAIT_TIME = 1800
82
83 VCAVERSION = '5.9'
84
85 __author__ = "Mustafa Bayramov, Arpita Kate, Sachin Bhangare"
86 __date__ = "$12-Jan-2017 11:09:29$"
87 __version__ = '0.1'
88
89 # -1: "Could not be created",
90 # 0: "Unresolved",
91 # 1: "Resolved",
92 # 2: "Deployed",
93 # 3: "Suspended",
94 # 4: "Powered on",
95 # 5: "Waiting for user input",
96 # 6: "Unknown state",
97 # 7: "Unrecognized state",
98 # 8: "Powered off",
99 # 9: "Inconsistent state",
100 # 10: "Children do not all have the same status",
101 # 11: "Upload initiated, OVF descriptor pending",
102 # 12: "Upload initiated, copying contents",
103 # 13: "Upload initiated , disk contents pending",
104 # 14: "Upload has been quarantined",
105 # 15: "Upload quarantine period has expired"
106
107 # mapping vCD status to MANO
108 vcdStatusCode2manoFormat = {4: 'ACTIVE',
109 7: 'PAUSED',
110 3: 'SUSPENDED',
111 8: 'INACTIVE',
112 12: 'BUILD',
113 -1: 'ERROR',
114 14: 'DELETED'}
115
116 #
117 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
118 'ERROR': 'ERROR', 'DELETED': 'DELETED'
119 }
120
121 class vimconnector(vimconn.vimconnector):
122 # dict used to store flavor in memory
123 flavorlist = {}
124
125 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
126 url=None, url_admin=None, user=None, passwd=None, log_level=None, config={}, persistent_info={}):
127 """
128 Constructor create vmware connector to vCloud director.
129
130 By default construct doesn't validate connection state. So client can create object with None arguments.
131 If client specified username , password and host and VDC name. Connector initialize other missing attributes.
132
133 a) It initialize organization UUID
134 b) Initialize tenant_id/vdc ID. (This information derived from tenant name)
135
136 Args:
137 uuid - is organization uuid.
138 name - is organization name that must be presented in vCloud director.
139 tenant_id - is VDC uuid it must be presented in vCloud director
140 tenant_name - is VDC name.
141 url - is hostname or ip address of vCloud director
142 url_admin - same as above.
143 user - is user that administrator for organization. Caller must make sure that
144 username has right privileges.
145
146 password - is password for a user.
147
148 VMware connector also requires PVDC administrative privileges and separate account.
149 This variables must be passed via config argument dict contains keys
150
151 dict['admin_username']
152 dict['admin_password']
153 config - Provide NSX and vCenter information
154
155 Returns:
156 Nothing.
157 """
158
159 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
160 url_admin, user, passwd, log_level, config)
161
162 self.logger = logging.getLogger('openmano.vim.vmware')
163 self.logger.setLevel(10)
164 self.persistent_info = persistent_info
165
166 self.name = name
167 self.id = uuid
168 self.url = url
169 self.url_admin = url_admin
170 self.tenant_id = tenant_id
171 self.tenant_name = tenant_name
172 self.user = user
173 self.passwd = passwd
174 self.config = config
175 self.admin_password = None
176 self.admin_user = None
177 self.org_name = ""
178 self.nsx_manager = None
179 self.nsx_user = None
180 self.nsx_password = None
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 # ############# Stub code for SRIOV #################
214 # try:
215 # self.dvs_name = config['dv_switch_name']
216 # except KeyError:
217 # raise vimconn.vimconnException(message="Error: distributed virtaul switch name is empty in Config")
218 #
219 # self.vlanID_range = config.get("vlanID_range", None)
220
221 self.org_uuid = None
222 self.vca = None
223
224 if not url:
225 raise vimconn.vimconnException('url param can not be NoneType')
226
227 if not self.url_admin: # try to use normal url
228 self.url_admin = self.url
229
230 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.org_name,
231 self.tenant_id, self.tenant_name))
232 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
233 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
234
235 # initialize organization
236 if self.user is not None and self.passwd is not None and self.url:
237 self.init_organization()
238
239 def __getitem__(self, index):
240 if index == 'name':
241 return self.name
242 if index == 'tenant_id':
243 return self.tenant_id
244 if index == 'tenant_name':
245 return self.tenant_name
246 elif index == 'id':
247 return self.id
248 elif index == 'org_name':
249 return self.org_name
250 elif index == 'org_uuid':
251 return self.org_uuid
252 elif index == 'user':
253 return self.user
254 elif index == 'passwd':
255 return self.passwd
256 elif index == 'url':
257 return self.url
258 elif index == 'url_admin':
259 return self.url_admin
260 elif index == "config":
261 return self.config
262 else:
263 raise KeyError("Invalid key '%s'" % str(index))
264
265 def __setitem__(self, index, value):
266 if index == 'name':
267 self.name = value
268 if index == 'tenant_id':
269 self.tenant_id = value
270 if index == 'tenant_name':
271 self.tenant_name = value
272 elif index == 'id':
273 self.id = value
274 elif index == 'org_name':
275 self.org_name = value
276 elif index == 'org_uuid':
277 self.org_uuid = value
278 elif index == 'user':
279 self.user = value
280 elif index == 'passwd':
281 self.passwd = value
282 elif index == 'url':
283 self.url = value
284 elif index == 'url_admin':
285 self.url_admin = value
286 else:
287 raise KeyError("Invalid key '%s'" % str(index))
288
289 def connect_as_admin(self):
290 """ Method connect as pvdc admin user to vCloud director.
291 There are certain action that can be done only by provider vdc admin user.
292 Organization creation / provider network creation etc.
293
294 Returns:
295 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
296 """
297
298 self.logger.debug("Logging in to a vca {} as admin.".format(self.org_name))
299
300 vca_admin = VCA(host=self.url,
301 username=self.admin_user,
302 service_type=STANDALONE,
303 version=VCAVERSION,
304 verify=False,
305 log=False)
306 result = vca_admin.login(password=self.admin_password, org='System')
307 if not result:
308 raise vimconn.vimconnConnectionException(
309 "Can't connect to a vCloud director as: {}".format(self.admin_user))
310 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
311 if result is True:
312 self.logger.info(
313 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
314
315 return vca_admin
316
317 def connect(self):
318 """ Method connect as normal user to vCloud director.
319
320 Returns:
321 The return vca object that letter can be used to connect to vCloud director as admin for VDC
322 """
323
324 try:
325 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.org_name,
326 self.user,
327 self.org_name))
328 vca = VCA(host=self.url,
329 username=self.user,
330 service_type=STANDALONE,
331 version=VCAVERSION,
332 verify=False,
333 log=False)
334
335 result = vca.login(password=self.passwd, org=self.org_name)
336 if not result:
337 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
338 result = vca.login(token=vca.token, org=self.org_name, org_url=vca.vcloud_session.org_url)
339 if result is True:
340 self.logger.info(
341 "Successfully logged to a vcloud direct org: {} as user: {}".format(self.org_name, self.user))
342
343 except:
344 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director org: "
345 "{} as user: {}".format(self.org_name, self.user))
346
347 return vca
348
349 def init_organization(self):
350 """ Method initialize organization UUID and VDC parameters.
351
352 At bare minimum client must provide organization name that present in vCloud director and VDC.
353
354 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
355 The Org - UUID will be initialized at the run time if data center present in vCloud director.
356
357 Returns:
358 The return vca object that letter can be used to connect to vcloud direct as admin
359 """
360 try:
361 if self.org_uuid is None:
362 org_dict = self.get_org_list()
363 for org in org_dict:
364 # we set org UUID at the init phase but we can do it only when we have valid credential.
365 if org_dict[org] == self.org_name:
366 self.org_uuid = org
367 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
368 break
369 else:
370 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
371
372 # if well good we require for org details
373 org_details_dict = self.get_org(org_uuid=self.org_uuid)
374
375 # we have two case if we want to initialize VDC ID or VDC name at run time
376 # tenant_name provided but no tenant id
377 if self.tenant_id is None and self.tenant_name is not None and 'vdcs' in org_details_dict:
378 vdcs_dict = org_details_dict['vdcs']
379 for vdc in vdcs_dict:
380 if vdcs_dict[vdc] == self.tenant_name:
381 self.tenant_id = vdc
382 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
383 self.org_name))
384 break
385 else:
386 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
387 # case two we have tenant_id but we don't have tenant name so we find and set it.
388 if self.tenant_id is not None and self.tenant_name is None and 'vdcs' in org_details_dict:
389 vdcs_dict = org_details_dict['vdcs']
390 for vdc in vdcs_dict:
391 if vdc == self.tenant_id:
392 self.tenant_name = vdcs_dict[vdc]
393 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
394 self.org_name))
395 break
396 else:
397 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
398 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
399 except:
400 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
401 self.logger.debug(traceback.format_exc())
402 self.org_uuid = None
403
404 def new_tenant(self, tenant_name=None, tenant_description=None):
405 """ Method adds a new tenant to VIM with this name.
406 This action requires access to create VDC action in vCloud director.
407
408 Args:
409 tenant_name is tenant_name to be created.
410 tenant_description not used for this call
411
412 Return:
413 returns the tenant identifier in UUID format.
414 If action is failed method will throw vimconn.vimconnException method
415 """
416 vdc_task = self.create_vdc(vdc_name=tenant_name)
417 if vdc_task is not None:
418 vdc_uuid, value = vdc_task.popitem()
419 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
420 return vdc_uuid
421 else:
422 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
423
424 def delete_tenant(self, tenant_id=None):
425 """Delete a tenant from VIM"""
426 'Returns the tenant identifier'
427 raise vimconn.vimconnNotImplemented("Should have implemented this")
428
429 def get_tenant_list(self, filter_dict={}):
430 """Obtain tenants of VIM
431 filter_dict can contain the following keys:
432 name: filter by tenant name
433 id: filter by tenant uuid/id
434 <other VIM specific>
435 Returns the tenant list of dictionaries:
436 [{'name':'<name>, 'id':'<id>, ...}, ...]
437
438 """
439 org_dict = self.get_org(self.org_uuid)
440 vdcs_dict = org_dict['vdcs']
441
442 vdclist = []
443 try:
444 for k in vdcs_dict:
445 entry = {'name': vdcs_dict[k], 'id': k}
446 # if caller didn't specify dictionary we return all tenants.
447 if filter_dict is not None and filter_dict:
448 filtered_entry = entry.copy()
449 filtered_dict = set(entry.keys()) - set(filter_dict)
450 for unwanted_key in filtered_dict: del entry[unwanted_key]
451 if filter_dict == entry:
452 vdclist.append(filtered_entry)
453 else:
454 vdclist.append(entry)
455 except:
456 self.logger.debug("Error in get_tenant_list()")
457 self.logger.debug(traceback.format_exc())
458 raise vimconn.vimconnException("Incorrect state. {}")
459
460 return vdclist
461
462 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
463 """Adds a tenant network to VIM
464 net_name is the name
465 net_type can be 'bridge','data'.'ptp'.
466 ip_profile is a dict containing the IP parameters of the network
467 shared is a boolean
468 Returns the network identifier"""
469
470 self.logger.debug("new_network tenant {} net_type {} ip_profile {} shared {}"
471 .format(net_name, net_type, ip_profile, shared))
472
473 isshared = 'false'
474 if shared:
475 isshared = 'true'
476
477 # ############# Stub code for SRIOV #################
478 # if net_type == "data" or net_type == "ptp":
479 # if self.config.get('dv_switch_name') == None:
480 # raise vimconn.vimconnConflictException("You must provide 'dv_switch_name' at config value")
481 # network_uuid = self.create_dvPort_group(net_name)
482
483 network_uuid = self.create_network(network_name=net_name, net_type=net_type,
484 ip_profile=ip_profile, isshared=isshared)
485 if network_uuid is not None:
486 return network_uuid
487 else:
488 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
489
490 def get_vcd_network_list(self):
491 """ Method available organization for a logged in tenant
492
493 Returns:
494 The return vca object that letter can be used to connect to vcloud direct as admin
495 """
496
497 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
498 vca = self.connect()
499 if not vca:
500 raise vimconn.vimconnConnectionException("self.connect() is failed.")
501
502 if not self.tenant_name:
503 raise vimconn.vimconnConnectionException("Tenant name is empty.")
504
505 vdc = vca.get_vdc(self.tenant_name)
506 if vdc is None:
507 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}".format(self.tenant_name))
508
509 vdc_uuid = vdc.get_id().split(":")[3]
510 networks = vca.get_networks(vdc.get_name())
511 network_list = []
512 try:
513 for network in networks:
514 filter_dict = {}
515 netid = network.get_id().split(":")
516 if len(netid) != 4:
517 continue
518
519 filter_dict["name"] = network.get_name()
520 filter_dict["id"] = netid[3]
521 filter_dict["shared"] = network.get_IsShared()
522 filter_dict["tenant_id"] = vdc_uuid
523 if network.get_status() == 1:
524 filter_dict["admin_state_up"] = True
525 else:
526 filter_dict["admin_state_up"] = False
527 filter_dict["status"] = "ACTIVE"
528 filter_dict["type"] = "bridge"
529 network_list.append(filter_dict)
530 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
531 except:
532 self.logger.debug("Error in get_vcd_network_list")
533 self.logger.debug(traceback.format_exc())
534 pass
535
536 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
537 return network_list
538
539 def get_network_list(self, filter_dict={}):
540 """Obtain tenant networks of VIM
541 Filter_dict can be:
542 name: network name OR/AND
543 id: network uuid OR/AND
544 shared: boolean OR/AND
545 tenant_id: tenant OR/AND
546 admin_state_up: boolean
547 status: 'ACTIVE'
548
549 [{key : value , key : value}]
550
551 Returns the network list of dictionaries:
552 [{<the fields at Filter_dict plus some VIM specific>}, ...]
553 List can be empty
554 """
555
556 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd {}".format(self.tenant_name))
557 vca = self.connect()
558 if not vca:
559 raise vimconn.vimconnConnectionException("self.connect() is failed.")
560
561 if not self.tenant_name:
562 raise vimconn.vimconnConnectionException("Tenant name is empty.")
563
564 vdc = vca.get_vdc(self.tenant_name)
565 if vdc is None:
566 raise vimconn.vimconnConnectionException("Can't retrieve information for a VDC {}.".format(self.tenant_name))
567
568 try:
569 vdcid = vdc.get_id().split(":")[3]
570 networks = vca.get_networks(vdc.get_name())
571 network_list = []
572
573 for network in networks:
574 filter_entry = {}
575 net_uuid = network.get_id().split(":")
576 if len(net_uuid) != 4:
577 continue
578 else:
579 net_uuid = net_uuid[3]
580 # create dict entry
581 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
582 vdcid,
583 network.get_name()))
584 filter_entry["name"] = network.get_name()
585 filter_entry["id"] = net_uuid
586 filter_entry["shared"] = network.get_IsShared()
587 filter_entry["tenant_id"] = vdcid
588 if network.get_status() == 1:
589 filter_entry["admin_state_up"] = True
590 else:
591 filter_entry["admin_state_up"] = False
592 filter_entry["status"] = "ACTIVE"
593 filter_entry["type"] = "bridge"
594 filtered_entry = filter_entry.copy()
595
596 if filter_dict is not None and filter_dict:
597 # we remove all the key : value we don't care and match only
598 # respected field
599 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
600 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
601 if filter_dict == filter_entry:
602 network_list.append(filtered_entry)
603 else:
604 network_list.append(filtered_entry)
605 except:
606 self.logger.debug("Error in get_vcd_network_list")
607 self.logger.debug(traceback.format_exc())
608
609 self.logger.debug("Returning {}".format(network_list))
610 return network_list
611
612 def get_network(self, net_id):
613 """Method obtains network details of net_id VIM network
614 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
615
616 vca = self.connect()
617 if not vca:
618 raise vimconn.vimconnConnectionException("self.connect() is failed")
619
620 try:
621 vdc = vca.get_vdc(self.tenant_name)
622 vdc_id = vdc.get_id().split(":")[3]
623
624 networks = vca.get_networks(vdc.get_name())
625 filter_dict = {}
626
627 for network in networks:
628 vdc_network_id = network.get_id().split(":")
629 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
630 filter_dict["name"] = network.get_name()
631 filter_dict["id"] = vdc_network_id[3]
632 filter_dict["shared"] = network.get_IsShared()
633 filter_dict["tenant_id"] = vdc_id
634 if network.get_status() == 1:
635 filter_dict["admin_state_up"] = True
636 else:
637 filter_dict["admin_state_up"] = False
638 filter_dict["status"] = "ACTIVE"
639 filter_dict["type"] = "bridge"
640 self.logger.debug("Returning {}".format(filter_dict))
641 return filter_dict
642 except:
643 self.logger.debug("Error in get_network")
644 self.logger.debug(traceback.format_exc())
645
646 return filter_dict
647
648 def delete_network(self, net_id):
649 """
650 Method Deletes a tenant network from VIM, provide the network id.
651
652 Returns the network identifier or raise an exception
653 """
654
655 vca = self.connect()
656 if not vca:
657 raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed.".format(self.tenant_name))
658
659 # ############# Stub code for SRIOV #################
660 # dvport_group = self.get_dvport_group(net_id)
661 # if dvport_group:
662 # #delete portgroup
663 # status = self.destroy_dvport_group(net_id)
664 # if status:
665 # # Remove vlanID from persistent info
666 # if net_id in self.persistent_info["used_vlanIDs"]:
667 # del self.persistent_info["used_vlanIDs"][net_id]
668 #
669 # return net_id
670
671 vcd_network = self.get_vcd_network(network_uuid=net_id)
672 if vcd_network is not None and vcd_network:
673 if self.delete_network_action(network_uuid=net_id):
674 return net_id
675 else:
676 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
677
678 def refresh_nets_status(self, net_list):
679 """Get the status of the networks
680 Params: the list of network identifiers
681 Returns a dictionary with:
682 net_id: #VIM id of this network
683 status: #Mandatory. Text with one of:
684 # DELETED (not found at vim)
685 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
686 # OTHER (Vim reported other status not understood)
687 # ERROR (VIM indicates an ERROR status)
688 # ACTIVE, INACTIVE, DOWN (admin down),
689 # BUILD (on building process)
690 #
691 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
692 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
693
694 """
695
696 vca = self.connect()
697 if not vca:
698 raise vimconn.vimconnConnectionException("self.connect() is failed")
699
700 dict_entry = {}
701 try:
702 for net in net_list:
703 errormsg = ''
704 vcd_network = self.get_vcd_network(network_uuid=net)
705 if vcd_network is not None and vcd_network:
706 if vcd_network['status'] == '1':
707 status = 'ACTIVE'
708 else:
709 status = 'DOWN'
710 else:
711 status = 'DELETED'
712 errormsg = 'Network not found.'
713
714 dict_entry[net] = {'status': status, 'error_msg': errormsg,
715 'vim_info': yaml.safe_dump(vcd_network)}
716 except:
717 self.logger.debug("Error in refresh_nets_status")
718 self.logger.debug(traceback.format_exc())
719
720 return dict_entry
721
722 def get_flavor(self, flavor_id):
723 """Obtain flavor details from the VIM
724 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
725 """
726 if flavor_id not in vimconnector.flavorlist:
727 raise vimconn.vimconnNotFoundException("Flavor not found.")
728 return vimconnector.flavorlist[flavor_id]
729
730 def new_flavor(self, flavor_data):
731 """Adds a tenant flavor to VIM
732 flavor_data contains a dictionary with information, keys:
733 name: flavor name
734 ram: memory (cloud type) in MBytes
735 vpcus: cpus (cloud type)
736 extended: EPA parameters
737 - numas: #items requested in same NUMA
738 memory: number of 1G huge pages memory
739 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
740 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
741 - name: interface name
742 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
743 bandwidth: X Gbps; requested guarantee bandwidth
744 vpci: requested virtual PCI address
745 disk: disk size
746 is_public:
747 #TODO to concrete
748 Returns the flavor identifier"""
749
750 # generate a new uuid put to internal dict and return it.
751 self.logger.debug("Creating new flavor - flavor_data: {}".format(flavor_data))
752 new_flavor=flavor_data
753 ram = flavor_data.get(FLAVOR_RAM_KEY, 1024)
754 cpu = flavor_data.get(FLAVOR_VCPUS_KEY, 1)
755 disk = flavor_data.get(FLAVOR_DISK_KEY, 1)
756
757 if not isinstance(ram, int):
758 raise vimconn.vimconnException("Non-integer value for ram")
759 elif not isinstance(cpu, int):
760 raise vimconn.vimconnException("Non-integer value for cpu")
761 elif not isinstance(disk, int):
762 raise vimconn.vimconnException("Non-integer value for disk")
763
764 extended_flv = flavor_data.get("extended")
765 if extended_flv:
766 numas=extended_flv.get("numas")
767 if numas:
768 for numa in numas:
769 #overwrite ram and vcpus
770 ram = numa['memory']*1024
771 if 'paired-threads' in numa:
772 cpu = numa['paired-threads']*2
773 elif 'cores' in numa:
774 cpu = numa['cores']
775 elif 'threads' in numa:
776 cpu = numa['threads']
777
778 new_flavor[FLAVOR_RAM_KEY] = ram
779 new_flavor[FLAVOR_VCPUS_KEY] = cpu
780 new_flavor[FLAVOR_DISK_KEY] = disk
781 # generate a new uuid put to internal dict and return it.
782 flavor_id = uuid.uuid4()
783 vimconnector.flavorlist[str(flavor_id)] = new_flavor
784 self.logger.debug("Created flavor - {} : {}".format(flavor_id, new_flavor))
785
786 return str(flavor_id)
787
788 def delete_flavor(self, flavor_id):
789 """Deletes a tenant flavor from VIM identify by its id
790
791 Returns the used id or raise an exception
792 """
793 if flavor_id not in vimconnector.flavorlist:
794 raise vimconn.vimconnNotFoundException("Flavor not found.")
795
796 vimconnector.flavorlist.pop(flavor_id, None)
797 return flavor_id
798
799 def new_image(self, image_dict):
800 """
801 Adds a tenant image to VIM
802 Returns:
803 200, image-id if the image is created
804 <0, message if there is an error
805 """
806
807 return self.get_image_id_from_path(image_dict['location'])
808
809 def delete_image(self, image_id):
810 """
811
812 :param image_id:
813 :return:
814 """
815
816 raise vimconn.vimconnNotImplemented("Should have implemented this")
817
818 def catalog_exists(self, catalog_name, catalogs):
819 """
820
821 :param catalog_name:
822 :param catalogs:
823 :return:
824 """
825 for catalog in catalogs:
826 if catalog.name == catalog_name:
827 return True
828 return False
829
830 def create_vimcatalog(self, vca=None, catalog_name=None):
831 """ Create new catalog entry in vCloud director.
832
833 Args
834 vca: vCloud director.
835 catalog_name catalog that client wish to create. Note no validation done for a name.
836 Client must make sure that provide valid string representation.
837
838 Return (bool) True if catalog created.
839
840 """
841 try:
842 task = vca.create_catalog(catalog_name, catalog_name)
843 result = vca.block_until_completed(task)
844 if not result:
845 return False
846 catalogs = vca.get_catalogs()
847 except:
848 return False
849 return self.catalog_exists(catalog_name, catalogs)
850
851 # noinspection PyIncorrectDocstring
852 def upload_ovf(self, vca=None, catalog_name=None, image_name=None, media_file_name=None,
853 description='', progress=False, chunk_bytes=128 * 1024):
854 """
855 Uploads a OVF file to a vCloud catalog
856
857 :param chunk_bytes:
858 :param progress:
859 :param description:
860 :param image_name:
861 :param vca:
862 :param catalog_name: (str): The name of the catalog to upload the media.
863 :param media_file_name: (str): The name of the local media file to upload.
864 :return: (bool) True if the media file was successfully uploaded, false otherwise.
865 """
866 os.path.isfile(media_file_name)
867 statinfo = os.stat(media_file_name)
868
869 # find a catalog entry where we upload OVF.
870 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
871 # status change.
872 # if VCD can parse OVF we upload VMDK file
873 try:
874 for catalog in vca.get_catalogs():
875 if catalog_name != catalog.name:
876 continue
877 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
878 link.get_rel() == 'add', catalog.get_Link())
879 assert len(link) == 1
880 data = """
881 <UploadVAppTemplateParams name="%s" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
882 """ % (escape(catalog_name), escape(description))
883 headers = vca.vcloud_session.get_vcloud_headers()
884 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
885 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
886 if response.status_code == requests.codes.created:
887 catalogItem = XmlElementTree.fromstring(response.content)
888 entity = [child for child in catalogItem if
889 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
890 href = entity.get('href')
891 template = href
892 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
893 verify=vca.verify, logger=self.logger)
894
895 if response.status_code == requests.codes.ok:
896 media = mediaType.parseString(response.content, True)
897 link = filter(lambda link: link.get_rel() == 'upload:default',
898 media.get_Files().get_File()[0].get_Link())[0]
899 headers = vca.vcloud_session.get_vcloud_headers()
900 headers['Content-Type'] = 'Content-Type text/xml'
901 response = Http.put(link.get_href(),
902 data=open(media_file_name, 'rb'),
903 headers=headers,
904 verify=vca.verify, logger=self.logger)
905 if response.status_code != requests.codes.ok:
906 self.logger.debug(
907 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
908 media_file_name))
909 return False
910
911 # TODO fix this with aync block
912 time.sleep(5)
913
914 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
915
916 # uploading VMDK file
917 # check status of OVF upload and upload remaining files.
918 response = Http.get(template,
919 headers=vca.vcloud_session.get_vcloud_headers(),
920 verify=vca.verify,
921 logger=self.logger)
922
923 if response.status_code == requests.codes.ok:
924 media = mediaType.parseString(response.content, True)
925 number_of_files = len(media.get_Files().get_File())
926 for index in xrange(0, number_of_files):
927 links_list = filter(lambda link: link.get_rel() == 'upload:default',
928 media.get_Files().get_File()[index].get_Link())
929 for link in links_list:
930 # we skip ovf since it already uploaded.
931 if 'ovf' in link.get_href():
932 continue
933 # The OVF file and VMDK must be in a same directory
934 head, tail = os.path.split(media_file_name)
935 file_vmdk = head + '/' + link.get_href().split("/")[-1]
936 if not os.path.isfile(file_vmdk):
937 return False
938 statinfo = os.stat(file_vmdk)
939 if statinfo.st_size == 0:
940 return False
941 hrefvmdk = link.get_href()
942
943 if progress:
944 print("Uploading file: {}".format(file_vmdk))
945 if progress:
946 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
947 FileTransferSpeed()]
948 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
949
950 bytes_transferred = 0
951 f = open(file_vmdk, 'rb')
952 while bytes_transferred < statinfo.st_size:
953 my_bytes = f.read(chunk_bytes)
954 if len(my_bytes) <= chunk_bytes:
955 headers = vca.vcloud_session.get_vcloud_headers()
956 headers['Content-Range'] = 'bytes %s-%s/%s' % (
957 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
958 headers['Content-Length'] = str(len(my_bytes))
959 response = Http.put(hrefvmdk,
960 headers=headers,
961 data=my_bytes,
962 verify=vca.verify,
963 logger=None)
964
965 if response.status_code == requests.codes.ok:
966 bytes_transferred += len(my_bytes)
967 if progress:
968 progress_bar.update(bytes_transferred)
969 else:
970 self.logger.debug(
971 'file upload failed with error: [%s] %s' % (response.status_code,
972 response.content))
973
974 f.close()
975 return False
976 f.close()
977 if progress:
978 progress_bar.finish()
979 time.sleep(10)
980 return True
981 else:
982 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
983 format(catalog_name, media_file_name))
984 return False
985 except Exception as exp:
986 self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
987 .format(catalog_name,media_file_name, exp))
988 raise vimconn.vimconnException(
989 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
990 .format(catalog_name,media_file_name, exp))
991
992 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
993 return False
994
995 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
996 """Upload media file"""
997 # TODO add named parameters for readability
998
999 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
1000 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
1001
1002 def validate_uuid4(self, uuid_string=None):
1003 """ Method validate correct format of UUID.
1004
1005 Return: true if string represent valid uuid
1006 """
1007 try:
1008 val = uuid.UUID(uuid_string, version=4)
1009 except ValueError:
1010 return False
1011 return True
1012
1013 def get_catalogid(self, catalog_name=None, catalogs=None):
1014 """ Method check catalog and return catalog ID in UUID format.
1015
1016 Args
1017 catalog_name: catalog name as string
1018 catalogs: list of catalogs.
1019
1020 Return: catalogs uuid
1021 """
1022
1023 for catalog in catalogs:
1024 if catalog.name == catalog_name:
1025 catalog_id = catalog.get_id().split(":")
1026 return catalog_id[3]
1027 return None
1028
1029 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
1030 """ Method check catalog and return catalog name lookup done by catalog UUID.
1031
1032 Args
1033 catalog_name: catalog name as string
1034 catalogs: list of catalogs.
1035
1036 Return: catalogs name or None
1037 """
1038
1039 if not self.validate_uuid4(uuid_string=catalog_uuid):
1040 return None
1041
1042 for catalog in catalogs:
1043 catalog_id = catalog.get_id().split(":")[3]
1044 if catalog_id == catalog_uuid:
1045 return catalog.name
1046 return None
1047
1048 def get_catalog_obj(self, catalog_uuid=None, catalogs=None):
1049 """ Method check catalog and return catalog name lookup done by catalog UUID.
1050
1051 Args
1052 catalog_name: catalog name as string
1053 catalogs: list of catalogs.
1054
1055 Return: catalogs name or None
1056 """
1057
1058 if not self.validate_uuid4(uuid_string=catalog_uuid):
1059 return None
1060
1061 for catalog in catalogs:
1062 catalog_id = catalog.get_id().split(":")[3]
1063 if catalog_id == catalog_uuid:
1064 return catalog
1065 return None
1066
1067 def get_image_id_from_path(self, path=None, progress=False):
1068 """ Method upload OVF image to vCloud director.
1069
1070 Each OVF image represented as single catalog entry in vcloud director.
1071 The method check for existing catalog entry. The check done by file name without file extension.
1072
1073 if given catalog name already present method will respond with existing catalog uuid otherwise
1074 it will create new catalog entry and upload OVF file to newly created catalog.
1075
1076 If method can't create catalog entry or upload a file it will throw exception.
1077
1078 Method accept boolean flag progress that will output progress bar. It useful method
1079 for standalone upload use case. In case to test large file upload.
1080
1081 Args
1082 path: - valid path to OVF file.
1083 progress - boolean progress bar show progress bar.
1084
1085 Return: if image uploaded correct method will provide image catalog UUID.
1086 """
1087 vca = self.connect()
1088 if not vca:
1089 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1090
1091 if not path:
1092 raise vimconn.vimconnException("Image path can't be None.")
1093
1094 if not os.path.isfile(path):
1095 raise vimconn.vimconnException("Can't read file. File not found.")
1096
1097 if not os.access(path, os.R_OK):
1098 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1099
1100 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1101
1102 dirpath, filename = os.path.split(path)
1103 flname, file_extension = os.path.splitext(path)
1104 if file_extension != '.ovf':
1105 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1106 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1107
1108 catalog_name = os.path.splitext(filename)[0]
1109 catalog_md5_name = hashlib.md5(path).hexdigest()
1110 self.logger.debug("File name {} Catalog Name {} file path {} "
1111 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1112
1113 try:
1114 catalogs = vca.get_catalogs()
1115 except Exception as exp:
1116 self.logger.debug("Failed get catalogs() with Exception {} ".format(exp))
1117 raise vimconn.vimconnException("Failed get catalogs() with Exception {} ".format(exp))
1118
1119 if len(catalogs) == 0:
1120 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1121 result = self.create_vimcatalog(vca, catalog_md5_name)
1122 if not result:
1123 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1124 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1125 media_name=filename, medial_file_name=path, progress=progress)
1126 if not result:
1127 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1128 return self.get_catalogid(catalog_name, vca.get_catalogs())
1129 else:
1130 for catalog in catalogs:
1131 # search for existing catalog if we find same name we return ID
1132 # TODO optimize this
1133 if catalog.name == catalog_md5_name:
1134 self.logger.debug("Found existing catalog entry for {} "
1135 "catalog id {}".format(catalog_name,
1136 self.get_catalogid(catalog_md5_name, catalogs)))
1137 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1138
1139 # if we didn't find existing catalog we create a new one and upload image.
1140 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1141 result = self.create_vimcatalog(vca, catalog_md5_name)
1142 if not result:
1143 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1144
1145 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1146 media_name=filename, medial_file_name=path, progress=progress)
1147 if not result:
1148 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1149
1150 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1151
1152 def get_image_list(self, filter_dict={}):
1153 '''Obtain tenant images from VIM
1154 Filter_dict can be:
1155 name: image name
1156 id: image uuid
1157 checksum: image checksum
1158 location: image path
1159 Returns the image list of dictionaries:
1160 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1161 List can be empty
1162 '''
1163 vca = self.connect()
1164 if not vca:
1165 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1166 try:
1167 image_list = []
1168 catalogs = vca.get_catalogs()
1169 if len(catalogs) == 0:
1170 return image_list
1171 else:
1172 for catalog in catalogs:
1173 catalog_uuid = catalog.get_id().split(":")[3]
1174 name = catalog.name
1175 filtered_dict = {}
1176 if filter_dict.get("name") and filter_dict["name"] != name:
1177 continue
1178 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1179 continue
1180 filtered_dict ["name"] = name
1181 filtered_dict ["id"] = catalog_uuid
1182 image_list.append(filtered_dict)
1183
1184 self.logger.debug("List of already created catalog items: {}".format(image_list))
1185 return image_list
1186 except Exception as exp:
1187 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
1188
1189 def get_vappid(self, vdc=None, vapp_name=None):
1190 """ Method takes vdc object and vApp name and returns vapp uuid or None
1191
1192 Args:
1193 vdc: The VDC object.
1194 vapp_name: is application vappp name identifier
1195
1196 Returns:
1197 The return vApp name otherwise None
1198 """
1199 if vdc is None or vapp_name is None:
1200 return None
1201 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1202 try:
1203 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1204 vdc.ResourceEntities.ResourceEntity)
1205 if len(refs) == 1:
1206 return refs[0].href.split("vapp")[1][1:]
1207 except Exception as e:
1208 self.logger.exception(e)
1209 return False
1210 return None
1211
1212 def check_vapp(self, vdc=None, vapp_uuid=None):
1213 """ Method Method returns True or False if vapp deployed in vCloud director
1214
1215 Args:
1216 vca: Connector to VCA
1217 vdc: The VDC object.
1218 vappid: vappid is application identifier
1219
1220 Returns:
1221 The return True if vApp deployed
1222 :param vdc:
1223 :param vapp_uuid:
1224 """
1225 try:
1226 refs = filter(lambda ref:
1227 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1228 vdc.ResourceEntities.ResourceEntity)
1229 for ref in refs:
1230 vappid = ref.href.split("vapp")[1][1:]
1231 # find vapp with respected vapp uuid
1232 if vappid == vapp_uuid:
1233 return True
1234 except Exception as e:
1235 self.logger.exception(e)
1236 return False
1237 return False
1238
1239 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
1240 """Method returns vApp name from vCD and lookup done by vapp_id.
1241
1242 Args:
1243 vca: Connector to VCA
1244 vdc: The VDC object.
1245 vapp_uuid: vappid is application identifier
1246
1247 Returns:
1248 The return vApp name otherwise None
1249 """
1250
1251 try:
1252 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1253 vdc.ResourceEntities.ResourceEntity)
1254 for ref in refs:
1255 # we care only about UUID the rest doesn't matter
1256 vappid = ref.href.split("vapp")[1][1:]
1257 if vappid == vapp_uuid:
1258 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1259 logger=self.logger)
1260 tree = XmlElementTree.fromstring(response.content)
1261 return tree.attrib['name']
1262 except Exception as e:
1263 self.logger.exception(e)
1264 return None
1265 return None
1266
1267 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
1268 cloud_config=None, disk_list=None):
1269 """Adds a VM instance to VIM
1270 Params:
1271 start: indicates if VM must start or boot in pause mode. Ignored
1272 image_id,flavor_id: image and flavor uuid
1273 net_list: list of interfaces, each one is a dictionary with:
1274 name:
1275 net_id: network uuid to connect
1276 vpci: virtual vcpi to assign
1277 model: interface model, virtio, e2000, ...
1278 mac_address:
1279 use: 'data', 'bridge', 'mgmt'
1280 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1281 vim_id: filled/added by this function
1282 cloud_config: can be a text script to be passed directly to cloud-init,
1283 or an object to inject users and ssh keys with format:
1284 key-pairs: [] list of keys to install to the default user
1285 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1286 #TODO ip, security groups
1287 Returns >=0, the instance identifier
1288 <0, error_text
1289 """
1290
1291 self.logger.info("Creating new instance for entry {}".format(name))
1292 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {} disk_list {}".format(
1293 description, start, image_id, flavor_id, net_list, cloud_config, disk_list))
1294 vca = self.connect()
1295 if not vca:
1296 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1297
1298 #new vm name = vmname + tenant_id + uuid
1299 new_vm_name = [name, '-', str(uuid.uuid4())]
1300 vmname_andid = ''.join(new_vm_name)
1301
1302 # if vm already deployed we return existing uuid
1303 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1304 # if vapp_uuid is not None:
1305 # return vapp_uuid
1306
1307 # we check for presence of VDC, Catalog entry and Flavor.
1308 vdc = vca.get_vdc(self.tenant_name)
1309 if vdc is None:
1310 raise vimconn.vimconnNotFoundException(
1311 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1312 catalogs = vca.get_catalogs()
1313 if catalogs is None:
1314 raise vimconn.vimconnNotFoundException(
1315 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1316
1317 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1318 if catalog_hash_name:
1319 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1320 else:
1321 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1322 "(Failed retrieve catalog information {})".format(name, image_id))
1323
1324
1325 # Set vCPU and Memory based on flavor.
1326 vm_cpus = None
1327 vm_memory = None
1328 vm_disk = None
1329
1330 if flavor_id is not None:
1331 if flavor_id not in vimconnector.flavorlist:
1332 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1333 "Failed retrieve flavor information "
1334 "flavor id {}".format(name, flavor_id))
1335 else:
1336 try:
1337 flavor = vimconnector.flavorlist[flavor_id]
1338 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1339 vm_memory = flavor[FLAVOR_RAM_KEY]
1340 vm_disk = flavor[FLAVOR_DISK_KEY]
1341 extended = flavor.get("extended", None)
1342 if extended:
1343 numas=extended.get("numas", None)
1344
1345 except Exception as exp:
1346 raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
1347
1348 # image upload creates template name as catalog name space Template.
1349 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1350 power_on = 'false'
1351 if start:
1352 power_on = 'true'
1353
1354 # client must provide at least one entry in net_list if not we report error
1355 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1356 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1357 primary_net = None
1358 primary_netname = None
1359 network_mode = 'bridged'
1360 if net_list is not None and len(net_list) > 0:
1361 for net in net_list:
1362 if 'use' in net and net['use'] == 'mgmt':
1363 primary_net = net
1364 if primary_net is None:
1365 primary_net = net_list[0]
1366
1367 try:
1368 primary_net_id = primary_net['net_id']
1369 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1370 if 'name' in network_dict:
1371 primary_netname = network_dict['name']
1372
1373 except KeyError:
1374 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1375 else:
1376 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1377
1378 # use: 'data', 'bridge', 'mgmt'
1379 # create vApp. Set vcpu and ram based on flavor id.
1380 try:
1381 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1382 self.get_catalogbyid(image_id, catalogs),
1383 network_name=None, # None while creating vapp
1384 network_mode=network_mode,
1385 vm_name=vmname_andid,
1386 vm_cpus=vm_cpus, # can be None if flavor is None
1387 vm_memory=vm_memory) # can be None if flavor is None
1388
1389 if vapptask is None or vapptask is False:
1390 raise vimconn.vimconnUnexpectedResponse(
1391 "new_vminstance(): failed to create vApp {}".format(vmname_andid))
1392 if type(vapptask) is VappTask:
1393 vca.block_until_completed(vapptask)
1394
1395 except Exception as exp:
1396 raise vimconn.vimconnUnexpectedResponse(
1397 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid, exp))
1398
1399 # we should have now vapp in undeployed state.
1400 try:
1401 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1402 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1403 except Exception as exp:
1404 raise vimconn.vimconnUnexpectedResponse(
1405 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1406 .format(vmname_andid, exp))
1407
1408 if vapp is None:
1409 raise vimconn.vimconnUnexpectedResponse(
1410 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1411 vmname_andid))
1412
1413 #Add PCI passthrough/SRIOV configrations
1414 vm_obj = None
1415 pci_devices_info = []
1416 sriov_net_info = []
1417 reserve_memory = False
1418
1419 for net in net_list:
1420 if net["type"]=="PF":
1421 pci_devices_info.append(net)
1422 elif (net["type"]=="VF" or net["type"]=="VFnotShared") and 'net_id'in net:
1423 sriov_net_info.append(net)
1424
1425 #Add PCI
1426 if len(pci_devices_info) > 0:
1427 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1428 vmname_andid ))
1429 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1430 pci_devices_info,
1431 vmname_andid)
1432 if PCI_devices_status:
1433 self.logger.info("Added PCI devives {} to VM {}".format(
1434 pci_devices_info,
1435 vmname_andid)
1436 )
1437 reserve_memory = True
1438 else:
1439 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1440 pci_devices_info,
1441 vmname_andid)
1442 )
1443 # Modify vm disk
1444 if vm_disk:
1445 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1446 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1447 if result :
1448 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1449
1450 #Add new or existing disks to vApp
1451 if disk_list:
1452 added_existing_disk = False
1453 for disk in disk_list:
1454 if 'device_type' in disk and disk['device_type'] == 'cdrom':
1455 image_id = disk['image_id']
1456 # Adding CD-ROM to VM
1457 # will revisit code once specification ready to support this feature
1458 self.insert_media_to_vm(vapp, image_id)
1459 elif "image_id" in disk and disk["image_id"] is not None:
1460 self.logger.debug("Adding existing disk from image {} to vm {} ".format(
1461 disk["image_id"] , vapp_uuid))
1462 self.add_existing_disk(catalogs=catalogs,
1463 image_id=disk["image_id"],
1464 size = disk["size"],
1465 template_name=templateName,
1466 vapp_uuid=vapp_uuid
1467 )
1468 added_existing_disk = True
1469 else:
1470 #Wait till added existing disk gets reflected into vCD database/API
1471 if added_existing_disk:
1472 time.sleep(5)
1473 added_existing_disk = False
1474 self.add_new_disk(vca, vapp_uuid, disk['size'])
1475
1476 if numas:
1477 # Assigning numa affinity setting
1478 for numa in numas:
1479 if 'paired-threads-id' in numa:
1480 paired_threads_id = numa['paired-threads-id']
1481 self.set_numa_affinity(vapp_uuid, paired_threads_id)
1482
1483 # add NICs & connect to networks in netlist
1484 try:
1485 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1486 nicIndex = 0
1487 primary_nic_index = 0
1488 for net in net_list:
1489 # openmano uses network id in UUID format.
1490 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1491 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1492 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1493
1494 if 'net_id' not in net:
1495 continue
1496
1497 interface_net_id = net['net_id']
1498 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1499 interface_network_mode = net['use']
1500
1501 if interface_network_mode == 'mgmt':
1502 primary_nic_index = nicIndex
1503
1504 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1505 - DHCP (The IP address is obtained from a DHCP service.)
1506 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1507 - NONE (No IP addressing mode specified.)"""
1508
1509 if primary_netname is not None:
1510 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1511 if len(nets) == 1:
1512 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1513 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1514 if type(task) is GenericTask:
1515 vca.block_until_completed(task)
1516 # connect network to VM - with all DHCP by default
1517
1518 type_list = ['PF','VF','VFnotShared']
1519 if 'type' in net and net['type'] not in type_list:
1520 # fetching nic type from vnf
1521 if 'model' in net:
1522 nic_type = net['model']
1523 self.logger.info("new_vminstance(): adding network adapter "\
1524 "to a network {}".format(nets[0].name))
1525 self.add_network_adapter_to_vms(vapp, nets[0].name,
1526 primary_nic_index,
1527 nicIndex,
1528 net,
1529 nic_type=nic_type)
1530 else:
1531 self.logger.info("new_vminstance(): adding network adapter "\
1532 "to a network {}".format(nets[0].name))
1533 self.add_network_adapter_to_vms(vapp, nets[0].name,
1534 primary_nic_index,
1535 nicIndex,
1536 net)
1537 nicIndex += 1
1538
1539 # cloud-init for ssh-key injection
1540 if cloud_config:
1541 self.cloud_init(vapp,cloud_config)
1542
1543 # deploy and power on vm
1544 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1545 deploytask = vapp.deploy(powerOn=False)
1546 if type(deploytask) is GenericTask:
1547 vca.block_until_completed(deploytask)
1548
1549 # ############# Stub code for SRIOV #################
1550 #Add SRIOV
1551 # if len(sriov_net_info) > 0:
1552 # self.logger.info("Need to add SRIOV adapters {} into VM {}".format(sriov_net_info,
1553 # vmname_andid ))
1554 # sriov_status, vm_obj, vcenter_conect = self.add_sriov(vapp_uuid,
1555 # sriov_net_info,
1556 # vmname_andid)
1557 # if sriov_status:
1558 # self.logger.info("Added SRIOV {} to VM {}".format(
1559 # sriov_net_info,
1560 # vmname_andid)
1561 # )
1562 # reserve_memory = True
1563 # else:
1564 # self.logger.info("Fail to add SRIOV {} to VM {}".format(
1565 # sriov_net_info,
1566 # vmname_andid)
1567 # )
1568
1569 # If VM has PCI devices or SRIOV reserve memory for VM
1570 if reserve_memory:
1571 memReserve = vm_obj.config.hardware.memoryMB
1572 spec = vim.vm.ConfigSpec()
1573 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1574 task = vm_obj.ReconfigVM_Task(spec=spec)
1575 if task:
1576 result = self.wait_for_vcenter_task(task, vcenter_conect)
1577 self.logger.info("Reserved memmoery {} MB for "\
1578 "VM VM status: {}".format(str(memReserve),result))
1579 else:
1580 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1581 str(memReserve),str(vm_obj)))
1582
1583 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1584 poweron_task = vapp.poweron()
1585 if type(poweron_task) is GenericTask:
1586 vca.block_until_completed(poweron_task)
1587
1588 except Exception as exp :
1589 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1590 self.logger.debug("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1591 raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1592
1593 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1594 wait_time = 0
1595 vapp_uuid = None
1596 while wait_time <= MAX_WAIT_TIME:
1597 try:
1598 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1599 except Exception as exp:
1600 raise vimconn.vimconnUnexpectedResponse(
1601 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1602 .format(vmname_andid, exp))
1603
1604 if vapp and vapp.me.deployed:
1605 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1606 break
1607 else:
1608 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1609 time.sleep(INTERVAL_TIME)
1610
1611 wait_time +=INTERVAL_TIME
1612
1613 if vapp_uuid is not None:
1614 return vapp_uuid
1615 else:
1616 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1617
1618 ##
1619 ##
1620 ## based on current discussion
1621 ##
1622 ##
1623 ## server:
1624 # created: '2016-09-08T11:51:58'
1625 # description: simple-instance.linux1.1
1626 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1627 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1628 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1629 # status: ACTIVE
1630 # error_msg:
1631 # interfaces: …
1632 #
1633 def get_vminstance(self, vim_vm_uuid=None):
1634 """Returns the VM instance information from VIM"""
1635
1636 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1637 vca = self.connect()
1638 if not vca:
1639 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1640
1641 vdc = vca.get_vdc(self.tenant_name)
1642 if vdc is None:
1643 raise vimconn.vimconnConnectionException(
1644 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1645
1646 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1647 if not vm_info_dict:
1648 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1649 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1650
1651 status_key = vm_info_dict['status']
1652 error = ''
1653 try:
1654 vm_dict = {'created': vm_info_dict['created'],
1655 'description': vm_info_dict['name'],
1656 'status': vcdStatusCode2manoFormat[int(status_key)],
1657 'hostId': vm_info_dict['vmuuid'],
1658 'error_msg': error,
1659 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1660
1661 if 'interfaces' in vm_info_dict:
1662 vm_dict['interfaces'] = vm_info_dict['interfaces']
1663 else:
1664 vm_dict['interfaces'] = []
1665 except KeyError:
1666 vm_dict = {'created': '',
1667 'description': '',
1668 'status': vcdStatusCode2manoFormat[int(-1)],
1669 'hostId': vm_info_dict['vmuuid'],
1670 'error_msg': "Inconsistency state",
1671 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1672
1673 return vm_dict
1674
1675 def delete_vminstance(self, vm__vim_uuid):
1676 """Method poweroff and remove VM instance from vcloud director network.
1677
1678 Args:
1679 vm__vim_uuid: VM UUID
1680
1681 Returns:
1682 Returns the instance identifier
1683 """
1684
1685 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1686 vca = self.connect()
1687 if not vca:
1688 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1689
1690 vdc = vca.get_vdc(self.tenant_name)
1691 if vdc is None:
1692 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1693 self.tenant_name))
1694 raise vimconn.vimconnException(
1695 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1696
1697 try:
1698 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1699 if vapp_name is None:
1700 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1701 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1702 else:
1703 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1704
1705 # Delete vApp and wait for status change if task executed and vApp is None.
1706 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1707
1708 if vapp:
1709 if vapp.me.deployed:
1710 self.logger.info("Powering off vApp {}".format(vapp_name))
1711 #Power off vApp
1712 powered_off = False
1713 wait_time = 0
1714 while wait_time <= MAX_WAIT_TIME:
1715 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1716 if not vapp:
1717 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1718 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1719
1720 power_off_task = vapp.poweroff()
1721 if type(power_off_task) is GenericTask:
1722 result = vca.block_until_completed(power_off_task)
1723 if result:
1724 powered_off = True
1725 break
1726 else:
1727 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1728 time.sleep(INTERVAL_TIME)
1729
1730 wait_time +=INTERVAL_TIME
1731 if not powered_off:
1732 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1733 else:
1734 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1735
1736 #Undeploy vApp
1737 self.logger.info("Undeploy vApp {}".format(vapp_name))
1738 wait_time = 0
1739 undeployed = False
1740 while wait_time <= MAX_WAIT_TIME:
1741 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1742 if not vapp:
1743 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1744 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1745 undeploy_task = vapp.undeploy(action='powerOff')
1746
1747 if type(undeploy_task) is GenericTask:
1748 result = vca.block_until_completed(undeploy_task)
1749 if result:
1750 undeployed = True
1751 break
1752 else:
1753 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1754 time.sleep(INTERVAL_TIME)
1755
1756 wait_time +=INTERVAL_TIME
1757
1758 if not undeployed:
1759 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1760
1761 # delete vapp
1762 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1763 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1764
1765 if vapp is not None:
1766 wait_time = 0
1767 result = False
1768
1769 while wait_time <= MAX_WAIT_TIME:
1770 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1771 if not vapp:
1772 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1773 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1774
1775 delete_task = vapp.delete()
1776
1777 if type(delete_task) is GenericTask:
1778 vca.block_until_completed(delete_task)
1779 result = vca.block_until_completed(delete_task)
1780 if result:
1781 break
1782 else:
1783 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1784 time.sleep(INTERVAL_TIME)
1785
1786 wait_time +=INTERVAL_TIME
1787
1788 if not result:
1789 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1790
1791 except:
1792 self.logger.debug(traceback.format_exc())
1793 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1794
1795 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1796 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1797 return vm__vim_uuid
1798 else:
1799 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1800
1801 def refresh_vms_status(self, vm_list):
1802 """Get the status of the virtual machines and their interfaces/ports
1803 Params: the list of VM identifiers
1804 Returns a dictionary with:
1805 vm_id: #VIM id of this Virtual Machine
1806 status: #Mandatory. Text with one of:
1807 # DELETED (not found at vim)
1808 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1809 # OTHER (Vim reported other status not understood)
1810 # ERROR (VIM indicates an ERROR status)
1811 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1812 # CREATING (on building process), ERROR
1813 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1814 #
1815 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1816 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1817 interfaces:
1818 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1819 mac_address: #Text format XX:XX:XX:XX:XX:XX
1820 vim_net_id: #network id where this interface is connected
1821 vim_interface_id: #interface/port VIM id
1822 ip_address: #null, or text with IPv4, IPv6 address
1823 """
1824
1825 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1826
1827 vca = self.connect()
1828 if not vca:
1829 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1830
1831 vdc = vca.get_vdc(self.tenant_name)
1832 if vdc is None:
1833 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1834
1835 vms_dict = {}
1836 nsx_edge_list = []
1837 for vmuuid in vm_list:
1838 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1839 if vmname is not None:
1840
1841 try:
1842 the_vapp = vca.get_vapp(vdc, vmname)
1843 vm_info = the_vapp.get_vms_details()
1844 vm_status = vm_info[0]['status']
1845 vm_pci_details = self.get_vm_pci_details(vmuuid)
1846 vm_info[0].update(vm_pci_details)
1847
1848 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1849 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1850 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1851
1852 # get networks
1853 vm_app_networks = the_vapp.get_vms_network_info()
1854 for vapp_network in vm_app_networks:
1855 for vm_network in vapp_network:
1856 if vm_network['name'] == vmname:
1857 #Assign IP Address based on MAC Address in NSX DHCP lease info
1858 if vm_network['ip'] is None:
1859 if not nsx_edge_list:
1860 nsx_edge_list = self.get_edge_details()
1861 if nsx_edge_list is None:
1862 raise vimconn.vimconnException("refresh_vms_status:"\
1863 "Failed to get edge details from NSX Manager")
1864 if vm_network['mac'] is not None:
1865 vm_network['ip'] = self.get_ipaddr_from_NSXedge(nsx_edge_list, vm_network['mac'])
1866
1867 vm_net_id = self.get_network_id_by_name(vm_network['network_name'])
1868 interface = {"mac_address": vm_network['mac'],
1869 "vim_net_id": vm_net_id,
1870 "vim_interface_id": vm_net_id,
1871 'ip_address': vm_network['ip']}
1872 # interface['vim_info'] = yaml.safe_dump(vm_network)
1873 vm_dict["interfaces"].append(interface)
1874 # add a vm to vm dict
1875 vms_dict.setdefault(vmuuid, vm_dict)
1876 except Exception as exp:
1877 self.logger.debug("Error in response {}".format(exp))
1878 self.logger.debug(traceback.format_exc())
1879
1880 return vms_dict
1881
1882
1883 def get_edge_details(self):
1884 """Get the NSX edge list from NSX Manager
1885 Returns list of NSX edges
1886 """
1887 edge_list = []
1888 rheaders = {'Content-Type': 'application/xml'}
1889 nsx_api_url = '/api/4.0/edges'
1890
1891 self.logger.debug("Get edge details from NSX Manager {} {}".format(self.nsx_manager, nsx_api_url))
1892
1893 try:
1894 resp = requests.get(self.nsx_manager + nsx_api_url,
1895 auth = (self.nsx_user, self.nsx_password),
1896 verify = False, headers = rheaders)
1897 if resp.status_code == requests.codes.ok:
1898 paged_Edge_List = XmlElementTree.fromstring(resp.text)
1899 for edge_pages in paged_Edge_List:
1900 if edge_pages.tag == 'edgePage':
1901 for edge_summary in edge_pages:
1902 if edge_summary.tag == 'pagingInfo':
1903 for element in edge_summary:
1904 if element.tag == 'totalCount' and element.text == '0':
1905 raise vimconn.vimconnException("get_edge_details: No NSX edges details found: {}"
1906 .format(self.nsx_manager))
1907
1908 if edge_summary.tag == 'edgeSummary':
1909 for element in edge_summary:
1910 if element.tag == 'id':
1911 edge_list.append(element.text)
1912 else:
1913 raise vimconn.vimconnException("get_edge_details: No NSX edge details found: {}"
1914 .format(self.nsx_manager))
1915
1916 if not edge_list:
1917 raise vimconn.vimconnException("get_edge_details: "\
1918 "No NSX edge details found: {}"
1919 .format(self.nsx_manager))
1920 else:
1921 self.logger.debug("get_edge_details: Found NSX edges {}".format(edge_list))
1922 return edge_list
1923 else:
1924 self.logger.debug("get_edge_details: "
1925 "Failed to get NSX edge details from NSX Manager: {}"
1926 .format(resp.content))
1927 return None
1928
1929 except Exception as exp:
1930 self.logger.debug("get_edge_details: "\
1931 "Failed to get NSX edge details from NSX Manager: {}"
1932 .format(exp))
1933 raise vimconn.vimconnException("get_edge_details: "\
1934 "Failed to get NSX edge details from NSX Manager: {}"
1935 .format(exp))
1936
1937
1938 def get_ipaddr_from_NSXedge(self, nsx_edges, mac_address):
1939 """Get IP address details from NSX edges, using the MAC address
1940 PARAMS: nsx_edges : List of NSX edges
1941 mac_address : Find IP address corresponding to this MAC address
1942 Returns: IP address corrresponding to the provided MAC address
1943 """
1944
1945 ip_addr = None
1946 rheaders = {'Content-Type': 'application/xml'}
1947
1948 self.logger.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
1949
1950 try:
1951 for edge in nsx_edges:
1952 nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
1953
1954 resp = requests.get(self.nsx_manager + nsx_api_url,
1955 auth = (self.nsx_user, self.nsx_password),
1956 verify = False, headers = rheaders)
1957
1958 if resp.status_code == requests.codes.ok:
1959 dhcp_leases = XmlElementTree.fromstring(resp.text)
1960 for child in dhcp_leases:
1961 if child.tag == 'dhcpLeaseInfo':
1962 dhcpLeaseInfo = child
1963 for leaseInfo in dhcpLeaseInfo:
1964 for elem in leaseInfo:
1965 if (elem.tag)=='macAddress':
1966 edge_mac_addr = elem.text
1967 if (elem.tag)=='ipAddress':
1968 ip_addr = elem.text
1969 if edge_mac_addr is not None:
1970 if edge_mac_addr == mac_address:
1971 self.logger.debug("Found ip addr {} for mac {} at NSX edge {}"
1972 .format(ip_addr, mac_address,edge))
1973 return ip_addr
1974 else:
1975 self.logger.debug("get_ipaddr_from_NSXedge: "\
1976 "Error occurred while getting DHCP lease info from NSX Manager: {}"
1977 .format(resp.content))
1978
1979 self.logger.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
1980 return None
1981
1982 except XmlElementTree.ParseError as Err:
1983 self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
1984
1985
1986 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1987 """Send and action over a VM instance from VIM
1988 Returns the vm_id if the action was successfully sent to the VIM"""
1989
1990 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1991 if vm__vim_uuid is None or action_dict is None:
1992 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1993
1994 vca = self.connect()
1995 if not vca:
1996 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1997
1998 vdc = vca.get_vdc(self.tenant_name)
1999 if vdc is None:
2000 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
2001
2002 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
2003 if vapp_name is None:
2004 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2005 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
2006 else:
2007 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
2008
2009 try:
2010 the_vapp = vca.get_vapp(vdc, vapp_name)
2011 # TODO fix all status
2012 if "start" in action_dict:
2013 vm_info = the_vapp.get_vms_details()
2014 vm_status = vm_info[0]['status']
2015 self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name))
2016 if vm_status == "Suspended" or vm_status == "Powered off":
2017 power_on_task = the_vapp.poweron()
2018 result = vca.block_until_completed(power_on_task)
2019 self.instance_actions_result("start", result, vapp_name)
2020 elif "rebuild" in action_dict:
2021 self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name))
2022 rebuild_task = the_vapp.deploy(powerOn=True)
2023 result = vca.block_until_completed(rebuild_task)
2024 self.instance_actions_result("rebuild", result, vapp_name)
2025 elif "pause" in action_dict:
2026 self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name))
2027 pause_task = the_vapp.undeploy(action='suspend')
2028 result = vca.block_until_completed(pause_task)
2029 self.instance_actions_result("pause", result, vapp_name)
2030 elif "resume" in action_dict:
2031 self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name))
2032 power_task = the_vapp.poweron()
2033 result = vca.block_until_completed(power_task)
2034 self.instance_actions_result("resume", result, vapp_name)
2035 elif "shutoff" in action_dict or "shutdown" in action_dict:
2036 action_name , value = action_dict.items()[0]
2037 self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name))
2038 power_off_task = the_vapp.undeploy(action='powerOff')
2039 result = vca.block_until_completed(power_off_task)
2040 if action_name == "shutdown":
2041 self.instance_actions_result("shutdown", result, vapp_name)
2042 else:
2043 self.instance_actions_result("shutoff", result, vapp_name)
2044 elif "forceOff" in action_dict:
2045 result = the_vapp.undeploy(action='force')
2046 self.instance_actions_result("forceOff", result, vapp_name)
2047 elif "reboot" in action_dict:
2048 self.logger.info("action_vminstance: reboot vApp: {}".format(vapp_name))
2049 reboot_task = the_vapp.reboot()
2050 else:
2051 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
2052 return vm__vim_uuid
2053 except Exception as exp :
2054 self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
2055 raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
2056
2057 def instance_actions_result(self, action, result, vapp_name):
2058 if result:
2059 self.logger.info("action_vminstance: Sucessfully {} the vApp: {}".format(action, vapp_name))
2060 else:
2061 self.logger.error("action_vminstance: Failed to {} vApp: {}".format(action, vapp_name))
2062
2063 def get_vminstance_console(self, vm_id, console_type="vnc"):
2064 """
2065 Get a console for the virtual machine
2066 Params:
2067 vm_id: uuid of the VM
2068 console_type, can be:
2069 "novnc" (by default), "xvpvnc" for VNC types,
2070 "rdp-html5" for RDP types, "spice-html5" for SPICE types
2071 Returns dict with the console parameters:
2072 protocol: ssh, ftp, http, https, ...
2073 server: usually ip address
2074 port: the http, ssh, ... port
2075 suffix: extra text, e.g. the http path and query string
2076 """
2077 raise vimconn.vimconnNotImplemented("Should have implemented this")
2078
2079 # NOT USED METHODS in current version
2080
2081 def host_vim2gui(self, host, server_dict):
2082 """Transform host dictionary from VIM format to GUI format,
2083 and append to the server_dict
2084 """
2085 raise vimconn.vimconnNotImplemented("Should have implemented this")
2086
2087 def get_hosts_info(self):
2088 """Get the information of deployed hosts
2089 Returns the hosts content"""
2090 raise vimconn.vimconnNotImplemented("Should have implemented this")
2091
2092 def get_hosts(self, vim_tenant):
2093 """Get the hosts and deployed instances
2094 Returns the hosts content"""
2095 raise vimconn.vimconnNotImplemented("Should have implemented this")
2096
2097 def get_processor_rankings(self):
2098 """Get the processor rankings in the VIM database"""
2099 raise vimconn.vimconnNotImplemented("Should have implemented this")
2100
2101 def new_host(self, host_data):
2102 """Adds a new host to VIM"""
2103 '''Returns status code of the VIM response'''
2104 raise vimconn.vimconnNotImplemented("Should have implemented this")
2105
2106 def new_external_port(self, port_data):
2107 """Adds a external port to VIM"""
2108 '''Returns the port identifier'''
2109 raise vimconn.vimconnNotImplemented("Should have implemented this")
2110
2111 def new_external_network(self, net_name, net_type):
2112 """Adds a external network to VIM (shared)"""
2113 '''Returns the network identifier'''
2114 raise vimconn.vimconnNotImplemented("Should have implemented this")
2115
2116 def connect_port_network(self, port_id, network_id, admin=False):
2117 """Connects a external port to a network"""
2118 '''Returns status code of the VIM response'''
2119 raise vimconn.vimconnNotImplemented("Should have implemented this")
2120
2121 def new_vminstancefromJSON(self, vm_data):
2122 """Adds a VM instance to VIM"""
2123 '''Returns the instance identifier'''
2124 raise vimconn.vimconnNotImplemented("Should have implemented this")
2125
2126 def get_network_name_by_id(self, network_uuid=None):
2127 """Method gets vcloud director network named based on supplied uuid.
2128
2129 Args:
2130 network_uuid: network_id
2131
2132 Returns:
2133 The return network name.
2134 """
2135
2136 vca = self.connect()
2137 if not vca:
2138 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2139
2140 if not network_uuid:
2141 return None
2142
2143 try:
2144 org_dict = self.get_org(self.org_uuid)
2145 if 'networks' in org_dict:
2146 org_network_dict = org_dict['networks']
2147 for net_uuid in org_network_dict:
2148 if net_uuid == network_uuid:
2149 return org_network_dict[net_uuid]
2150 except:
2151 self.logger.debug("Exception in get_network_name_by_id")
2152 self.logger.debug(traceback.format_exc())
2153
2154 return None
2155
2156 def get_network_id_by_name(self, network_name=None):
2157 """Method gets vcloud director network uuid based on supplied name.
2158
2159 Args:
2160 network_name: network_name
2161 Returns:
2162 The return network uuid.
2163 network_uuid: network_id
2164 """
2165
2166 vca = self.connect()
2167 if not vca:
2168 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2169
2170 if not network_name:
2171 self.logger.debug("get_network_id_by_name() : Network name is empty")
2172 return None
2173
2174 try:
2175 org_dict = self.get_org(self.org_uuid)
2176 if org_dict and 'networks' in org_dict:
2177 org_network_dict = org_dict['networks']
2178 for net_uuid,net_name in org_network_dict.iteritems():
2179 if net_name == network_name:
2180 return net_uuid
2181
2182 except KeyError as exp:
2183 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
2184
2185 return None
2186
2187 def list_org_action(self):
2188 """
2189 Method leverages vCloud director and query for available organization for particular user
2190
2191 Args:
2192 vca - is active VCA connection.
2193 vdc_name - is a vdc name that will be used to query vms action
2194
2195 Returns:
2196 The return XML respond
2197 """
2198
2199 vca = self.connect()
2200 if not vca:
2201 raise vimconn.vimconnConnectionException("self.connect() is failed")
2202
2203 url_list = [vca.host, '/api/org']
2204 vm_list_rest_call = ''.join(url_list)
2205
2206 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2207 response = Http.get(url=vm_list_rest_call,
2208 headers=vca.vcloud_session.get_vcloud_headers(),
2209 verify=vca.verify,
2210 logger=vca.logger)
2211 if response.status_code == requests.codes.ok:
2212 return response.content
2213
2214 return None
2215
2216 def get_org_action(self, org_uuid=None):
2217 """
2218 Method leverages vCloud director and retrieve available object fdr organization.
2219
2220 Args:
2221 vca - is active VCA connection.
2222 vdc_name - is a vdc name that will be used to query vms action
2223
2224 Returns:
2225 The return XML respond
2226 """
2227
2228 vca = self.connect()
2229 if not vca:
2230 raise vimconn.vimconnConnectionException("self.connect() is failed")
2231
2232 if org_uuid is None:
2233 return None
2234
2235 url_list = [vca.host, '/api/org/', org_uuid]
2236 vm_list_rest_call = ''.join(url_list)
2237
2238 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2239 response = Http.get(url=vm_list_rest_call,
2240 headers=vca.vcloud_session.get_vcloud_headers(),
2241 verify=vca.verify,
2242 logger=vca.logger)
2243 if response.status_code == requests.codes.ok:
2244 return response.content
2245
2246 return None
2247
2248 def get_org(self, org_uuid=None):
2249 """
2250 Method retrieves available organization in vCloud Director
2251
2252 Args:
2253 org_uuid - is a organization uuid.
2254
2255 Returns:
2256 The return dictionary with following key
2257 "network" - for network list under the org
2258 "catalogs" - for network list under the org
2259 "vdcs" - for vdc list under org
2260 """
2261
2262 org_dict = {}
2263 vca = self.connect()
2264 if not vca:
2265 raise vimconn.vimconnConnectionException("self.connect() is failed")
2266
2267 if org_uuid is None:
2268 return org_dict
2269
2270 content = self.get_org_action(org_uuid=org_uuid)
2271 try:
2272 vdc_list = {}
2273 network_list = {}
2274 catalog_list = {}
2275 vm_list_xmlroot = XmlElementTree.fromstring(content)
2276 for child in vm_list_xmlroot:
2277 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2278 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2279 org_dict['vdcs'] = vdc_list
2280 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2281 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2282 org_dict['networks'] = network_list
2283 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2284 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2285 org_dict['catalogs'] = catalog_list
2286 except:
2287 pass
2288
2289 return org_dict
2290
2291 def get_org_list(self):
2292 """
2293 Method retrieves available organization in vCloud Director
2294
2295 Args:
2296 vca - is active VCA connection.
2297
2298 Returns:
2299 The return dictionary and key for each entry VDC UUID
2300 """
2301
2302 org_dict = {}
2303 vca = self.connect()
2304 if not vca:
2305 raise vimconn.vimconnConnectionException("self.connect() is failed")
2306
2307 content = self.list_org_action()
2308 try:
2309 vm_list_xmlroot = XmlElementTree.fromstring(content)
2310 for vm_xml in vm_list_xmlroot:
2311 if vm_xml.tag.split("}")[1] == 'Org':
2312 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2313 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2314 except:
2315 pass
2316
2317 return org_dict
2318
2319 def vms_view_action(self, vdc_name=None):
2320 """ Method leverages vCloud director vms query call
2321
2322 Args:
2323 vca - is active VCA connection.
2324 vdc_name - is a vdc name that will be used to query vms action
2325
2326 Returns:
2327 The return XML respond
2328 """
2329 vca = self.connect()
2330 if vdc_name is None:
2331 return None
2332
2333 url_list = [vca.host, '/api/vms/query']
2334 vm_list_rest_call = ''.join(url_list)
2335
2336 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2337 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
2338 vca.vcloud_session.organization.Link)
2339 if len(refs) == 1:
2340 response = Http.get(url=vm_list_rest_call,
2341 headers=vca.vcloud_session.get_vcloud_headers(),
2342 verify=vca.verify,
2343 logger=vca.logger)
2344 if response.status_code == requests.codes.ok:
2345 return response.content
2346
2347 return None
2348
2349 def get_vapp_list(self, vdc_name=None):
2350 """
2351 Method retrieves vApp list deployed vCloud director and returns a dictionary
2352 contains a list of all vapp deployed for queried VDC.
2353 The key for a dictionary is vApp UUID
2354
2355
2356 Args:
2357 vca - is active VCA connection.
2358 vdc_name - is a vdc name that will be used to query vms action
2359
2360 Returns:
2361 The return dictionary and key for each entry vapp UUID
2362 """
2363
2364 vapp_dict = {}
2365 if vdc_name is None:
2366 return vapp_dict
2367
2368 content = self.vms_view_action(vdc_name=vdc_name)
2369 try:
2370 vm_list_xmlroot = XmlElementTree.fromstring(content)
2371 for vm_xml in vm_list_xmlroot:
2372 if vm_xml.tag.split("}")[1] == 'VMRecord':
2373 if vm_xml.attrib['isVAppTemplate'] == 'true':
2374 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2375 if 'vappTemplate-' in rawuuid[0]:
2376 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2377 # vm and use raw UUID as key
2378 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2379 except:
2380 pass
2381
2382 return vapp_dict
2383
2384 def get_vm_list(self, vdc_name=None):
2385 """
2386 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2387 contains a list of all VM's deployed for queried VDC.
2388 The key for a dictionary is VM UUID
2389
2390
2391 Args:
2392 vca - is active VCA connection.
2393 vdc_name - is a vdc name that will be used to query vms action
2394
2395 Returns:
2396 The return dictionary and key for each entry vapp UUID
2397 """
2398 vm_dict = {}
2399
2400 if vdc_name is None:
2401 return vm_dict
2402
2403 content = self.vms_view_action(vdc_name=vdc_name)
2404 try:
2405 vm_list_xmlroot = XmlElementTree.fromstring(content)
2406 for vm_xml in vm_list_xmlroot:
2407 if vm_xml.tag.split("}")[1] == 'VMRecord':
2408 if vm_xml.attrib['isVAppTemplate'] == 'false':
2409 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2410 if 'vm-' in rawuuid[0]:
2411 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2412 # vm and use raw UUID as key
2413 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2414 except:
2415 pass
2416
2417 return vm_dict
2418
2419 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2420 """
2421 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2422 contains a list of all VM's deployed for queried VDC.
2423 The key for a dictionary is VM UUID
2424
2425
2426 Args:
2427 vca - is active VCA connection.
2428 vdc_name - is a vdc name that will be used to query vms action
2429
2430 Returns:
2431 The return dictionary and key for each entry vapp UUID
2432 """
2433 vm_dict = {}
2434 vca = self.connect()
2435 if not vca:
2436 raise vimconn.vimconnConnectionException("self.connect() is failed")
2437
2438 if vdc_name is None:
2439 return vm_dict
2440
2441 content = self.vms_view_action(vdc_name=vdc_name)
2442 try:
2443 vm_list_xmlroot = XmlElementTree.fromstring(content)
2444 for vm_xml in vm_list_xmlroot:
2445 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2446 # lookup done by UUID
2447 if isuuid:
2448 if vapp_name in vm_xml.attrib['container']:
2449 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2450 if 'vm-' in rawuuid[0]:
2451 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2452 break
2453 # lookup done by Name
2454 else:
2455 if vapp_name in vm_xml.attrib['name']:
2456 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2457 if 'vm-' in rawuuid[0]:
2458 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2459 break
2460 except:
2461 pass
2462
2463 return vm_dict
2464
2465 def get_network_action(self, network_uuid=None):
2466 """
2467 Method leverages vCloud director and query network based on network uuid
2468
2469 Args:
2470 vca - is active VCA connection.
2471 network_uuid - is a network uuid
2472
2473 Returns:
2474 The return XML respond
2475 """
2476
2477 vca = self.connect()
2478 if not vca:
2479 raise vimconn.vimconnConnectionException("self.connect() is failed")
2480
2481 if network_uuid is None:
2482 return None
2483
2484 url_list = [vca.host, '/api/network/', network_uuid]
2485 vm_list_rest_call = ''.join(url_list)
2486
2487 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2488 response = Http.get(url=vm_list_rest_call,
2489 headers=vca.vcloud_session.get_vcloud_headers(),
2490 verify=vca.verify,
2491 logger=vca.logger)
2492 if response.status_code == requests.codes.ok:
2493 return response.content
2494
2495 return None
2496
2497 def get_vcd_network(self, network_uuid=None):
2498 """
2499 Method retrieves available network from vCloud Director
2500
2501 Args:
2502 network_uuid - is VCD network UUID
2503
2504 Each element serialized as key : value pair
2505
2506 Following keys available for access. network_configuration['Gateway'}
2507 <Configuration>
2508 <IpScopes>
2509 <IpScope>
2510 <IsInherited>true</IsInherited>
2511 <Gateway>172.16.252.100</Gateway>
2512 <Netmask>255.255.255.0</Netmask>
2513 <Dns1>172.16.254.201</Dns1>
2514 <Dns2>172.16.254.202</Dns2>
2515 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2516 <IsEnabled>true</IsEnabled>
2517 <IpRanges>
2518 <IpRange>
2519 <StartAddress>172.16.252.1</StartAddress>
2520 <EndAddress>172.16.252.99</EndAddress>
2521 </IpRange>
2522 </IpRanges>
2523 </IpScope>
2524 </IpScopes>
2525 <FenceMode>bridged</FenceMode>
2526
2527 Returns:
2528 The return dictionary and key for each entry vapp UUID
2529 """
2530
2531 network_configuration = {}
2532 if network_uuid is None:
2533 return network_uuid
2534
2535 try:
2536 content = self.get_network_action(network_uuid=network_uuid)
2537 vm_list_xmlroot = XmlElementTree.fromstring(content)
2538
2539 network_configuration['status'] = vm_list_xmlroot.get("status")
2540 network_configuration['name'] = vm_list_xmlroot.get("name")
2541 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2542
2543 for child in vm_list_xmlroot:
2544 if child.tag.split("}")[1] == 'IsShared':
2545 network_configuration['isShared'] = child.text.strip()
2546 if child.tag.split("}")[1] == 'Configuration':
2547 for configuration in child.iter():
2548 tagKey = configuration.tag.split("}")[1].strip()
2549 if tagKey != "":
2550 network_configuration[tagKey] = configuration.text.strip()
2551 return network_configuration
2552 except Exception as exp :
2553 self.logger.debug("get_vcd_network: Failed with Exception {}".format(exp))
2554 raise vimconn.vimconnException("get_vcd_network: Failed with Exception {}".format(exp))
2555
2556 return network_configuration
2557
2558 def delete_network_action(self, network_uuid=None):
2559 """
2560 Method delete given network from vCloud director
2561
2562 Args:
2563 network_uuid - is a network uuid that client wish to delete
2564
2565 Returns:
2566 The return None or XML respond or false
2567 """
2568
2569 vca = self.connect_as_admin()
2570 if not vca:
2571 raise vimconn.vimconnConnectionException("self.connect() is failed")
2572 if network_uuid is None:
2573 return False
2574
2575 url_list = [vca.host, '/api/admin/network/', network_uuid]
2576 vm_list_rest_call = ''.join(url_list)
2577
2578 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2579 response = Http.delete(url=vm_list_rest_call,
2580 headers=vca.vcloud_session.get_vcloud_headers(),
2581 verify=vca.verify,
2582 logger=vca.logger)
2583
2584 if response.status_code == 202:
2585 return True
2586
2587 return False
2588
2589 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2590 ip_profile=None, isshared='true'):
2591 """
2592 Method create network in vCloud director
2593
2594 Args:
2595 network_name - is network name to be created.
2596 net_type - can be 'bridge','data','ptp','mgmt'.
2597 ip_profile is a dict containing the IP parameters of the network
2598 isshared - is a boolean
2599 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2600 It optional attribute. by default if no parent network indicate the first available will be used.
2601
2602 Returns:
2603 The return network uuid or return None
2604 """
2605
2606 new_network_name = [network_name, '-', str(uuid.uuid4())]
2607 content = self.create_network_rest(network_name=''.join(new_network_name),
2608 ip_profile=ip_profile,
2609 net_type=net_type,
2610 parent_network_uuid=parent_network_uuid,
2611 isshared=isshared)
2612 if content is None:
2613 self.logger.debug("Failed create network {}.".format(network_name))
2614 return None
2615
2616 try:
2617 vm_list_xmlroot = XmlElementTree.fromstring(content)
2618 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2619 if len(vcd_uuid) == 4:
2620 self.logger.info("Created new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2621 return vcd_uuid[3]
2622 except:
2623 self.logger.debug("Failed create network {}".format(network_name))
2624 return None
2625
2626 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2627 ip_profile=None, isshared='true'):
2628 """
2629 Method create network in vCloud director
2630
2631 Args:
2632 network_name - is network name to be created.
2633 net_type - can be 'bridge','data','ptp','mgmt'.
2634 ip_profile is a dict containing the IP parameters of the network
2635 isshared - is a boolean
2636 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2637 It optional attribute. by default if no parent network indicate the first available will be used.
2638
2639 Returns:
2640 The return network uuid or return None
2641 """
2642
2643 vca = self.connect_as_admin()
2644 if not vca:
2645 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2646 if network_name is None:
2647 return None
2648
2649 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2650 vm_list_rest_call = ''.join(url_list)
2651 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2652 response = Http.get(url=vm_list_rest_call,
2653 headers=vca.vcloud_session.get_vcloud_headers(),
2654 verify=vca.verify,
2655 logger=vca.logger)
2656
2657 provider_network = None
2658 available_networks = None
2659 add_vdc_rest_url = None
2660
2661 if response.status_code != requests.codes.ok:
2662 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2663 response.status_code))
2664 return None
2665 else:
2666 try:
2667 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2668 for child in vm_list_xmlroot:
2669 if child.tag.split("}")[1] == 'ProviderVdcReference':
2670 provider_network = child.attrib.get('href')
2671 # application/vnd.vmware.admin.providervdc+xml
2672 if child.tag.split("}")[1] == 'Link':
2673 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2674 and child.attrib.get('rel') == 'add':
2675 add_vdc_rest_url = child.attrib.get('href')
2676 except:
2677 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2678 self.logger.debug("Respond body {}".format(response.content))
2679 return None
2680
2681 # find pvdc provided available network
2682 response = Http.get(url=provider_network,
2683 headers=vca.vcloud_session.get_vcloud_headers(),
2684 verify=vca.verify,
2685 logger=vca.logger)
2686 if response.status_code != requests.codes.ok:
2687 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2688 response.status_code))
2689 return None
2690
2691 # available_networks.split("/")[-1]
2692
2693 if parent_network_uuid is None:
2694 try:
2695 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2696 for child in vm_list_xmlroot.iter():
2697 if child.tag.split("}")[1] == 'AvailableNetworks':
2698 for networks in child.iter():
2699 # application/vnd.vmware.admin.network+xml
2700 if networks.attrib.get('href') is not None:
2701 available_networks = networks.attrib.get('href')
2702 break
2703 except:
2704 return None
2705
2706 try:
2707 #Configure IP profile of the network
2708 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2709
2710 if 'subnet_address' not in ip_profile or ip_profile['subnet_address'] is None:
2711 subnet_rand = random.randint(0, 255)
2712 ip_base = "192.168.{}.".format(subnet_rand)
2713 ip_profile['subnet_address'] = ip_base + "0/24"
2714 else:
2715 ip_base = ip_profile['subnet_address'].rsplit('.',1)[0] + '.'
2716
2717 if 'gateway_address' not in ip_profile or ip_profile['gateway_address'] is None:
2718 ip_profile['gateway_address']=ip_base + "1"
2719 if 'dhcp_count' not in ip_profile or ip_profile['dhcp_count'] is None:
2720 ip_profile['dhcp_count']=DEFAULT_IP_PROFILE['dhcp_count']
2721 if 'dhcp_enabled' not in ip_profile or ip_profile['dhcp_enabled'] is None:
2722 ip_profile['dhcp_enabled']=DEFAULT_IP_PROFILE['dhcp_enabled']
2723 if 'dhcp_start_address' not in ip_profile or ip_profile['dhcp_start_address'] is None:
2724 ip_profile['dhcp_start_address']=ip_base + "3"
2725 if 'ip_version' not in ip_profile or ip_profile['ip_version'] is None:
2726 ip_profile['ip_version']=DEFAULT_IP_PROFILE['ip_version']
2727 if 'dns_address' not in ip_profile or ip_profile['dns_address'] is None:
2728 ip_profile['dns_address']=ip_base + "2"
2729
2730 gateway_address=ip_profile['gateway_address']
2731 dhcp_count=int(ip_profile['dhcp_count'])
2732 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2733
2734 if ip_profile['dhcp_enabled']==True:
2735 dhcp_enabled='true'
2736 else:
2737 dhcp_enabled='false'
2738 dhcp_start_address=ip_profile['dhcp_start_address']
2739
2740 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2741 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2742 end_ip_int += dhcp_count - 1
2743 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2744
2745 ip_version=ip_profile['ip_version']
2746 dns_address=ip_profile['dns_address']
2747 except KeyError as exp:
2748 self.logger.debug("Create Network REST: Key error {}".format(exp))
2749 raise vimconn.vimconnException("Create Network REST: Key error{}".format(exp))
2750
2751 # either use client provided UUID or search for a first available
2752 # if both are not defined we return none
2753 if parent_network_uuid is not None:
2754 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2755 add_vdc_rest_url = ''.join(url_list)
2756
2757 #Creating all networks as Direct Org VDC type networks.
2758 #Unused in case of Underlay (data/ptp) network interface.
2759 fence_mode="bridged"
2760 is_inherited='false'
2761 dns_list = dns_address.split(";")
2762 dns1 = dns_list[0]
2763 dns2_text = ""
2764 if len(dns_list) >= 2:
2765 dns2_text = "\n <Dns2>{}</Dns2>\n".format(dns_list[1])
2766 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2767 <Description>Openmano created</Description>
2768 <Configuration>
2769 <IpScopes>
2770 <IpScope>
2771 <IsInherited>{1:s}</IsInherited>
2772 <Gateway>{2:s}</Gateway>
2773 <Netmask>{3:s}</Netmask>
2774 <Dns1>{4:s}</Dns1>{5:s}
2775 <IsEnabled>{6:s}</IsEnabled>
2776 <IpRanges>
2777 <IpRange>
2778 <StartAddress>{7:s}</StartAddress>
2779 <EndAddress>{8:s}</EndAddress>
2780 </IpRange>
2781 </IpRanges>
2782 </IpScope>
2783 </IpScopes>
2784 <ParentNetwork href="{9:s}"/>
2785 <FenceMode>{10:s}</FenceMode>
2786 </Configuration>
2787 <IsShared>{11:s}</IsShared>
2788 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2789 subnet_address, dns1, dns2_text, dhcp_enabled,
2790 dhcp_start_address, dhcp_end_address, available_networks,
2791 fence_mode, isshared)
2792
2793 headers = vca.vcloud_session.get_vcloud_headers()
2794 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2795 try:
2796 response = Http.post(url=add_vdc_rest_url,
2797 headers=headers,
2798 data=data,
2799 verify=vca.verify,
2800 logger=vca.logger)
2801
2802 if response.status_code != 201:
2803 self.logger.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2804 .format(response.status_code,response.content))
2805 else:
2806 network = networkType.parseString(response.content, True)
2807 create_nw_task = network.get_Tasks().get_Task()[0]
2808
2809 # if we all ok we respond with content after network creation completes
2810 # otherwise by default return None
2811 if create_nw_task is not None:
2812 self.logger.debug("Create Network REST : Waiting for Network creation complete")
2813 status = vca.block_until_completed(create_nw_task)
2814 if status:
2815 return response.content
2816 else:
2817 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2818 .format(response.content))
2819 except Exception as exp:
2820 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2821
2822 return None
2823
2824 def convert_cidr_to_netmask(self, cidr_ip=None):
2825 """
2826 Method sets convert CIDR netmask address to normal IP format
2827 Args:
2828 cidr_ip : CIDR IP address
2829 Returns:
2830 netmask : Converted netmask
2831 """
2832 if cidr_ip is not None:
2833 if '/' in cidr_ip:
2834 network, net_bits = cidr_ip.split('/')
2835 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2836 else:
2837 netmask = cidr_ip
2838 return netmask
2839 return None
2840
2841 def get_provider_rest(self, vca=None):
2842 """
2843 Method gets provider vdc view from vcloud director
2844
2845 Args:
2846 network_name - is network name to be created.
2847 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2848 It optional attribute. by default if no parent network indicate the first available will be used.
2849
2850 Returns:
2851 The return xml content of respond or None
2852 """
2853
2854 url_list = [vca.host, '/api/admin']
2855 response = Http.get(url=''.join(url_list),
2856 headers=vca.vcloud_session.get_vcloud_headers(),
2857 verify=vca.verify,
2858 logger=vca.logger)
2859
2860 if response.status_code == requests.codes.ok:
2861 return response.content
2862 return None
2863
2864 def create_vdc(self, vdc_name=None):
2865
2866 vdc_dict = {}
2867
2868 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2869 if xml_content is not None:
2870 try:
2871 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2872 for child in task_resp_xmlroot:
2873 if child.tag.split("}")[1] == 'Owner':
2874 vdc_id = child.attrib.get('href').split("/")[-1]
2875 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2876 return vdc_dict
2877 except:
2878 self.logger.debug("Respond body {}".format(xml_content))
2879
2880 return None
2881
2882 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2883 """
2884 Method create vdc in vCloud director based on VDC template.
2885 it uses pre-defined template that must be named openmano
2886
2887 Args:
2888 vdc_name - name of a new vdc.
2889
2890 Returns:
2891 The return xml content of respond or None
2892 """
2893
2894 self.logger.info("Creating new vdc {}".format(vdc_name))
2895 vca = self.connect()
2896 if not vca:
2897 raise vimconn.vimconnConnectionException("self.connect() is failed")
2898 if vdc_name is None:
2899 return None
2900
2901 url_list = [vca.host, '/api/vdcTemplates']
2902 vm_list_rest_call = ''.join(url_list)
2903 response = Http.get(url=vm_list_rest_call,
2904 headers=vca.vcloud_session.get_vcloud_headers(),
2905 verify=vca.verify,
2906 logger=vca.logger)
2907
2908 # container url to a template
2909 vdc_template_ref = None
2910 try:
2911 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2912 for child in vm_list_xmlroot:
2913 # application/vnd.vmware.admin.providervdc+xml
2914 # we need find a template from witch we instantiate VDC
2915 if child.tag.split("}")[1] == 'VdcTemplate':
2916 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2917 'name') == 'openmano':
2918 vdc_template_ref = child.attrib.get('href')
2919 except:
2920 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2921 self.logger.debug("Respond body {}".format(response.content))
2922 return None
2923
2924 # if we didn't found required pre defined template we return None
2925 if vdc_template_ref is None:
2926 return None
2927
2928 try:
2929 # instantiate vdc
2930 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2931 vm_list_rest_call = ''.join(url_list)
2932 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2933 <Source href="{1:s}"></Source>
2934 <Description>opnemano</Description>
2935 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2936 headers = vca.vcloud_session.get_vcloud_headers()
2937 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2938 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2939 logger=vca.logger)
2940 # if we all ok we respond with content otherwise by default None
2941 if response.status_code >= 200 and response.status_code < 300:
2942 return response.content
2943 return None
2944 except:
2945 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2946 self.logger.debug("Respond body {}".format(response.content))
2947
2948 return None
2949
2950 def create_vdc_rest(self, vdc_name=None):
2951 """
2952 Method create network in vCloud director
2953
2954 Args:
2955 network_name - is network name to be created.
2956 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2957 It optional attribute. by default if no parent network indicate the first available will be used.
2958
2959 Returns:
2960 The return network uuid or return None
2961 """
2962
2963 self.logger.info("Creating new vdc {}".format(vdc_name))
2964
2965 vca = self.connect_as_admin()
2966 if not vca:
2967 raise vimconn.vimconnConnectionException("self.connect() is failed")
2968 if vdc_name is None:
2969 return None
2970
2971 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2972 vm_list_rest_call = ''.join(url_list)
2973 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2974 response = Http.get(url=vm_list_rest_call,
2975 headers=vca.vcloud_session.get_vcloud_headers(),
2976 verify=vca.verify,
2977 logger=vca.logger)
2978
2979 provider_vdc_ref = None
2980 add_vdc_rest_url = None
2981 available_networks = None
2982
2983 if response.status_code != requests.codes.ok:
2984 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2985 response.status_code))
2986 return None
2987 else:
2988 try:
2989 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2990 for child in vm_list_xmlroot:
2991 # application/vnd.vmware.admin.providervdc+xml
2992 if child.tag.split("}")[1] == 'Link':
2993 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2994 and child.attrib.get('rel') == 'add':
2995 add_vdc_rest_url = child.attrib.get('href')
2996 except:
2997 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2998 self.logger.debug("Respond body {}".format(response.content))
2999 return None
3000
3001 response = self.get_provider_rest(vca=vca)
3002 try:
3003 vm_list_xmlroot = XmlElementTree.fromstring(response)
3004 for child in vm_list_xmlroot:
3005 if child.tag.split("}")[1] == 'ProviderVdcReferences':
3006 for sub_child in child:
3007 provider_vdc_ref = sub_child.attrib.get('href')
3008 except:
3009 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
3010 self.logger.debug("Respond body {}".format(response))
3011 return None
3012
3013 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
3014 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
3015 <AllocationModel>ReservationPool</AllocationModel>
3016 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
3017 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
3018 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
3019 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
3020 <ProviderVdcReference
3021 name="Main Provider"
3022 href="{2:s}" />
3023 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
3024 escape(vdc_name),
3025 provider_vdc_ref)
3026
3027 headers = vca.vcloud_session.get_vcloud_headers()
3028 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
3029 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
3030 logger=vca.logger)
3031
3032 # if we all ok we respond with content otherwise by default None
3033 if response.status_code == 201:
3034 return response.content
3035 return None
3036
3037 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
3038 """
3039 Method retrieve vapp detail from vCloud director
3040
3041 Args:
3042 vapp_uuid - is vapp identifier.
3043
3044 Returns:
3045 The return network uuid or return None
3046 """
3047
3048 parsed_respond = {}
3049 vca = None
3050
3051 if need_admin_access:
3052 vca = self.connect_as_admin()
3053 else:
3054 vca = self.connect()
3055
3056 if not vca:
3057 raise vimconn.vimconnConnectionException("self.connect() is failed")
3058 if vapp_uuid is None:
3059 return None
3060
3061 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
3062 get_vapp_restcall = ''.join(url_list)
3063
3064 if vca.vcloud_session and vca.vcloud_session.organization:
3065 response = Http.get(url=get_vapp_restcall,
3066 headers=vca.vcloud_session.get_vcloud_headers(),
3067 verify=vca.verify,
3068 logger=vca.logger)
3069
3070 if response.status_code != requests.codes.ok:
3071 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
3072 response.status_code))
3073 return parsed_respond
3074
3075 try:
3076 xmlroot_respond = XmlElementTree.fromstring(response.content)
3077 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
3078
3079 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
3080 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
3081 'vmw': 'http://www.vmware.com/schema/ovf',
3082 'vm': 'http://www.vmware.com/vcloud/v1.5',
3083 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3084 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3085 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3086 }
3087
3088 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
3089 if created_section is not None:
3090 parsed_respond['created'] = created_section.text
3091
3092 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
3093 if network_section is not None and 'networkName' in network_section.attrib:
3094 parsed_respond['networkname'] = network_section.attrib['networkName']
3095
3096 ipscopes_section = \
3097 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3098 namespaces)
3099 if ipscopes_section is not None:
3100 for ipscope in ipscopes_section:
3101 for scope in ipscope:
3102 tag_key = scope.tag.split("}")[1]
3103 if tag_key == 'IpRanges':
3104 ip_ranges = scope.getchildren()
3105 for ipblock in ip_ranges:
3106 for block in ipblock:
3107 parsed_respond[block.tag.split("}")[1]] = block.text
3108 else:
3109 parsed_respond[tag_key] = scope.text
3110
3111 # parse children section for other attrib
3112 children_section = xmlroot_respond.find('vm:Children/', namespaces)
3113 if children_section is not None:
3114 parsed_respond['name'] = children_section.attrib['name']
3115 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
3116 if "nestedHypervisorEnabled" in children_section.attrib else None
3117 parsed_respond['deployed'] = children_section.attrib['deployed']
3118 parsed_respond['status'] = children_section.attrib['status']
3119 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
3120 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
3121 nic_list = []
3122 for adapters in network_adapter:
3123 adapter_key = adapters.tag.split("}")[1]
3124 if adapter_key == 'PrimaryNetworkConnectionIndex':
3125 parsed_respond['primarynetwork'] = adapters.text
3126 if adapter_key == 'NetworkConnection':
3127 vnic = {}
3128 if 'network' in adapters.attrib:
3129 vnic['network'] = adapters.attrib['network']
3130 for adapter in adapters:
3131 setting_key = adapter.tag.split("}")[1]
3132 vnic[setting_key] = adapter.text
3133 nic_list.append(vnic)
3134
3135 for link in children_section:
3136 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3137 if link.attrib['rel'] == 'screen:acquireTicket':
3138 parsed_respond['acquireTicket'] = link.attrib
3139 if link.attrib['rel'] == 'screen:acquireMksTicket':
3140 parsed_respond['acquireMksTicket'] = link.attrib
3141
3142 parsed_respond['interfaces'] = nic_list
3143 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
3144 if vCloud_extension_section is not None:
3145 vm_vcenter_info = {}
3146 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
3147 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
3148 if vmext is not None:
3149 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
3150 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
3151
3152 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
3153 vm_virtual_hardware_info = {}
3154 if virtual_hardware_section is not None:
3155 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
3156 if item.find("rasd:Description",namespaces).text == "Hard disk":
3157 disk_size = item.find("rasd:HostResource" ,namespaces
3158 ).attrib["{"+namespaces['vm']+"}capacity"]
3159
3160 vm_virtual_hardware_info["disk_size"]= disk_size
3161 break
3162
3163 for link in virtual_hardware_section:
3164 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3165 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
3166 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
3167 break
3168
3169 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
3170 except Exception as exp :
3171 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
3172 return parsed_respond
3173
3174 def acuire_console(self, vm_uuid=None):
3175
3176 vca = self.connect()
3177 if not vca:
3178 raise vimconn.vimconnConnectionException("self.connect() is failed")
3179 if vm_uuid is None:
3180 return None
3181
3182 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
3183 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
3184 console_dict = vm_dict['acquireTicket']
3185 console_rest_call = console_dict['href']
3186
3187 response = Http.post(url=console_rest_call,
3188 headers=vca.vcloud_session.get_vcloud_headers(),
3189 verify=vca.verify,
3190 logger=vca.logger)
3191
3192 if response.status_code == requests.codes.ok:
3193 return response.content
3194
3195 return None
3196
3197 def modify_vm_disk(self, vapp_uuid, flavor_disk):
3198 """
3199 Method retrieve vm disk details
3200
3201 Args:
3202 vapp_uuid - is vapp identifier.
3203 flavor_disk - disk size as specified in VNFD (flavor)
3204
3205 Returns:
3206 The return network uuid or return None
3207 """
3208 status = None
3209 try:
3210 #Flavor disk is in GB convert it into MB
3211 flavor_disk = int(flavor_disk) * 1024
3212 vm_details = self.get_vapp_details_rest(vapp_uuid)
3213 if vm_details:
3214 vm_name = vm_details["name"]
3215 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
3216
3217 if vm_details and "vm_virtual_hardware" in vm_details:
3218 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
3219 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
3220
3221 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
3222
3223 if flavor_disk > vm_disk:
3224 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
3225 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
3226 vm_disk, flavor_disk ))
3227 else:
3228 status = True
3229 self.logger.info("No need to modify disk of VM {}".format(vm_name))
3230
3231 return status
3232 except Exception as exp:
3233 self.logger.info("Error occurred while modifing disk size {}".format(exp))
3234
3235
3236 def modify_vm_disk_rest(self, disk_href , disk_size):
3237 """
3238 Method retrieve modify vm disk size
3239
3240 Args:
3241 disk_href - vCD API URL to GET and PUT disk data
3242 disk_size - disk size as specified in VNFD (flavor)
3243
3244 Returns:
3245 The return network uuid or return None
3246 """
3247 vca = self.connect()
3248 if not vca:
3249 raise vimconn.vimconnConnectionException("self.connect() is failed")
3250 if disk_href is None or disk_size is None:
3251 return None
3252
3253 if vca.vcloud_session and vca.vcloud_session.organization:
3254 response = Http.get(url=disk_href,
3255 headers=vca.vcloud_session.get_vcloud_headers(),
3256 verify=vca.verify,
3257 logger=vca.logger)
3258
3259 if response.status_code != requests.codes.ok:
3260 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
3261 response.status_code))
3262 return None
3263 try:
3264 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
3265 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
3266 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3267
3268 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
3269 if item.find("rasd:Description",namespaces).text == "Hard disk":
3270 disk_item = item.find("rasd:HostResource" ,namespaces )
3271 if disk_item is not None:
3272 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
3273 break
3274
3275 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
3276 xml_declaration=True)
3277
3278 #Send PUT request to modify disk size
3279 headers = vca.vcloud_session.get_vcloud_headers()
3280 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3281
3282 response = Http.put(url=disk_href,
3283 data=data,
3284 headers=headers,
3285 verify=vca.verify, logger=self.logger)
3286
3287 if response.status_code != 202:
3288 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
3289 response.status_code))
3290 else:
3291 modify_disk_task = taskType.parseString(response.content, True)
3292 if type(modify_disk_task) is GenericTask:
3293 status = vca.block_until_completed(modify_disk_task)
3294 return status
3295
3296 return None
3297
3298 except Exception as exp :
3299 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
3300 return None
3301
3302 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
3303 """
3304 Method to attach pci devices to VM
3305
3306 Args:
3307 vapp_uuid - uuid of vApp/VM
3308 pci_devices - pci devices infromation as specified in VNFD (flavor)
3309
3310 Returns:
3311 The status of add pci device task , vm object and
3312 vcenter_conect object
3313 """
3314 vm_obj = None
3315 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3316 vcenter_conect, content = self.get_vcenter_content()
3317 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
3318
3319 if vm_moref_id:
3320 try:
3321 no_of_pci_devices = len(pci_devices)
3322 if no_of_pci_devices > 0:
3323 #Get VM and its host
3324 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
3325 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3326 if host_obj and vm_obj:
3327 #get PCI devies from host on which vapp is currently installed
3328 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3329
3330 if avilable_pci_devices is None:
3331 #find other hosts with active pci devices
3332 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3333 content,
3334 no_of_pci_devices
3335 )
3336
3337 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3338 #Migrate vm to the host where PCI devices are availble
3339 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3340 task = self.relocate_vm(new_host_obj, vm_obj)
3341 if task is not None:
3342 result = self.wait_for_vcenter_task(task, vcenter_conect)
3343 self.logger.info("Migrate VM status: {}".format(result))
3344 host_obj = new_host_obj
3345 else:
3346 self.logger.info("Fail to migrate VM : {}".format(result))
3347 raise vimconn.vimconnNotFoundException(
3348 "Fail to migrate VM : {} to host {}".format(
3349 vmname_andid,
3350 new_host_obj)
3351 )
3352
3353 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3354 #Add PCI devices one by one
3355 for pci_device in avilable_pci_devices:
3356 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3357 if task:
3358 status= self.wait_for_vcenter_task(task, vcenter_conect)
3359 if status:
3360 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3361 else:
3362 self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3363 return True, vm_obj, vcenter_conect
3364 else:
3365 self.logger.error("Currently there is no host with"\
3366 " {} number of avaialble PCI devices required for VM {}".format(
3367 no_of_pci_devices,
3368 vmname_andid)
3369 )
3370 raise vimconn.vimconnNotFoundException(
3371 "Currently there is no host with {} "\
3372 "number of avaialble PCI devices required for VM {}".format(
3373 no_of_pci_devices,
3374 vmname_andid))
3375 else:
3376 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3377
3378 except vmodl.MethodFault as error:
3379 self.logger.error("Error occurred while adding PCI devices {} ",error)
3380 return None, vm_obj, vcenter_conect
3381
3382 def get_vm_obj(self, content, mob_id):
3383 """
3384 Method to get the vsphere VM object associated with a given morf ID
3385 Args:
3386 vapp_uuid - uuid of vApp/VM
3387 content - vCenter content object
3388 mob_id - mob_id of VM
3389
3390 Returns:
3391 VM and host object
3392 """
3393 vm_obj = None
3394 host_obj = None
3395 try :
3396 container = content.viewManager.CreateContainerView(content.rootFolder,
3397 [vim.VirtualMachine], True
3398 )
3399 for vm in container.view:
3400 mobID = vm._GetMoId()
3401 if mobID == mob_id:
3402 vm_obj = vm
3403 host_obj = vm_obj.runtime.host
3404 break
3405 except Exception as exp:
3406 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3407 return host_obj, vm_obj
3408
3409 def get_pci_devices(self, host, need_devices):
3410 """
3411 Method to get the details of pci devices on given host
3412 Args:
3413 host - vSphere host object
3414 need_devices - number of pci devices needed on host
3415
3416 Returns:
3417 array of pci devices
3418 """
3419 all_devices = []
3420 all_device_ids = []
3421 used_devices_ids = []
3422
3423 try:
3424 if host:
3425 pciPassthruInfo = host.config.pciPassthruInfo
3426 pciDevies = host.hardware.pciDevice
3427
3428 for pci_status in pciPassthruInfo:
3429 if pci_status.passthruActive:
3430 for device in pciDevies:
3431 if device.id == pci_status.id:
3432 all_device_ids.append(device.id)
3433 all_devices.append(device)
3434
3435 #check if devices are in use
3436 avalible_devices = all_devices
3437 for vm in host.vm:
3438 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3439 vm_devices = vm.config.hardware.device
3440 for device in vm_devices:
3441 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3442 if device.backing.id in all_device_ids:
3443 for use_device in avalible_devices:
3444 if use_device.id == device.backing.id:
3445 avalible_devices.remove(use_device)
3446 used_devices_ids.append(device.backing.id)
3447 self.logger.debug("Device {} from devices {}"\
3448 "is in use".format(device.backing.id,
3449 device)
3450 )
3451 if len(avalible_devices) < need_devices:
3452 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3453 need_devices))
3454 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3455 avalible_devices))
3456 return None
3457 else:
3458 required_devices = avalible_devices[:need_devices]
3459 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3460 len(avalible_devices),
3461 host,
3462 need_devices))
3463 self.logger.info("Retruning {} devices as {}".format(need_devices,
3464 required_devices ))
3465 return required_devices
3466
3467 except Exception as exp:
3468 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3469
3470 return None
3471
3472 def get_host_and_PCIdevices(self, content, need_devices):
3473 """
3474 Method to get the details of pci devices infromation on all hosts
3475
3476 Args:
3477 content - vSphere host object
3478 need_devices - number of pci devices needed on host
3479
3480 Returns:
3481 array of pci devices and host object
3482 """
3483 host_obj = None
3484 pci_device_objs = None
3485 try:
3486 if content:
3487 container = content.viewManager.CreateContainerView(content.rootFolder,
3488 [vim.HostSystem], True)
3489 for host in container.view:
3490 devices = self.get_pci_devices(host, need_devices)
3491 if devices:
3492 host_obj = host
3493 pci_device_objs = devices
3494 break
3495 except Exception as exp:
3496 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3497
3498 return host_obj,pci_device_objs
3499
3500 def relocate_vm(self, dest_host, vm) :
3501 """
3502 Method to get the relocate VM to new host
3503
3504 Args:
3505 dest_host - vSphere host object
3506 vm - vSphere VM object
3507
3508 Returns:
3509 task object
3510 """
3511 task = None
3512 try:
3513 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3514 task = vm.Relocate(relocate_spec)
3515 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3516 except Exception as exp:
3517 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3518 dest_host, vm, exp))
3519 return task
3520
3521 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3522 """
3523 Waits and provides updates on a vSphere task
3524 """
3525 while task.info.state == vim.TaskInfo.State.running:
3526 time.sleep(2)
3527
3528 if task.info.state == vim.TaskInfo.State.success:
3529 if task.info.result is not None and not hideResult:
3530 self.logger.info('{} completed successfully, result: {}'.format(
3531 actionName,
3532 task.info.result))
3533 else:
3534 self.logger.info('Task {} completed successfully.'.format(actionName))
3535 else:
3536 self.logger.error('{} did not complete successfully: {} '.format(
3537 actionName,
3538 task.info.error)
3539 )
3540
3541 return task.info.result
3542
3543 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3544 """
3545 Method to add pci device in given VM
3546
3547 Args:
3548 host_object - vSphere host object
3549 vm_object - vSphere VM object
3550 host_pci_dev - host_pci_dev must be one of the devices from the
3551 host_object.hardware.pciDevice list
3552 which is configured as a PCI passthrough device
3553
3554 Returns:
3555 task object
3556 """
3557 task = None
3558 if vm_object and host_object and host_pci_dev:
3559 try :
3560 #Add PCI device to VM
3561 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3562 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3563
3564 if host_pci_dev.id not in systemid_by_pciid:
3565 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3566 return None
3567
3568 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3569 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3570 id=host_pci_dev.id,
3571 systemId=systemid_by_pciid[host_pci_dev.id],
3572 vendorId=host_pci_dev.vendorId,
3573 deviceName=host_pci_dev.deviceName)
3574
3575 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3576
3577 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3578 new_device_config.operation = "add"
3579 vmConfigSpec = vim.vm.ConfigSpec()
3580 vmConfigSpec.deviceChange = [new_device_config]
3581
3582 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3583 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3584 host_pci_dev, vm_object, host_object)
3585 )
3586 except Exception as exp:
3587 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3588 host_pci_dev,
3589 vm_object,
3590 exp))
3591 return task
3592
3593 def get_vm_vcenter_info(self):
3594 """
3595 Method to get details of vCenter and vm
3596
3597 Args:
3598 vapp_uuid - uuid of vApp or VM
3599
3600 Returns:
3601 Moref Id of VM and deails of vCenter
3602 """
3603 vm_vcenter_info = {}
3604
3605 if self.vcenter_ip is not None:
3606 vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
3607 else:
3608 raise vimconn.vimconnException(message="vCenter IP is not provided."\
3609 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3610 if self.vcenter_port is not None:
3611 vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
3612 else:
3613 raise vimconn.vimconnException(message="vCenter port is not provided."\
3614 " Please provide vCenter port while attaching datacenter to tenant in --config")
3615 if self.vcenter_user is not None:
3616 vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
3617 else:
3618 raise vimconn.vimconnException(message="vCenter user is not provided."\
3619 " Please provide vCenter user while attaching datacenter to tenant in --config")
3620
3621 if self.vcenter_password is not None:
3622 vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
3623 else:
3624 raise vimconn.vimconnException(message="vCenter user password is not provided."\
3625 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3626
3627 return vm_vcenter_info
3628
3629
3630 def get_vm_pci_details(self, vmuuid):
3631 """
3632 Method to get VM PCI device details from vCenter
3633
3634 Args:
3635 vm_obj - vSphere VM object
3636
3637 Returns:
3638 dict of PCI devives attached to VM
3639
3640 """
3641 vm_pci_devices_info = {}
3642 try:
3643 vcenter_conect, content = self.get_vcenter_content()
3644 vm_moref_id = self.get_vm_moref_id(vmuuid)
3645 if vm_moref_id:
3646 #Get VM and its host
3647 if content:
3648 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
3649 if host_obj and vm_obj:
3650 vm_pci_devices_info["host_name"]= host_obj.name
3651 vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
3652 for device in vm_obj.config.hardware.device:
3653 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3654 device_details={'devide_id':device.backing.id,
3655 'pciSlotNumber':device.slotInfo.pciSlotNumber,
3656 }
3657 vm_pci_devices_info[device.deviceInfo.label] = device_details
3658 else:
3659 self.logger.error("Can not connect to vCenter while getting "\
3660 "PCI devices infromationn")
3661 return vm_pci_devices_info
3662 except Exception as exp:
3663 self.logger.error("Error occurred while getting VM infromationn"\
3664 " for VM : {}".format(exp))
3665 raise vimconn.vimconnException(message=exp)
3666
3667 def add_network_adapter_to_vms(self, vapp, network_name, primary_nic_index, nicIndex, net, nic_type=None):
3668 """
3669 Method to add network adapter type to vm
3670 Args :
3671 network_name - name of network
3672 primary_nic_index - int value for primary nic index
3673 nicIndex - int value for nic index
3674 nic_type - specify model name to which add to vm
3675 Returns:
3676 None
3677 """
3678 vca = self.connect()
3679 if not vca:
3680 raise vimconn.vimconnConnectionException("Failed to connect vCloud director")
3681
3682 try:
3683 ip_address = None
3684 floating_ip = False
3685 if 'floating_ip' in net: floating_ip = net['floating_ip']
3686
3687 # Stub for ip_address feature
3688 if 'ip_address' in net: ip_address = net['ip_address']
3689
3690 if floating_ip:
3691 allocation_mode = "POOL"
3692 elif ip_address:
3693 allocation_mode = "MANUAL"
3694 else:
3695 allocation_mode = "DHCP"
3696
3697 if not nic_type:
3698 for vms in vapp._get_vms():
3699 vm_id = (vms.id).split(':')[-1]
3700
3701 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3702
3703 response = Http.get(url=url_rest_call,
3704 headers=vca.vcloud_session.get_vcloud_headers(),
3705 verify=vca.verify,
3706 logger=vca.logger)
3707 if response.status_code != 200:
3708 self.logger.error("REST call {} failed reason : {}"\
3709 "status code : {}".format(url_rest_call,
3710 response.content,
3711 response.status_code))
3712 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3713 "network connection section")
3714
3715 data = response.content
3716 if '<PrimaryNetworkConnectionIndex>' not in data:
3717 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3718 <NetworkConnection network="{}">
3719 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3720 <IsConnected>true</IsConnected>
3721 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3722 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3723 allocation_mode)
3724 # Stub for ip_address feature
3725 if ip_address:
3726 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3727 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3728
3729 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3730 else:
3731 new_item = """<NetworkConnection network="{}">
3732 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3733 <IsConnected>true</IsConnected>
3734 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3735 </NetworkConnection>""".format(network_name, nicIndex,
3736 allocation_mode)
3737 # Stub for ip_address feature
3738 if ip_address:
3739 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3740 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3741
3742 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3743
3744 headers = vca.vcloud_session.get_vcloud_headers()
3745 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3746 response = Http.put(url=url_rest_call, headers=headers, data=data,
3747 verify=vca.verify,
3748 logger=vca.logger)
3749 if response.status_code != 202:
3750 self.logger.error("REST call {} failed reason : {}"\
3751 "status code : {} ".format(url_rest_call,
3752 response.content,
3753 response.status_code))
3754 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3755 "network connection section")
3756 else:
3757 nic_task = taskType.parseString(response.content, True)
3758 if isinstance(nic_task, GenericTask):
3759 vca.block_until_completed(nic_task)
3760 self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\
3761 "default NIC type".format(vm_id))
3762 else:
3763 self.logger.error("add_network_adapter_to_vms(): VM {} failed to "\
3764 "connect NIC type".format(vm_id))
3765 else:
3766 for vms in vapp._get_vms():
3767 vm_id = (vms.id).split(':')[-1]
3768
3769 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3770
3771 response = Http.get(url=url_rest_call,
3772 headers=vca.vcloud_session.get_vcloud_headers(),
3773 verify=vca.verify,
3774 logger=vca.logger)
3775 if response.status_code != 200:
3776 self.logger.error("REST call {} failed reason : {}"\
3777 "status code : {}".format(url_rest_call,
3778 response.content,
3779 response.status_code))
3780 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3781 "network connection section")
3782 data = response.content
3783 if '<PrimaryNetworkConnectionIndex>' not in data:
3784 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3785 <NetworkConnection network="{}">
3786 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3787 <IsConnected>true</IsConnected>
3788 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3789 <NetworkAdapterType>{}</NetworkAdapterType>
3790 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3791 allocation_mode, nic_type)
3792 # Stub for ip_address feature
3793 if ip_address:
3794 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3795 item = item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3796
3797 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3798 else:
3799 new_item = """<NetworkConnection network="{}">
3800 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3801 <IsConnected>true</IsConnected>
3802 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3803 <NetworkAdapterType>{}</NetworkAdapterType>
3804 </NetworkConnection>""".format(network_name, nicIndex,
3805 allocation_mode, nic_type)
3806 # Stub for ip_address feature
3807 if ip_address:
3808 ip_tag = '<IpAddress>{}</IpAddress>'.format(ip_address)
3809 new_item = new_item.replace('</NetworkConnectionIndex>\n','</NetworkConnectionIndex>\n{}\n'.format(ip_tag))
3810
3811 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3812
3813 headers = vca.vcloud_session.get_vcloud_headers()
3814 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3815 response = Http.put(url=url_rest_call, headers=headers, data=data,
3816 verify=vca.verify,
3817 logger=vca.logger)
3818
3819 if response.status_code != 202:
3820 self.logger.error("REST call {} failed reason : {}"\
3821 "status code : {}".format(url_rest_call,
3822 response.content,
3823 response.status_code))
3824 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3825 "network connection section")
3826 else:
3827 nic_task = taskType.parseString(response.content, True)
3828 if isinstance(nic_task, GenericTask):
3829 vca.block_until_completed(nic_task)
3830 self.logger.info("add_network_adapter_to_vms(): VM {} "\
3831 "conneced to NIC type {}".format(vm_id, nic_type))
3832 else:
3833 self.logger.error("add_network_adapter_to_vms(): VM {} "\
3834 "failed to connect NIC type {}".format(vm_id, nic_type))
3835 except Exception as exp:
3836 self.logger.error("add_network_adapter_to_vms() : exception occurred "\
3837 "while adding Network adapter")
3838 raise vimconn.vimconnException(message=exp)
3839
3840
3841 def set_numa_affinity(self, vmuuid, paired_threads_id):
3842 """
3843 Method to assign numa affinity in vm configuration parammeters
3844 Args :
3845 vmuuid - vm uuid
3846 paired_threads_id - one or more virtual processor
3847 numbers
3848 Returns:
3849 return if True
3850 """
3851 try:
3852 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
3853 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3854 context = None
3855 if hasattr(ssl, '_create_unverified_context'):
3856 context = ssl._create_unverified_context()
3857 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3858 pwd=self.passwd, port=int(vm_vcenter_port),
3859 sslContext=context)
3860 atexit.register(Disconnect, vcenter_conect)
3861 content = vcenter_conect.RetrieveContent()
3862
3863 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3864 if vm_obj:
3865 config_spec = vim.vm.ConfigSpec()
3866 config_spec.extraConfig = []
3867 opt = vim.option.OptionValue()
3868 opt.key = 'numa.nodeAffinity'
3869 opt.value = str(paired_threads_id)
3870 config_spec.extraConfig.append(opt)
3871 task = vm_obj.ReconfigVM_Task(config_spec)
3872 if task:
3873 result = self.wait_for_vcenter_task(task, vcenter_conect)
3874 extra_config = vm_obj.config.extraConfig
3875 flag = False
3876 for opts in extra_config:
3877 if 'numa.nodeAffinity' in opts.key:
3878 flag = True
3879 self.logger.info("set_numa_affinity: Sucessfully assign numa affinity "\
3880 "value {} for vm {}".format(opt.value, vm_obj))
3881 if flag:
3882 return
3883 else:
3884 self.logger.error("set_numa_affinity: Failed to assign numa affinity")
3885 except Exception as exp:
3886 self.logger.error("set_numa_affinity : exception occurred while setting numa affinity "\
3887 "for VM {} : {}".format(vm_obj, vm_moref_id))
3888 raise vimconn.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
3889 "affinity".format(exp))
3890
3891
3892
3893 def cloud_init(self, vapp, cloud_config):
3894 """
3895 Method to inject ssh-key
3896 vapp - vapp object
3897 cloud_config a dictionary with:
3898 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
3899 'users': (optional) list of users to be inserted, each item is a dict with:
3900 'name': (mandatory) user name,
3901 'key-pairs': (optional) list of strings with the public key to be inserted to the user
3902 'user-data': (optional) string is a text script to be passed directly to cloud-init
3903 'config-files': (optional). List of files to be transferred. Each item is a dict with:
3904 'dest': (mandatory) string with the destination absolute path
3905 'encoding': (optional, by default text). Can be one of:
3906 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
3907 'content' (mandatory): string with the content of the file
3908 'permissions': (optional) string with file permissions, typically octal notation '0644'
3909 'owner': (optional) file owner, string with the format 'owner:group'
3910 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
3911 """
3912 vca = self.connect()
3913 if not vca:
3914 raise vimconn.vimconnConnectionException("Failed to connect vCloud director")
3915
3916 try:
3917 if isinstance(cloud_config, dict):
3918 key_pairs = []
3919 userdata = []
3920 if "key-pairs" in cloud_config:
3921 key_pairs = cloud_config["key-pairs"]
3922
3923 if "users" in cloud_config:
3924 userdata = cloud_config["users"]
3925
3926 for key in key_pairs:
3927 for user in userdata:
3928 if 'name' in user: user_name = user['name']
3929 if 'key-pairs' in user and len(user['key-pairs']) > 0:
3930 for user_key in user['key-pairs']:
3931 customize_script = """
3932 #!/bin/bash
3933 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3934 if [ "$1" = "precustomization" ];then
3935 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3936 if [ ! -d /root/.ssh ];then
3937 mkdir /root/.ssh
3938 chown root:root /root/.ssh
3939 chmod 700 /root/.ssh
3940 touch /root/.ssh/authorized_keys
3941 chown root:root /root/.ssh/authorized_keys
3942 chmod 600 /root/.ssh/authorized_keys
3943 # make centos with selinux happy
3944 which restorecon && restorecon -Rv /root/.ssh
3945 echo '{key}' >> /root/.ssh/authorized_keys
3946 else
3947 touch /root/.ssh/authorized_keys
3948 chown root:root /root/.ssh/authorized_keys
3949 chmod 600 /root/.ssh/authorized_keys
3950 echo '{key}' >> /root/.ssh/authorized_keys
3951 fi
3952 if [ -d /home/{user_name} ];then
3953 if [ ! -d /home/{user_name}/.ssh ];then
3954 mkdir /home/{user_name}/.ssh
3955 chown {user_name}:{user_name} /home/{user_name}/.ssh
3956 chmod 700 /home/{user_name}/.ssh
3957 touch /home/{user_name}/.ssh/authorized_keys
3958 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3959 chmod 600 /home/{user_name}/.ssh/authorized_keys
3960 # make centos with selinux happy
3961 which restorecon && restorecon -Rv /home/{user_name}/.ssh
3962 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3963 else
3964 touch /home/{user_name}/.ssh/authorized_keys
3965 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3966 chmod 600 /home/{user_name}/.ssh/authorized_keys
3967 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3968 fi
3969 fi
3970 fi""".format(key=key, user_name=user_name, user_key=user_key)
3971
3972 for vm in vapp._get_vms():
3973 vm_name = vm.name
3974 task = vapp.customize_guest_os(vm_name, customization_script=customize_script)
3975 if isinstance(task, GenericTask):
3976 vca.block_until_completed(task)
3977 self.logger.info("cloud_init : customized guest os task "\
3978 "completed for VM {}".format(vm_name))
3979 else:
3980 self.logger.error("cloud_init : task for customized guest os"\
3981 "failed for VM {}".format(vm_name))
3982 except Exception as exp:
3983 self.logger.error("cloud_init : exception occurred while injecting "\
3984 "ssh-key")
3985 raise vimconn.vimconnException("cloud_init : Error {} failed to inject "\
3986 "ssh-key".format(exp))
3987
3988
3989 def add_new_disk(self, vca, vapp_uuid, disk_size):
3990 """
3991 Method to create an empty vm disk
3992
3993 Args:
3994 vapp_uuid - is vapp identifier.
3995 disk_size - size of disk to be created in GB
3996
3997 Returns:
3998 None
3999 """
4000 status = False
4001 vm_details = None
4002 try:
4003 #Disk size in GB, convert it into MB
4004 if disk_size is not None:
4005 disk_size_mb = int(disk_size) * 1024
4006 vm_details = self.get_vapp_details_rest(vapp_uuid)
4007
4008 if vm_details and "vm_virtual_hardware" in vm_details:
4009 self.logger.info("Adding disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
4010 disk_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
4011 status = self.add_new_disk_rest(vca, disk_href, disk_size_mb)
4012
4013 except Exception as exp:
4014 msg = "Error occurred while creating new disk {}.".format(exp)
4015 self.rollback_newvm(vapp_uuid, msg)
4016
4017 if status:
4018 self.logger.info("Added new disk to VM: {} disk size:{}GB".format(vm_details["name"], disk_size))
4019 else:
4020 #If failed to add disk, delete VM
4021 msg = "add_new_disk: Failed to add new disk to {}".format(vm_details["name"])
4022 self.rollback_newvm(vapp_uuid, msg)
4023
4024
4025 def add_new_disk_rest(self, vca, disk_href, disk_size_mb):
4026 """
4027 Retrives vApp Disks section & add new empty disk
4028
4029 Args:
4030 disk_href: Disk section href to addd disk
4031 disk_size_mb: Disk size in MB
4032
4033 Returns: Status of add new disk task
4034 """
4035 status = False
4036 if vca.vcloud_session and vca.vcloud_session.organization:
4037 response = Http.get(url=disk_href,
4038 headers=vca.vcloud_session.get_vcloud_headers(),
4039 verify=vca.verify,
4040 logger=vca.logger)
4041
4042 if response.status_code != requests.codes.ok:
4043 self.logger.error("add_new_disk_rest: GET REST API call {} failed. Return status code {}"
4044 .format(disk_href, response.status_code))
4045 return status
4046 try:
4047 #Find but type & max of instance IDs assigned to disks
4048 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
4049 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
4050 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
4051 instance_id = 0
4052 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
4053 if item.find("rasd:Description",namespaces).text == "Hard disk":
4054 inst_id = int(item.find("rasd:InstanceID" ,namespaces).text)
4055 if inst_id > instance_id:
4056 instance_id = inst_id
4057 disk_item = item.find("rasd:HostResource" ,namespaces)
4058 bus_subtype = disk_item.attrib["{"+namespaces['xmlns']+"}busSubType"]
4059 bus_type = disk_item.attrib["{"+namespaces['xmlns']+"}busType"]
4060
4061 instance_id = instance_id + 1
4062 new_item = """<Item>
4063 <rasd:Description>Hard disk</rasd:Description>
4064 <rasd:ElementName>New disk</rasd:ElementName>
4065 <rasd:HostResource
4066 xmlns:vcloud="http://www.vmware.com/vcloud/v1.5"
4067 vcloud:capacity="{}"
4068 vcloud:busSubType="{}"
4069 vcloud:busType="{}"></rasd:HostResource>
4070 <rasd:InstanceID>{}</rasd:InstanceID>
4071 <rasd:ResourceType>17</rasd:ResourceType>
4072 </Item>""".format(disk_size_mb, bus_subtype, bus_type, instance_id)
4073
4074 new_data = response.content
4075 #Add new item at the bottom
4076 new_data = new_data.replace('</Item>\n</RasdItemsList>', '</Item>\n{}\n</RasdItemsList>'.format(new_item))
4077
4078 # Send PUT request to modify virtual hardware section with new disk
4079 headers = vca.vcloud_session.get_vcloud_headers()
4080 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
4081
4082 response = Http.put(url=disk_href,
4083 data=new_data,
4084 headers=headers,
4085 verify=vca.verify, logger=self.logger)
4086
4087 if response.status_code != 202:
4088 self.logger.error("PUT REST API call {} failed. Return status code {}. Response Content:{}"
4089 .format(disk_href, response.status_code, response.content))
4090 else:
4091 add_disk_task = taskType.parseString(response.content, True)
4092 if type(add_disk_task) is GenericTask:
4093 status = vca.block_until_completed(add_disk_task)
4094 if not status:
4095 self.logger.error("Add new disk REST task failed to add {} MB disk".format(disk_size_mb))
4096
4097 except Exception as exp:
4098 self.logger.error("Error occurred calling rest api for creating new disk {}".format(exp))
4099
4100 return status
4101
4102
4103 def add_existing_disk(self, catalogs=None, image_id=None, size=None, template_name=None, vapp_uuid=None):
4104 """
4105 Method to add existing disk to vm
4106 Args :
4107 catalogs - List of VDC catalogs
4108 image_id - Catalog ID
4109 template_name - Name of template in catalog
4110 vapp_uuid - UUID of vApp
4111 Returns:
4112 None
4113 """
4114 disk_info = None
4115 vcenter_conect, content = self.get_vcenter_content()
4116 #find moref-id of vm in image
4117 catalog_vm_info = self.get_vapp_template_details(catalogs=catalogs,
4118 image_id=image_id,
4119 )
4120
4121 if catalog_vm_info and "vm_vcenter_info" in catalog_vm_info:
4122 if "vm_moref_id" in catalog_vm_info["vm_vcenter_info"]:
4123 catalog_vm_moref_id = catalog_vm_info["vm_vcenter_info"].get("vm_moref_id", None)
4124 if catalog_vm_moref_id:
4125 self.logger.info("Moref_id of VM in catalog : {}" .format(catalog_vm_moref_id))
4126 host, catalog_vm_obj = self.get_vm_obj(content, catalog_vm_moref_id)
4127 if catalog_vm_obj:
4128 #find existing disk
4129 disk_info = self.find_disk(catalog_vm_obj)
4130 else:
4131 exp_msg = "No VM with image id {} found".format(image_id)
4132 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
4133 else:
4134 exp_msg = "No Image found with image ID {} ".format(image_id)
4135 self.rollback_newvm(vapp_uuid, exp_msg, exp_type="NotFound")
4136
4137 if disk_info:
4138 self.logger.info("Existing disk_info : {}".format(disk_info))
4139 #get VM
4140 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
4141 host, vm_obj = self.get_vm_obj(content, vm_moref_id)
4142 if vm_obj:
4143 status = self.add_disk(vcenter_conect=vcenter_conect,
4144 vm=vm_obj,
4145 disk_info=disk_info,
4146 size=size,
4147 vapp_uuid=vapp_uuid
4148 )
4149 if status:
4150 self.logger.info("Disk from image id {} added to {}".format(image_id,
4151 vm_obj.config.name)
4152 )
4153 else:
4154 msg = "No disk found with image id {} to add in VM {}".format(
4155 image_id,
4156 vm_obj.config.name)
4157 self.rollback_newvm(vapp_uuid, msg, exp_type="NotFound")
4158
4159
4160 def find_disk(self, vm_obj):
4161 """
4162 Method to find details of existing disk in VM
4163 Args :
4164 vm_obj - vCenter object of VM
4165 image_id - Catalog ID
4166 Returns:
4167 disk_info : dict of disk details
4168 """
4169 disk_info = {}
4170 if vm_obj:
4171 try:
4172 devices = vm_obj.config.hardware.device
4173 for device in devices:
4174 if type(device) is vim.vm.device.VirtualDisk:
4175 if isinstance(device.backing,vim.vm.device.VirtualDisk.FlatVer2BackingInfo) and hasattr(device.backing, 'fileName'):
4176 disk_info["full_path"] = device.backing.fileName
4177 disk_info["datastore"] = device.backing.datastore
4178 disk_info["capacityKB"] = device.capacityInKB
4179 break
4180 except Exception as exp:
4181 self.logger.error("find_disk() : exception occurred while "\
4182 "getting existing disk details :{}".format(exp))
4183 return disk_info
4184
4185
4186 def add_disk(self, vcenter_conect=None, vm=None, size=None, vapp_uuid=None, disk_info={}):
4187 """
4188 Method to add existing disk in VM
4189 Args :
4190 vcenter_conect - vCenter content object
4191 vm - vCenter vm object
4192 disk_info : dict of disk details
4193 Returns:
4194 status : status of add disk task
4195 """
4196 datastore = disk_info["datastore"] if "datastore" in disk_info else None
4197 fullpath = disk_info["full_path"] if "full_path" in disk_info else None
4198 capacityKB = disk_info["capacityKB"] if "capacityKB" in disk_info else None
4199 if size is not None:
4200 #Convert size from GB to KB
4201 sizeKB = int(size) * 1024 * 1024
4202 #compare size of existing disk and user given size.Assign whicherver is greater
4203 self.logger.info("Add Existing disk : sizeKB {} , capacityKB {}".format(
4204 sizeKB, capacityKB))
4205 if sizeKB > capacityKB:
4206 capacityKB = sizeKB
4207
4208 if datastore and fullpath and capacityKB:
4209 try:
4210 spec = vim.vm.ConfigSpec()
4211 # get all disks on a VM, set unit_number to the next available
4212 unit_number = 0
4213 for dev in vm.config.hardware.device:
4214 if hasattr(dev.backing, 'fileName'):
4215 unit_number = int(dev.unitNumber) + 1
4216 # unit_number 7 reserved for scsi controller
4217 if unit_number == 7:
4218 unit_number += 1
4219 if isinstance(dev, vim.vm.device.VirtualDisk):
4220 #vim.vm.device.VirtualSCSIController
4221 controller_key = dev.controllerKey
4222
4223 self.logger.info("Add Existing disk : unit number {} , controller key {}".format(
4224 unit_number, controller_key))
4225 # add disk here
4226 dev_changes = []
4227 disk_spec = vim.vm.device.VirtualDeviceSpec()
4228 disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
4229 disk_spec.device = vim.vm.device.VirtualDisk()
4230 disk_spec.device.backing = \
4231 vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
4232 disk_spec.device.backing.thinProvisioned = True
4233 disk_spec.device.backing.diskMode = 'persistent'
4234 disk_spec.device.backing.datastore = datastore
4235 disk_spec.device.backing.fileName = fullpath
4236
4237 disk_spec.device.unitNumber = unit_number
4238 disk_spec.device.capacityInKB = capacityKB
4239 disk_spec.device.controllerKey = controller_key
4240 dev_changes.append(disk_spec)
4241 spec.deviceChange = dev_changes
4242 task = vm.ReconfigVM_Task(spec=spec)
4243 status = self.wait_for_vcenter_task(task, vcenter_conect)
4244 return status
4245 except Exception as exp:
4246 exp_msg = "add_disk() : exception {} occurred while adding disk "\
4247 "{} to vm {}".format(exp,
4248 fullpath,
4249 vm.config.name)
4250 self.rollback_newvm(vapp_uuid, exp_msg)
4251 else:
4252 msg = "add_disk() : Can not add disk to VM with disk info {} ".format(disk_info)
4253 self.rollback_newvm(vapp_uuid, msg)
4254
4255
4256 def get_vcenter_content(self):
4257 """
4258 Get the vsphere content object
4259 """
4260 try:
4261 vm_vcenter_info = self.get_vm_vcenter_info()
4262 except Exception as exp:
4263 self.logger.error("Error occurred while getting vCenter infromationn"\
4264 " for VM : {}".format(exp))
4265 raise vimconn.vimconnException(message=exp)
4266
4267 context = None
4268 if hasattr(ssl, '_create_unverified_context'):
4269 context = ssl._create_unverified_context()
4270
4271 vcenter_conect = SmartConnect(
4272 host=vm_vcenter_info["vm_vcenter_ip"],
4273 user=vm_vcenter_info["vm_vcenter_user"],
4274 pwd=vm_vcenter_info["vm_vcenter_password"],
4275 port=int(vm_vcenter_info["vm_vcenter_port"]),
4276 sslContext=context
4277 )
4278 atexit.register(Disconnect, vcenter_conect)
4279 content = vcenter_conect.RetrieveContent()
4280 return vcenter_conect, content
4281
4282
4283 def get_vm_moref_id(self, vapp_uuid):
4284 """
4285 Get the moref_id of given VM
4286 """
4287 try:
4288 if vapp_uuid:
4289 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
4290 if vm_details and "vm_vcenter_info" in vm_details:
4291 vm_moref_id = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
4292
4293 return vm_moref_id
4294
4295 except Exception as exp:
4296 self.logger.error("Error occurred while getting VM moref ID "\
4297 " for VM : {}".format(exp))
4298 return None
4299
4300
4301 def get_vapp_template_details(self, catalogs=None, image_id=None , template_name=None):
4302 """
4303 Method to get vApp template details
4304 Args :
4305 catalogs - list of VDC catalogs
4306 image_id - Catalog ID to find
4307 template_name : template name in catalog
4308 Returns:
4309 parsed_respond : dict of vApp tempalte details
4310 """
4311 parsed_response = {}
4312
4313 vca = self.connect_as_admin()
4314 if not vca:
4315 raise vimconn.vimconnConnectionException("self.connect() is failed")
4316
4317 try:
4318 catalog = self.get_catalog_obj(image_id, catalogs)
4319 if catalog:
4320 template_name = self.get_catalogbyid(image_id, catalogs)
4321 catalog_items = filter(lambda catalogItemRef: catalogItemRef.get_name() == template_name, catalog.get_CatalogItems().get_CatalogItem())
4322 if len(catalog_items) == 1:
4323 response = Http.get(catalog_items[0].get_href(),
4324 headers=vca.vcloud_session.get_vcloud_headers(),
4325 verify=vca.verify,
4326 logger=vca.logger)
4327 catalogItem = XmlElementTree.fromstring(response.content)
4328 entity = [child for child in catalogItem if child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
4329 vapp_tempalte_href = entity.get("href")
4330 #get vapp details and parse moref id
4331
4332 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
4333 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
4334 'vmw': 'http://www.vmware.com/schema/ovf',
4335 'vm': 'http://www.vmware.com/vcloud/v1.5',
4336 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
4337 'vmext':"http://www.vmware.com/vcloud/extension/v1.5",
4338 'xmlns':"http://www.vmware.com/vcloud/v1.5"
4339 }
4340
4341 if vca.vcloud_session and vca.vcloud_session.organization:
4342 response = Http.get(url=vapp_tempalte_href,
4343 headers=vca.vcloud_session.get_vcloud_headers(),
4344 verify=vca.verify,
4345 logger=vca.logger
4346 )
4347
4348 if response.status_code != requests.codes.ok:
4349 self.logger.debug("REST API call {} failed. Return status code {}".format(
4350 vapp_tempalte_href, response.status_code))
4351
4352 else:
4353 xmlroot_respond = XmlElementTree.fromstring(response.content)
4354 children_section = xmlroot_respond.find('vm:Children/', namespaces)
4355 if children_section is not None:
4356 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
4357 if vCloud_extension_section is not None:
4358 vm_vcenter_info = {}
4359 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
4360 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
4361 if vmext is not None:
4362 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
4363 parsed_response["vm_vcenter_info"]= vm_vcenter_info
4364
4365 except Exception as exp :
4366 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
4367
4368 return parsed_response
4369
4370
4371 def rollback_newvm(self, vapp_uuid, msg , exp_type="Genric"):
4372 """
4373 Method to delete vApp
4374 Args :
4375 vapp_uuid - vApp UUID
4376 msg - Error message to be logged
4377 exp_type : Exception type
4378 Returns:
4379 None
4380 """
4381 if vapp_uuid:
4382 status = self.delete_vminstance(vapp_uuid)
4383 else:
4384 msg = "No vApp ID"
4385 self.logger.error(msg)
4386 if exp_type == "Genric":
4387 raise vimconn.vimconnException(msg)
4388 elif exp_type == "NotFound":
4389 raise vimconn.vimconnNotFoundException(message=msg)
4390
4391 def add_sriov(self, vapp_uuid, sriov_nets, vmname_andid):
4392 """
4393 Method to attach SRIOV adapters to VM
4394
4395 Args:
4396 vapp_uuid - uuid of vApp/VM
4397 sriov_nets - SRIOV devices infromation as specified in VNFD (flavor)
4398 vmname_andid - vmname
4399
4400 Returns:
4401 The status of add SRIOV adapter task , vm object and
4402 vcenter_conect object
4403 """
4404 vm_obj = None
4405 vcenter_conect, content = self.get_vcenter_content()
4406 vm_moref_id = self.get_vm_moref_id(vapp_uuid)
4407
4408 if vm_moref_id:
4409 try:
4410 no_of_sriov_devices = len(sriov_nets)
4411 if no_of_sriov_devices > 0:
4412 #Get VM and its host
4413 host_obj, vm_obj = self.get_vm_obj(content, vm_moref_id)
4414 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
4415 if host_obj and vm_obj:
4416 #get SRIOV devies from host on which vapp is currently installed
4417 avilable_sriov_devices = self.get_sriov_devices(host_obj,
4418 no_of_sriov_devices,
4419 )
4420
4421 if len(avilable_sriov_devices) == 0:
4422 #find other hosts with active pci devices
4423 new_host_obj , avilable_sriov_devices = self.get_host_and_sriov_devices(
4424 content,
4425 no_of_sriov_devices,
4426 )
4427
4428 if new_host_obj is not None and len(avilable_sriov_devices)> 0:
4429 #Migrate vm to the host where SRIOV devices are available
4430 self.logger.info("Relocate VM {} on new host {}".format(vm_obj,
4431 new_host_obj))
4432 task = self.relocate_vm(new_host_obj, vm_obj)
4433 if task is not None:
4434 result = self.wait_for_vcenter_task(task, vcenter_conect)
4435 self.logger.info("Migrate VM status: {}".format(result))
4436 host_obj = new_host_obj
4437 else:
4438 self.logger.info("Fail to migrate VM : {}".format(result))
4439 raise vimconn.vimconnNotFoundException(
4440 "Fail to migrate VM : {} to host {}".format(
4441 vmname_andid,
4442 new_host_obj)
4443 )
4444
4445 if host_obj is not None and avilable_sriov_devices is not None and len(avilable_sriov_devices)> 0:
4446 #Add SRIOV devices one by one
4447 for sriov_net in sriov_nets:
4448 network_name = sriov_net.get('net_id')
4449 dvs_portgr_name = self.create_dvPort_group(network_name)
4450 if sriov_net.get('type') == "VF":
4451 #add vlan ID ,Modify portgroup for vlan ID
4452 self.configure_vlanID(content, vcenter_conect, network_name)
4453
4454 task = self.add_sriov_to_vm(content,
4455 vm_obj,
4456 host_obj,
4457 network_name,
4458 avilable_sriov_devices[0]
4459 )
4460 if task:
4461 status= self.wait_for_vcenter_task(task, vcenter_conect)
4462 if status:
4463 self.logger.info("Added SRIOV {} to VM {}".format(
4464 no_of_sriov_devices,
4465 str(vm_obj)))
4466 else:
4467 self.logger.error("Fail to add SRIOV {} to VM {}".format(
4468 no_of_sriov_devices,
4469 str(vm_obj)))
4470 raise vimconn.vimconnUnexpectedResponse(
4471 "Fail to add SRIOV adapter in VM ".format(str(vm_obj))
4472 )
4473 return True, vm_obj, vcenter_conect
4474 else:
4475 self.logger.error("Currently there is no host with"\
4476 " {} number of avaialble SRIOV "\
4477 "VFs required for VM {}".format(
4478 no_of_sriov_devices,
4479 vmname_andid)
4480 )
4481 raise vimconn.vimconnNotFoundException(
4482 "Currently there is no host with {} "\
4483 "number of avaialble SRIOV devices required for VM {}".format(
4484 no_of_sriov_devices,
4485 vmname_andid))
4486 else:
4487 self.logger.debug("No infromation about SRIOV devices {} ",sriov_nets)
4488
4489 except vmodl.MethodFault as error:
4490 self.logger.error("Error occurred while adding SRIOV {} ",error)
4491 return None, vm_obj, vcenter_conect
4492
4493
4494 def get_sriov_devices(self,host, no_of_vfs):
4495 """
4496 Method to get the details of SRIOV devices on given host
4497 Args:
4498 host - vSphere host object
4499 no_of_vfs - number of VFs needed on host
4500
4501 Returns:
4502 array of SRIOV devices
4503 """
4504 sriovInfo=[]
4505 if host:
4506 for device in host.config.pciPassthruInfo:
4507 if isinstance(device,vim.host.SriovInfo) and device.sriovActive:
4508 if device.numVirtualFunction >= no_of_vfs:
4509 sriovInfo.append(device)
4510 break
4511 return sriovInfo
4512
4513
4514 def get_host_and_sriov_devices(self, content, no_of_vfs):
4515 """
4516 Method to get the details of SRIOV devices infromation on all hosts
4517
4518 Args:
4519 content - vSphere host object
4520 no_of_vfs - number of pci VFs needed on host
4521
4522 Returns:
4523 array of SRIOV devices and host object
4524 """
4525 host_obj = None
4526 sriov_device_objs = None
4527 try:
4528 if content:
4529 container = content.viewManager.CreateContainerView(content.rootFolder,
4530 [vim.HostSystem], True)
4531 for host in container.view:
4532 devices = self.get_sriov_devices(host, no_of_vfs)
4533 if devices:
4534 host_obj = host
4535 sriov_device_objs = devices
4536 break
4537 except Exception as exp:
4538 self.logger.error("Error {} occurred while finding SRIOV devices on host: {}".format(exp, host_obj))
4539
4540 return host_obj,sriov_device_objs
4541
4542
4543 def add_sriov_to_vm(self,content, vm_obj, host_obj, network_name, sriov_device):
4544 """
4545 Method to add SRIOV adapter to vm
4546
4547 Args:
4548 host_obj - vSphere host object
4549 vm_obj - vSphere vm object
4550 content - vCenter content object
4551 network_name - name of distributed virtaul portgroup
4552 sriov_device - SRIOV device info
4553
4554 Returns:
4555 task object
4556 """
4557 devices = []
4558 vnic_label = "sriov nic"
4559 try:
4560 dvs_portgr = self.get_dvport_group(network_name)
4561 network_name = dvs_portgr.name
4562 nic = vim.vm.device.VirtualDeviceSpec()
4563 # VM device
4564 nic.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
4565 nic.device = vim.vm.device.VirtualSriovEthernetCard()
4566 nic.device.addressType = 'assigned'
4567 #nic.device.key = 13016
4568 nic.device.deviceInfo = vim.Description()
4569 nic.device.deviceInfo.label = vnic_label
4570 nic.device.deviceInfo.summary = network_name
4571 nic.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
4572
4573 nic.device.backing.network = self.get_obj(content, [vim.Network], network_name)
4574 nic.device.backing.deviceName = network_name
4575 nic.device.backing.useAutoDetect = False
4576 nic.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
4577 nic.device.connectable.startConnected = True
4578 nic.device.connectable.allowGuestControl = True
4579
4580 nic.device.sriovBacking = vim.vm.device.VirtualSriovEthernetCard.SriovBackingInfo()
4581 nic.device.sriovBacking.physicalFunctionBacking = vim.vm.device.VirtualPCIPassthrough.DeviceBackingInfo()
4582 nic.device.sriovBacking.physicalFunctionBacking.id = sriov_device.id
4583
4584 devices.append(nic)
4585 vmconf = vim.vm.ConfigSpec(deviceChange=devices)
4586 task = vm_obj.ReconfigVM_Task(vmconf)
4587 return task
4588 except Exception as exp:
4589 self.logger.error("Error {} occurred while adding SRIOV adapter in VM: {}".format(exp, vm_obj))
4590 return None
4591
4592
4593 def create_dvPort_group(self, network_name):
4594 """
4595 Method to create disributed virtual portgroup
4596
4597 Args:
4598 network_name - name of network/portgroup
4599
4600 Returns:
4601 portgroup key
4602 """
4603 try:
4604 new_network_name = [network_name, '-', str(uuid.uuid4())]
4605 network_name=''.join(new_network_name)
4606 vcenter_conect, content = self.get_vcenter_content()
4607
4608 dv_switch = self.get_obj(content, [vim.DistributedVirtualSwitch], self.dvs_name)
4609 if dv_switch:
4610 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
4611 dv_pg_spec.name = network_name
4612
4613 dv_pg_spec.type = vim.dvs.DistributedVirtualPortgroup.PortgroupType.earlyBinding
4614 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
4615 dv_pg_spec.defaultPortConfig.securityPolicy = vim.dvs.VmwareDistributedVirtualSwitch.SecurityPolicy()
4616 dv_pg_spec.defaultPortConfig.securityPolicy.allowPromiscuous = vim.BoolPolicy(value=False)
4617 dv_pg_spec.defaultPortConfig.securityPolicy.forgedTransmits = vim.BoolPolicy(value=False)
4618 dv_pg_spec.defaultPortConfig.securityPolicy.macChanges = vim.BoolPolicy(value=False)
4619
4620 task = dv_switch.AddDVPortgroup_Task([dv_pg_spec])
4621 self.wait_for_vcenter_task(task, vcenter_conect)
4622
4623 dvPort_group = self.get_obj(content, [vim.dvs.DistributedVirtualPortgroup], network_name)
4624 if dvPort_group:
4625 self.logger.info("Created disributed virtaul port group: {}".format(dvPort_group))
4626 return dvPort_group.key
4627 else:
4628 self.logger.debug("No disributed virtual switch found with name {}".format(network_name))
4629
4630 except Exception as exp:
4631 self.logger.error("Error occurred while creating disributed virtaul port group {}"\
4632 " : {}".format(network_name, exp))
4633 return None
4634
4635 def reconfig_portgroup(self, content, dvPort_group_name , config_info={}):
4636 """
4637 Method to reconfigure disributed virtual portgroup
4638
4639 Args:
4640 dvPort_group_name - name of disributed virtual portgroup
4641 content - vCenter content object
4642 config_info - disributed virtual portgroup configuration
4643
4644 Returns:
4645 task object
4646 """
4647 try:
4648 dvPort_group = self.get_dvport_group(dvPort_group_name)
4649 if dvPort_group:
4650 dv_pg_spec = vim.dvs.DistributedVirtualPortgroup.ConfigSpec()
4651 dv_pg_spec.configVersion = dvPort_group.config.configVersion
4652 dv_pg_spec.defaultPortConfig = vim.dvs.VmwareDistributedVirtualSwitch.VmwarePortConfigPolicy()
4653 if "vlanID" in config_info:
4654 dv_pg_spec.defaultPortConfig.vlan = vim.dvs.VmwareDistributedVirtualSwitch.VlanIdSpec()
4655 dv_pg_spec.defaultPortConfig.vlan.vlanId = config_info.get('vlanID')
4656
4657 task = dvPort_group.ReconfigureDVPortgroup_Task(spec=dv_pg_spec)
4658 return task
4659 else:
4660 return None
4661 except Exception as exp:
4662 self.logger.error("Error occurred while reconfiguraing disributed virtaul port group {}"\
4663 " : {}".format(dvPort_group_name, exp))
4664 return None
4665
4666
4667 def destroy_dvport_group(self , dvPort_group_name):
4668 """
4669 Method to destroy disributed virtual portgroup
4670
4671 Args:
4672 network_name - name of network/portgroup
4673
4674 Returns:
4675 True if portgroup successfully got deleted else false
4676 """
4677 vcenter_conect, content = self.get_vcenter_content()
4678 try:
4679 status = None
4680 dvPort_group = self.get_dvport_group(dvPort_group_name)
4681 if dvPort_group:
4682 task = dvPort_group.Destroy_Task()
4683 status = self.wait_for_vcenter_task(task, vcenter_conect)
4684 return status
4685 except vmodl.MethodFault as exp:
4686 self.logger.error("Caught vmodl fault {} while deleting disributed virtaul port group {}".format(
4687 exp, dvPort_group_name))
4688 return None
4689
4690
4691 def get_dvport_group(self, dvPort_group_name):
4692 """
4693 Method to get disributed virtual portgroup
4694
4695 Args:
4696 network_name - name of network/portgroup
4697
4698 Returns:
4699 portgroup object
4700 """
4701 vcenter_conect, content = self.get_vcenter_content()
4702 dvPort_group = None
4703 try:
4704 container = content.viewManager.CreateContainerView(content.rootFolder, [vim.dvs.DistributedVirtualPortgroup], True)
4705 for item in container.view:
4706 if item.key == dvPort_group_name:
4707 dvPort_group = item
4708 break
4709 return dvPort_group
4710 except vmodl.MethodFault as exp:
4711 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4712 exp, dvPort_group_name))
4713 return None
4714
4715 def get_vlanID_from_dvs_portgr(self, dvPort_group_name):
4716 """
4717 Method to get disributed virtual portgroup vlanID
4718
4719 Args:
4720 network_name - name of network/portgroup
4721
4722 Returns:
4723 vlan ID
4724 """
4725 vlanId = None
4726 try:
4727 dvPort_group = self.get_dvport_group(dvPort_group_name)
4728 if dvPort_group:
4729 vlanId = dvPort_group.config.defaultPortConfig.vlan.vlanId
4730 except vmodl.MethodFault as exp:
4731 self.logger.error("Caught vmodl fault {} for disributed virtaul port group {}".format(
4732 exp, dvPort_group_name))
4733 return vlanId
4734
4735
4736 def configure_vlanID(self, content, vcenter_conect, dvPort_group_name):
4737 """
4738 Method to configure vlanID in disributed virtual portgroup vlanID
4739
4740 Args:
4741 network_name - name of network/portgroup
4742
4743 Returns:
4744 None
4745 """
4746 vlanID = self.get_vlanID_from_dvs_portgr(dvPort_group_name)
4747 if vlanID == 0:
4748 #configure vlanID
4749 vlanID = self.genrate_vlanID(dvPort_group_name)
4750 config = {"vlanID":vlanID}
4751 task = self.reconfig_portgroup(content, dvPort_group_name,
4752 config_info=config)
4753 if task:
4754 status= self.wait_for_vcenter_task(task, vcenter_conect)
4755 if status:
4756 self.logger.info("Reconfigured Port group {} for vlan ID {}".format(
4757 dvPort_group_name,vlanID))
4758 else:
4759 self.logger.error("Fail reconfigure portgroup {} for vlanID{}".format(
4760 dvPort_group_name, vlanID))
4761
4762
4763 def genrate_vlanID(self, network_name):
4764 """
4765 Method to get unused vlanID
4766 Args:
4767 network_name - name of network/portgroup
4768 Returns:
4769 vlanID
4770 """
4771 vlan_id = None
4772 used_ids = []
4773 if self.config.get('vlanID_range') == None:
4774 raise vimconn.vimconnConflictException("You must provide a 'vlanID_range' "\
4775 "at config value before creating sriov network with vlan tag")
4776 if "used_vlanIDs" not in self.persistent_info:
4777 self.persistent_info["used_vlanIDs"] = {}
4778 else:
4779 used_ids = self.persistent_info["used_vlanIDs"].values()
4780
4781 for vlanID_range in self.config.get('vlanID_range'):
4782 start_vlanid , end_vlanid = vlanID_range.split("-")
4783 if start_vlanid > end_vlanid:
4784 raise vimconn.vimconnConflictException("Invalid vlan ID range {}".format(
4785 vlanID_range))
4786
4787 for id in xrange(int(start_vlanid), int(end_vlanid) + 1):
4788 if id not in used_ids:
4789 vlan_id = id
4790 self.persistent_info["used_vlanIDs"][network_name] = vlan_id
4791 return vlan_id
4792 if vlan_id is None:
4793 raise vimconn.vimconnConflictException("All Vlan IDs are in use")
4794
4795
4796 def get_obj(self, content, vimtype, name):
4797 """
4798 Get the vsphere object associated with a given text name
4799 """
4800 obj = None
4801 container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True)
4802 for item in container.view:
4803 if item.name == name:
4804 obj = item
4805 break
4806 return obj
4807
4808
4809 def insert_media_to_vm(self, vapp, image_id):
4810 """
4811 Method to insert media CD-ROM (ISO image) from catalog to vm.
4812 vapp - vapp object to get vm id
4813 Image_id - image id for cdrom to be inerted to vm
4814 """
4815 # create connection object
4816 vca = self.connect()
4817 try:
4818 # fetching catalog details
4819 rest_url = "{}/api/catalog/{}".format(vca.host,image_id)
4820 response = Http.get(url=rest_url,
4821 headers=vca.vcloud_session.get_vcloud_headers(),
4822 verify=vca.verify,
4823 logger=vca.logger)
4824
4825 if response.status_code != 200:
4826 self.logger.error("REST call {} failed reason : {}"\
4827 "status code : {}".format(url_rest_call,
4828 response.content,
4829 response.status_code))
4830 raise vimconn.vimconnException("insert_media_to_vm(): Failed to get "\
4831 "catalog details")
4832 # searching iso name and id
4833 iso_name,media_id = self.get_media_details(vca, response.content)
4834
4835 if iso_name and media_id:
4836 data ="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
4837 <ns6:MediaInsertOrEjectParams
4838 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">
4839 <ns6:Media
4840 type="application/vnd.vmware.vcloud.media+xml"
4841 name="{}.iso"
4842 id="urn:vcloud:media:{}"
4843 href="https://{}/api/media/{}"/>
4844 </ns6:MediaInsertOrEjectParams>""".format(iso_name, media_id,
4845 vca.host,media_id)
4846
4847 for vms in vapp._get_vms():
4848 vm_id = (vms.id).split(':')[-1]
4849
4850 headers = vca.vcloud_session.get_vcloud_headers()
4851 headers['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
4852 rest_url = "{}/api/vApp/vm-{}/media/action/insertMedia".format(vca.host,vm_id)
4853
4854 response = Http.post(url=rest_url,
4855 headers=headers,
4856 data=data,
4857 verify=vca.verify,
4858 logger=vca.logger)
4859
4860 if response.status_code != 202:
4861 self.logger.error("Failed to insert CD-ROM to vm")
4862 raise vimconn.vimconnException("insert_media_to_vm() : Failed to insert"\
4863 "ISO image to vm")
4864 else:
4865 task = taskType.parseString(response.content, True)
4866 if isinstance(task, GenericTask):
4867 vca.block_until_completed(task)
4868 self.logger.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
4869 " image to vm {}".format(vm_id))
4870 except Exception as exp:
4871 self.logger.error("insert_media_to_vm() : exception occurred "\
4872 "while inserting media CD-ROM")
4873 raise vimconn.vimconnException(message=exp)
4874
4875
4876 def get_media_details(self, vca, content):
4877 """
4878 Method to get catalog item details
4879 vca - connection object
4880 content - Catalog details
4881 Return - Media name, media id
4882 """
4883 cataloghref_list = []
4884 try:
4885 if content:
4886 vm_list_xmlroot = XmlElementTree.fromstring(content)
4887 for child in vm_list_xmlroot.iter():
4888 if 'CatalogItem' in child.tag:
4889 cataloghref_list.append(child.attrib.get('href'))
4890 if cataloghref_list is not None:
4891 for href in cataloghref_list:
4892 if href:
4893 response = Http.get(url=href,
4894 headers=vca.vcloud_session.get_vcloud_headers(),
4895 verify=vca.verify,
4896 logger=vca.logger)
4897 if response.status_code != 200:
4898 self.logger.error("REST call {} failed reason : {}"\
4899 "status code : {}".format(href,
4900 response.content,
4901 response.status_code))
4902 raise vimconn.vimconnException("get_media_details : Failed to get "\
4903 "catalogitem details")
4904 list_xmlroot = XmlElementTree.fromstring(response.content)
4905 for child in list_xmlroot.iter():
4906 if 'Entity' in child.tag:
4907 if 'media' in child.attrib.get('href'):
4908 name = child.attrib.get('name')
4909 media_id = child.attrib.get('href').split('/').pop()
4910 return name,media_id
4911 else:
4912 self.logger.debug("Media name and id not found")
4913 return False,False
4914 except Exception as exp:
4915 self.logger.error("get_media_details : exception occurred "\
4916 "getting media details")
4917 raise vimconn.vimconnException(message=exp)
4918