1.Modified code for instance actions 2.Added code for network adapter type support
[osm/RO.git] / vimconn_vmware.py
1 # -*- coding: utf-8 -*-
2
3 ##
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openmano
6 # All Rights Reserved.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
18 # under the License.
19 #
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
22 ##
23
24 """
25 vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
26 mbayramov@vmware.com
27 """
28 from progressbar import Percentage, Bar, ETA, FileTransferSpeed, ProgressBar
29
30 import vimconn
31 import os
32 import traceback
33 import itertools
34 import requests
35 import ssl
36 import atexit
37
38 from pyVmomi import vim, vmodl
39 from pyVim.connect import SmartConnect, Disconnect
40
41 from xml.etree import ElementTree as XmlElementTree
42 from lxml import etree as lxmlElementTree
43
44 import yaml
45 from pyvcloud import Http
46 from pyvcloud.vcloudair import VCA
47 from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
48 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
49 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
50 from xml.sax.saxutils import escape
51
52 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
53 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
54 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
55 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
56
57 import logging
58 import json
59 import time
60 import uuid
61 import httplib
62 import hashlib
63 import socket
64 import struct
65 import netaddr
66
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 try:
562 vdcid = vdc.get_id().split(":")[3]
563 networks = vca.get_networks(vdc.get_name())
564 network_list = []
565
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 try:
614 vdc = vca.get_vdc(self.tenant_name)
615 vdc_id = vdc.get_id().split(":")[3]
616
617 networks = vca.get_networks(vdc.get_name())
618 filter_dict = {}
619
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 try:
848 for catalog in vca.get_catalogs():
849 if catalog_name != catalog.name:
850 continue
851 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
852 link.get_rel() == 'add', catalog.get_Link())
853 assert len(link) == 1
854 data = """
855 <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>
856 """ % (escape(catalog_name), escape(description))
857 headers = vca.vcloud_session.get_vcloud_headers()
858 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
859 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
860 if response.status_code == requests.codes.created:
861 catalogItem = XmlElementTree.fromstring(response.content)
862 entity = [child for child in catalogItem if
863 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
864 href = entity.get('href')
865 template = href
866 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
867 verify=vca.verify, logger=self.logger)
868
869 if response.status_code == requests.codes.ok:
870 media = mediaType.parseString(response.content, True)
871 link = filter(lambda link: link.get_rel() == 'upload:default',
872 media.get_Files().get_File()[0].get_Link())[0]
873 headers = vca.vcloud_session.get_vcloud_headers()
874 headers['Content-Type'] = 'Content-Type text/xml'
875 response = Http.put(link.get_href(),
876 data=open(media_file_name, 'rb'),
877 headers=headers,
878 verify=vca.verify, logger=self.logger)
879 if response.status_code != requests.codes.ok:
880 self.logger.debug(
881 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
882 media_file_name))
883 return False
884
885 # TODO fix this with aync block
886 time.sleep(5)
887
888 self.logger.debug("vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
889
890 # uploading VMDK file
891 # check status of OVF upload and upload remaining files.
892 response = Http.get(template,
893 headers=vca.vcloud_session.get_vcloud_headers(),
894 verify=vca.verify,
895 logger=self.logger)
896
897 if response.status_code == requests.codes.ok:
898 media = mediaType.parseString(response.content, True)
899 number_of_files = len(media.get_Files().get_File())
900 for index in xrange(0, number_of_files):
901 links_list = filter(lambda link: link.get_rel() == 'upload:default',
902 media.get_Files().get_File()[index].get_Link())
903 for link in links_list:
904 # we skip ovf since it already uploaded.
905 if 'ovf' in link.get_href():
906 continue
907 # The OVF file and VMDK must be in a same directory
908 head, tail = os.path.split(media_file_name)
909 file_vmdk = head + '/' + link.get_href().split("/")[-1]
910 if not os.path.isfile(file_vmdk):
911 return False
912 statinfo = os.stat(file_vmdk)
913 if statinfo.st_size == 0:
914 return False
915 hrefvmdk = link.get_href()
916
917 if progress:
918 print("Uploading file: {}".format(file_vmdk))
919 if progress:
920 widgets = ['Uploading file: ', Percentage(), ' ', Bar(), ' ', ETA(), ' ',
921 FileTransferSpeed()]
922 progress_bar = ProgressBar(widgets=widgets, maxval=statinfo.st_size).start()
923
924 bytes_transferred = 0
925 f = open(file_vmdk, 'rb')
926 while bytes_transferred < statinfo.st_size:
927 my_bytes = f.read(chunk_bytes)
928 if len(my_bytes) <= chunk_bytes:
929 headers = vca.vcloud_session.get_vcloud_headers()
930 headers['Content-Range'] = 'bytes %s-%s/%s' % (
931 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
932 headers['Content-Length'] = str(len(my_bytes))
933 response = Http.put(hrefvmdk,
934 headers=headers,
935 data=my_bytes,
936 verify=vca.verify,
937 logger=None)
938
939 if response.status_code == requests.codes.ok:
940 bytes_transferred += len(my_bytes)
941 if progress:
942 progress_bar.update(bytes_transferred)
943 else:
944 self.logger.debug(
945 'file upload failed with error: [%s] %s' % (response.status_code,
946 response.content))
947
948 f.close()
949 return False
950 f.close()
951 if progress:
952 progress_bar.finish()
953 time.sleep(10)
954 return True
955 else:
956 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
957 format(catalog_name, media_file_name))
958 return False
959 except Exception as exp:
960 self.logger.debug("Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
961 .format(catalog_name,media_file_name, exp))
962 raise vimconn.vimconnException(
963 "Failed while uploading OVF to catalog {} for OVF file {} with Exception {}"
964 .format(catalog_name,media_file_name, exp))
965
966 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
967 return False
968
969 def upload_vimimage(self, vca=None, catalog_name=None, media_name=None, medial_file_name=None, progress=False):
970 """Upload media file"""
971 # TODO add named parameters for readability
972
973 return self.upload_ovf(vca=vca, catalog_name=catalog_name, image_name=media_name.split(".")[0],
974 media_file_name=medial_file_name, description='medial_file_name', progress=progress)
975
976 def validate_uuid4(self, uuid_string=None):
977 """ Method validate correct format of UUID.
978
979 Return: true if string represent valid uuid
980 """
981 try:
982 val = uuid.UUID(uuid_string, version=4)
983 except ValueError:
984 return False
985 return True
986
987 def get_catalogid(self, catalog_name=None, catalogs=None):
988 """ Method check catalog and return catalog ID in UUID format.
989
990 Args
991 catalog_name: catalog name as string
992 catalogs: list of catalogs.
993
994 Return: catalogs uuid
995 """
996
997 for catalog in catalogs:
998 if catalog.name == catalog_name:
999 catalog_id = catalog.get_id().split(":")
1000 return catalog_id[3]
1001 return None
1002
1003 def get_catalogbyid(self, catalog_uuid=None, catalogs=None):
1004 """ Method check catalog and return catalog name lookup done by catalog UUID.
1005
1006 Args
1007 catalog_name: catalog name as string
1008 catalogs: list of catalogs.
1009
1010 Return: catalogs name or None
1011 """
1012
1013 if not self.validate_uuid4(uuid_string=catalog_uuid):
1014 return None
1015
1016 for catalog in catalogs:
1017 catalog_id = catalog.get_id().split(":")[3]
1018 if catalog_id == catalog_uuid:
1019 return catalog.name
1020 return None
1021
1022 def get_image_id_from_path(self, path=None, progress=False):
1023 """ Method upload OVF image to vCloud director.
1024
1025 Each OVF image represented as single catalog entry in vcloud director.
1026 The method check for existing catalog entry. The check done by file name without file extension.
1027
1028 if given catalog name already present method will respond with existing catalog uuid otherwise
1029 it will create new catalog entry and upload OVF file to newly created catalog.
1030
1031 If method can't create catalog entry or upload a file it will throw exception.
1032
1033 Method accept boolean flag progress that will output progress bar. It useful method
1034 for standalone upload use case. In case to test large file upload.
1035
1036 Args
1037 path: - valid path to OVF file.
1038 progress - boolean progress bar show progress bar.
1039
1040 Return: if image uploaded correct method will provide image catalog UUID.
1041 """
1042 vca = self.connect()
1043 if not vca:
1044 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1045
1046 if not path:
1047 raise vimconn.vimconnException("Image path can't be None.")
1048
1049 if not os.path.isfile(path):
1050 raise vimconn.vimconnException("Can't read file. File not found.")
1051
1052 if not os.access(path, os.R_OK):
1053 raise vimconn.vimconnException("Can't read file. Check file permission to read.")
1054
1055 self.logger.debug("get_image_id_from_path() client requesting {} ".format(path))
1056
1057 dirpath, filename = os.path.split(path)
1058 flname, file_extension = os.path.splitext(path)
1059 if file_extension != '.ovf':
1060 self.logger.debug("Wrong file extension {} connector support only OVF container.".format(file_extension))
1061 raise vimconn.vimconnException("Wrong container. vCloud director supports only OVF.")
1062
1063 catalog_name = os.path.splitext(filename)[0]
1064 catalog_md5_name = hashlib.md5(path).hexdigest()
1065 self.logger.debug("File name {} Catalog Name {} file path {} "
1066 "vdc catalog name {}".format(filename, catalog_name, path, catalog_md5_name))
1067
1068 try:
1069 catalogs = vca.get_catalogs()
1070 except Exception as exp:
1071 self.logger.debug("Failed get catalogs() with Exception {} ".format(exp))
1072 raise vimconn.vimconnException("Failed get catalogs() with Exception {} ".format(exp))
1073
1074 if len(catalogs) == 0:
1075 self.logger.info("Creating a new catalog entry {} in vcloud director".format(catalog_name))
1076 result = self.create_vimcatalog(vca, catalog_md5_name)
1077 if not result:
1078 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1079 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1080 media_name=filename, medial_file_name=path, progress=progress)
1081 if not result:
1082 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_name))
1083 return self.get_catalogid(catalog_name, vca.get_catalogs())
1084 else:
1085 for catalog in catalogs:
1086 # search for existing catalog if we find same name we return ID
1087 # TODO optimize this
1088 if catalog.name == catalog_md5_name:
1089 self.logger.debug("Found existing catalog entry for {} "
1090 "catalog id {}".format(catalog_name,
1091 self.get_catalogid(catalog_md5_name, catalogs)))
1092 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1093
1094 # if we didn't find existing catalog we create a new one and upload image.
1095 self.logger.debug("Creating new catalog entry {} - {}".format(catalog_name, catalog_md5_name))
1096 result = self.create_vimcatalog(vca, catalog_md5_name)
1097 if not result:
1098 raise vimconn.vimconnException("Failed create new catalog {} ".format(catalog_md5_name))
1099
1100 result = self.upload_vimimage(vca=vca, catalog_name=catalog_md5_name,
1101 media_name=filename, medial_file_name=path, progress=progress)
1102 if not result:
1103 raise vimconn.vimconnException("Failed create vApp template for catalog {} ".format(catalog_md5_name))
1104
1105 return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
1106
1107 def get_image_list(self, filter_dict={}):
1108 '''Obtain tenant images from VIM
1109 Filter_dict can be:
1110 name: image name
1111 id: image uuid
1112 checksum: image checksum
1113 location: image path
1114 Returns the image list of dictionaries:
1115 [{<the fields at Filter_dict plus some VIM specific>}, ...]
1116 List can be empty
1117 '''
1118 vca = self.connect()
1119 if not vca:
1120 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1121 try:
1122 image_list = []
1123 catalogs = vca.get_catalogs()
1124 if len(catalogs) == 0:
1125 return image_list
1126 else:
1127 for catalog in catalogs:
1128 catalog_uuid = catalog.get_id().split(":")[3]
1129 name = catalog.name
1130 filtered_dict = {}
1131 if filter_dict.get("name") and filter_dict["name"] != name:
1132 continue
1133 if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
1134 continue
1135 filtered_dict ["name"] = name
1136 filtered_dict ["id"] = catalog_uuid
1137 image_list.append(filtered_dict)
1138
1139 self.logger.debug("List of already created catalog items: {}".format(image_list))
1140 return image_list
1141 except Exception as exp:
1142 raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
1143
1144 def get_vappid(self, vdc=None, vapp_name=None):
1145 """ Method takes vdc object and vApp name and returns vapp uuid or None
1146
1147 Args:
1148 vdc: The VDC object.
1149 vapp_name: is application vappp name identifier
1150
1151 Returns:
1152 The return vApp name otherwise None
1153 """
1154 if vdc is None or vapp_name is None:
1155 return None
1156 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
1157 try:
1158 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1159 vdc.ResourceEntities.ResourceEntity)
1160 if len(refs) == 1:
1161 return refs[0].href.split("vapp")[1][1:]
1162 except Exception as e:
1163 self.logger.exception(e)
1164 return False
1165 return None
1166
1167 def check_vapp(self, vdc=None, vapp_uuid=None):
1168 """ Method Method returns True or False if vapp deployed in vCloud director
1169
1170 Args:
1171 vca: Connector to VCA
1172 vdc: The VDC object.
1173 vappid: vappid is application identifier
1174
1175 Returns:
1176 The return True if vApp deployed
1177 :param vdc:
1178 :param vapp_uuid:
1179 """
1180 try:
1181 refs = filter(lambda ref:
1182 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1183 vdc.ResourceEntities.ResourceEntity)
1184 for ref in refs:
1185 vappid = ref.href.split("vapp")[1][1:]
1186 # find vapp with respected vapp uuid
1187 if vappid == vapp_uuid:
1188 return True
1189 except Exception as e:
1190 self.logger.exception(e)
1191 return False
1192 return False
1193
1194 def get_namebyvappid(self, vca=None, vdc=None, vapp_uuid=None):
1195 """Method returns vApp name from vCD and lookup done by vapp_id.
1196
1197 Args:
1198 vca: Connector to VCA
1199 vdc: The VDC object.
1200 vapp_uuid: vappid is application identifier
1201
1202 Returns:
1203 The return vApp name otherwise None
1204 """
1205
1206 try:
1207 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
1208 vdc.ResourceEntities.ResourceEntity)
1209 for ref in refs:
1210 # we care only about UUID the rest doesn't matter
1211 vappid = ref.href.split("vapp")[1][1:]
1212 if vappid == vapp_uuid:
1213 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
1214 logger=self.logger)
1215 tree = XmlElementTree.fromstring(response.content)
1216 return tree.attrib['name']
1217 except Exception as e:
1218 self.logger.exception(e)
1219 return None
1220 return None
1221
1222 def new_vminstance(self, name=None, description="", start=False, image_id=None, flavor_id=None, net_list={},
1223 cloud_config=None, disk_list=None):
1224 """Adds a VM instance to VIM
1225 Params:
1226 start: indicates if VM must start or boot in pause mode. Ignored
1227 image_id,flavor_id: image and flavor uuid
1228 net_list: list of interfaces, each one is a dictionary with:
1229 name:
1230 net_id: network uuid to connect
1231 vpci: virtual vcpi to assign
1232 model: interface model, virtio, e2000, ...
1233 mac_address:
1234 use: 'data', 'bridge', 'mgmt'
1235 type: 'virtual', 'PF', 'VF', 'VFnotShared'
1236 vim_id: filled/added by this function
1237 cloud_config: can be a text script to be passed directly to cloud-init,
1238 or an object to inject users and ssh keys with format:
1239 key-pairs: [] list of keys to install to the default user
1240 users: [{ name, key-pairs: []}] list of users to add with their key-pair
1241 #TODO ip, security groups
1242 Returns >=0, the instance identifier
1243 <0, error_text
1244 """
1245
1246 self.logger.info("Creating new instance for entry {}".format(name))
1247 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".format(
1248 description, start, image_id, flavor_id, net_list, cloud_config))
1249 vca = self.connect()
1250 if not vca:
1251 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1252
1253 #new vm name = vmname + tenant_id + uuid
1254 new_vm_name = [name, '-', str(uuid.uuid4())]
1255 vmname_andid = ''.join(new_vm_name)
1256
1257 # if vm already deployed we return existing uuid
1258 # vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1259 # if vapp_uuid is not None:
1260 # return vapp_uuid
1261
1262 # we check for presence of VDC, Catalog entry and Flavor.
1263 vdc = vca.get_vdc(self.tenant_name)
1264 if vdc is None:
1265 raise vimconn.vimconnNotFoundException(
1266 "new_vminstance(): Failed create vApp {}: (Failed retrieve VDC information)".format(name))
1267 catalogs = vca.get_catalogs()
1268 if catalogs is None:
1269 raise vimconn.vimconnNotFoundException(
1270 "new_vminstance(): Failed create vApp {}: (Failed retrieve catalogs list)".format(name))
1271
1272 catalog_hash_name = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1273 if catalog_hash_name:
1274 self.logger.info("Found catalog entry {} for image id {}".format(catalog_hash_name, image_id))
1275 else:
1276 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1277 "(Failed retrieve catalog information {})".format(name, image_id))
1278
1279
1280 # Set vCPU and Memory based on flavor.
1281 #
1282 vm_cpus = None
1283 vm_memory = None
1284 vm_disk = None
1285 pci_devices_info = []
1286 if flavor_id is not None:
1287 if flavor_id not in vimconnector.flavorlist:
1288 raise vimconn.vimconnNotFoundException("new_vminstance(): Failed create vApp {}: "
1289 "Failed retrieve flavor information "
1290 "flavor id {}".format(name, flavor_id))
1291 else:
1292 try:
1293 flavor = vimconnector.flavorlist[flavor_id]
1294 vm_cpus = flavor[FLAVOR_VCPUS_KEY]
1295 vm_memory = flavor[FLAVOR_RAM_KEY]
1296 vm_disk = flavor[FLAVOR_DISK_KEY]
1297 extended = flavor.get("extended", None)
1298 if extended:
1299 numas=extended.get("numas", None)
1300 if numas:
1301 for numa in numas:
1302 for interface in numa.get("interfaces",() ):
1303 if interface["dedicated"].strip()=="yes":
1304 pci_devices_info.append(interface)
1305 except Exception as exp:
1306 raise vimconn.vimconnException("Corrupted flavor. {}.Exception: {}".format(flavor_id, exp))
1307
1308 # image upload creates template name as catalog name space Template.
1309 templateName = self.get_catalogbyid(catalog_uuid=image_id, catalogs=catalogs)
1310 power_on = 'false'
1311 if start:
1312 power_on = 'true'
1313
1314 # client must provide at least one entry in net_list if not we report error
1315 #If net type is mgmt, then configure it as primary net & use its NIC index as primary NIC
1316 #If no mgmt, then the 1st NN in netlist is considered as primary net.
1317 primary_net = None
1318 primary_netname = None
1319 network_mode = 'bridged'
1320 if net_list is not None and len(net_list) > 0:
1321 for net in net_list:
1322 if 'use' in net and net['use'] == 'mgmt':
1323 primary_net = net
1324 if primary_net is None:
1325 primary_net = net_list[0]
1326
1327 try:
1328 primary_net_id = primary_net['net_id']
1329 network_dict = self.get_vcd_network(network_uuid=primary_net_id)
1330 if 'name' in network_dict:
1331 primary_netname = network_dict['name']
1332
1333 except KeyError:
1334 raise vimconn.vimconnException("Corrupted flavor. {}".format(primary_net))
1335 else:
1336 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1337
1338 # use: 'data', 'bridge', 'mgmt'
1339 # create vApp. Set vcpu and ram based on flavor id.
1340 try:
1341 vapptask = vca.create_vapp(self.tenant_name, vmname_andid, templateName,
1342 self.get_catalogbyid(image_id, catalogs),
1343 network_name=None, # None while creating vapp
1344 network_mode=network_mode,
1345 vm_name=vmname_andid,
1346 vm_cpus=vm_cpus, # can be None if flavor is None
1347 vm_memory=vm_memory) # can be None if flavor is None
1348
1349 if vapptask is None or vapptask is False:
1350 raise vimconn.vimconnUnexpectedResponse(
1351 "new_vminstance(): failed to create vApp {}".format(vmname_andid))
1352 if type(vapptask) is VappTask:
1353 vca.block_until_completed(vapptask)
1354
1355 except Exception as exp:
1356 raise vimconn.vimconnUnexpectedResponse(
1357 "new_vminstance(): failed to create vApp {} with Exception:{}".format(vmname_andid, exp))
1358
1359 # we should have now vapp in undeployed state.
1360 try:
1361 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1362 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1363 except Exception as exp:
1364 raise vimconn.vimconnUnexpectedResponse(
1365 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1366 .format(vmname_andid, exp))
1367
1368 if vapp is None:
1369 raise vimconn.vimconnUnexpectedResponse(
1370 "new_vminstance(): Failed to retrieve vApp {} after creation".format(
1371 vmname_andid))
1372
1373 #Add PCI passthrough configrations
1374 PCI_devices_status = False
1375 vm_obj = None
1376 si = None
1377 if len(pci_devices_info) > 0:
1378 self.logger.info("Need to add PCI devices {} into VM {}".format(pci_devices_info,
1379 vmname_andid ))
1380 PCI_devices_status, vm_obj, vcenter_conect = self.add_pci_devices(vapp_uuid,
1381 pci_devices_info,
1382 vmname_andid)
1383 if PCI_devices_status:
1384 self.logger.info("Added PCI devives {} to VM {}".format(
1385 pci_devices_info,
1386 vmname_andid)
1387 )
1388 else:
1389 self.logger.info("Fail to add PCI devives {} to VM {}".format(
1390 pci_devices_info,
1391 vmname_andid)
1392 )
1393 # add vm disk
1394 if vm_disk:
1395 #Assuming there is only one disk in ovf and fast provisioning in organization vDC is disabled
1396 result = self.modify_vm_disk(vapp_uuid, vm_disk)
1397 if result :
1398 self.logger.debug("Modified Disk size of VM {} ".format(vmname_andid))
1399
1400 # add NICs & connect to networks in netlist
1401 try:
1402 self.logger.info("Request to connect VM to a network: {}".format(net_list))
1403 nicIndex = 0
1404 primary_nic_index = 0
1405 for net in net_list:
1406 # openmano uses network id in UUID format.
1407 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1408 # [{'use': 'bridge', 'net_id': '527d4bf7-566a-41e7-a9e7-ca3cdd9cef4f', 'type': 'virtual',
1409 # 'vpci': '0000:00:11.0', 'name': 'eth0'}]
1410
1411 if 'net_id' not in net:
1412 continue
1413
1414 interface_net_id = net['net_id']
1415 interface_net_name = self.get_network_name_by_id(network_uuid=interface_net_id)
1416 interface_network_mode = net['use']
1417
1418 if interface_network_mode == 'mgmt':
1419 primary_nic_index = nicIndex
1420
1421 """- POOL (A static IP address is allocated automatically from a pool of addresses.)
1422 - DHCP (The IP address is obtained from a DHCP service.)
1423 - MANUAL (The IP address is assigned manually in the IpAddress element.)
1424 - NONE (No IP addressing mode specified.)"""
1425
1426 if primary_netname is not None:
1427 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1428 if len(nets) == 1:
1429 self.logger.info("new_vminstance(): Found requested network: {}".format(nets[0].name))
1430 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1431 if type(task) is GenericTask:
1432 vca.block_until_completed(task)
1433 # connect network to VM - with all DHCP by default
1434
1435 type_list = ['PF','VF','VFnotShared']
1436 if 'type' in net and net['type'] not in type_list:
1437 # fetching nic type from vnf
1438 if 'model' in net:
1439 nic_type = net['model']
1440 self.logger.info("new_vminstance(): adding network adapter "\
1441 "to a network {}".format(nets[0].name))
1442 self.add_network_adapter_to_vms(vapp, nets[0].name,
1443 primary_nic_index,
1444 nicIndex,
1445 nic_type=nic_type)
1446 else:
1447 self.logger.info("new_vminstance(): adding network adapter "\
1448 "to a network {}".format(nets[0].name))
1449 self.add_network_adapter_to_vms(vapp, nets[0].name,
1450 primary_nic_index,
1451 nicIndex)
1452 nicIndex += 1
1453
1454 # deploy and power on vm
1455 self.logger.debug("new_vminstance(): Deploying vApp {} ".format(name))
1456 deploytask = vapp.deploy(powerOn=False)
1457 if type(deploytask) is GenericTask:
1458 vca.block_until_completed(deploytask)
1459
1460 # If VM has PCI devices reserve memory for VM
1461 if PCI_devices_status and vm_obj and vcenter_conect:
1462 memReserve = vm_obj.config.hardware.memoryMB
1463 spec = vim.vm.ConfigSpec()
1464 spec.memoryAllocation = vim.ResourceAllocationInfo(reservation=memReserve)
1465 task = vm_obj.ReconfigVM_Task(spec=spec)
1466 if task:
1467 result = self.wait_for_vcenter_task(task, vcenter_conect)
1468 self.logger.info("Reserved memmoery {} MB for "\
1469 "VM VM status: {}".format(str(memReserve),result))
1470 else:
1471 self.logger.info("Fail to reserved memmoery {} to VM {}".format(
1472 str(memReserve),str(vm_obj)))
1473
1474 self.logger.debug("new_vminstance(): power on vApp {} ".format(name))
1475 poweron_task = vapp.poweron()
1476 if type(poweron_task) is GenericTask:
1477 vca.block_until_completed(poweron_task)
1478
1479 except Exception as exp :
1480 # it might be a case if specific mandatory entry in dict is empty or some other pyVcloud exception
1481 self.logger.debug("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1482 raise vimconn.vimconnException("new_vminstance(): Failed create new vm instance {}".format(name, exp))
1483
1484 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1485 wait_time = 0
1486 vapp_uuid = None
1487 while wait_time <= MAX_WAIT_TIME:
1488 try:
1489 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vmname_andid)
1490 except Exception as exp:
1491 raise vimconn.vimconnUnexpectedResponse(
1492 "new_vminstance(): Failed to retrieve vApp {} after creation: Exception:{}"
1493 .format(vmname_andid, exp))
1494
1495 if vapp and vapp.me.deployed:
1496 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), vmname_andid)
1497 break
1498 else:
1499 self.logger.debug("new_vminstance(): Wait for vApp {} to deploy".format(name))
1500 time.sleep(INTERVAL_TIME)
1501
1502 wait_time +=INTERVAL_TIME
1503
1504 if vapp_uuid is not None:
1505 return vapp_uuid
1506 else:
1507 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1508
1509 ##
1510 ##
1511 ## based on current discussion
1512 ##
1513 ##
1514 ## server:
1515 # created: '2016-09-08T11:51:58'
1516 # description: simple-instance.linux1.1
1517 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1518 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1519 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1520 # status: ACTIVE
1521 # error_msg:
1522 # interfaces: …
1523 #
1524 def get_vminstance(self, vim_vm_uuid=None):
1525 """Returns the VM instance information from VIM"""
1526
1527 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1528 vca = self.connect()
1529 if not vca:
1530 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1531
1532 vdc = vca.get_vdc(self.tenant_name)
1533 if vdc is None:
1534 raise vimconn.vimconnConnectionException(
1535 "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1536
1537 vm_info_dict = self.get_vapp_details_rest(vapp_uuid=vim_vm_uuid)
1538 if not vm_info_dict:
1539 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1540 raise vimconn.vimconnNotFoundException("Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1541
1542 status_key = vm_info_dict['status']
1543 error = ''
1544 try:
1545 vm_dict = {'created': vm_info_dict['created'],
1546 'description': vm_info_dict['name'],
1547 'status': vcdStatusCode2manoFormat[int(status_key)],
1548 'hostId': vm_info_dict['vmuuid'],
1549 'error_msg': error,
1550 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1551
1552 if 'interfaces' in vm_info_dict:
1553 vm_dict['interfaces'] = vm_info_dict['interfaces']
1554 else:
1555 vm_dict['interfaces'] = []
1556 except KeyError:
1557 vm_dict = {'created': '',
1558 'description': '',
1559 'status': vcdStatusCode2manoFormat[int(-1)],
1560 'hostId': vm_info_dict['vmuuid'],
1561 'error_msg': "Inconsistency state",
1562 'vim_info': yaml.safe_dump(vm_info_dict), 'interfaces': []}
1563
1564 return vm_dict
1565
1566 def delete_vminstance(self, vm__vim_uuid):
1567 """Method poweroff and remove VM instance from vcloud director network.
1568
1569 Args:
1570 vm__vim_uuid: VM UUID
1571
1572 Returns:
1573 Returns the instance identifier
1574 """
1575
1576 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1577 vca = self.connect()
1578 if not vca:
1579 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1580
1581 vdc = vca.get_vdc(self.tenant_name)
1582 if vdc is None:
1583 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1584 self.tenant_name))
1585 raise vimconn.vimconnException(
1586 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1587
1588 try:
1589 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1590 if vapp_name is None:
1591 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1592 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1593 else:
1594 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1595
1596 # Delete vApp and wait for status change if task executed and vApp is None.
1597 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1598
1599 if vapp:
1600 if vapp.me.deployed:
1601 self.logger.info("Powering off vApp {}".format(vapp_name))
1602 #Power off vApp
1603 powered_off = False
1604 wait_time = 0
1605 while wait_time <= MAX_WAIT_TIME:
1606 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1607 if not vapp:
1608 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1609 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1610
1611 power_off_task = vapp.poweroff()
1612 if type(power_off_task) is GenericTask:
1613 result = vca.block_until_completed(power_off_task)
1614 if result:
1615 powered_off = True
1616 break
1617 else:
1618 self.logger.info("Wait for vApp {} to power off".format(vapp_name))
1619 time.sleep(INTERVAL_TIME)
1620
1621 wait_time +=INTERVAL_TIME
1622 if not powered_off:
1623 self.logger.debug("delete_vminstance(): Failed to power off VM instance {} ".format(vm__vim_uuid))
1624 else:
1625 self.logger.info("delete_vminstance(): Powered off VM instance {} ".format(vm__vim_uuid))
1626
1627 #Undeploy vApp
1628 self.logger.info("Undeploy vApp {}".format(vapp_name))
1629 wait_time = 0
1630 undeployed = False
1631 while wait_time <= MAX_WAIT_TIME:
1632 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1633 if not vapp:
1634 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1635 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1636 undeploy_task = vapp.undeploy(action='powerOff')
1637
1638 if type(undeploy_task) is GenericTask:
1639 result = vca.block_until_completed(undeploy_task)
1640 if result:
1641 undeployed = True
1642 break
1643 else:
1644 self.logger.debug("Wait for vApp {} to undeploy".format(vapp_name))
1645 time.sleep(INTERVAL_TIME)
1646
1647 wait_time +=INTERVAL_TIME
1648
1649 if not undeployed:
1650 self.logger.debug("delete_vminstance(): Failed to undeploy vApp {} ".format(vm__vim_uuid))
1651
1652 # delete vapp
1653 self.logger.info("Start deletion of vApp {} ".format(vapp_name))
1654 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1655
1656 if vapp is not None:
1657 wait_time = 0
1658 result = False
1659
1660 while wait_time <= MAX_WAIT_TIME:
1661 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1662 if not vapp:
1663 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1664 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1665
1666 delete_task = vapp.delete()
1667
1668 if type(delete_task) is GenericTask:
1669 vca.block_until_completed(delete_task)
1670 result = vca.block_until_completed(delete_task)
1671 if result:
1672 break
1673 else:
1674 self.logger.debug("Wait for vApp {} to delete".format(vapp_name))
1675 time.sleep(INTERVAL_TIME)
1676
1677 wait_time +=INTERVAL_TIME
1678
1679 if not result:
1680 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1681
1682 except:
1683 self.logger.debug(traceback.format_exc())
1684 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1685
1686 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1687 self.logger.info("Deleted vm instance {} sccessfully".format(vm__vim_uuid))
1688 return vm__vim_uuid
1689 else:
1690 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1691
1692 def refresh_vms_status(self, vm_list):
1693 """Get the status of the virtual machines and their interfaces/ports
1694 Params: the list of VM identifiers
1695 Returns a dictionary with:
1696 vm_id: #VIM id of this Virtual Machine
1697 status: #Mandatory. Text with one of:
1698 # DELETED (not found at vim)
1699 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1700 # OTHER (Vim reported other status not understood)
1701 # ERROR (VIM indicates an ERROR status)
1702 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1703 # CREATING (on building process), ERROR
1704 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1705 #
1706 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1707 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1708 interfaces:
1709 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1710 mac_address: #Text format XX:XX:XX:XX:XX:XX
1711 vim_net_id: #network id where this interface is connected
1712 vim_interface_id: #interface/port VIM id
1713 ip_address: #null, or text with IPv4, IPv6 address
1714 """
1715
1716 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1717
1718 mac_ip_addr={}
1719 rheaders = {'Content-Type': 'application/xml'}
1720 iso_edges = ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
1721
1722 try:
1723 for edge in iso_edges:
1724 nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
1725 self.logger.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url))
1726
1727 resp = requests.get(self.nsx_manager + nsx_api_url,
1728 auth = (self.nsx_user, self.nsx_password),
1729 verify = False, headers = rheaders)
1730
1731 if resp.status_code == requests.codes.ok:
1732 dhcp_leases = XmlElementTree.fromstring(resp.text)
1733 for child in dhcp_leases:
1734 if child.tag == 'dhcpLeaseInfo':
1735 dhcpLeaseInfo = child
1736 for leaseInfo in dhcpLeaseInfo:
1737 for elem in leaseInfo:
1738 if (elem.tag)=='macAddress':
1739 mac_addr = elem.text
1740 if (elem.tag)=='ipAddress':
1741 ip_addr = elem.text
1742 if (mac_addr) is not None:
1743 mac_ip_addr[mac_addr]= ip_addr
1744 self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr))
1745 else:
1746 self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content))
1747 except KeyError:
1748 self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message))
1749 self.logger.debug(traceback.format_exc())
1750
1751 vca = self.connect()
1752 if not vca:
1753 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1754
1755 vdc = vca.get_vdc(self.tenant_name)
1756 if vdc is None:
1757 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1758
1759 vms_dict = {}
1760 for vmuuid in vm_list:
1761 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1762 if vmname is not None:
1763
1764 try:
1765 the_vapp = vca.get_vapp(vdc, vmname)
1766 vm_info = the_vapp.get_vms_details()
1767 vm_status = vm_info[0]['status']
1768 vm_pci_details = self.get_vm_pci_details(vmuuid)
1769 vm_info[0].update(vm_pci_details)
1770
1771 vm_dict = {'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1772 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1773 'vim_info': yaml.safe_dump(vm_info), 'interfaces': []}
1774
1775 # get networks
1776 vm_app_networks = the_vapp.get_vms_network_info()
1777 for vapp_network in vm_app_networks:
1778 for vm_network in vapp_network:
1779 if vm_network['name'] == vmname:
1780 #Assign IP Address based on MAC Address in NSX DHCP lease info
1781 for mac_adres,ip_adres in mac_ip_addr.iteritems():
1782 if mac_adres == vm_network['mac']:
1783 vm_network['ip']=ip_adres
1784 interface = {"mac_address": vm_network['mac'],
1785 "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
1786 "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
1787 'ip_address': vm_network['ip']}
1788 # interface['vim_info'] = yaml.safe_dump(vm_network)
1789 vm_dict["interfaces"].append(interface)
1790 # add a vm to vm dict
1791 vms_dict.setdefault(vmuuid, vm_dict)
1792 except Exception as exp:
1793 self.logger.debug("Error in response {}".format(exp))
1794 self.logger.debug(traceback.format_exc())
1795
1796 return vms_dict
1797
1798 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1799 """Send and action over a VM instance from VIM
1800 Returns the vm_id if the action was successfully sent to the VIM"""
1801
1802 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1803 if vm__vim_uuid is None or action_dict is None:
1804 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1805
1806 vca = self.connect()
1807 if not vca:
1808 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1809
1810 vdc = vca.get_vdc(self.tenant_name)
1811 if vdc is None:
1812 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1813
1814 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1815 if vapp_name is None:
1816 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1817 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1818 else:
1819 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1820
1821 try:
1822 the_vapp = vca.get_vapp(vdc, vapp_name)
1823 # TODO fix all status
1824 if "start" in action_dict:
1825 vm_info = the_vapp.get_vms_details()
1826 vm_status = vm_info[0]['status']
1827 self.logger.info("action_vminstance: Power on vApp: {}".format(vapp_name))
1828 if vm_status == "Suspended" or vm_status == "Powered off":
1829 power_on_task = the_vapp.poweron()
1830 result = vca.block_until_completed(power_on_task)
1831 self.instance_actions_result("start", result, vapp_name)
1832 elif "rebuild" in action_dict:
1833 self.logger.info("action_vminstance: Rebuild vApp: {}".format(vapp_name))
1834 rebuild_task = the_vapp.deploy(powerOn=True)
1835 result = vca.block_until_completed(rebuild_task)
1836 self.instance_actions_result("rebuild", result, vapp_name)
1837 elif "pause" in action_dict:
1838 self.logger.info("action_vminstance: pause vApp: {}".format(vapp_name))
1839 pause_task = the_vapp.undeploy(action='suspend')
1840 result = vca.block_until_completed(pause_task)
1841 self.instance_actions_result("pause", result, vapp_name)
1842 elif "resume" in action_dict:
1843 self.logger.info("action_vminstance: resume vApp: {}".format(vapp_name))
1844 power_task = the_vapp.poweron()
1845 result = vca.block_until_completed(power_task)
1846 self.instance_actions_result("resume", result, vapp_name)
1847 elif "shutoff" in action_dict or "shutdown" in action_dict:
1848 action_name , value = action_dict.items()[0]
1849 self.logger.info("action_vminstance: {} vApp: {}".format(action_name, vapp_name))
1850 power_off_task = the_vapp.undeploy(action='powerOff')
1851 result = vca.block_until_completed(power_off_task)
1852 if action_name == "shutdown":
1853 self.instance_actions_result("shutdown", result, vapp_name)
1854 else:
1855 self.instance_actions_result("shutoff", result, vapp_name)
1856 elif "forceOff" in action_dict:
1857 result = the_vapp.undeploy(action='force')
1858 self.instance_actions_result("forceOff", result, vapp_name)
1859 elif "reboot" in action_dict:
1860 self.logger.info("action_vminstance: reboot vApp: {}".format(vapp_name))
1861 reboot_task = the_vapp.reboot()
1862 else:
1863 raise vimconn.vimconnException("action_vminstance: Invalid action {} or action is None.".format(action_dict))
1864 return vm__vim_uuid
1865 except Exception as exp :
1866 self.logger.debug("action_vminstance: Failed with Exception {}".format(exp))
1867 raise vimconn.vimconnException("action_vminstance: Failed with Exception {}".format(exp))
1868
1869 def instance_actions_result(self, action, result, vapp_name):
1870 if result:
1871 self.logger.info("action_vminstance: Sucessfully {} the vApp: {}".format(action, vapp_name))
1872 else:
1873 self.logger.error("action_vminstance: Failed to {} vApp: {}".format(action, vapp_name))
1874
1875 def get_vminstance_console(self, vm_id, console_type="vnc"):
1876 """
1877 Get a console for the virtual machine
1878 Params:
1879 vm_id: uuid of the VM
1880 console_type, can be:
1881 "novnc" (by default), "xvpvnc" for VNC types,
1882 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1883 Returns dict with the console parameters:
1884 protocol: ssh, ftp, http, https, ...
1885 server: usually ip address
1886 port: the http, ssh, ... port
1887 suffix: extra text, e.g. the http path and query string
1888 """
1889 raise vimconn.vimconnNotImplemented("Should have implemented this")
1890
1891 # NOT USED METHODS in current version
1892
1893 def host_vim2gui(self, host, server_dict):
1894 """Transform host dictionary from VIM format to GUI format,
1895 and append to the server_dict
1896 """
1897 raise vimconn.vimconnNotImplemented("Should have implemented this")
1898
1899 def get_hosts_info(self):
1900 """Get the information of deployed hosts
1901 Returns the hosts content"""
1902 raise vimconn.vimconnNotImplemented("Should have implemented this")
1903
1904 def get_hosts(self, vim_tenant):
1905 """Get the hosts and deployed instances
1906 Returns the hosts content"""
1907 raise vimconn.vimconnNotImplemented("Should have implemented this")
1908
1909 def get_processor_rankings(self):
1910 """Get the processor rankings in the VIM database"""
1911 raise vimconn.vimconnNotImplemented("Should have implemented this")
1912
1913 def new_host(self, host_data):
1914 """Adds a new host to VIM"""
1915 '''Returns status code of the VIM response'''
1916 raise vimconn.vimconnNotImplemented("Should have implemented this")
1917
1918 def new_external_port(self, port_data):
1919 """Adds a external port to VIM"""
1920 '''Returns the port identifier'''
1921 raise vimconn.vimconnNotImplemented("Should have implemented this")
1922
1923 def new_external_network(self, net_name, net_type):
1924 """Adds a external network to VIM (shared)"""
1925 '''Returns the network identifier'''
1926 raise vimconn.vimconnNotImplemented("Should have implemented this")
1927
1928 def connect_port_network(self, port_id, network_id, admin=False):
1929 """Connects a external port to a network"""
1930 '''Returns status code of the VIM response'''
1931 raise vimconn.vimconnNotImplemented("Should have implemented this")
1932
1933 def new_vminstancefromJSON(self, vm_data):
1934 """Adds a VM instance to VIM"""
1935 '''Returns the instance identifier'''
1936 raise vimconn.vimconnNotImplemented("Should have implemented this")
1937
1938 def get_network_name_by_id(self, network_uuid=None):
1939 """Method gets vcloud director network named based on supplied uuid.
1940
1941 Args:
1942 network_uuid: network_id
1943
1944 Returns:
1945 The return network name.
1946 """
1947
1948 vca = self.connect()
1949 if not vca:
1950 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1951
1952 if not network_uuid:
1953 return None
1954
1955 try:
1956 org_dict = self.get_org(self.org_uuid)
1957 if 'networks' in org_dict:
1958 org_network_dict = org_dict['networks']
1959 for net_uuid in org_network_dict:
1960 if net_uuid == network_uuid:
1961 return org_network_dict[net_uuid]
1962 except:
1963 self.logger.debug("Exception in get_network_name_by_id")
1964 self.logger.debug(traceback.format_exc())
1965
1966 return None
1967
1968 def get_network_id_by_name(self, network_name=None):
1969 """Method gets vcloud director network uuid based on supplied name.
1970
1971 Args:
1972 network_name: network_name
1973 Returns:
1974 The return network uuid.
1975 network_uuid: network_id
1976 """
1977
1978 vca = self.connect()
1979 if not vca:
1980 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1981
1982 if not network_name:
1983 self.logger.debug("get_network_id_by_name() : Network name is empty")
1984 return None
1985
1986 try:
1987 org_dict = self.get_org(self.org_uuid)
1988 if org_dict and 'networks' in org_dict:
1989 org_network_dict = org_dict['networks']
1990 for net_uuid,net_name in org_network_dict.iteritems():
1991 if net_name == network_name:
1992 return net_uuid
1993
1994 except KeyError as exp:
1995 self.logger.debug("get_network_id_by_name() : KeyError- {} ".format(exp))
1996
1997 return None
1998
1999 def list_org_action(self):
2000 """
2001 Method leverages vCloud director and query for available organization for particular user
2002
2003 Args:
2004 vca - is active VCA connection.
2005 vdc_name - is a vdc name that will be used to query vms action
2006
2007 Returns:
2008 The return XML respond
2009 """
2010
2011 vca = self.connect()
2012 if not vca:
2013 raise vimconn.vimconnConnectionException("self.connect() is failed")
2014
2015 url_list = [vca.host, '/api/org']
2016 vm_list_rest_call = ''.join(url_list)
2017
2018 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2019 response = Http.get(url=vm_list_rest_call,
2020 headers=vca.vcloud_session.get_vcloud_headers(),
2021 verify=vca.verify,
2022 logger=vca.logger)
2023 if response.status_code == requests.codes.ok:
2024 return response.content
2025
2026 return None
2027
2028 def get_org_action(self, org_uuid=None):
2029 """
2030 Method leverages vCloud director and retrieve available object fdr organization.
2031
2032 Args:
2033 vca - is active VCA connection.
2034 vdc_name - is a vdc name that will be used to query vms action
2035
2036 Returns:
2037 The return XML respond
2038 """
2039
2040 vca = self.connect()
2041 if not vca:
2042 raise vimconn.vimconnConnectionException("self.connect() is failed")
2043
2044 if org_uuid is None:
2045 return None
2046
2047 url_list = [vca.host, '/api/org/', org_uuid]
2048 vm_list_rest_call = ''.join(url_list)
2049
2050 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2051 response = Http.get(url=vm_list_rest_call,
2052 headers=vca.vcloud_session.get_vcloud_headers(),
2053 verify=vca.verify,
2054 logger=vca.logger)
2055 if response.status_code == requests.codes.ok:
2056 return response.content
2057
2058 return None
2059
2060 def get_org(self, org_uuid=None):
2061 """
2062 Method retrieves available organization in vCloud Director
2063
2064 Args:
2065 org_uuid - is a organization uuid.
2066
2067 Returns:
2068 The return dictionary with following key
2069 "network" - for network list under the org
2070 "catalogs" - for network list under the org
2071 "vdcs" - for vdc list under org
2072 """
2073
2074 org_dict = {}
2075 vca = self.connect()
2076 if not vca:
2077 raise vimconn.vimconnConnectionException("self.connect() is failed")
2078
2079 if org_uuid is None:
2080 return org_dict
2081
2082 content = self.get_org_action(org_uuid=org_uuid)
2083 try:
2084 vdc_list = {}
2085 network_list = {}
2086 catalog_list = {}
2087 vm_list_xmlroot = XmlElementTree.fromstring(content)
2088 for child in vm_list_xmlroot:
2089 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
2090 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2091 org_dict['vdcs'] = vdc_list
2092 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
2093 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2094 org_dict['networks'] = network_list
2095 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
2096 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
2097 org_dict['catalogs'] = catalog_list
2098 except:
2099 pass
2100
2101 return org_dict
2102
2103 def get_org_list(self):
2104 """
2105 Method retrieves available organization in vCloud Director
2106
2107 Args:
2108 vca - is active VCA connection.
2109
2110 Returns:
2111 The return dictionary and key for each entry VDC UUID
2112 """
2113
2114 org_dict = {}
2115 vca = self.connect()
2116 if not vca:
2117 raise vimconn.vimconnConnectionException("self.connect() is failed")
2118
2119 content = self.list_org_action()
2120 try:
2121 vm_list_xmlroot = XmlElementTree.fromstring(content)
2122 for vm_xml in vm_list_xmlroot:
2123 if vm_xml.tag.split("}")[1] == 'Org':
2124 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
2125 org_dict[org_uuid[0]] = vm_xml.attrib['name']
2126 except:
2127 pass
2128
2129 return org_dict
2130
2131 def vms_view_action(self, vdc_name=None):
2132 """ Method leverages vCloud director vms query call
2133
2134 Args:
2135 vca - is active VCA connection.
2136 vdc_name - is a vdc name that will be used to query vms action
2137
2138 Returns:
2139 The return XML respond
2140 """
2141 vca = self.connect()
2142 if vdc_name is None:
2143 return None
2144
2145 url_list = [vca.host, '/api/vms/query']
2146 vm_list_rest_call = ''.join(url_list)
2147
2148 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2149 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
2150 vca.vcloud_session.organization.Link)
2151 if len(refs) == 1:
2152 response = Http.get(url=vm_list_rest_call,
2153 headers=vca.vcloud_session.get_vcloud_headers(),
2154 verify=vca.verify,
2155 logger=vca.logger)
2156 if response.status_code == requests.codes.ok:
2157 return response.content
2158
2159 return None
2160
2161 def get_vapp_list(self, vdc_name=None):
2162 """
2163 Method retrieves vApp list deployed vCloud director and returns a dictionary
2164 contains a list of all vapp deployed for queried VDC.
2165 The key for a dictionary is vApp UUID
2166
2167
2168 Args:
2169 vca - is active VCA connection.
2170 vdc_name - is a vdc name that will be used to query vms action
2171
2172 Returns:
2173 The return dictionary and key for each entry vapp UUID
2174 """
2175
2176 vapp_dict = {}
2177 if vdc_name is None:
2178 return vapp_dict
2179
2180 content = self.vms_view_action(vdc_name=vdc_name)
2181 try:
2182 vm_list_xmlroot = XmlElementTree.fromstring(content)
2183 for vm_xml in vm_list_xmlroot:
2184 if vm_xml.tag.split("}")[1] == 'VMRecord':
2185 if vm_xml.attrib['isVAppTemplate'] == 'true':
2186 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
2187 if 'vappTemplate-' in rawuuid[0]:
2188 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2189 # vm and use raw UUID as key
2190 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
2191 except:
2192 pass
2193
2194 return vapp_dict
2195
2196 def get_vm_list(self, vdc_name=None):
2197 """
2198 Method retrieves VM's list deployed vCloud director. It returns a dictionary
2199 contains a list of all VM's deployed for queried VDC.
2200 The key for a dictionary is VM UUID
2201
2202
2203 Args:
2204 vca - is active VCA connection.
2205 vdc_name - is a vdc name that will be used to query vms action
2206
2207 Returns:
2208 The return dictionary and key for each entry vapp UUID
2209 """
2210 vm_dict = {}
2211
2212 if vdc_name is None:
2213 return vm_dict
2214
2215 content = self.vms_view_action(vdc_name=vdc_name)
2216 try:
2217 vm_list_xmlroot = XmlElementTree.fromstring(content)
2218 for vm_xml in vm_list_xmlroot:
2219 if vm_xml.tag.split("}")[1] == 'VMRecord':
2220 if vm_xml.attrib['isVAppTemplate'] == 'false':
2221 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2222 if 'vm-' in rawuuid[0]:
2223 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
2224 # vm and use raw UUID as key
2225 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2226 except:
2227 pass
2228
2229 return vm_dict
2230
2231 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
2232 """
2233 Method retrieves VM deployed vCloud director. It returns VM attribute as dictionary
2234 contains a list of all VM's deployed for queried VDC.
2235 The key for a dictionary is VM UUID
2236
2237
2238 Args:
2239 vca - is active VCA connection.
2240 vdc_name - is a vdc name that will be used to query vms action
2241
2242 Returns:
2243 The return dictionary and key for each entry vapp UUID
2244 """
2245 vm_dict = {}
2246 vca = self.connect()
2247 if not vca:
2248 raise vimconn.vimconnConnectionException("self.connect() is failed")
2249
2250 if vdc_name is None:
2251 return vm_dict
2252
2253 content = self.vms_view_action(vdc_name=vdc_name)
2254 try:
2255 vm_list_xmlroot = XmlElementTree.fromstring(content)
2256 for vm_xml in vm_list_xmlroot:
2257 if vm_xml.tag.split("}")[1] == 'VMRecord' and vm_xml.attrib['isVAppTemplate'] == 'false':
2258 # lookup done by UUID
2259 if isuuid:
2260 if vapp_name in vm_xml.attrib['container']:
2261 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2262 if 'vm-' in rawuuid[0]:
2263 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2264 break
2265 # lookup done by Name
2266 else:
2267 if vapp_name in vm_xml.attrib['name']:
2268 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
2269 if 'vm-' in rawuuid[0]:
2270 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
2271 break
2272 except:
2273 pass
2274
2275 return vm_dict
2276
2277 def get_network_action(self, network_uuid=None):
2278 """
2279 Method leverages vCloud director and query network based on network uuid
2280
2281 Args:
2282 vca - is active VCA connection.
2283 network_uuid - is a network uuid
2284
2285 Returns:
2286 The return XML respond
2287 """
2288
2289 vca = self.connect()
2290 if not vca:
2291 raise vimconn.vimconnConnectionException("self.connect() is failed")
2292
2293 if network_uuid is None:
2294 return None
2295
2296 url_list = [vca.host, '/api/network/', network_uuid]
2297 vm_list_rest_call = ''.join(url_list)
2298
2299 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2300 response = Http.get(url=vm_list_rest_call,
2301 headers=vca.vcloud_session.get_vcloud_headers(),
2302 verify=vca.verify,
2303 logger=vca.logger)
2304 if response.status_code == requests.codes.ok:
2305 return response.content
2306
2307 return None
2308
2309 def get_vcd_network(self, network_uuid=None):
2310 """
2311 Method retrieves available network from vCloud Director
2312
2313 Args:
2314 network_uuid - is VCD network UUID
2315
2316 Each element serialized as key : value pair
2317
2318 Following keys available for access. network_configuration['Gateway'}
2319 <Configuration>
2320 <IpScopes>
2321 <IpScope>
2322 <IsInherited>true</IsInherited>
2323 <Gateway>172.16.252.100</Gateway>
2324 <Netmask>255.255.255.0</Netmask>
2325 <Dns1>172.16.254.201</Dns1>
2326 <Dns2>172.16.254.202</Dns2>
2327 <DnsSuffix>vmwarelab.edu</DnsSuffix>
2328 <IsEnabled>true</IsEnabled>
2329 <IpRanges>
2330 <IpRange>
2331 <StartAddress>172.16.252.1</StartAddress>
2332 <EndAddress>172.16.252.99</EndAddress>
2333 </IpRange>
2334 </IpRanges>
2335 </IpScope>
2336 </IpScopes>
2337 <FenceMode>bridged</FenceMode>
2338
2339 Returns:
2340 The return dictionary and key for each entry vapp UUID
2341 """
2342
2343 network_configuration = {}
2344 if network_uuid is None:
2345 return network_uuid
2346
2347 try:
2348 content = self.get_network_action(network_uuid=network_uuid)
2349 vm_list_xmlroot = XmlElementTree.fromstring(content)
2350
2351 network_configuration['status'] = vm_list_xmlroot.get("status")
2352 network_configuration['name'] = vm_list_xmlroot.get("name")
2353 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
2354
2355 for child in vm_list_xmlroot:
2356 if child.tag.split("}")[1] == 'IsShared':
2357 network_configuration['isShared'] = child.text.strip()
2358 if child.tag.split("}")[1] == 'Configuration':
2359 for configuration in child.iter():
2360 tagKey = configuration.tag.split("}")[1].strip()
2361 if tagKey != "":
2362 network_configuration[tagKey] = configuration.text.strip()
2363 return network_configuration
2364 except Exception as exp :
2365 self.logger.debug("get_vcd_network: Failed with Exception {}".format(exp))
2366 raise vimconn.vimconnException("get_vcd_network: Failed with Exception {}".format(exp))
2367
2368 return network_configuration
2369
2370 def delete_network_action(self, network_uuid=None):
2371 """
2372 Method delete given network from vCloud director
2373
2374 Args:
2375 network_uuid - is a network uuid that client wish to delete
2376
2377 Returns:
2378 The return None or XML respond or false
2379 """
2380
2381 vca = self.connect_as_admin()
2382 if not vca:
2383 raise vimconn.vimconnConnectionException("self.connect() is failed")
2384 if network_uuid is None:
2385 return False
2386
2387 url_list = [vca.host, '/api/admin/network/', network_uuid]
2388 vm_list_rest_call = ''.join(url_list)
2389
2390 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2391 response = Http.delete(url=vm_list_rest_call,
2392 headers=vca.vcloud_session.get_vcloud_headers(),
2393 verify=vca.verify,
2394 logger=vca.logger)
2395
2396 if response.status_code == 202:
2397 return True
2398
2399 return False
2400
2401 def create_network(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2402 ip_profile=None, isshared='true'):
2403 """
2404 Method create network in vCloud director
2405
2406 Args:
2407 network_name - is network name to be created.
2408 net_type - can be 'bridge','data','ptp','mgmt'.
2409 ip_profile is a dict containing the IP parameters of the network
2410 isshared - is a boolean
2411 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2412 It optional attribute. by default if no parent network indicate the first available will be used.
2413
2414 Returns:
2415 The return network uuid or return None
2416 """
2417
2418 new_network_name = [network_name, '-', str(uuid.uuid4())]
2419 content = self.create_network_rest(network_name=''.join(new_network_name),
2420 ip_profile=ip_profile,
2421 net_type=net_type,
2422 parent_network_uuid=parent_network_uuid,
2423 isshared=isshared)
2424 if content is None:
2425 self.logger.debug("Failed create network {}.".format(network_name))
2426 return None
2427
2428 try:
2429 vm_list_xmlroot = XmlElementTree.fromstring(content)
2430 vcd_uuid = vm_list_xmlroot.get('id').split(":")
2431 if len(vcd_uuid) == 4:
2432 self.logger.info("Created new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
2433 return vcd_uuid[3]
2434 except:
2435 self.logger.debug("Failed create network {}".format(network_name))
2436 return None
2437
2438 def create_network_rest(self, network_name=None, net_type='bridge', parent_network_uuid=None,
2439 ip_profile=None, isshared='true'):
2440 """
2441 Method create network in vCloud director
2442
2443 Args:
2444 network_name - is network name to be created.
2445 net_type - can be 'bridge','data','ptp','mgmt'.
2446 ip_profile is a dict containing the IP parameters of the network
2447 isshared - is a boolean
2448 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2449 It optional attribute. by default if no parent network indicate the first available will be used.
2450
2451 Returns:
2452 The return network uuid or return None
2453 """
2454
2455 vca = self.connect_as_admin()
2456 if not vca:
2457 raise vimconn.vimconnConnectionException("self.connect() is failed.")
2458 if network_name is None:
2459 return None
2460
2461 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
2462 vm_list_rest_call = ''.join(url_list)
2463 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2464 response = Http.get(url=vm_list_rest_call,
2465 headers=vca.vcloud_session.get_vcloud_headers(),
2466 verify=vca.verify,
2467 logger=vca.logger)
2468
2469 provider_network = None
2470 available_networks = None
2471 add_vdc_rest_url = None
2472
2473 if response.status_code != requests.codes.ok:
2474 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2475 response.status_code))
2476 return None
2477 else:
2478 try:
2479 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2480 for child in vm_list_xmlroot:
2481 if child.tag.split("}")[1] == 'ProviderVdcReference':
2482 provider_network = child.attrib.get('href')
2483 # application/vnd.vmware.admin.providervdc+xml
2484 if child.tag.split("}")[1] == 'Link':
2485 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
2486 and child.attrib.get('rel') == 'add':
2487 add_vdc_rest_url = child.attrib.get('href')
2488 except:
2489 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2490 self.logger.debug("Respond body {}".format(response.content))
2491 return None
2492
2493 # find pvdc provided available network
2494 response = Http.get(url=provider_network,
2495 headers=vca.vcloud_session.get_vcloud_headers(),
2496 verify=vca.verify,
2497 logger=vca.logger)
2498 if response.status_code != requests.codes.ok:
2499 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2500 response.status_code))
2501 return None
2502
2503 # available_networks.split("/")[-1]
2504
2505 if parent_network_uuid is None:
2506 try:
2507 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2508 for child in vm_list_xmlroot.iter():
2509 if child.tag.split("}")[1] == 'AvailableNetworks':
2510 for networks in child.iter():
2511 # application/vnd.vmware.admin.network+xml
2512 if networks.attrib.get('href') is not None:
2513 available_networks = networks.attrib.get('href')
2514 break
2515 except:
2516 return None
2517
2518 try:
2519 #Configure IP profile of the network
2520 ip_profile = ip_profile if ip_profile is not None else DEFAULT_IP_PROFILE
2521
2522 if 'gateway_address' not in ip_profile or ip_profile['gateway_address'] is None:
2523 ip_profile['gateway_address']=DEFAULT_IP_PROFILE['gateway_address']
2524 if 'dhcp_count' not in ip_profile or ip_profile['dhcp_count'] is None:
2525 ip_profile['dhcp_count']=DEFAULT_IP_PROFILE['dhcp_count']
2526 if 'subnet_address' not in ip_profile or ip_profile['subnet_address'] is None:
2527 ip_profile['subnet_address']=DEFAULT_IP_PROFILE['subnet_address']
2528 if 'dhcp_enabled' not in ip_profile or ip_profile['dhcp_enabled'] is None:
2529 ip_profile['dhcp_enabled']=DEFAULT_IP_PROFILE['dhcp_enabled']
2530 if 'dhcp_start_address' not in ip_profile or ip_profile['dhcp_start_address'] is None:
2531 ip_profile['dhcp_start_address']=DEFAULT_IP_PROFILE['dhcp_start_address']
2532 if 'ip_version' not in ip_profile or ip_profile['ip_version'] is None:
2533 ip_profile['ip_version']=DEFAULT_IP_PROFILE['ip_version']
2534 if 'dns_address' not in ip_profile or ip_profile['dns_address'] is None:
2535 ip_profile['dns_address']=DEFAULT_IP_PROFILE['dns_address']
2536
2537 gateway_address=ip_profile['gateway_address']
2538 dhcp_count=int(ip_profile['dhcp_count'])
2539 subnet_address=self.convert_cidr_to_netmask(ip_profile['subnet_address'])
2540
2541 if ip_profile['dhcp_enabled']==True:
2542 dhcp_enabled='true'
2543 else:
2544 dhcp_enabled='false'
2545 dhcp_start_address=ip_profile['dhcp_start_address']
2546
2547 #derive dhcp_end_address from dhcp_start_address & dhcp_count
2548 end_ip_int = int(netaddr.IPAddress(dhcp_start_address))
2549 end_ip_int += dhcp_count - 1
2550 dhcp_end_address = str(netaddr.IPAddress(end_ip_int))
2551
2552 ip_version=ip_profile['ip_version']
2553 dns_address=ip_profile['dns_address']
2554 except KeyError as exp:
2555 self.logger.debug("Create Network REST: Key error {}".format(exp))
2556 raise vimconn.vimconnException("Create Network REST: Key error{}".format(exp))
2557
2558 # either use client provided UUID or search for a first available
2559 # if both are not defined we return none
2560 if parent_network_uuid is not None:
2561 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
2562 add_vdc_rest_url = ''.join(url_list)
2563
2564 if net_type=='ptp':
2565 fence_mode="isolated"
2566 isshared='false'
2567 is_inherited='false'
2568 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2569 <Description>Openmano created</Description>
2570 <Configuration>
2571 <IpScopes>
2572 <IpScope>
2573 <IsInherited>{1:s}</IsInherited>
2574 <Gateway>{2:s}</Gateway>
2575 <Netmask>{3:s}</Netmask>
2576 <Dns1>{4:s}</Dns1>
2577 <IsEnabled>{5:s}</IsEnabled>
2578 <IpRanges>
2579 <IpRange>
2580 <StartAddress>{6:s}</StartAddress>
2581 <EndAddress>{7:s}</EndAddress>
2582 </IpRange>
2583 </IpRanges>
2584 </IpScope>
2585 </IpScopes>
2586 <FenceMode>{8:s}</FenceMode>
2587 </Configuration>
2588 <IsShared>{9:s}</IsShared>
2589 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2590 subnet_address, dns_address, dhcp_enabled,
2591 dhcp_start_address, dhcp_end_address, fence_mode, isshared)
2592
2593 else:
2594 fence_mode="bridged"
2595 is_inherited='false'
2596 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2597 <Description>Openmano created</Description>
2598 <Configuration>
2599 <IpScopes>
2600 <IpScope>
2601 <IsInherited>{1:s}</IsInherited>
2602 <Gateway>{2:s}</Gateway>
2603 <Netmask>{3:s}</Netmask>
2604 <Dns1>{4:s}</Dns1>
2605 <IsEnabled>{5:s}</IsEnabled>
2606 <IpRanges>
2607 <IpRange>
2608 <StartAddress>{6:s}</StartAddress>
2609 <EndAddress>{7:s}</EndAddress>
2610 </IpRange>
2611 </IpRanges>
2612 </IpScope>
2613 </IpScopes>
2614 <ParentNetwork href="{8:s}"/>
2615 <FenceMode>{9:s}</FenceMode>
2616 </Configuration>
2617 <IsShared>{10:s}</IsShared>
2618 </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
2619 subnet_address, dns_address, dhcp_enabled,
2620 dhcp_start_address, dhcp_end_address, available_networks,
2621 fence_mode, isshared)
2622
2623 headers = vca.vcloud_session.get_vcloud_headers()
2624 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
2625 try:
2626 response = Http.post(url=add_vdc_rest_url,
2627 headers=headers,
2628 data=data,
2629 verify=vca.verify,
2630 logger=vca.logger)
2631
2632 if response.status_code != 201:
2633 self.logger.debug("Create Network POST REST API call failed. Return status code {}, Response content: {}"
2634 .format(response.status_code,response.content))
2635 else:
2636 network = networkType.parseString(response.content, True)
2637 create_nw_task = network.get_Tasks().get_Task()[0]
2638
2639 # if we all ok we respond with content after network creation completes
2640 # otherwise by default return None
2641 if create_nw_task is not None:
2642 self.logger.debug("Create Network REST : Waiting for Network creation complete")
2643 status = vca.block_until_completed(create_nw_task)
2644 if status:
2645 return response.content
2646 else:
2647 self.logger.debug("create_network_rest task failed. Network Create response : {}"
2648 .format(response.content))
2649 except Exception as exp:
2650 self.logger.debug("create_network_rest : Exception : {} ".format(exp))
2651
2652 return None
2653
2654 def convert_cidr_to_netmask(self, cidr_ip=None):
2655 """
2656 Method sets convert CIDR netmask address to normal IP format
2657 Args:
2658 cidr_ip : CIDR IP address
2659 Returns:
2660 netmask : Converted netmask
2661 """
2662 if cidr_ip is not None:
2663 if '/' in cidr_ip:
2664 network, net_bits = cidr_ip.split('/')
2665 netmask = socket.inet_ntoa(struct.pack(">I", (0xffffffff << (32 - int(net_bits))) & 0xffffffff))
2666 else:
2667 netmask = cidr_ip
2668 return netmask
2669 return None
2670
2671 def get_provider_rest(self, vca=None):
2672 """
2673 Method gets provider vdc view from vcloud director
2674
2675 Args:
2676 network_name - is network name to be created.
2677 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2678 It optional attribute. by default if no parent network indicate the first available will be used.
2679
2680 Returns:
2681 The return xml content of respond or None
2682 """
2683
2684 url_list = [vca.host, '/api/admin']
2685 response = Http.get(url=''.join(url_list),
2686 headers=vca.vcloud_session.get_vcloud_headers(),
2687 verify=vca.verify,
2688 logger=vca.logger)
2689
2690 if response.status_code == requests.codes.ok:
2691 return response.content
2692 return None
2693
2694 def create_vdc(self, vdc_name=None):
2695
2696 vdc_dict = {}
2697
2698 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2699 if xml_content is not None:
2700 try:
2701 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2702 for child in task_resp_xmlroot:
2703 if child.tag.split("}")[1] == 'Owner':
2704 vdc_id = child.attrib.get('href').split("/")[-1]
2705 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2706 return vdc_dict
2707 except:
2708 self.logger.debug("Respond body {}".format(xml_content))
2709
2710 return None
2711
2712 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2713 """
2714 Method create vdc in vCloud director based on VDC template.
2715 it uses pre-defined template that must be named openmano
2716
2717 Args:
2718 vdc_name - name of a new vdc.
2719
2720 Returns:
2721 The return xml content of respond or None
2722 """
2723
2724 self.logger.info("Creating new vdc {}".format(vdc_name))
2725 vca = self.connect()
2726 if not vca:
2727 raise vimconn.vimconnConnectionException("self.connect() is failed")
2728 if vdc_name is None:
2729 return None
2730
2731 url_list = [vca.host, '/api/vdcTemplates']
2732 vm_list_rest_call = ''.join(url_list)
2733 response = Http.get(url=vm_list_rest_call,
2734 headers=vca.vcloud_session.get_vcloud_headers(),
2735 verify=vca.verify,
2736 logger=vca.logger)
2737
2738 # container url to a template
2739 vdc_template_ref = None
2740 try:
2741 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2742 for child in vm_list_xmlroot:
2743 # application/vnd.vmware.admin.providervdc+xml
2744 # we need find a template from witch we instantiate VDC
2745 if child.tag.split("}")[1] == 'VdcTemplate':
2746 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2747 'name') == 'openmano':
2748 vdc_template_ref = child.attrib.get('href')
2749 except:
2750 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2751 self.logger.debug("Respond body {}".format(response.content))
2752 return None
2753
2754 # if we didn't found required pre defined template we return None
2755 if vdc_template_ref is None:
2756 return None
2757
2758 try:
2759 # instantiate vdc
2760 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2761 vm_list_rest_call = ''.join(url_list)
2762 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2763 <Source href="{1:s}"></Source>
2764 <Description>opnemano</Description>
2765 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2766 headers = vca.vcloud_session.get_vcloud_headers()
2767 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2768 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2769 logger=vca.logger)
2770 # if we all ok we respond with content otherwise by default None
2771 if response.status_code >= 200 and response.status_code < 300:
2772 return response.content
2773 return None
2774 except:
2775 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2776 self.logger.debug("Respond body {}".format(response.content))
2777
2778 return None
2779
2780 def create_vdc_rest(self, vdc_name=None):
2781 """
2782 Method create network in vCloud director
2783
2784 Args:
2785 network_name - is network name to be created.
2786 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2787 It optional attribute. by default if no parent network indicate the first available will be used.
2788
2789 Returns:
2790 The return network uuid or return None
2791 """
2792
2793 self.logger.info("Creating new vdc {}".format(vdc_name))
2794
2795 vca = self.connect_as_admin()
2796 if not vca:
2797 raise vimconn.vimconnConnectionException("self.connect() is failed")
2798 if vdc_name is None:
2799 return None
2800
2801 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2802 vm_list_rest_call = ''.join(url_list)
2803 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2804 response = Http.get(url=vm_list_rest_call,
2805 headers=vca.vcloud_session.get_vcloud_headers(),
2806 verify=vca.verify,
2807 logger=vca.logger)
2808
2809 provider_vdc_ref = None
2810 add_vdc_rest_url = None
2811 available_networks = None
2812
2813 if response.status_code != requests.codes.ok:
2814 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2815 response.status_code))
2816 return None
2817 else:
2818 try:
2819 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2820 for child in vm_list_xmlroot:
2821 # application/vnd.vmware.admin.providervdc+xml
2822 if child.tag.split("}")[1] == 'Link':
2823 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2824 and child.attrib.get('rel') == 'add':
2825 add_vdc_rest_url = child.attrib.get('href')
2826 except:
2827 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2828 self.logger.debug("Respond body {}".format(response.content))
2829 return None
2830
2831 response = self.get_provider_rest(vca=vca)
2832 try:
2833 vm_list_xmlroot = XmlElementTree.fromstring(response)
2834 for child in vm_list_xmlroot:
2835 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2836 for sub_child in child:
2837 provider_vdc_ref = sub_child.attrib.get('href')
2838 except:
2839 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2840 self.logger.debug("Respond body {}".format(response))
2841 return None
2842
2843 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2844 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2845 <AllocationModel>ReservationPool</AllocationModel>
2846 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2847 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2848 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2849 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2850 <ProviderVdcReference
2851 name="Main Provider"
2852 href="{2:s}" />
2853 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2854 escape(vdc_name),
2855 provider_vdc_ref)
2856
2857 headers = vca.vcloud_session.get_vcloud_headers()
2858 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2859 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2860 logger=vca.logger)
2861
2862 # if we all ok we respond with content otherwise by default None
2863 if response.status_code == 201:
2864 return response.content
2865 return None
2866
2867 def get_vapp_details_rest(self, vapp_uuid=None, need_admin_access=False):
2868 """
2869 Method retrieve vapp detail from vCloud director
2870
2871 Args:
2872 vapp_uuid - is vapp identifier.
2873
2874 Returns:
2875 The return network uuid or return None
2876 """
2877
2878 parsed_respond = {}
2879 vca = None
2880
2881 if need_admin_access:
2882 vca = self.connect_as_admin()
2883 else:
2884 vca = self.connect()
2885
2886 if not vca:
2887 raise vimconn.vimconnConnectionException("self.connect() is failed")
2888 if vapp_uuid is None:
2889 return None
2890
2891 url_list = [vca.host, '/api/vApp/vapp-', vapp_uuid]
2892 get_vapp_restcall = ''.join(url_list)
2893
2894 if vca.vcloud_session and vca.vcloud_session.organization:
2895 response = Http.get(url=get_vapp_restcall,
2896 headers=vca.vcloud_session.get_vcloud_headers(),
2897 verify=vca.verify,
2898 logger=vca.logger)
2899
2900 if response.status_code != requests.codes.ok:
2901 self.logger.debug("REST API call {} failed. Return status code {}".format(get_vapp_restcall,
2902 response.status_code))
2903 return parsed_respond
2904
2905 try:
2906 xmlroot_respond = XmlElementTree.fromstring(response.content)
2907 parsed_respond['ovfDescriptorUploaded'] = xmlroot_respond.attrib['ovfDescriptorUploaded']
2908
2909 namespaces = {"vssd":"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" ,
2910 'ovf': 'http://schemas.dmtf.org/ovf/envelope/1',
2911 'vmw': 'http://www.vmware.com/schema/ovf',
2912 'vm': 'http://www.vmware.com/vcloud/v1.5',
2913 'rasd':"http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData",
2914 "vmext":"http://www.vmware.com/vcloud/extension/v1.5",
2915 "xmlns":"http://www.vmware.com/vcloud/v1.5"
2916 }
2917
2918 created_section = xmlroot_respond.find('vm:DateCreated', namespaces)
2919 if created_section is not None:
2920 parsed_respond['created'] = created_section.text
2921
2922 network_section = xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig', namespaces)
2923 if network_section is not None and 'networkName' in network_section.attrib:
2924 parsed_respond['networkname'] = network_section.attrib['networkName']
2925
2926 ipscopes_section = \
2927 xmlroot_respond.find('vm:NetworkConfigSection/vm:NetworkConfig/vm:Configuration/vm:IpScopes',
2928 namespaces)
2929 if ipscopes_section is not None:
2930 for ipscope in ipscopes_section:
2931 for scope in ipscope:
2932 tag_key = scope.tag.split("}")[1]
2933 if tag_key == 'IpRanges':
2934 ip_ranges = scope.getchildren()
2935 for ipblock in ip_ranges:
2936 for block in ipblock:
2937 parsed_respond[block.tag.split("}")[1]] = block.text
2938 else:
2939 parsed_respond[tag_key] = scope.text
2940
2941 # parse children section for other attrib
2942 children_section = xmlroot_respond.find('vm:Children/', namespaces)
2943 if children_section is not None:
2944 parsed_respond['name'] = children_section.attrib['name']
2945 parsed_respond['nestedHypervisorEnabled'] = children_section.attrib['nestedHypervisorEnabled'] \
2946 if "nestedHypervisorEnabled" in children_section.attrib else None
2947 parsed_respond['deployed'] = children_section.attrib['deployed']
2948 parsed_respond['status'] = children_section.attrib['status']
2949 parsed_respond['vmuuid'] = children_section.attrib['id'].split(":")[-1]
2950 network_adapter = children_section.find('vm:NetworkConnectionSection', namespaces)
2951 nic_list = []
2952 for adapters in network_adapter:
2953 adapter_key = adapters.tag.split("}")[1]
2954 if adapter_key == 'PrimaryNetworkConnectionIndex':
2955 parsed_respond['primarynetwork'] = adapters.text
2956 if adapter_key == 'NetworkConnection':
2957 vnic = {}
2958 if 'network' in adapters.attrib:
2959 vnic['network'] = adapters.attrib['network']
2960 for adapter in adapters:
2961 setting_key = adapter.tag.split("}")[1]
2962 vnic[setting_key] = adapter.text
2963 nic_list.append(vnic)
2964
2965 for link in children_section:
2966 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2967 if link.attrib['rel'] == 'screen:acquireTicket':
2968 parsed_respond['acquireTicket'] = link.attrib
2969 if link.attrib['rel'] == 'screen:acquireMksTicket':
2970 parsed_respond['acquireMksTicket'] = link.attrib
2971
2972 parsed_respond['interfaces'] = nic_list
2973 vCloud_extension_section = children_section.find('xmlns:VCloudExtension', namespaces)
2974 if vCloud_extension_section is not None:
2975 vm_vcenter_info = {}
2976 vim_info = vCloud_extension_section.find('vmext:VmVimInfo', namespaces)
2977 vmext = vim_info.find('vmext:VmVimObjectRef', namespaces)
2978 if vmext is not None:
2979 vm_vcenter_info["vm_moref_id"] = vmext.find('vmext:MoRef', namespaces).text
2980 parsed_respond["vm_vcenter_info"]= vm_vcenter_info
2981
2982 virtual_hardware_section = children_section.find('ovf:VirtualHardwareSection', namespaces)
2983 vm_virtual_hardware_info = {}
2984 if virtual_hardware_section is not None:
2985 for item in virtual_hardware_section.iterfind('ovf:Item',namespaces):
2986 if item.find("rasd:Description",namespaces).text == "Hard disk":
2987 disk_size = item.find("rasd:HostResource" ,namespaces
2988 ).attrib["{"+namespaces['vm']+"}capacity"]
2989
2990 vm_virtual_hardware_info["disk_size"]= disk_size
2991 break
2992
2993 for link in virtual_hardware_section:
2994 if link.tag.split("}")[1] == 'Link' and 'rel' in link.attrib:
2995 if link.attrib['rel'] == 'edit' and link.attrib['href'].endswith("/disks"):
2996 vm_virtual_hardware_info["disk_edit_href"] = link.attrib['href']
2997 break
2998
2999 parsed_respond["vm_virtual_hardware"]= vm_virtual_hardware_info
3000 except Exception as exp :
3001 self.logger.info("Error occurred calling rest api for getting vApp details {}".format(exp))
3002 return parsed_respond
3003
3004 def acuire_console(self, vm_uuid=None):
3005
3006 vca = self.connect()
3007 if not vca:
3008 raise vimconn.vimconnConnectionException("self.connect() is failed")
3009 if vm_uuid is None:
3010 return None
3011
3012 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
3013 vm_dict = self.get_vapp_details_rest(self, vapp_uuid=vm_uuid)
3014 console_dict = vm_dict['acquireTicket']
3015 console_rest_call = console_dict['href']
3016
3017 response = Http.post(url=console_rest_call,
3018 headers=vca.vcloud_session.get_vcloud_headers(),
3019 verify=vca.verify,
3020 logger=vca.logger)
3021
3022 if response.status_code == requests.codes.ok:
3023 return response.content
3024
3025 return None
3026
3027 def modify_vm_disk(self, vapp_uuid, flavor_disk):
3028 """
3029 Method retrieve vm disk details
3030
3031 Args:
3032 vapp_uuid - is vapp identifier.
3033 flavor_disk - disk size as specified in VNFD (flavor)
3034
3035 Returns:
3036 The return network uuid or return None
3037 """
3038 status = None
3039 try:
3040 #Flavor disk is in GB convert it into MB
3041 flavor_disk = int(flavor_disk) * 1024
3042 vm_details = self.get_vapp_details_rest(vapp_uuid)
3043 if vm_details:
3044 vm_name = vm_details["name"]
3045 self.logger.info("VM: {} flavor_disk :{}".format(vm_name , flavor_disk))
3046
3047 if vm_details and "vm_virtual_hardware" in vm_details:
3048 vm_disk = int(vm_details["vm_virtual_hardware"]["disk_size"])
3049 disk_edit_href = vm_details["vm_virtual_hardware"]["disk_edit_href"]
3050
3051 self.logger.info("VM: {} VM_disk :{}".format(vm_name , vm_disk))
3052
3053 if flavor_disk > vm_disk:
3054 status = self.modify_vm_disk_rest(disk_edit_href ,flavor_disk)
3055 self.logger.info("Modify disk of VM {} from {} to {} MB".format(vm_name,
3056 vm_disk, flavor_disk ))
3057 else:
3058 status = True
3059 self.logger.info("No need to modify disk of VM {}".format(vm_name))
3060
3061 return status
3062 except Exception as exp:
3063 self.logger.info("Error occurred while modifing disk size {}".format(exp))
3064
3065
3066 def modify_vm_disk_rest(self, disk_href , disk_size):
3067 """
3068 Method retrieve modify vm disk size
3069
3070 Args:
3071 disk_href - vCD API URL to GET and PUT disk data
3072 disk_size - disk size as specified in VNFD (flavor)
3073
3074 Returns:
3075 The return network uuid or return None
3076 """
3077 vca = self.connect()
3078 if not vca:
3079 raise vimconn.vimconnConnectionException("self.connect() is failed")
3080 if disk_href is None or disk_size is None:
3081 return None
3082
3083 if vca.vcloud_session and vca.vcloud_session.organization:
3084 response = Http.get(url=disk_href,
3085 headers=vca.vcloud_session.get_vcloud_headers(),
3086 verify=vca.verify,
3087 logger=vca.logger)
3088
3089 if response.status_code != requests.codes.ok:
3090 self.logger.debug("GET REST API call {} failed. Return status code {}".format(disk_href,
3091 response.status_code))
3092 return None
3093 try:
3094 lxmlroot_respond = lxmlElementTree.fromstring(response.content)
3095 namespaces = {prefix:uri for prefix,uri in lxmlroot_respond.nsmap.iteritems() if prefix}
3096 namespaces["xmlns"]= "http://www.vmware.com/vcloud/v1.5"
3097
3098 for item in lxmlroot_respond.iterfind('xmlns:Item',namespaces):
3099 if item.find("rasd:Description",namespaces).text == "Hard disk":
3100 disk_item = item.find("rasd:HostResource" ,namespaces )
3101 if disk_item is not None:
3102 disk_item.attrib["{"+namespaces['xmlns']+"}capacity"] = str(disk_size)
3103 break
3104
3105 data = lxmlElementTree.tostring(lxmlroot_respond, encoding='utf8', method='xml',
3106 xml_declaration=True)
3107
3108 #Send PUT request to modify disk size
3109 headers = vca.vcloud_session.get_vcloud_headers()
3110 headers['Content-Type'] = 'application/vnd.vmware.vcloud.rasdItemsList+xml; charset=ISO-8859-1'
3111
3112 response = Http.put(url=disk_href,
3113 data=data,
3114 headers=headers,
3115 verify=vca.verify, logger=self.logger)
3116
3117 if response.status_code != 202:
3118 self.logger.debug("PUT REST API call {} failed. Return status code {}".format(disk_href,
3119 response.status_code))
3120 else:
3121 modify_disk_task = taskType.parseString(response.content, True)
3122 if type(modify_disk_task) is GenericTask:
3123 status = vca.block_until_completed(modify_disk_task)
3124 return status
3125
3126 return None
3127
3128 except Exception as exp :
3129 self.logger.info("Error occurred calling rest api for modifing disk size {}".format(exp))
3130 return None
3131
3132 def add_pci_devices(self, vapp_uuid , pci_devices , vmname_andid):
3133 """
3134 Method to attach pci devices to VM
3135
3136 Args:
3137 vapp_uuid - uuid of vApp/VM
3138 pci_devices - pci devices infromation as specified in VNFD (flavor)
3139
3140 Returns:
3141 The status of add pci device task , vm object and
3142 vcenter_conect object
3143 """
3144 vm_obj = None
3145 vcenter_conect = None
3146 self.logger.info("Add pci devices {} into vApp {}".format(pci_devices , vapp_uuid))
3147 try:
3148 vm_vcenter_info = self.get_vm_vcenter_info(vapp_uuid)
3149 except Exception as exp:
3150 self.logger.error("Error occurred while getting vCenter infromationn"\
3151 " for VM : {}".format(exp))
3152 raise vimconn.vimconnException(message=exp)
3153
3154 if vm_vcenter_info["vm_moref_id"]:
3155 context = None
3156 if hasattr(ssl, '_create_unverified_context'):
3157 context = ssl._create_unverified_context()
3158 try:
3159 no_of_pci_devices = len(pci_devices)
3160 if no_of_pci_devices > 0:
3161 vcenter_conect = SmartConnect(
3162 host=vm_vcenter_info["vm_vcenter_ip"],
3163 user=vm_vcenter_info["vm_vcenter_user"],
3164 pwd=vm_vcenter_info["vm_vcenter_password"],
3165 port=int(vm_vcenter_info["vm_vcenter_port"]),
3166 sslContext=context)
3167 atexit.register(Disconnect, vcenter_conect)
3168 content = vcenter_conect.RetrieveContent()
3169
3170 #Get VM and its host
3171 host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
3172 self.logger.info("VM {} is currently on host {}".format(vm_obj, host_obj))
3173 if host_obj and vm_obj:
3174 #get PCI devies from host on which vapp is currently installed
3175 avilable_pci_devices = self.get_pci_devices(host_obj, no_of_pci_devices)
3176
3177 if avilable_pci_devices is None:
3178 #find other hosts with active pci devices
3179 new_host_obj , avilable_pci_devices = self.get_host_and_PCIdevices(
3180 content,
3181 no_of_pci_devices
3182 )
3183
3184 if new_host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3185 #Migrate vm to the host where PCI devices are availble
3186 self.logger.info("Relocate VM {} on new host {}".format(vm_obj, new_host_obj))
3187 task = self.relocate_vm(new_host_obj, vm_obj)
3188 if task is not None:
3189 result = self.wait_for_vcenter_task(task, vcenter_conect)
3190 self.logger.info("Migrate VM status: {}".format(result))
3191 host_obj = new_host_obj
3192 else:
3193 self.logger.info("Fail to migrate VM : {}".format(result))
3194 raise vimconn.vimconnNotFoundException(
3195 "Fail to migrate VM : {} to host {}".format(
3196 vmname_andid,
3197 new_host_obj)
3198 )
3199
3200 if host_obj is not None and avilable_pci_devices is not None and len(avilable_pci_devices)> 0:
3201 #Add PCI devices one by one
3202 for pci_device in avilable_pci_devices:
3203 task = self.add_pci_to_vm(host_obj, vm_obj, pci_device)
3204 if task:
3205 status= self.wait_for_vcenter_task(task, vcenter_conect)
3206 if status:
3207 self.logger.info("Added PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3208 else:
3209 self.logger.error("Fail to add PCI device {} to VM {}".format(pci_device,str(vm_obj)))
3210 return True, vm_obj, vcenter_conect
3211 else:
3212 self.logger.error("Currently there is no host with"\
3213 " {} number of avaialble PCI devices required for VM {}".format(
3214 no_of_pci_devices,
3215 vmname_andid)
3216 )
3217 raise vimconn.vimconnNotFoundException(
3218 "Currently there is no host with {} "\
3219 "number of avaialble PCI devices required for VM {}".format(
3220 no_of_pci_devices,
3221 vmname_andid))
3222 else:
3223 self.logger.debug("No infromation about PCI devices {} ",pci_devices)
3224
3225 except vmodl.MethodFault as error:
3226 self.logger.error("Error occurred while adding PCI devices {} ",error)
3227 return None, vm_obj, vcenter_conect
3228
3229 def get_vm_obj(self, content, mob_id):
3230 """
3231 Method to get the vsphere VM object associated with a given morf ID
3232 Args:
3233 vapp_uuid - uuid of vApp/VM
3234 content - vCenter content object
3235 mob_id - mob_id of VM
3236
3237 Returns:
3238 VM and host object
3239 """
3240 vm_obj = None
3241 host_obj = None
3242 try :
3243 container = content.viewManager.CreateContainerView(content.rootFolder,
3244 [vim.VirtualMachine], True
3245 )
3246 for vm in container.view:
3247 mobID = vm._GetMoId()
3248 if mobID == mob_id:
3249 vm_obj = vm
3250 host_obj = vm_obj.runtime.host
3251 break
3252 except Exception as exp:
3253 self.logger.error("Error occurred while finding VM object : {}".format(exp))
3254 return host_obj, vm_obj
3255
3256 def get_pci_devices(self, host, need_devices):
3257 """
3258 Method to get the details of pci devices on given host
3259 Args:
3260 host - vSphere host object
3261 need_devices - number of pci devices needed on host
3262
3263 Returns:
3264 array of pci devices
3265 """
3266 all_devices = []
3267 all_device_ids = []
3268 used_devices_ids = []
3269
3270 try:
3271 if host:
3272 pciPassthruInfo = host.config.pciPassthruInfo
3273 pciDevies = host.hardware.pciDevice
3274
3275 for pci_status in pciPassthruInfo:
3276 if pci_status.passthruActive:
3277 for device in pciDevies:
3278 if device.id == pci_status.id:
3279 all_device_ids.append(device.id)
3280 all_devices.append(device)
3281
3282 #check if devices are in use
3283 avalible_devices = all_devices
3284 for vm in host.vm:
3285 if vm.runtime.powerState == vim.VirtualMachinePowerState.poweredOn:
3286 vm_devices = vm.config.hardware.device
3287 for device in vm_devices:
3288 if type(device) is vim.vm.device.VirtualPCIPassthrough:
3289 if device.backing.id in all_device_ids:
3290 for use_device in avalible_devices:
3291 if use_device.id == device.backing.id:
3292 avalible_devices.remove(use_device)
3293 used_devices_ids.append(device.backing.id)
3294 self.logger.debug("Device {} from devices {}"\
3295 "is in use".format(device.backing.id,
3296 device)
3297 )
3298 if len(avalible_devices) < need_devices:
3299 self.logger.debug("Host {} don't have {} number of active devices".format(host,
3300 need_devices))
3301 self.logger.debug("found only {} devives {}".format(len(avalible_devices),
3302 avalible_devices))
3303 return None
3304 else:
3305 required_devices = avalible_devices[:need_devices]
3306 self.logger.info("Found {} PCI devivces on host {} but required only {}".format(
3307 len(avalible_devices),
3308 host,
3309 need_devices))
3310 self.logger.info("Retruning {} devices as {}".format(need_devices,
3311 required_devices ))
3312 return required_devices
3313
3314 except Exception as exp:
3315 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host))
3316
3317 return None
3318
3319 def get_host_and_PCIdevices(self, content, need_devices):
3320 """
3321 Method to get the details of pci devices infromation on all hosts
3322
3323 Args:
3324 content - vSphere host object
3325 need_devices - number of pci devices needed on host
3326
3327 Returns:
3328 array of pci devices and host object
3329 """
3330 host_obj = None
3331 pci_device_objs = None
3332 try:
3333 if content:
3334 container = content.viewManager.CreateContainerView(content.rootFolder,
3335 [vim.HostSystem], True)
3336 for host in container.view:
3337 devices = self.get_pci_devices(host, need_devices)
3338 if devices:
3339 host_obj = host
3340 pci_device_objs = devices
3341 break
3342 except Exception as exp:
3343 self.logger.error("Error {} occurred while finding pci devices on host: {}".format(exp, host_obj))
3344
3345 return host_obj,pci_device_objs
3346
3347 def relocate_vm(self, dest_host, vm) :
3348 """
3349 Method to get the relocate VM to new host
3350
3351 Args:
3352 dest_host - vSphere host object
3353 vm - vSphere VM object
3354
3355 Returns:
3356 task object
3357 """
3358 task = None
3359 try:
3360 relocate_spec = vim.vm.RelocateSpec(host=dest_host)
3361 task = vm.Relocate(relocate_spec)
3362 self.logger.info("Migrating {} to destination host {}".format(vm, dest_host))
3363 except Exception as exp:
3364 self.logger.error("Error occurred while relocate VM {} to new host {}: {}".format(
3365 dest_host, vm, exp))
3366 return task
3367
3368 def wait_for_vcenter_task(self, task, actionName='job', hideResult=False):
3369 """
3370 Waits and provides updates on a vSphere task
3371 """
3372 while task.info.state == vim.TaskInfo.State.running:
3373 time.sleep(2)
3374
3375 if task.info.state == vim.TaskInfo.State.success:
3376 if task.info.result is not None and not hideResult:
3377 self.logger.info('{} completed successfully, result: {}'.format(
3378 actionName,
3379 task.info.result))
3380 else:
3381 self.logger.info('Task {} completed successfully.'.format(actionName))
3382 else:
3383 self.logger.error('{} did not complete successfully: {} '.format(
3384 actionName,
3385 task.info.error)
3386 )
3387
3388 return task.info.result
3389
3390 def add_pci_to_vm(self,host_object, vm_object, host_pci_dev):
3391 """
3392 Method to add pci device in given VM
3393
3394 Args:
3395 host_object - vSphere host object
3396 vm_object - vSphere VM object
3397 host_pci_dev - host_pci_dev must be one of the devices from the
3398 host_object.hardware.pciDevice list
3399 which is configured as a PCI passthrough device
3400
3401 Returns:
3402 task object
3403 """
3404 task = None
3405 if vm_object and host_object and host_pci_dev:
3406 try :
3407 #Add PCI device to VM
3408 pci_passthroughs = vm_object.environmentBrowser.QueryConfigTarget(host=None).pciPassthrough
3409 systemid_by_pciid = {item.pciDevice.id: item.systemId for item in pci_passthroughs}
3410
3411 if host_pci_dev.id not in systemid_by_pciid:
3412 self.logger.error("Device {} is not a passthrough device ".format(host_pci_dev))
3413 return None
3414
3415 deviceId = hex(host_pci_dev.deviceId % 2**16).lstrip('0x')
3416 backing = vim.VirtualPCIPassthroughDeviceBackingInfo(deviceId=deviceId,
3417 id=host_pci_dev.id,
3418 systemId=systemid_by_pciid[host_pci_dev.id],
3419 vendorId=host_pci_dev.vendorId,
3420 deviceName=host_pci_dev.deviceName)
3421
3422 hba_object = vim.VirtualPCIPassthrough(key=-100, backing=backing)
3423
3424 new_device_config = vim.VirtualDeviceConfigSpec(device=hba_object)
3425 new_device_config.operation = "add"
3426 vmConfigSpec = vim.vm.ConfigSpec()
3427 vmConfigSpec.deviceChange = [new_device_config]
3428
3429 task = vm_object.ReconfigVM_Task(spec=vmConfigSpec)
3430 self.logger.info("Adding PCI device {} into VM {} from host {} ".format(
3431 host_pci_dev, vm_object, host_object)
3432 )
3433 except Exception as exp:
3434 self.logger.error("Error occurred while adding pci devive {} to VM {}: {}".format(
3435 host_pci_dev,
3436 vm_object,
3437 exp))
3438 return task
3439
3440 def get_vm_vcenter_info(self , vapp_uuid):
3441 """
3442 Method to get details of vCenter and vm
3443
3444 Args:
3445 vapp_uuid - uuid of vApp or VM
3446
3447 Returns:
3448 Moref Id of VM and deails of vCenter
3449 """
3450 vm_vcenter_info = {}
3451
3452 if self.vcenter_ip is not None:
3453 vm_vcenter_info["vm_vcenter_ip"] = self.vcenter_ip
3454 else:
3455 raise vimconn.vimconnException(message="vCenter IP is not provided."\
3456 " Please provide vCenter IP while attaching datacenter to tenant in --config")
3457 if self.vcenter_port is not None:
3458 vm_vcenter_info["vm_vcenter_port"] = self.vcenter_port
3459 else:
3460 raise vimconn.vimconnException(message="vCenter port is not provided."\
3461 " Please provide vCenter port while attaching datacenter to tenant in --config")
3462 if self.vcenter_user is not None:
3463 vm_vcenter_info["vm_vcenter_user"] = self.vcenter_user
3464 else:
3465 raise vimconn.vimconnException(message="vCenter user is not provided."\
3466 " Please provide vCenter user while attaching datacenter to tenant in --config")
3467
3468 if self.vcenter_password is not None:
3469 vm_vcenter_info["vm_vcenter_password"] = self.vcenter_password
3470 else:
3471 raise vimconn.vimconnException(message="vCenter user password is not provided."\
3472 " Please provide vCenter user password while attaching datacenter to tenant in --config")
3473 try:
3474 vm_details = self.get_vapp_details_rest(vapp_uuid, need_admin_access=True)
3475 if vm_details and "vm_vcenter_info" in vm_details:
3476 vm_vcenter_info["vm_moref_id"] = vm_details["vm_vcenter_info"].get("vm_moref_id", None)
3477
3478 return vm_vcenter_info
3479
3480 except Exception as exp:
3481 self.logger.error("Error occurred while getting vCenter infromationn"\
3482 " for VM : {}".format(exp))
3483
3484
3485 def get_vm_pci_details(self, vmuuid):
3486 """
3487 Method to get VM PCI device details from vCenter
3488
3489 Args:
3490 vm_obj - vSphere VM object
3491
3492 Returns:
3493 dict of PCI devives attached to VM
3494
3495 """
3496 vm_pci_devices_info = {}
3497 try:
3498 vm_vcenter_info = self.get_vm_vcenter_info(vmuuid)
3499 if vm_vcenter_info["vm_moref_id"]:
3500 context = None
3501 if hasattr(ssl, '_create_unverified_context'):
3502 context = ssl._create_unverified_context()
3503 vcenter_conect = SmartConnect(host=vm_vcenter_info["vm_vcenter_ip"],
3504 user=vm_vcenter_info["vm_vcenter_user"],
3505 pwd=vm_vcenter_info["vm_vcenter_password"],
3506 port=int(vm_vcenter_info["vm_vcenter_port"]),
3507 sslContext=context
3508 )
3509 atexit.register(Disconnect, vcenter_conect)
3510 content = vcenter_conect.RetrieveContent()
3511
3512 #Get VM and its host
3513 if content:
3514 host_obj, vm_obj = self.get_vm_obj(content ,vm_vcenter_info["vm_moref_id"])
3515 if host_obj and vm_obj:
3516 vm_pci_devices_info["host_name"]= host_obj.name
3517 vm_pci_devices_info["host_ip"]= host_obj.config.network.vnic[0].spec.ip.ipAddress
3518 for device in vm_obj.config.hardware.device:
3519 if type(device) == vim.vm.device.VirtualPCIPassthrough:
3520 device_details={'devide_id':device.backing.id,
3521 'pciSlotNumber':device.slotInfo.pciSlotNumber,
3522 }
3523 vm_pci_devices_info[device.deviceInfo.label] = device_details
3524 else:
3525 self.logger.error("Can not connect to vCenter while getting "\
3526 "PCI devices infromationn")
3527 return vm_pci_devices_info
3528 except Exception as exp:
3529 self.logger.error("Error occurred while getting VM infromationn"\
3530 " for VM : {}".format(exp))
3531 raise vimconn.vimconnException(message=exp)
3532
3533 def add_network_adapter_to_vms(self, vapp, network_name, primary_nic_index, nicIndex, nic_type=None):
3534 """
3535 Method to add network adapter type to vm
3536 Args :
3537 network_name - name of network
3538 primary_nic_index - int value for primary nic index
3539 nicIndex - int value for nic index
3540 nic_type - specify model name to which add to vm
3541 Returns:
3542 None
3543 """
3544 try:
3545 vca = self.connect()
3546 if not vca:
3547 raise vimconn.vimconnConnectionException("Failed to connect vCloud director")
3548
3549 if not nic_type:
3550 for vms in vapp._get_vms():
3551 vm_id = (vms.id).split(':')[-1]
3552
3553 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3554
3555 response = Http.get(url=url_rest_call,
3556 headers=vca.vcloud_session.get_vcloud_headers(),
3557 verify=vca.verify,
3558 logger=vca.logger)
3559 if response.status_code != 200:
3560 self.logger.error("add_network_adapter_to_vms : Get network connection section "\
3561 "REST call {} failed status code : {}".format(url_rest_call,
3562 response.status_code))
3563 self.logger.error("Failed reason to get network adapter : {} ".format(response.content))
3564
3565 data = response.content
3566 if '<PrimaryNetworkConnectionIndex>' not in data:
3567 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3568 <NetworkConnection network="{}">
3569 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3570 <IsConnected>true</IsConnected>
3571 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3572 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex)
3573 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3574 else:
3575 new_item = """<NetworkConnection network="{}">
3576 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3577 <IsConnected>true</IsConnected>
3578 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3579 </NetworkConnection>""".format(network_name, nicIndex)
3580 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3581
3582 headers = vca.vcloud_session.get_vcloud_headers()
3583 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3584 response = Http.put(url=url_rest_call, headers=headers, data=data,
3585 verify=vca.verify,
3586 logger=vca.logger)
3587
3588 if response.status_code != 202:
3589 self.logger.error("add_network_adapter_to_vms : Put network adapter REST call {} "\
3590 "failed status code : {}".format(url_rest_call,
3591 response.status_code))
3592 self.logger.error("Failed reason to add network adapter: {} ".format(response.content))
3593 else:
3594 nic_task = taskType.parseString(response.content, True)
3595 if isinstance(nic_task, GenericTask):
3596 vca.block_until_completed(nic_task)
3597 self.logger.info("add_network_adapter_to_vms(): VM {} conneced to "\
3598 "default NIC type".format(vm_id))
3599 else:
3600 self.logger.error("add_network_adapter_to_vms(): VM {} failed to "\
3601 "connect NIC type".format(vm_id))
3602 else:
3603 for vms in vapp._get_vms():
3604 vm_id = (vms.id).split(':')[-1]
3605
3606 url_rest_call = "{}/api/vApp/vm-{}/networkConnectionSection/".format(vca.host, vm_id)
3607
3608 response = Http.get(url=url_rest_call,
3609 headers=vca.vcloud_session.get_vcloud_headers(),
3610 verify=vca.verify,
3611 logger=vca.logger)
3612 if response.status_code != 200:
3613 self.logger.error("add_network_adapter_to_vms : Get network connection section "\
3614 "REST call {} failed status code : {}".format(url_rest_call,
3615 response.status_code))
3616 self.logger.error("Failed reason to get network adapter : {} ".format(response.content))
3617
3618 data = response.content
3619 if '<PrimaryNetworkConnectionIndex>' not in data:
3620 item = """<PrimaryNetworkConnectionIndex>{}</PrimaryNetworkConnectionIndex>
3621 <NetworkConnection network="{}">
3622 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3623 <IsConnected>true</IsConnected>
3624 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3625 <NetworkAdapterType>{}</NetworkAdapterType>
3626 </NetworkConnection>""".format(primary_nic_index, network_name, nicIndex, nic_type)
3627 data = data.replace('</ovf:Info>\n','</ovf:Info>\n{}\n'.format(item))
3628 else:
3629 new_item = """<NetworkConnection network="{}">
3630 <NetworkConnectionIndex>{}</NetworkConnectionIndex>
3631 <IsConnected>true</IsConnected>
3632 <IpAddressAllocationMode>DHCP</IpAddressAllocationMode>
3633 <NetworkAdapterType>{}</NetworkAdapterType>
3634 </NetworkConnection>""".format(network_name, nicIndex, nic_type)
3635 data = data.replace('</NetworkConnection>\n','</NetworkConnection>\n{}\n'.format(new_item))
3636
3637 headers = vca.vcloud_session.get_vcloud_headers()
3638 headers['Content-Type'] = 'application/vnd.vmware.vcloud.networkConnectionSection+xml'
3639 response = Http.put(url=url_rest_call, headers=headers, data=data,
3640 verify=vca.verify,
3641 logger=vca.logger)
3642
3643 if response.status_code != 202:
3644 self.logger.error("add_network_adapter_to_vms : Put network adapter call {} "\
3645 "failed status code : {}".format(url_rest_call,
3646 response.status_code))
3647 self.logger.error("Failed reason to add network adapter: {} ".format(response.content))
3648 else:
3649 nic_task = taskType.parseString(response.content, True)
3650 if isinstance(nic_task, GenericTask):
3651 vca.block_until_completed(nic_task)
3652 self.logger.info("add_network_adapter_to_vms(): VM {} "\
3653 "conneced to NIC type {}".format(vm_id, nic_type))
3654 else:
3655 self.logger.error("add_network_adapter_to_vms(): VM {} "\
3656 "failed to connect NIC type {}".format(vm_id, nic_type))
3657 except Exception as exp:
3658 self.logger.error("add_network_adapter_to_vms() : exception {} occurred "\
3659 "while adding Network adapter".format(exp))