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