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