10111c1fb6582f588120bb47b217f91e68370054
[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 net,
1450 nic_type=nic_type)
1451 else:
1452 self.logger.info("new_vminstance(): adding network adapter "\
1453 "to a network {}".format(nets[0].name))
1454 self.add_network_adapter_to_vms(vapp, nets[0].name,
1455 primary_nic_index,
1456 nicIndex,
1457 net)
1458 nicIndex += 1
1459
1460 # cloud-init for ssh-key injection
1461 if cloud_config:
1462 self.cloud_init(vapp,cloud_config)
1463
1464 # deploy and power on vm
1465 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1466 deploytask = vapp.deploy(powerOn=False)
1467 if type(deploytask) is GenericTask:
1468 vca.block_until_completed(deploytask)
1469
1470 # If VM has PCI devices reserve memory for VM
1471 if PCI_devices_status and vm_obj and vcenter_conect:
1472 memReserve = vm_obj.config.hardware.memoryMB
1473 spec = vim.vm.ConfigSpec()
1474 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1475 task = vm_obj.ReconfigVM_Task(spec=spec)
1476 if task:
1477 result = self.wait_for_vcenter_task(task, vcenter_conect)
1478 self.logger.info("Reserved memmoery {} MB for "\
1479 "VM VM status: {}".format(str(memReserve),result))
1480 else:
1481 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1482 str(memReserve),str(vm_obj)))
1483
1484 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1485 poweron_task = vapp.poweron()
1486 if type(poweron_task) is GenericTask:
1487 vca.block_until_completed(poweron_task)
1488
1489 except Exception as exp :
1490 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1491 self.logger.debug("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1492 raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1493
1494 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1495 wait_time = 0
1496 vapp_uuid = None
1497 while wait_time <= MAX_WAIT_TIME:
1498 try:
1499 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1500 except Exception as exp:
1501 raise vimconn.vimconnUnexpectedResponse(
1502 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1503 .format(vmname_andid, exp))
1504
1505 if vapp and vapp.me.deployed:
1506 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1507 break
1508 else:
1509 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1510 time.sleep(INTERVAL_TIME)
1511
1512 wait_time +=INTERVAL_TIME
1513
1514 if vapp_uuid is not None:
1515 return vapp_uuid
1516 else:
1517 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1518
1519 ##
1520 ##
1521 ## based on current discussion
1522 ##
1523 ##
1524 ## server:
1525 # created: '2016-09-08T11:51:58'
1526 # description: simple-instance.linux1.1
1527 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1528 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1529 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1530 # status: ACTIVE
1531 # error_msg:
1532 # interfaces: …
1533 #
1534 def get_vminstance(self, vim_vm_uuid=None):
1535 """Returns the VM instance information from VIM"""
1536
1537 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1538 vca = self.connect()
1539 if not vca:
1540 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1541
1542 vdc = vca.get_vdc(self.tenant_name)
1543 if vdc is None:
1544 raise vimconn.vimconnConnectionException(
1545 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1546
1547 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1548 if not vm_info_dict:
1549 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1550 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1551
1552 status_key = vm_info_dict['status']
1553 error = ''
1554 try:
1555 vm_dict = {'created': vm_info_dict['created'],
1556 'description': vm_info_dict['name'],
1557 'status': vcdStatusCode2manoFormat[int(status_key)],
1558 'hostId': vm_info_dict['vmuuid'],
1559 'error_msg': error,
1560 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1561
1562 if 'interfaces' in vm_info_dict:
1563 vm_dict['interfaces'] = vm_info_dict['interfaces']
1564 else:
1565 vm_dict['interfaces'] = []
1566 except KeyError:
1567 vm_dict = {'created': '',
1568 'description': '',
1569 'status': vcdStatusCode2manoFormat[int(-1)],
1570 'hostId': vm_info_dict['vmuuid'],
1571 'error_msg': "Inconsistency state",
1572 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1573
1574 return vm_dict
1575
1576 def delete_vminstance(self, vm__vim_uuid):
1577 """Method poweroff and remove VM instance from vcloud director network.
1578
1579 Args:
1580 vm__vim_uuid: VM UUID
1581
1582 Returns:
1583 Returns the instance identifier
1584 """
1585
1586 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1587 vca = self.connect()
1588 if not vca:
1589 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1590
1591 vdc = vca.get_vdc(self.tenant_name)
1592 if vdc is None:
1593 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1594 self.tenant_name))
1595 raise vimconn.vimconnException(
1596 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1597
1598 try:
1599 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1600 if vapp_name is None:
1601 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1602 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1603 else:
1604 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1605
1606 # Delete vApp and wait for status change if task executed and vApp is None.
1607 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1608
1609 if vapp:
1610 if vapp.me.deployed:
1611 self.logger.info("Powering off vApp {}".format(vapp_name))
1612 #Power off vApp
1613 powered_off = False
1614 wait_time = 0
1615 while wait_time <= MAX_WAIT_TIME:
1616 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1617 if not vapp:
1618 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1619 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1620
1621 power_off_task = vapp.poweroff()
1622 if type(power_off_task) is GenericTask:
1623 result = vca.block_until_completed(power_off_task)
1624 if result:
1625 powered_off = True
1626 break
1627 else:
1628 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1629 time.sleep(INTERVAL_TIME)
1630
1631 wait_time +=INTERVAL_TIME
1632 if not powered_off:
1633 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1634 else:
1635 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1636
1637 #Undeploy vApp
1638 self.logger.info("Undeploy vApp {}".format(vapp_name))
1639 wait_time = 0
1640 undeployed = False
1641 while wait_time <= MAX_WAIT_TIME:
1642 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1643 if not vapp:
1644 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1645 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1646 undeploy_task = vapp.undeploy(action='powerOff')
1647
1648 if type(undeploy_task) is GenericTask:
1649 result = vca.block_until_completed(undeploy_task)
1650 if result:
1651 undeployed = True
1652 break
1653 else:
1654 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1655 time.sleep(INTERVAL_TIME)
1656
1657 wait_time +=INTERVAL_TIME
1658
1659 if not undeployed:
1660 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1661
1662 # delete vapp
1663 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1664 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1665
1666 if vapp is not None:
1667 wait_time = 0
1668 result = False
1669
1670 while wait_time <= MAX_WAIT_TIME:
1671 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1672 if not vapp:
1673 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1674 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1675
1676 delete_task = vapp.delete()
1677
1678 if type(delete_task) is GenericTask:
1679 vca.block_until_completed(delete_task)
1680 result = vca.block_until_completed(delete_task)
1681 if result:
1682 break
1683 else:
1684 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1685 time.sleep(INTERVAL_TIME)
1686
1687 wait_time +=INTERVAL_TIME
1688
1689 if not result:
1690 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1691
1692 except:
1693 self.logger.debug(traceback.format_exc())
1694 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1695
1696 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1697 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1698 return vm__vim_uuid
1699 else:
1700 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1701
1702 def refresh_vms_status(self, vm_list):
1703 """Get the status of the virtual machines and their interfaces/ports
1704 Params: the list of VM identifiers
1705 Returns a dictionary with:
1706 vm_id: #VIM id of this Virtual Machine
1707 status: #Mandatory. Text with one of:
1708 # DELETED (not found at vim)
1709 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1710 # OTHER (Vim reported other status not understood)
1711 # ERROR (VIM indicates an ERROR status)
1712 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1713 # CREATING (on building process), ERROR
1714 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1715 #
1716 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1717 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1718 interfaces:
1719 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1720 mac_address: #Text format XX:XX:XX:XX:XX:XX
1721 vim_net_id: #network id where this interface is connected
1722 vim_interface_id: #interface/port VIM id
1723 ip_address: #null, or text with IPv4, IPv6 address
1724 """
1725
1726 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1727
1728 vca = self.connect()
1729 if not vca:
1730 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1731
1732 vdc = vca.get_vdc(self.tenant_name)
1733 if vdc is None:
1734 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1735
1736 vms_dict = {}
1737 nsx_edge_list = []
1738 for vmuuid in vm_list:
1739 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1740 if vmname is not None:
1741
1742 try:
1743 the_vapp = vca.get_vapp(vdc, vmname)
1744 vm_info = the_vapp.get_vms_details()
1745 vm_status = vm_info[0]['status']
1746 vm_pci_details = self.get_vm_pci_details(vmuuid)
1747 vm_info[0].update(vm_pci_details)
1748
1749 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1750 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1751 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1752
1753 # get networks
1754 vm_app_networks = the_vapp.get_vms_network_info()
1755 for vapp_network in vm_app_networks:
1756 for vm_network in vapp_network:
1757 if vm_network['name'] == vmname:
1758 #Assign IP Address based on MAC Address in NSX DHCP lease info
1759 if vm_network['ip'] is None:
1760 if not nsx_edge_list:
1761 nsx_edge_list = self.get_edge_details()
1762 if nsx_edge_list is None:
1763 raise vimconn.vimconnException("refresh_vms_status:"\
1764 "Failed to get edge details from NSX Manager")
1765 if vm_network['mac'] is not None:
1766 vm_network['ip'] = self.get_ipaddr_from_NSXedge(nsx_edge_list, vm_network['mac'])
1767
1768 vm_net_id = self.get_network_id_by_name(vm_network['network_name'])
1769 interface = {"mac_address": vm_network['mac'],
1770 "vim_net_id": vm_net_id,
1771 "vim_interface_id": vm_net_id,
1772 'ip_address': vm_network['ip']}
1773 # interface['vim_info'] = yaml.safe_dump(vm_network)
1774 vm_dict["interfaces"].append(interface)
1775 # add a vm to vm dict
1776 vms_dict.setdefault(vmuuid, vm_dict)
1777 except Exception as exp:
1778 self.logger.debug("Error in response {}".format(exp))
1779 self.logger.debug(traceback.format_exc())
1780
1781 return vms_dict
1782
1783
1784 def get_edge_details(self):
1785 """Get the NSX edge list from NSX Manager
1786 Returns list of NSX edges
1787 """
1788 edge_list = []
1789 rheaders = {'Content-Type': 'application/xml'}
1790 nsx_api_url = '/api/4.0/edges'
1791
1792 self.logger.debug("Get edge details from NSX Manager {} {}".format(self.nsx_manager, nsx_api_url))
1793
1794 try:
1795 resp = requests.get(self.nsx_manager + nsx_api_url,
1796 auth = (self.nsx_user, self.nsx_password),
1797 verify = False, headers = rheaders)
1798 if resp.status_code == requests.codes.ok:
1799 paged_Edge_List = XmlElementTree.fromstring(resp.text)
1800 for edge_pages in paged_Edge_List:
1801 if edge_pages.tag == 'edgePage':
1802 for edge_summary in edge_pages:
1803 if edge_summary.tag == 'pagingInfo':
1804 for element in edge_summary:
1805 if element.tag == 'totalCount' and element.text == '0':
1806 raise vimconn.vimconnException("get_edge_details: No NSX edges details found: {}"
1807 .format(self.nsx_manager))
1808
1809 if edge_summary.tag == 'edgeSummary':
1810 for element in edge_summary:
1811 if element.tag == 'id':
1812 edge_list.append(element.text)
1813 else:
1814 raise vimconn.vimconnException("get_edge_details: No NSX edge details found: {}"
1815 .format(self.nsx_manager))
1816
1817 if not edge_list:
1818 raise vimconn.vimconnException("get_edge_details: "\
1819 "No NSX edge details found: {}"
1820 .format(self.nsx_manager))
1821 else:
1822 self.logger.debug("get_edge_details: Found NSX edges {}".format(edge_list))
1823 return edge_list
1824 else:
1825 self.logger.debug("get_edge_details: "
1826 "Failed to get NSX edge details from NSX Manager: {}"
1827 .format(resp.content))
1828 return None
1829
1830 except Exception as exp:
1831 self.logger.debug("get_edge_details: "\
1832 "Failed to get NSX edge details from NSX Manager: {}"
1833 .format(exp))
1834 raise vimconn.vimconnException("get_edge_details: "\
1835 "Failed to get NSX edge details from NSX Manager: {}"
1836 .format(exp))
1837
1838
1839 def get_ipaddr_from_NSXedge(self, nsx_edges, mac_address):
1840 """Get IP address details from NSX edges, using the MAC address
1841 PARAMS: nsx_edges : List of NSX edges
1842 mac_address : Find IP address corresponding to this MAC address
1843 Returns: IP address corrresponding to the provided MAC address
1844 """
1845
1846 ip_addr = None
1847 rheaders = {'Content-Type': 'application/xml'}
1848
1849 self.logger.debug("get_ipaddr_from_NSXedge: Finding IP addr from NSX edge")
1850
1851 try:
1852 for edge in nsx_edges:
1853 nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
1854
1855 resp = requests.get(self.nsx_manager + nsx_api_url,
1856 auth = (self.nsx_user, self.nsx_password),
1857 verify = False, headers = rheaders)
1858
1859 if resp.status_code == requests.codes.ok:
1860 dhcp_leases = XmlElementTree.fromstring(resp.text)
1861 for child in dhcp_leases:
1862 if child.tag == 'dhcpLeaseInfo':
1863 dhcpLeaseInfo = child
1864 for leaseInfo in dhcpLeaseInfo:
1865 for elem in leaseInfo:
1866 if (elem.tag)=='macAddress':
1867 edge_mac_addr = elem.text
1868 if (elem.tag)=='ipAddress':
1869 ip_addr = elem.text
1870 if edge_mac_addr is not None:
1871 if edge_mac_addr == mac_address:
1872 self.logger.debug("Found ip addr {} for mac {} at NSX edge {}"
1873 .format(ip_addr, mac_address,edge))
1874 return ip_addr
1875 else:
1876 self.logger.debug("get_ipaddr_from_NSXedge: "\
1877 "Error occurred while getting DHCP lease info from NSX Manager: {}"
1878 .format(resp.content))
1879
1880 self.logger.debug("get_ipaddr_from_NSXedge: No IP addr found in any NSX edge")
1881 return None
1882
1883 except XmlElementTree.ParseError as Err:
1884 self.logger.debug("ParseError in response from NSX Manager {}".format(Err.message), exc_info=True)
1885
1886
1887 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1888 """Send and action over a VM instance from VIM
1889 Returns the vm_id if the action was successfully sent to the VIM"""
1890
1891 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1892 if vm__vim_uuid is None or action_dict is None:
1893 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1894
1895 vca = self.connect()
1896 if not vca:
1897 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1898
1899 vdc = vca.get_vdc(self.tenant_name)
1900 if vdc is None:
1901 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1902
1903 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1904 if vapp_name is None:
1905 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1906 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1907 else:
1908 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1909
1910 try:
1911 the_vapp = vca.get_vapp(vdc, vapp_name)
1912 # TODO fix all status
1913 if "start" in action_dict:
1914 vm_info = the_vapp.get_vms_details()
1915 vm_status = vm_info[0]['status']
1916 self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name))
1917 if vm_status == "Suspended" or vm_status == "Powered off":
1918 power_on_task = the_vapp.poweron()
1919 result = vca.block_until_completed(power_on_task)
1920 self.instance_actions_result("start", result, vapp_name)
1921 elif "rebuild" in action_dict:
1922 self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name))
1923 rebuild_task = the_vapp.deploy(powerOn=True)
1924 result = vca.block_until_completed(rebuild_task)
1925 self.instance_actions_result("rebuild", result, vapp_name)
1926 elif "pause" in action_dict:
1927 self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name))
1928 pause_task = the_vapp.undeploy(action='suspend')
1929 result = vca.block_until_completed(pause_task)
1930 self.instance_actions_result("pause", result, vapp_name)
1931 elif "resume" in action_dict:
1932 self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name))
1933 power_task = the_vapp.poweron()
1934 result = vca.block_until_completed(power_task)
1935 self.instance_actions_result("resume", result, vapp_name)
1936 elif "shutoff" in action_dict or "shutdown" in action_dict:
1937 action_name , value = action_dict.items()[0]
1938 self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name))
1939 power_off_task = the_vapp.undeploy(action='powerOff')
1940 result = vca.block_until_completed(power_off_task)
1941 if action_name == "shutdown":
1942 self.instance_actions_result("shutdown", result, vapp_name)
1943 else:
1944 self.instance_actions_result("shutoff", result, vapp_name)
1945 elif "forceOff" in action_dict:
1946 result = the_vapp.undeploy(action='force')
1947 self.instance_actions_result("forceOff", result, vapp_name)
1948 elif "reboot" in action_dict:
1949 self.logger.info("action_vminstance: reboot vApp: {}".format(vapp_name))
1950 reboot_task = the_vapp.reboot()
1951 else:
1952 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
1953 return vm__vim_uuid
1954 except Exception as exp :
1955 self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
1956 raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
1957
1958 def instance_actions_result(self, action, result, vapp_name):
1959 if result:
1960 self.logger.info("action_vminstance: Sucessfully {} the vApp: {}".format(action, vapp_name))
1961 else:
1962 self.logger.error("action_vminstance: Failed to {} vApp: {}".format(action, vapp_name))
1963
1964 def get_vminstance_console(self, vm_id, console_type="vnc"):
1965 """
1966 Get a console for the virtual machine
1967 Params:
1968 vm_id: uuid of the VM
1969 console_type, can be:
1970 "novnc" (by default), "xvpvnc" for VNC types,
1971 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1972 Returns dict with the console parameters:
1973 protocol: ssh, ftp, http, https, ...
1974 server: usually ip address
1975 port: the http, ssh, ... port
1976 suffix: extra text, e.g. the http path and query string
1977 """
1978 raise vimconn.vimconnNotImplemented("Should have implemented this")
1979
1980 # NOT USED METHODS in current version
1981
1982 def host_vim2gui(self, host, server_dict):
1983 """Transform host dictionary from VIM format to GUI format,
1984 and append to the server_dict
1985 """
1986 raise vimconn.vimconnNotImplemented("Should have implemented this")
1987
1988 def get_hosts_info(self):
1989 """Get the information of deployed hosts
1990 Returns the hosts content"""
1991 raise vimconn.vimconnNotImplemented("Should have implemented this")
1992
1993 def get_hosts(self, vim_tenant):
1994 """Get the hosts and deployed instances
1995 Returns the hosts content"""
1996 raise vimconn.vimconnNotImplemented("Should have implemented this")
1997
1998 def get_processor_rankings(self):
1999 """Get the processor rankings in the VIM database"""
2000 raise vimconn.vimconnNotImplemented("Should have implemented this")
2001
2002 def new_host(self, host_data):
2003 """Adds a new host to VIM"""
2004 '''Returns status code of the VIM response'''
2005 raise vimconn.vimconnNotImplemented("Should have implemented this")
2006
2007 def new_external_port(self, port_data):
2008 """Adds a external port to VIM"""
2009 '''Returns the port identifier'''
2010 raise vimconn.vimconnNotImplemented("Should have implemented this")
2011
2012 def new_external_network(self, net_name, net_type):
2013 """Adds a external network to VIM (shared)"""
2014 '''Returns the network identifier'''
2015 raise vimconn.vimconnNotImplemented("Should have implemented this")
2016
2017 def connect_port_network(self, port_id, network_id, admin=False):
2018 """Connects a external port to a network"""
2019 '''Returns status code of the VIM response'''
2020 raise vimconn.vimconnNotImplemented("Should have implemented this")
2021
2022 def new_vminstancefromJSON(self, vm_data):
2023 """Adds a VM instance to VIM"""
2024 '''Returns the instance identifier'''
2025 raise vimconn.vimconnNotImplemented("Should have implemented this")
2026
2027 def get_network_name_by_id(self, network_uuid=None):
2028 """Method gets vcloud director network named based on supplied uuid.
2029
2030 Args:
2031 network_uuid: network_id
2032
2033 Returns:
2034 The return network name.
2035 """
2036
2037 vca = self.connect()
2038 if not vca:
2039 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2040
2041 if not network_uuid:
2042 return None
2043
2044 try:
2045 org_dict = self.get_org(self.org_uuid)
2046 if 'networks' in org_dict:
2047 org_network_dict = org_dict['networks']
2048 for net_uuid in org_network_dict:
2049 if net_uuid == network_uuid:
2050 return org_network_dict[net_uuid]
2051 except:
2052 self.logger.debug("Exception in get_network_name_by_id")
2053 self.logger.debug(traceback.format_exc())
2054
2055 return None
2056
2057 def get_network_id_by_name(self, network_name=None):
2058 """Method gets vcloud director network uuid based on supplied name.
2059
2060 Args:
2061 network_name: network_name
2062 Returns:
2063 The return network uuid.
2064 network_uuid: network_id
2065 """
2066
2067 vca = self.connect()
2068 if not vca:
2069 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2070
2071 if not network_name:
2072 self.logger.debug("get_network_id_by_name() : Network name is empty")
2073 return None
2074
2075 try:
2076 org_dict = self.get_org(self.org_uuid)
2077 if org_dict and 'networks' in org_dict:
2078 org_network_dict = org_dict['networks']
2079 for net_uuid,net_name in org_network_dict.iteritems():
2080 if net_name == network_name:
2081 return net_uuid
2082
2083 except KeyError as exp:
2084 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
2085
2086 return None
2087
2088 def list_org_action(self):
2089 """
2090 Method leverages vCloud director and query for available organization for particular user
2091
2092 Args:
2093 vca - is active VCA connection.
2094 vdc_name - is a vdc name that will be used to query vms action
2095
2096 Returns:
2097 The return XML respond
2098 """
2099
2100 vca = self.connect()
2101 if not vca:
2102 raise vimconn.vimconnConnectionException("self.connect() is failed")
2103
2104 url_list = [vca.host, '/api/org']
2105 vm_list_rest_call = ''.join(url_list)
2106
2107 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2108 response = Http.get(url=vm_list_rest_call,
2109 headers=vca.vcloud_session.get_vcloud_headers(),
2110 verify=vca.verify,
2111 logger=vca.logger)
2112 if response.status_code == requests.codes.ok:
2113 return response.content
2114
2115 return None
2116
2117 def get_org_action(self, org_uuid=None):
2118 """
2119 Method leverages vCloud director and retrieve available object fdr organization.
2120
2121 Args:
2122 vca - is active VCA connection.
2123 vdc_name - is a vdc name that will be used to query vms action
2124
2125 Returns:
2126 The return XML respond
2127 """
2128
2129 vca = self.connect()
2130 if not vca:
2131 raise vimconn.vimconnConnectionException("self.connect() is failed")
2132
2133 if org_uuid is None:
2134 return None
2135
2136 url_list = [vca.host, '/api/org/', org_uuid]
2137 vm_list_rest_call = ''.join(url_list)
2138
2139 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2140 response = Http.get(url=vm_list_rest_call,
2141 headers=vca.vcloud_session.get_vcloud_headers(),
2142 verify=vca.verify,
2143 logger=vca.logger)
2144 if response.status_code == requests.codes.ok:
2145 return response.content
2146
2147 return None
2148
2149 def get_org(self, org_uuid=None):
2150 """
2151 Method retrieves available organization in vCloud Director
2152
2153 Args:
2154 org_uuid - is a organization uuid.
2155
2156 Returns:
2157 The return dictionary with following key
2158 "network" - for network list under the org
2159 "catalogs" - for network list under the org
2160 "vdcs" - for vdc list under org
2161 """
2162
2163 org_dict = {}
2164 vca = self.connect()
2165 if not vca:
2166 raise vimconn.vimconnConnectionException("self.connect() is failed")
2167
2168 if org_uuid is None:
2169 return org_dict
2170
2171 content = self.get_org_action(org_uuid=org_uuid)
2172 try:
2173 vdc_list = {}
2174 network_list = {}
2175 catalog_list = {}
2176 vm_list_xmlroot = XmlElementTree.fromstring(content)
2177 for child in vm_list_xmlroot:
2178 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2179 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2180 org_dict['vdcs'] = vdc_list
2181 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2182 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2183 org_dict['networks'] = network_list
2184 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2185 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2186 org_dict['catalogs'] = catalog_list
2187 except:
2188 pass
2189
2190 return org_dict
2191
2192 def get_org_list(self):
2193 """
2194 Method retrieves available organization in vCloud Director
2195
2196 Args:
2197 vca - is active VCA connection.
2198
2199 Returns:
2200 The return dictionary and key for each entry VDC UUID
2201 """
2202
2203 org_dict = {}
2204 vca = self.connect()
2205 if not vca:
2206 raise vimconn.vimconnConnectionException("self.connect() is failed")
2207
2208 content = self.list_org_action()
2209 try:
2210 vm_list_xmlroot = XmlElementTree.fromstring(content)
2211 for vm_xml in vm_list_xmlroot:
2212 if vm_xml.tag.split("}")[1] == 'Org':
2213 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2214 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2215 except:
2216 pass
2217
2218 return org_dict
2219
2220 def vms_view_action(self, vdc_name=None):
2221 """ Method leverages vCloud director vms query call
2222
2223 Args:
2224 vca - is active VCA connection.
2225 vdc_name - is a vdc name that will be used to query vms action
2226
2227 Returns:
2228 The return XML respond
2229 """
2230 vca = self.connect()
2231 if vdc_name is None:
2232 return None
2233
2234 url_list = [vca.host, '/api/vms/query']
2235 vm_list_rest_call = ''.join(url_list)
2236
2237 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2238 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
2239 vca.vcloud_session.organization.Link)
2240 if len(refs) == 1:
2241 response = Http.get(url=vm_list_rest_call,
2242 headers=vca.vcloud_session.get_vcloud_headers(),
2243 verify=vca.verify,
2244 logger=vca.logger)
2245 if response.status_code == requests.codes.ok:
2246 return response.content
2247
2248 return None
2249
2250 def get_vapp_list(self, vdc_name=None):
2251 """
2252 Method retrieves vApp list deployed vCloud director and returns a dictionary
2253 contains a list of all vapp deployed for queried VDC.
2254 The key for a dictionary is vApp UUID
2255
2256
2257 Args:
2258 vca - is active VCA connection.
2259 vdc_name - is a vdc name that will be used to query vms action
2260
2261 Returns:
2262 The return dictionary and key for each entry vapp UUID
2263 """
2264
2265 vapp_dict = {}
2266 if vdc_name is None:
2267 return vapp_dict
2268
2269 content = self.vms_view_action(vdc_name=vdc_name)
2270 try:
2271 vm_list_xmlroot = XmlElementTree.fromstring(content)
2272 for vm_xml in vm_list_xmlroot:
2273 if vm_xml.tag.split("}")[1] == 'VMRecord':
2274 if vm_xml.attrib['isVAppTemplate'] == 'true':
2275 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2276 if 'vappTemplate-' in rawuuid[0]:
2277 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2278 # vm and use raw UUID as key
2279 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2280 except:
2281 pass
2282
2283 return vapp_dict
2284
2285 def get_vm_list(self, vdc_name=None):
2286 """
2287 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2288 contains a list of all VM's deployed for queried VDC.
2289 The key for a dictionary is VM UUID
2290
2291
2292 Args:
2293 vca - is active VCA connection.
2294 vdc_name - is a vdc name that will be used to query vms action
2295
2296 Returns:
2297 The return dictionary and key for each entry vapp UUID
2298 """
2299 vm_dict = {}
2300
2301 if vdc_name is None:
2302 return vm_dict
2303
2304 content = self.vms_view_action(vdc_name=vdc_name)
2305 try:
2306 vm_list_xmlroot = XmlElementTree.fromstring(content)
2307 for vm_xml in vm_list_xmlroot:
2308 if vm_xml.tag.split("}")[1] == 'VMRecord':
2309 if vm_xml.attrib['isVAppTemplate'] == 'false':
2310 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2311 if 'vm-' in rawuuid[0]:
2312 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2313 # vm and use raw UUID as key
2314 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2315 except:
2316 pass
2317
2318 return vm_dict
2319
2320 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2321 """
2322 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2323 contains a list of all VM's deployed for queried VDC.
2324 The key for a dictionary is VM UUID
2325
2326
2327 Args:
2328 vca - is active VCA connection.
2329 vdc_name - is a vdc name that will be used to query vms action
2330
2331 Returns:
2332 The return dictionary and key for each entry vapp UUID
2333 """
2334 vm_dict = {}
2335 vca = self.connect()
2336 if not vca:
2337 raise vimconn.vimconnConnectionException("self.connect() is failed")
2338
2339 if vdc_name is None:
2340 return vm_dict
2341
2342 content = self.vms_view_action(vdc_name=vdc_name)
2343 try:
2344 vm_list_xmlroot = XmlElementTree.fromstring(content)
2345 for vm_xml in vm_list_xmlroot:
2346 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2347 # lookup done by UUID
2348 if isuuid:
2349 if vapp_name in vm_xml.attrib['container']:
2350 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2351 if 'vm-' in rawuuid[0]:
2352 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2353 break
2354 # lookup done by Name
2355 else:
2356 if vapp_name in vm_xml.attrib['name']:
2357 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2358 if 'vm-' in rawuuid[0]:
2359 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2360 break
2361 except:
2362 pass
2363
2364 return vm_dict
2365
2366 def get_network_action(self, network_uuid=None):
2367 """
2368 Method leverages vCloud director and query network based on network uuid
2369
2370 Args:
2371 vca - is active VCA connection.
2372 network_uuid - is a network uuid
2373
2374 Returns:
2375 The return XML respond
2376 """
2377
2378 vca = self.connect()
2379 if not vca:
2380 raise vimconn.vimconnConnectionException("self.connect() is failed")
2381
2382 if network_uuid is None:
2383 return None
2384
2385 url_list = [vca.host, '/api/network/', network_uuid]
2386 vm_list_rest_call = ''.join(url_list)
2387
2388 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2389 response = Http.get(url=vm_list_rest_call,
2390 headers=vca.vcloud_session.get_vcloud_headers(),
2391 verify=vca.verify,
2392 logger=vca.logger)
2393 if response.status_code == requests.codes.ok:
2394 return response.content
2395
2396 return None
2397
2398 def get_vcd_network(self, network_uuid=None):
2399 """
2400 Method retrieves available network from vCloud Director
2401
2402 Args:
2403 network_uuid - is VCD network UUID
2404
2405 Each element serialized as key : value pair
2406
2407 Following keys available for access. network_configuration['Gateway'}
2408 <Configuration>
2409 <IpScopes>
2410 <IpScope>
2411 <IsInherited>true</IsInherited>
2412 <Gateway>172.16.252.100</Gateway>
2413 <Netmask>255.255.255.0</Netmask>
2414 <Dns1>172.16.254.201</Dns1>
2415 <Dns2>172.16.254.202</Dns2>
2416 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2417 <IsEnabled>true</IsEnabled>
2418 <IpRanges>
2419 <IpRange>
2420 <StartAddress>172.16.252.1</StartAddress>
2421 <EndAddress>172.16.252.99</EndAddress>
2422 </IpRange>
2423 </IpRanges>
2424 </IpScope>
2425 </IpScopes>
2426 <FenceMode>bridged</FenceMode>
2427
2428 Returns:
2429 The return dictionary and key for each entry vapp UUID
2430 """
2431
2432 network_configuration = {}
2433 if network_uuid is None:
2434 return network_uuid
2435
2436 try:
2437 content = self.get_network_action(network_uuid=network_uuid)
2438 vm_list_xmlroot = XmlElementTree.fromstring(content)
2439
2440 network_configuration['status'] = vm_list_xmlroot.get("status")
2441 network_configuration['name'] = vm_list_xmlroot.get("name")
2442 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2443
2444 for child in vm_list_xmlroot:
2445 if child.tag.split("}")[1] == 'IsShared':
2446 network_configuration['isShared'] = child.text.strip()
2447 if child.tag.split("}")[1] == 'Configuration':
2448 for configuration in child.iter():
2449 tagKey = configuration.tag.split("}")[1].strip()
2450 if tagKey != "":
2451 network_configuration[tagKey] = configuration.text.strip()
2452 return network_configuration
2453 except Exception as exp :
2454 self.logger.debug("get_vcd_network: Failed with Exception {}".format(exp))
2455 raise vimconn.vimconnException("get_vcd_network: Failed with Exception {}".format(exp))
2456
2457 return network_configuration
2458
2459 def delete_network_action(self, network_uuid=None):
2460 """
2461 Method delete given network from vCloud director
2462
2463 Args:
2464 network_uuid - is a network uuid that client wish to delete
2465
2466 Returns:
2467 The return None or XML respond or false
2468 """
2469
2470 vca = self.connect_as_admin()
2471 if not vca:
2472 raise vimconn.vimconnConnectionException("self.connect() is failed")
2473 if network_uuid is None:
2474 return False
2475
2476 url_list = [vca.host, '/api/admin/network/', network_uuid]
2477 vm_list_rest_call = ''.join(url_list)
2478
2479 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2480 response = Http.delete(url=vm_list_rest_call,
2481 headers=vca.vcloud_session.get_vcloud_headers(),
2482 verify=vca.verify,
2483 logger=vca.logger)
2484
2485 if response.status_code == 202:
2486 return True
2487
2488 return False
2489
2490 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2491 ip_profile=None, isshared='true'):
2492 """
2493 Method create network in vCloud director
2494
2495 Args:
2496 network_name - is network name to be created.
2497 net_type - can be 'bridge','data','ptp','mgmt'.
2498 ip_profile is a dict containing the IP parameters of the network
2499 isshared - is a boolean
2500 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2501 It optional attribute. by default if no parent network indicate the first available will be used.
2502
2503 Returns:
2504 The return network uuid or return None
2505 """
2506
2507 new_network_name = [network_name, '-', str(uuid.uuid4())]
2508 content = self.create_network_rest(network_name=''.join(new_network_name),
2509 ip_profile=ip_profile,
2510 net_type=net_type,
2511 parent_network_uuid=parent_network_uuid,
2512 isshared=isshared)
2513 if content is None:
2514 self.logger.debug("Failed create network {}.".format(network_name))
2515 return None
2516
2517 try:
2518 vm_list_xmlroot = XmlElementTree.fromstring(content)
2519 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2520 if len(vcd_uuid) == 4:
2521 self.logger.info("Created new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2522 return vcd_uuid[3]
2523 except:
2524 self.logger.debug("Failed create network {}".format(network_name))
2525 return None
2526
2527 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2528 ip_profile=None, isshared='true'):
2529 """
2530 Method create network in vCloud director
2531
2532 Args:
2533 network_name - is network name to be created.
2534 net_type - can be 'bridge','data','ptp','mgmt'.
2535 ip_profile is a dict containing the IP parameters of the network
2536 isshared - is a boolean
2537 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2538 It optional attribute. by default if no parent network indicate the first available will be used.
2539
2540 Returns:
2541 The return network uuid or return None
2542 """
2543
2544 vca = self.connect_as_admin()
2545 if not vca:
2546 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2547 if network_name is None:
2548 return None
2549
2550 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2551 vm_list_rest_call = ''.join(url_list)
2552 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2553 response = Http.get(url=vm_list_rest_call,
2554 headers=vca.vcloud_session.get_vcloud_headers(),
2555 verify=vca.verify,
2556 logger=vca.logger)
2557
2558 provider_network = None
2559 available_networks = None
2560 add_vdc_rest_url = None
2561
2562 if response.status_code != requests.codes.ok:
2563 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2564 response.status_code))
2565 return None
2566 else:
2567 try:
2568 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2569 for child in vm_list_xmlroot:
2570 if child.tag.split("}")[1] == 'ProviderVdcReference':
2571 provider_network = child.attrib.get('href')
2572 # application/vnd.vmware.admin.providervdc+xml
2573 if child.tag.split("}")[1] == 'Link':
2574 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2575 and child.attrib.get('rel') == 'add':
2576 add_vdc_rest_url = child.attrib.get('href')
2577 except:
2578 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2579 self.logger.debug("Respond body {}".format(response.content))
2580 return None
2581
2582 # find pvdc provided available network
2583 response = Http.get(url=provider_network,
2584 headers=vca.vcloud_session.get_vcloud_headers(),
2585 verify=vca.verify,
2586 logger=vca.logger)
2587 if response.status_code != requests.codes.ok:
2588 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2589 response.status_code))
2590 return None
2591
2592 # available_networks.split("/")[-1]
2593
2594 if parent_network_uuid is None:
2595 try:
2596 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2597 for child in vm_list_xmlroot.iter():
2598 if child.tag.split("}")[1] == 'AvailableNetworks':
2599 for networks in child.iter():
2600 # application/vnd.vmware.admin.network+xml
2601 if networks.attrib.get('href') is not None:
2602 available_networks = networks.attrib.get('href')
2603 break
2604 except:
2605 return None
2606
2607 try:
2608 #Configure IP profile of the network
2609 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2610
2611 if 'subnet_address' not in ip_profile or ip_profile['subnet_address'] is None:
2612 subnet_rand = random.randint(0, 255)
2613 ip_base = "192.168.{}.".format(subnet_rand)
2614 ip_profile['subnet_address'] = ip_base + "0/24"
2615 else:
2616 ip_base = ip_profile['subnet_address'].rsplit('.',1)[0] + '.'
2617
2618 if 'gateway_address' not in ip_profile or ip_profile['gateway_address'] is None:
2619 ip_profile['gateway_address']=ip_base + "1"
2620 if 'dhcp_count' not in ip_profile or ip_profile['dhcp_count'] is None:
2621 ip_profile['dhcp_count']=DEFAULT_IP_PROFILE['dhcp_count']
2622 if 'dhcp_enabled' not in ip_profile or ip_profile['dhcp_enabled'] is None:
2623 ip_profile['dhcp_enabled']=DEFAULT_IP_PROFILE['dhcp_enabled']
2624 if 'dhcp_start_address' not in ip_profile or ip_profile['dhcp_start_address'] is None:
2625 ip_profile['dhcp_start_address']=ip_base + "3"
2626 if 'ip_version' not in ip_profile or ip_profile['ip_version'] is None:
2627 ip_profile['ip_version']=DEFAULT_IP_PROFILE['ip_version']
2628 if 'dns_address' not in ip_profile or ip_profile['dns_address'] is None:
2629 ip_profile['dns_address']=ip_base + "2"
2630
2631 gateway_address=ip_profile['gateway_address']
2632 dhcp_count=int(ip_profile['dhcp_count'])
2633 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2634
2635 if ip_profile['dhcp_enabled']==True:
2636 dhcp_enabled='true'
2637 else:
2638 dhcp_enabled='false'
2639 dhcp_start_address=ip_profile['dhcp_start_address']
2640
2641 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2642 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2643 end_ip_int += dhcp_count - 1
2644 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2645
2646 ip_version=ip_profile['ip_version']
2647 dns_address=ip_profile['dns_address']
2648 except KeyError as exp:
2649 self.logger.debug("Create Network REST: Key error {}".format(exp))
2650 raise vimconn.vimconnException("Create Network REST: Key error{}".format(exp))
2651
2652 # either use client provided UUID or search for a first available
2653 # if both are not defined we return none
2654 if parent_network_uuid is not None:
2655 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2656 add_vdc_rest_url = ''.join(url_list)
2657
2658 if net_type=='ptp':
2659 fence_mode="isolated"
2660 isshared='false'
2661 is_inherited='false'
2662 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2663 <Description>Openmano created</Description>
2664 <Configuration>
2665 <IpScopes>
2666 <IpScope>
2667 <IsInherited>{1:s}</IsInherited>
2668 <Gateway>{2:s}</Gateway>
2669 <Netmask>{3:s}</Netmask>
2670 <Dns1>{4:s}</Dns1>
2671 <IsEnabled>{5:s}</IsEnabled>
2672 <IpRanges>
2673 <IpRange>
2674 <StartAddress>{6:s}</StartAddress>
2675 <EndAddress>{7:s}</EndAddress>
2676 </IpRange>
2677 </IpRanges>
2678 </IpScope>
2679 </IpScopes>
2680 <FenceMode>{8:s}</FenceMode>
2681 </Configuration>
2682 <IsShared>{9:s}</IsShared>
2683 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2684 subnet_address, dns_address, dhcp_enabled,
2685 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2686
2687 else:
2688 fence_mode="bridged"
2689 is_inherited='false'
2690 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2691 <Description>Openmano created</Description>
2692 <Configuration>
2693 <IpScopes>
2694 <IpScope>
2695 <IsInherited>{1:s}</IsInherited>
2696 <Gateway>{2:s}</Gateway>
2697 <Netmask>{3:s}</Netmask>
2698 <Dns1>{4:s}</Dns1>
2699 <IsEnabled>{5:s}</IsEnabled>
2700 <IpRanges>
2701 <IpRange>
2702 <StartAddress>{6:s}</StartAddress>
2703 <EndAddress>{7:s}</EndAddress>
2704 </IpRange>
2705 </IpRanges>
2706 </IpScope>
2707 </IpScopes>
2708 <ParentNetwork href="{8:s}"/>
2709 <FenceMode>{9:s}</FenceMode>
2710 </Configuration>
2711 <IsShared>{10:s}</IsShared>
2712 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2713 subnet_address, dns_address, dhcp_enabled,
2714 dhcp_start_address, dhcp_end_address, available_networks,
2715 fence_mode, isshared)
2716
2717 headers = vca.vcloud_session.get_vcloud_headers()
2718 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2719 try:
2720 response = Http.post(url=add_vdc_rest_url,
2721 headers=headers,
2722 data=data,
2723 verify=vca.verify,
2724 logger=vca.logger)
2725
2726 if response.status_code != 201:
2727 self.logger.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2728 .format(response.status_code,response.content))
2729 else:
2730 network = networkType.parseString(response.content, True)
2731 create_nw_task = network.get_Tasks().get_Task()[0]
2732
2733 # if we all ok we respond with content after network creation completes
2734 # otherwise by default return None
2735 if create_nw_task is not None:
2736 self.logger.debug("Create Network REST : Waiting for Network creation complete")
2737 status = vca.block_until_completed(create_nw_task)
2738 if status:
2739 return response.content
2740 else:
2741 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2742 .format(response.content))
2743 except Exception as exp:
2744 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2745
2746 return None
2747
2748 def convert_cidr_to_netmask(self, cidr_ip=None):
2749 """
2750 Method sets convert CIDR netmask address to normal IP format
2751 Args:
2752 cidr_ip : CIDR IP address
2753 Returns:
2754 netmask : Converted netmask
2755 """
2756 if cidr_ip is not None:
2757 if '/' in cidr_ip:
2758 network, net_bits = cidr_ip.split('/')
2759 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2760 else:
2761 netmask = cidr_ip
2762 return netmask
2763 return None
2764
2765 def get_provider_rest(self, vca=None):
2766 """
2767 Method gets provider vdc view from vcloud director
2768
2769 Args:
2770 network_name - is network name to be created.
2771 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2772 It optional attribute. by default if no parent network indicate the first available will be used.
2773
2774 Returns:
2775 The return xml content of respond or None
2776 """
2777
2778 url_list = [vca.host, '/api/admin']
2779 response = Http.get(url=''.join(url_list),
2780 headers=vca.vcloud_session.get_vcloud_headers(),
2781 verify=vca.verify,
2782 logger=vca.logger)
2783
2784 if response.status_code == requests.codes.ok:
2785 return response.content
2786 return None
2787
2788 def create_vdc(self, vdc_name=None):
2789
2790 vdc_dict = {}
2791
2792 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2793 if xml_content is not None:
2794 try:
2795 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2796 for child in task_resp_xmlroot:
2797 if child.tag.split("}")[1] == 'Owner':
2798 vdc_id = child.attrib.get('href').split("/")[-1]
2799 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2800 return vdc_dict
2801 except:
2802 self.logger.debug("Respond body {}".format(xml_content))
2803
2804 return None
2805
2806 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2807 """
2808 Method create vdc in vCloud director based on VDC template.
2809 it uses pre-defined template that must be named openmano
2810
2811 Args:
2812 vdc_name - name of a new vdc.
2813
2814 Returns:
2815 The return xml content of respond or None
2816 """
2817
2818 self.logger.info("Creating new vdc {}".format(vdc_name))
2819 vca = self.connect()
2820 if not vca:
2821 raise vimconn.vimconnConnectionException("self.connect() is failed")
2822 if vdc_name is None:
2823 return None
2824
2825 url_list = [vca.host, '/api/vdcTemplates']
2826 vm_list_rest_call = ''.join(url_list)
2827 response = Http.get(url=vm_list_rest_call,
2828 headers=vca.vcloud_session.get_vcloud_headers(),
2829 verify=vca.verify,
2830 logger=vca.logger)
2831
2832 # container url to a template
2833 vdc_template_ref = None
2834 try:
2835 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2836 for child in vm_list_xmlroot:
2837 # application/vnd.vmware.admin.providervdc+xml
2838 # we need find a template from witch we instantiate VDC
2839 if child.tag.split("}")[1] == 'VdcTemplate':
2840 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2841 'name') == 'openmano':
2842 vdc_template_ref = child.attrib.get('href')
2843 except:
2844 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2845 self.logger.debug("Respond body {}".format(response.content))
2846 return None
2847
2848 # if we didn't found required pre defined template we return None
2849 if vdc_template_ref is None:
2850 return None
2851
2852 try:
2853 # instantiate vdc
2854 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2855 vm_list_rest_call = ''.join(url_list)
2856 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2857 <Source href="{1:s}"></Source>
2858 <Description>opnemano</Description>
2859 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2860 headers = vca.vcloud_session.get_vcloud_headers()
2861 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2862 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2863 logger=vca.logger)
2864 # if we all ok we respond with content otherwise by default None
2865 if response.status_code >= 200 and response.status_code < 300:
2866 return response.content
2867 return None
2868 except:
2869 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2870 self.logger.debug("Respond body {}".format(response.content))
2871
2872 return None
2873
2874 def create_vdc_rest(self, vdc_name=None):
2875 """
2876 Method create network in vCloud director
2877
2878 Args:
2879 network_name - is network name to be created.
2880 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2881 It optional attribute. by default if no parent network indicate the first available will be used.
2882
2883 Returns:
2884 The return network uuid or return None
2885 """
2886
2887 self.logger.info("Creating new vdc {}".format(vdc_name))
2888
2889 vca = self.connect_as_admin()
2890 if not vca:
2891 raise vimconn.vimconnConnectionException("self.connect() is failed")
2892 if vdc_name is None:
2893 return None
2894
2895 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2896 vm_list_rest_call = ''.join(url_list)
2897 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2898 response = Http.get(url=vm_list_rest_call,
2899 headers=vca.vcloud_session.get_vcloud_headers(),
2900 verify=vca.verify,
2901 logger=vca.logger)
2902
2903 provider_vdc_ref = None
2904 add_vdc_rest_url = None
2905 available_networks = None
2906
2907 if response.status_code != requests.codes.ok:
2908 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2909 response.status_code))
2910 return None
2911 else:
2912 try:
2913 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2914 for child in vm_list_xmlroot:
2915 # application/vnd.vmware.admin.providervdc+xml
2916 if child.tag.split("}")[1] == 'Link':
2917 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2918 and child.attrib.get('rel') == 'add':
2919 add_vdc_rest_url = child.attrib.get('href')
2920 except:
2921 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2922 self.logger.debug("Respond body {}".format(response.content))
2923 return None
2924
2925 response = self.get_provider_rest(vca=vca)
2926 try:
2927 vm_list_xmlroot = XmlElementTree.fromstring(response)
2928 for child in vm_list_xmlroot:
2929 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2930 for sub_child in child:
2931 provider_vdc_ref = sub_child.attrib.get('href')
2932 except:
2933 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2934 self.logger.debug("Respond body {}".format(response))
2935 return None
2936
2937 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2938 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2939 <AllocationModel>ReservationPool</AllocationModel>
2940 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2941 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2942 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2943 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2944 <ProviderVdcReference
2945 name="Main Provider"
2946 href="{2:s}" />
2947 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2948 escape(vdc_name),
2949 provider_vdc_ref)
2950
2951 headers = vca.vcloud_session.get_vcloud_headers()
2952 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2953 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2954 logger=vca.logger)
2955
2956 # if we all ok we respond with content otherwise by default None
2957 if response.status_code == 201:
2958 return response.content
2959 return None
2960
2961 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
2962 """
2963 Method retrieve vapp detail from vCloud director
2964
2965 Args:
2966 vapp_uuid - is vapp identifier.
2967
2968 Returns:
2969 The return network uuid or return None
2970 """
2971
2972 parsed_respond = {}
2973 vca = None
2974
2975 if need_admin_access:
2976 vca = self.connect_as_admin()
2977 else:
2978 vca = self.connect()
2979
2980 if not vca:
2981 raise vimconn.vimconnConnectionException("self.connect() is failed")
2982 if vapp_uuid is None:
2983 return None
2984
2985 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2986 get_vapp_restcall = ''.join(url_list)
2987
2988 if vca.vcloud_session and vca.vcloud_session.organization:
2989 response = Http.get(url=get_vapp_restcall,
2990 headers=vca.vcloud_session.get_vcloud_headers(),
2991 verify=vca.verify,
2992 logger=vca.logger)
2993
2994 if response.status_code != requests.codes.ok:
2995 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2996 response.status_code))
2997 return parsed_respond
2998
2999 try:
3000 xmlroot_respond = XmlElementTree.fromstring(response.content)
3001 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
3002
3003 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
3004 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
3005 'vmw': 'http://www.vmware.com/schema/ovf',
3006 'vm': 'http://www.vmware.com/vcloud/v1.5',
3007 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
3008 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
3009 "xmlns":"http://www.vmware.com/vcloud/v1.5"
3010 }
3011
3012 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
3013 if created_section is not None:
3014 parsed_respond['created'] = created_section.text
3015
3016 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
3017 if network_section is not None and 'networkName' in network_section.attrib:
3018 parsed_respond['networkname'] = network_section.attrib['networkName']
3019
3020 ipscopes_section = \
3021 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
3022 namespaces)
3023 if ipscopes_section is not None:
3024 for ipscope in ipscopes_section:
3025 for scope in ipscope:
3026 tag_key = scope.tag.split("}")[1]
3027 if tag_key == 'IpRanges':
3028 ip_ranges = scope.getchildren()
3029 for ipblock in ip_ranges:
3030 for block in ipblock:
3031 parsed_respond[block.tag.split("}")[1]] = block.text
3032 else:
3033 parsed_respond[tag_key] = scope.text
3034
3035 # parse children section for other attrib
3036 children_section = xmlroot_respond.find('vm:Children/', namespaces)
3037 if children_section is not None:
3038 parsed_respond['name'] = children_section.attrib['name']
3039 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
3040 if "nestedHypervisorEnabled" in children_section.attrib else None
3041 parsed_respond['deployed'] = children_section.attrib['deployed']
3042 parsed_respond['status'] = children_section.attrib['status']
3043 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
3044 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
3045 nic_list = []
3046 for adapters in network_adapter:
3047 adapter_key = adapters.tag.split("}")[1]
3048 if adapter_key == 'PrimaryNetworkConnectionIndex':
3049 parsed_respond['primarynetwork'] = adapters.text
3050 if adapter_key == 'NetworkConnection':
3051 vnic = {}
3052 if 'network' in adapters.attrib:
3053 vnic['network'] = adapters.attrib['network']
3054 for adapter in adapters:
3055 setting_key = adapter.tag.split("}")[1]
3056 vnic[setting_key] = adapter.text
3057 nic_list.append(vnic)
3058
3059 for link in children_section:
3060 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3061 if link.attrib['rel'] == 'screen:acquireTicket':
3062 parsed_respond['acquireTicket'] = link.attrib
3063 if link.attrib['rel'] == 'screen:acquireMksTicket':
3064 parsed_respond['acquireMksTicket'] = link.attrib
3065
3066 parsed_respond['interfaces'] = nic_list
3067 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
3068 if vCloud_extension_section is not None:
3069 vm_vcenter_info = {}
3070 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
3071 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
3072 if vmext is not None:
3073 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
3074 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
3075
3076 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
3077 vm_virtual_hardware_info = {}
3078 if virtual_hardware_section is not None:
3079 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
3080 if item.find("rasd:Description",namespaces).text == "Hard disk":
3081 disk_size = item.find("rasd:HostResource" ,namespaces
3082 ).attrib["{"+namespaces['vm']+"}capacity"]
3083
3084 vm_virtual_hardware_info["disk_size"]= disk_size
3085 break
3086
3087 for link in virtual_hardware_section:
3088 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
3089 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
3090 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
3091 break
3092
3093 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
3094 except Exception as exp :
3095 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
3096 return parsed_respond
3097
3098 def acuire_console(self, vm_uuid=None):
3099
3100 vca = self.connect()
3101 if not vca:
3102 raise vimconn.vimconnConnectionException("self.connect() is failed")
3103 if vm_uuid is None:
3104 return None
3105
3106 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
3107 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
3108 console_dict = vm_dict['acquireTicket']
3109 console_rest_call = console_dict['href']
3110
3111 response = Http.post(url=console_rest_call,
3112 headers=vca.vcloud_session.get_vcloud_headers(),
3113 verify=vca.verify,
3114 logger=vca.logger)
3115
3116 if response.status_code == requests.codes.ok:
3117 return response.content
3118
3119 return None
3120
3121 def modify_vm_disk(self, vapp_uuid, flavor_disk):
3122 """
3123 Method retrieve vm disk details
3124
3125 Args:
3126 vapp_uuid - is vapp identifier.
3127 flavor_disk - disk size as specified in VNFD (flavor)
3128
3129 Returns:
3130 The return network uuid or return None
3131 """
3132 status = None
3133 try:
3134 #Flavor disk is in GB convert it into MB
3135 flavor_disk = int(flavor_disk) * 1024
3136 vm_details = self.get_vapp_details_rest(vapp_uuid)
3137 if vm_details:
3138 vm_name = vm_details["name"]
3139 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
3140
3141 if vm_details and "vm_virtual_hardware" in vm_details:
3142 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
3143 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
3144
3145 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
3146
3147 if flavor_disk > vm_disk:
3148 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
3149 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
3150 vm_disk, flavor_disk ))
3151 else:
3152 status = True
3153 self.logger.info("No need to modify disk of VM {}".format(vm_name))
3154
3155 return status
3156 except Exception as exp:
3157 self.logger.info("Error occurred while modifing disk size {}".format(exp))
3158
3159
3160 def modify_vm_disk_rest(self, disk_href , disk_size):
3161 """
3162 Method retrieve modify vm disk size
3163
3164 Args:
3165 disk_href - vCD API URL to GET and PUT disk data
3166 disk_size - disk size as specified in VNFD (flavor)
3167
3168 Returns:
3169 The return network uuid or return None
3170 """
3171 vca = self.connect()
3172 if not vca:
3173 raise vimconn.vimconnConnectionException("self.connect() is failed")
3174 if disk_href is None or disk_size is None:
3175 return None
3176
3177 if vca.vcloud_session and vca.vcloud_session.organization:
3178 response = Http.get(url=disk_href,
3179 headers=vca.vcloud_session.get_vcloud_headers(),
3180 verify=vca.verify,
3181 logger=vca.logger)
3182
3183 if response.status_code != requests.codes.ok:
3184 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
3185 response.status_code))
3186 return None
3187 try:
3188 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
3189 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
3190 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3191
3192 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
3193 if item.find("rasd:Description",namespaces).text == "Hard disk":
3194 disk_item = item.find("rasd:HostResource" ,namespaces )
3195 if disk_item is not None:
3196 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
3197 break
3198
3199 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
3200 xml_declaration=True)
3201
3202 #Send PUT request to modify disk size
3203 headers = vca.vcloud_session.get_vcloud_headers()
3204 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3205
3206 response = Http.put(url=disk_href,
3207 data=data,
3208 headers=headers,
3209 verify=vca.verify, logger=self.logger)
3210
3211 if response.status_code != 202:
3212 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
3213 response.status_code))
3214 else:
3215 modify_disk_task = taskType.parseString(response.content, True)
3216 if type(modify_disk_task) is GenericTask:
3217 status = vca.block_until_completed(modify_disk_task)
3218 return status
3219
3220 return None
3221
3222 except Exception as exp :
3223 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
3224 return None
3225
3226 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
3227 """
3228 Method to attach pci devices to VM
3229
3230 Args:
3231 vapp_uuid - uuid of vApp/VM
3232 pci_devices - pci devices infromation as specified in VNFD (flavor)
3233
3234 Returns:
3235 The status of add pci device task , vm object and
3236 vcenter_conect object
3237 """
3238 vm_obj = None
3239 vcenter_conect = None
3240 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3241 try:
3242 vm_vcenter_info = self.get_vm_vcenter_info(vapp_uuid)
3243 except Exception as exp:
3244 self.logger.error("Error occurred while getting vCenter infromationn"\
3245 " for VM : {}".format(exp))
3246 raise vimconn.vimconnException(message=exp)
3247
3248 if vm_vcenter_info["vm_moref_id"]:
3249 context = None
3250 if hasattr(ssl, '_create_unverified_context'):
3251 context = ssl._create_unverified_context()
3252 try:
3253 no_of_pci_devices = len(pci_devices)
3254 if no_of_pci_devices > 0:
3255 vcenter_conect = SmartConnect(
3256 host=vm_vcenter_info["vm_vcenter_ip"],
3257 user=vm_vcenter_info["vm_vcenter_user"],
3258 pwd=vm_vcenter_info["vm_vcenter_password"],
3259 port=int(vm_vcenter_info["vm_vcenter_port"]),
3260 sslContext=context)
3261 atexit.register(Disconnect, vcenter_conect)
3262 content = vcenter_conect.RetrieveContent()
3263
3264 #Get VM and its host
3265 host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
3266 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3267 if host_obj and vm_obj:
3268 #get PCI devies from host on which vapp is currently installed
3269 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3270
3271 if avilable_pci_devices is None:
3272 #find other hosts with active pci devices
3273 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3274 content,
3275 no_of_pci_devices
3276 )
3277
3278 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3279 #Migrate vm to the host where PCI devices are availble
3280 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3281 task = self.relocate_vm(new_host_obj, vm_obj)
3282 if task is not None:
3283 result = self.wait_for_vcenter_task(task, vcenter_conect)
3284 self.logger.info("Migrate VM status: {}".format(result))
3285 host_obj = new_host_obj
3286 else:
3287 self.logger.info("Fail to migrate VM : {}".format(result))
3288 raise vimconn.vimconnNotFoundException(
3289 "Fail to migrate VM : {} to host {}".format(
3290 vmname_andid,
3291 new_host_obj)
3292 )
3293
3294 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3295 #Add PCI devices one by one
3296 for pci_device in avilable_pci_devices:
3297 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3298 if task:
3299 status= self.wait_for_vcenter_task(task, vcenter_conect)
3300 if status:
3301 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3302 else:
3303 self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3304 return True, vm_obj, vcenter_conect
3305 else:
3306 self.logger.error("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 )
3311 raise vimconn.vimconnNotFoundException(
3312 "Currently there is no host with {} "\
3313 "number of avaialble PCI devices required for VM {}".format(
3314 no_of_pci_devices,
3315 vmname_andid))
3316 else:
3317 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3318
3319 except vmodl.MethodFault as error:
3320 self.logger.error("Error occurred while adding PCI devices {} ",error)
3321 return None, vm_obj, vcenter_conect
3322
3323 def get_vm_obj(self, content, mob_id):
3324 """
3325 Method to get the vsphere VM object associated with a given morf ID
3326 Args:
3327 vapp_uuid - uuid of vApp/VM
3328 content - vCenter content object
3329 mob_id - mob_id of VM
3330
3331 Returns:
3332 VM and host object
3333 """
3334 vm_obj = None
3335 host_obj = None
3336 try :
3337 container = content.viewManager.CreateContainerView(content.rootFolder,
3338 [vim.VirtualMachine], True
3339 )
3340 for vm in container.view:
3341 mobID = vm._GetMoId()
3342 if mobID == mob_id:
3343 vm_obj = vm
3344 host_obj = vm_obj.runtime.host
3345 break
3346 except Exception as exp:
3347 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3348 return host_obj, vm_obj
3349
3350 def get_pci_devices(self, host, need_devices):
3351 """
3352 Method to get the details of pci devices on given host
3353 Args:
3354 host - vSphere host object
3355 need_devices - number of pci devices needed on host
3356
3357 Returns:
3358 array of pci devices
3359 """
3360 all_devices = []
3361 all_device_ids = []
3362 used_devices_ids = []
3363
3364 try:
3365 if host:
3366 pciPassthruInfo = host.config.pciPassthruInfo
3367 pciDevies = host.hardware.pciDevice
3368
3369 for pci_status in pciPassthruInfo:
3370 if pci_status.passthruActive:
3371 for device in pciDevies:
3372 if device.id == pci_status.id:
3373 all_device_ids.append(device.id)
3374 all_devices.append(device)
3375
3376 #check if devices are in use
3377 avalible_devices = all_devices
3378 for vm in host.vm:
3379 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3380 vm_devices = vm.config.hardware.device
3381 for device in vm_devices:
3382 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3383 if device.backing.id in all_device_ids:
3384 for use_device in avalible_devices:
3385 if use_device.id == device.backing.id:
3386 avalible_devices.remove(use_device)
3387 used_devices_ids.append(device.backing.id)
3388 self.logger.debug("Device {} from devices {}"\
3389 "is in use".format(device.backing.id,
3390 device)
3391 )
3392 if len(avalible_devices) < need_devices:
3393 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3394 need_devices))
3395 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3396 avalible_devices))
3397 return None
3398 else:
3399 required_devices = avalible_devices[:need_devices]
3400 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3401 len(avalible_devices),
3402 host,
3403 need_devices))
3404 self.logger.info("Retruning {} devices as {}".format(need_devices,
3405 required_devices ))
3406 return required_devices
3407
3408 except Exception as exp:
3409 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3410
3411 return None
3412
3413 def get_host_and_PCIdevices(self, content, need_devices):
3414 """
3415 Method to get the details of pci devices infromation on all hosts
3416
3417 Args:
3418 content - vSphere host object
3419 need_devices - number of pci devices needed on host
3420
3421 Returns:
3422 array of pci devices and host object
3423 """
3424 host_obj = None
3425 pci_device_objs = None
3426 try:
3427 if content:
3428 container = content.viewManager.CreateContainerView(content.rootFolder,
3429 [vim.HostSystem], True)
3430 for host in container.view:
3431 devices = self.get_pci_devices(host, need_devices)
3432 if devices:
3433 host_obj = host
3434 pci_device_objs = devices
3435 break
3436 except Exception as exp:
3437 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3438
3439 return host_obj,pci_device_objs
3440
3441 def relocate_vm(self, dest_host, vm) :
3442 """
3443 Method to get the relocate VM to new host
3444
3445 Args:
3446 dest_host - vSphere host object
3447 vm - vSphere VM object
3448
3449 Returns:
3450 task object
3451 """
3452 task = None
3453 try:
3454 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3455 task = vm.Relocate(relocate_spec)
3456 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3457 except Exception as exp:
3458 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3459 dest_host, vm, exp))
3460 return task
3461
3462 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3463 """
3464 Waits and provides updates on a vSphere task
3465 """
3466 while task.info.state == vim.TaskInfo.State.running:
3467 time.sleep(2)
3468
3469 if task.info.state == vim.TaskInfo.State.success:
3470 if task.info.result is not None and not hideResult:
3471 self.logger.info('{} completed successfully, result: {}'.format(
3472 actionName,
3473 task.info.result))
3474 else:
3475 self.logger.info('Task {} completed successfully.'.format(actionName))
3476 else:
3477 self.logger.error('{} did not complete successfully: {} '.format(
3478 actionName,
3479 task.info.error)
3480 )
3481
3482 return task.info.result
3483
3484 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3485 """
3486 Method to add pci device in given VM
3487
3488 Args:
3489 host_object - vSphere host object
3490 vm_object - vSphere VM object
3491 host_pci_dev - host_pci_dev must be one of the devices from the
3492 host_object.hardware.pciDevice list
3493 which is configured as a PCI passthrough device
3494
3495 Returns:
3496 task object
3497 """
3498 task = None
3499 if vm_object and host_object and host_pci_dev:
3500 try :
3501 #Add PCI device to VM
3502 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3503 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3504
3505 if host_pci_dev.id not in systemid_by_pciid:
3506 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3507 return None
3508
3509 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3510 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3511 id=host_pci_dev.id,
3512 systemId=systemid_by_pciid[host_pci_dev.id],
3513 vendorId=host_pci_dev.vendorId,
3514 deviceName=host_pci_dev.deviceName)
3515
3516 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3517
3518 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3519 new_device_config.operation = "add"
3520 vmConfigSpec = vim.vm.ConfigSpec()
3521 vmConfigSpec.deviceChange = [new_device_config]
3522
3523 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3524 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3525 host_pci_dev, vm_object, host_object)
3526 )
3527 except Exception as exp:
3528 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3529 host_pci_dev,
3530 vm_object,
3531 exp))
3532 return task
3533
3534 def get_vm_vcenter_info(self , vapp_uuid):
3535 """
3536 Method to get details of vCenter and vm
3537
3538 Args:
3539 vapp_uuid - uuid of vApp or VM
3540
3541 Returns:
3542 Moref Id of VM and deails of vCenter
3543 """
3544 vm_vcenter_info = {}
3545
3546 if self.vcenter_ip is not None:
3547 vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
3548 else:
3549 raise vimconn.vimconnException(message="vCenter IP is not provided."\
3550 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3551 if self.vcenter_port is not None:
3552 vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
3553 else:
3554 raise vimconn.vimconnException(message="vCenter port is not provided."\
3555 " Please provide vCenter port while attaching datacenter to tenant in --config")
3556 if self.vcenter_user is not None:
3557 vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
3558 else:
3559 raise vimconn.vimconnException(message="vCenter user is not provided."\
3560 " Please provide vCenter user while attaching datacenter to tenant in --config")
3561
3562 if self.vcenter_password is not None:
3563 vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
3564 else:
3565 raise vimconn.vimconnException(message="vCenter user password is not provided."\
3566 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3567 try:
3568 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
3569 if vm_details and "vm_vcenter_info" in vm_details:
3570 vm_vcenter_info["vm_moref_id"] = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
3571
3572 return vm_vcenter_info
3573
3574 except Exception as exp:
3575 self.logger.error("Error occurred while getting vCenter infromationn"\
3576 " for VM : {}".format(exp))
3577
3578
3579 def get_vm_pci_details(self, vmuuid):
3580 """
3581 Method to get VM PCI device details from vCenter
3582
3583 Args:
3584 vm_obj - vSphere VM object
3585
3586 Returns:
3587 dict of PCI devives attached to VM
3588
3589 """
3590 vm_pci_devices_info = {}
3591 try:
3592 vm_vcenter_info = self.get_vm_vcenter_info(vmuuid)
3593 if vm_vcenter_info["vm_moref_id"]:
3594 context = None
3595 if hasattr(ssl, '_create_unverified_context'):
3596 context = ssl._create_unverified_context()
3597 vcenter_conect = SmartConnect(host=vm_vcenter_info["vm_vcenter_ip"],
3598 user=vm_vcenter_info["vm_vcenter_user"],
3599 pwd=vm_vcenter_info["vm_vcenter_password"],
3600 port=int(vm_vcenter_info["vm_vcenter_port"]),
3601 sslContext=context
3602 )
3603 atexit.register(Disconnect, vcenter_conect)
3604 content = vcenter_conect.RetrieveContent()
3605
3606 #Get VM and its host
3607 if content:
3608 host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
3609 if host_obj and vm_obj:
3610 vm_pci_devices_info["host_name"]= host_obj.name
3611 vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
3612 for device in vm_obj.config.hardware.device:
3613 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3614 device_details={'devide_id':device.backing.id,
3615 'pciSlotNumber':device.slotInfo.pciSlotNumber,
3616 }
3617 vm_pci_devices_info[device.deviceInfo.label] = device_details
3618 else:
3619 self.logger.error("Can not connect to vCenter while getting "\
3620 "PCI devices infromationn")
3621 return vm_pci_devices_info
3622 except Exception as exp:
3623 self.logger.error("Error occurred while getting VM infromationn"\
3624 " for VM : {}".format(exp))
3625 raise vimconn.vimconnException(message=exp)
3626
3627 def add_network_adapter_to_vms(self, vapp, network_name, primary_nic_index, nicIndex, net, nic_type=None):
3628 """
3629 Method to add network adapter type to vm
3630 Args :
3631 network_name - name of network
3632 primary_nic_index - int value for primary nic index
3633 nicIndex - int value for nic index
3634 nic_type - specify model name to which add to vm
3635 Returns:
3636 None
3637 """
3638 vca = self.connect()
3639 if not vca:
3640 raise vimconn.vimconnConnectionException("Failed to connect vCloud director")
3641
3642 try:
3643 floating_ip = False
3644 if 'floating_ip' in net: floating_ip = net['floating_ip']
3645 allocation_mode = "POOL" if floating_ip else "DHCP"
3646
3647 if not nic_type:
3648 for vms in vapp._get_vms():
3649 vm_id = (vms.id).split(':')[-1]
3650
3651 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3652
3653 response = Http.get(url=url_rest_call,
3654 headers=vca.vcloud_session.get_vcloud_headers(),
3655 verify=vca.verify,
3656 logger=vca.logger)
3657 if response.status_code != 200:
3658 self.logger.error("REST call {} failed reason : {}"\
3659 "status code : {}".format(url_rest_call,
3660 response.content,
3661 response.status_code))
3662 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3663 "network connection section")
3664
3665 data = response.content
3666 if '<PrimaryNetworkConnectionIndex>' not in data:
3667 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3668 <NetworkConnection network="{}">
3669 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3670 <IsConnected>true</IsConnected>
3671 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3672 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3673 allocation_mode)
3674
3675 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3676 else:
3677 new_item = """<NetworkConnection network="{}">
3678 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3679 <IsConnected>true</IsConnected>
3680 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3681 </NetworkConnection>""".format(network_name, nicIndex,
3682 allocation_mode)
3683
3684 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3685
3686 headers = vca.vcloud_session.get_vcloud_headers()
3687 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3688 response = Http.put(url=url_rest_call, headers=headers, data=data,
3689 verify=vca.verify,
3690 logger=vca.logger)
3691 if response.status_code != 202:
3692 self.logger.error("REST call {} failed reason : {}"\
3693 "status code : {} ".format(url_rest_call,
3694 response.content,
3695 response.status_code))
3696 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3697 "network connection section")
3698 else:
3699 nic_task = taskType.parseString(response.content, True)
3700 if isinstance(nic_task, GenericTask):
3701 vca.block_until_completed(nic_task)
3702 self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\
3703 "default NIC type".format(vm_id))
3704 else:
3705 self.logger.error("add_network_adapter_to_vms(): VM {} failed to "\
3706 "connect NIC type".format(vm_id))
3707 else:
3708 for vms in vapp._get_vms():
3709 vm_id = (vms.id).split(':')[-1]
3710
3711 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3712
3713 response = Http.get(url=url_rest_call,
3714 headers=vca.vcloud_session.get_vcloud_headers(),
3715 verify=vca.verify,
3716 logger=vca.logger)
3717 if response.status_code != 200:
3718 self.logger.error("REST call {} failed reason : {}"\
3719 "status code : {}".format(url_rest_call,
3720 response.content,
3721 response.status_code))
3722 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to get "\
3723 "network connection section")
3724 data = response.content
3725 if '<PrimaryNetworkConnectionIndex>' not in data:
3726 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3727 <NetworkConnection network="{}">
3728 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3729 <IsConnected>true</IsConnected>
3730 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3731 <NetworkAdapterType>{}</NetworkAdapterType>
3732 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex,
3733 allocation_mode, nic_type)
3734
3735 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3736 else:
3737 new_item = """<NetworkConnection network="{}">
3738 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3739 <IsConnected>true</IsConnected>
3740 <IpAddressAllocationMode>{}</IpAddressAllocationMode>
3741 <NetworkAdapterType>{}</NetworkAdapterType>
3742 </NetworkConnection>""".format(network_name, nicIndex,
3743 allocation_mode, nic_type)
3744
3745 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3746
3747 headers = vca.vcloud_session.get_vcloud_headers()
3748 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3749 response = Http.put(url=url_rest_call, headers=headers, data=data,
3750 verify=vca.verify,
3751 logger=vca.logger)
3752
3753 if response.status_code != 202:
3754 self.logger.error("REST call {} failed reason : {}"\
3755 "status code : {}".format(url_rest_call,
3756 response.content,
3757 response.status_code))
3758 raise vimconn.vimconnException("add_network_adapter_to_vms : Failed to update "\
3759 "network connection section")
3760 else:
3761 nic_task = taskType.parseString(response.content, True)
3762 if isinstance(nic_task, GenericTask):
3763 vca.block_until_completed(nic_task)
3764 self.logger.info("add_network_adapter_to_vms(): VM {} "\
3765 "conneced to NIC type {}".format(vm_id, nic_type))
3766 else:
3767 self.logger.error("add_network_adapter_to_vms(): VM {} "\
3768 "failed to connect NIC type {}".format(vm_id, nic_type))
3769 except Exception as exp:
3770 self.logger.error("add_network_adapter_to_vms() : exception occurred "\
3771 "while adding Network adapter")
3772 raise vimconn.vimconnException(message=exp)
3773
3774
3775 def set_numa_affinity(self, vmuuid, paired_threads_id):
3776 """
3777 Method to assign numa affinity in vm configuration parammeters
3778 Args :
3779 vmuuid - vm uuid
3780 paired_threads_id - one or more virtual processor
3781 numbers
3782 Returns:
3783 return if True
3784 """
3785 try:
3786 vm_moref_id , vm_vcenter_host , vm_vcenter_username, vm_vcenter_port = self.get_vcenter_info_rest(vmuuid)
3787 if vm_moref_id and vm_vcenter_host and vm_vcenter_username:
3788 context = None
3789 if hasattr(ssl, '_create_unverified_context'):
3790 context = ssl._create_unverified_context()
3791 vcenter_conect = SmartConnect(host=vm_vcenter_host, user=vm_vcenter_username,
3792 pwd=self.passwd, port=int(vm_vcenter_port),
3793 sslContext=context)
3794 atexit.register(Disconnect, vcenter_conect)
3795 content = vcenter_conect.RetrieveContent()
3796
3797 host_obj, vm_obj = self.get_vm_obj(content ,vm_moref_id)
3798 if vm_obj:
3799 config_spec = vim.vm.ConfigSpec()
3800 config_spec.extraConfig = []
3801 opt = vim.option.OptionValue()
3802 opt.key = 'numa.nodeAffinity'
3803 opt.value = str(paired_threads_id)
3804 config_spec.extraConfig.append(opt)
3805 task = vm_obj.ReconfigVM_Task(config_spec)
3806 if task:
3807 result = self.wait_for_vcenter_task(task, vcenter_conect)
3808 extra_config = vm_obj.config.extraConfig
3809 flag = False
3810 for opts in extra_config:
3811 if 'numa.nodeAffinity' in opts.key:
3812 flag = True
3813 self.logger.info("set_numa_affinity: Sucessfully assign numa affinity "\
3814 "value {} for vm {}".format(opt.value, vm_obj))
3815 if flag:
3816 return
3817 else:
3818 self.logger.error("set_numa_affinity: Failed to assign numa affinity")
3819 except Exception as exp:
3820 self.logger.error("set_numa_affinity : exception occurred while setting numa affinity "\
3821 "for VM {} : {}".format(vm_obj, vm_moref_id))
3822 raise vimconn.vimconnException("set_numa_affinity : Error {} failed to assign numa "\
3823 "affinity".format(exp))
3824
3825
3826 def cloud_init(self, vapp, cloud_config):
3827 """
3828 Method to inject ssh-key
3829 vapp - vapp object
3830 cloud_config a dictionary with:
3831 'key-pairs': (optional) list of strings with the public key to be inserted to the default user
3832 'users': (optional) list of users to be inserted, each item is a dict with:
3833 'name': (mandatory) user name,
3834 'key-pairs': (optional) list of strings with the public key to be inserted to the user
3835 'user-data': (optional) string is a text script to be passed directly to cloud-init
3836 'config-files': (optional). List of files to be transferred. Each item is a dict with:
3837 'dest': (mandatory) string with the destination absolute path
3838 'encoding': (optional, by default text). Can be one of:
3839 'b64', 'base64', 'gz', 'gz+b64', 'gz+base64', 'gzip+b64', 'gzip+base64'
3840 'content' (mandatory): string with the content of the file
3841 'permissions': (optional) string with file permissions, typically octal notation '0644'
3842 'owner': (optional) file owner, string with the format 'owner:group'
3843 'boot-data-drive': boolean to indicate if user-data must be passed using a boot drive (hard disk
3844 """
3845 vca = self.connect()
3846 if not vca:
3847 raise vimconn.vimconnConnectionException("Failed to connect vCloud director")
3848
3849 try:
3850 if isinstance(cloud_config, dict):
3851 key_pairs = []
3852 userdata = []
3853 if "key-pairs" in cloud_config:
3854 key_pairs = cloud_config["key-pairs"]
3855
3856 if "users" in cloud_config:
3857 userdata = cloud_config["users"]
3858
3859 for key in key_pairs:
3860 for user in userdata:
3861 if 'name' in user: user_name = user['name']
3862 if 'key-pairs' in user and len(user['key-pairs']) > 0:
3863 for user_key in user['key-pairs']:
3864 customize_script = """
3865 #!/bin/bash
3866 echo performing customization tasks with param $1 at `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3867 if [ "$1" = "precustomization" ];then
3868 echo performing precustomization tasks on `date "+DATE: %Y-%m-%d - TIME: %H:%M:%S"` >> /root/customization.log
3869 if [ ! -d /root/.ssh ];then
3870 mkdir /root/.ssh
3871 chown root:root /root/.ssh
3872 chmod 700 /root/.ssh
3873 touch /root/.ssh/authorized_keys
3874 chown root:root /root/.ssh/authorized_keys
3875 chmod 600 /root/.ssh/authorized_keys
3876 # make centos with selinux happy
3877 which restorecon && restorecon -Rv /root/.ssh
3878 echo '{key}' >> /root/.ssh/authorized_keys
3879 else
3880 touch /root/.ssh/authorized_keys
3881 chown root:root /root/.ssh/authorized_keys
3882 chmod 600 /root/.ssh/authorized_keys
3883 echo '{key}' >> /root/.ssh/authorized_keys
3884 fi
3885 if [ -d /home/{user_name} ];then
3886 if [ ! -d /home/{user_name}/.ssh ];then
3887 mkdir /home/{user_name}/.ssh
3888 chown {user_name}:{user_name} /home/{user_name}/.ssh
3889 chmod 700 /home/{user_name}/.ssh
3890 touch /home/{user_name}/.ssh/authorized_keys
3891 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3892 chmod 600 /home/{user_name}/.ssh/authorized_keys
3893 # make centos with selinux happy
3894 which restorecon && restorecon -Rv /home/{user_name}/.ssh
3895 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3896 else
3897 touch /home/{user_name}/.ssh/authorized_keys
3898 chown {user_name}:{user_name} /home/{user_name}/.ssh/authorized_keys
3899 chmod 600 /home/{user_name}/.ssh/authorized_keys
3900 echo '{user_key}' >> /home/{user_name}/.ssh/authorized_keys
3901 fi
3902 fi
3903 fi""".format(key=key, user_name=user_name, user_key=user_key)
3904
3905 for vm in vapp._get_vms():
3906 vm_name = vm.name
3907 task = vapp.customize_guest_os(vm_name, customization_script=customize_script)
3908 if isinstance(task, GenericTask):
3909 vca.block_until_completed(task)
3910 self.logger.info("cloud_init : customized guest os task "\
3911 "completed for VM {}".format(vm_name))
3912 else:
3913 self.logger.error("cloud_init : task for customized guest os"\
3914 "failed for VM {}".format(vm_name))
3915 except Exception as exp:
3916 self.logger.error("cloud_init : exception occurred while injecting "\
3917 "ssh-key")
3918 raise vimconn.vimconnException("cloud_init : Error {} failed to inject "\
3919 "ssh-key".format(exp))