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