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