Fixed issue with exceptions. Now instead of return code upon an appropriate failure...
[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
29 import vimconn
30 import os
31 import traceback
32 import itertools
33 import requests
34
35 from xml.etree import ElementTree as XmlElementTree
36
37 import yaml
38 from pyvcloud import Http
39 from pyvcloud.vcloudair import VCA
40 from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
41 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
42 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
43 from xml.sax.saxutils import escape
44
45 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TaskType
46 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.taskType import TaskType as GenericTask
47 from pyvcloud.schema.vcd.v1_5.schemas.vcloud.vAppType import TaskType as VappTask
48 from pyvcloud.schema.vcd.v1_5.schemas.admin.vCloudEntities import TasksInProgressType
49
50 import logging
51 import json
52 import vimconn
53 import time
54 import uuid
55 import httplib
56
57 # global variable for vcd connector type
58 STANDALONE = 'standalone'
59
60 # global variable for number of retry
61 DELETE_INSTANCE_RETRY = 3
62
63 VCAVERSION = '5.9'
64
65 __author__ = "Mustafa Bayramov"
66 __date__ = "$26-Aug-2016 11:09:29$"
67
68 # -1: "Could not be created",
69 # 0: "Unresolved",
70 # 1: "Resolved",
71 # 2: "Deployed",
72 # 3: "Suspended",
73 # 4: "Powered on",
74 # 5: "Waiting for user input",
75 # 6: "Unknown state",
76 # 7: "Unrecognized state",
77 # 8: "Powered off",
78 # 9: "Inconsistent state",
79 # 10: "Children do not all have the same status",
80 # 11: "Upload initiated, OVF descriptor pending",
81 # 12: "Upload initiated, copying contents",
82 # 13: "Upload initiated , disk contents pending",
83 # 14: "Upload has been quarantined",
84 # 15: "Upload quarantine period has expired"
85
86 # mapping vCD status to MANO
87 vcdStatusCode2manoFormat = {4: 'ACTIVE',
88 7: 'PAUSED',
89 3: 'SUSPENDED',
90 8: 'INACTIVE',
91 12: 'BUILD',
92 -1: 'ERROR',
93 14: 'DELETED'}
94
95 #
96 netStatus2manoFormat = {'ACTIVE': 'ACTIVE', 'PAUSED': 'PAUSED', 'INACTIVE': 'INACTIVE', 'BUILD': 'BUILD',
97 'ERROR': 'ERROR', 'DELETED': 'DELETED'
98 }
99
100 # dict used to store flavor in memory
101 flavorlist = {}
102
103
104 class vimconnector(vimconn.vimconnector):
105 def __init__(self, uuid=None, name=None, tenant_id=None, tenant_name=None,
106 url=None, url_admin=None, user=None, passwd=None, log_level="ERROR", config={}):
107 vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url,
108 url_admin, user, passwd, log_level, config)
109 self.id = uuid
110 self.name = name
111 self.org_name = name
112 self.url = url
113 self.url_admin = url_admin
114 self.tenant_id = tenant_id
115 self.tenant_name = tenant_name
116 self.user = user
117 self.passwd = passwd
118 self.config = config
119 self.admin_password = None
120 self.admin_user = None
121
122 self.logger = logging.getLogger('openmano.vim.vmware')
123 self.logger.setLevel(10)
124
125 try:
126 self.admin_user = config['admin_username']
127 self.admin_password = config['admin_password']
128 except KeyError:
129 raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
130
131 self.logger = logging.getLogger('mano.vim.vmware')
132 self.org_uuid = None
133 self.vca = None
134
135 if not url:
136 raise TypeError, 'url param can not be NoneType'
137
138 if not self.url_admin: # try to use normal url
139 self.url_admin = self.url
140
141 logging.debug("Calling constructor with following paramters")
142 logging.debug("UUID: {} name: {} tenant_id: {} tenant name {}".format(self.id, self.name,
143 self.tenant_id, self.tenant_name))
144 logging.debug("vcd url {} vcd username: {} vcd password: {}".format(self.url, self.user, self.passwd))
145 logging.debug("vcd admin username {} vcd admin passowrd {}".format(self.admin_user, self.admin_password))
146
147 # initialize organization
148 if self.user is not None and self.passwd is not None and self.url:
149 self.init_organization()
150
151 def __getitem__(self, index):
152 if index == 'tenant_id':
153 return self.tenant_id
154 if index == 'tenant_name':
155 return self.tenant_name
156 elif index == 'id':
157 return self.id
158 elif index == 'name':
159 return self.name
160 elif index == 'org_name':
161 return self.org_name
162 elif index == 'org_uuid':
163 return self.org_uuid
164 elif index == 'user':
165 return self.user
166 elif index == 'passwd':
167 return self.passwd
168 elif index == 'url':
169 return self.url
170 elif index == 'url_admin':
171 return self.url_admin
172 elif index == "config":
173 return self.config
174 else:
175 raise KeyError("Invalid key '%s'" % str(index))
176
177 def __setitem__(self, index, value):
178 if index == 'tenant_id':
179 self.tenant_id = value
180 if index == 'tenant_name':
181 self.tenant_name = value
182 elif index == 'id':
183 self.id = value
184 # we use name = org #TODO later refactor
185 elif index == 'name':
186 self.name = value
187 self.org = value
188 elif index == 'org_name':
189 self.org_name = value
190 self.name = value
191 elif index == 'org_uuid':
192 self.org_name = value
193 elif index == 'user':
194 self.user = value
195 elif index == 'passwd':
196 self.passwd = value
197 elif index == 'url':
198 self.url = value
199 elif index == 'url_admin':
200 self.url_admin = value
201 else:
202 raise KeyError("Invalid key '%s'" % str(index))
203
204 def connect_as_admin(self):
205 """ Method connect as admin user to vCloud director.
206
207 Returns:
208 The return vca object that letter can be used to connect to vcloud direct as admin for provider vdc
209 """
210
211 self.logger.debug("Logging in to a vca {} as admin.".format(self.name))
212 service_type = STANDALONE
213
214 vca_admin = VCA(host=self.url,
215 username=self.admin_user,
216 service_type=STANDALONE,
217 version=VCAVERSION,
218 verify=False,
219 log=False)
220 result = vca_admin.login(password=self.admin_password, org='System')
221 if not result:
222 raise vimconn.vimconnConnectionException(
223 "Can't connect to a vCloud director as: {}".format(self.admin_user))
224 result = vca_admin.login(token=vca_admin.token, org='System', org_url=vca_admin.vcloud_session.org_url)
225 if result is True:
226 self.logger.info(
227 "Successfully logged to a vcloud direct org: {} as user: {}".format('System', self.admin_user))
228
229 return vca_admin
230
231 def connect(self):
232 """ Method connect as normal user to vCloud director.
233
234 Returns:
235 The return vca object that letter can be used to connect to vCloud director as admin for VDC
236 """
237
238 service_type = 'standalone'
239 version = '5.9'
240
241 self.logger.debug("Logging in to a vca {} as {} to datacenter {}.".format(self.name, self.user, self.name))
242 vca = VCA(host=self.url,
243 username=self.user,
244 service_type=STANDALONE,
245 version=VCAVERSION,
246 verify=False,
247 log=False)
248 result = vca.login(password=self.passwd, org=self.name)
249 if not result:
250 raise vimconn.vimconnConnectionException("Can't connect to a vCloud director as: {}".format(self.user))
251 result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
252 if result is True:
253 self.logger.info("Successfully logged to a vcloud direct org: {} as user: {}".format(self.name, self.user))
254
255 return vca
256
257 def init_organization(self):
258 """ Method initialize organization UUID and VDC parameters.
259
260 At bare minimum client must provide organization name that present in vCloud director and VDC.
261
262 The VDC - UUID ( tenant_id) will be initialized at the run time if client didn't call constructor.
263 The Org - UUID will be initialized at the run time if data center present in vCloud director.
264
265 Returns:
266 The return vca object that letter can be used to connect to vcloud direct as admin
267 """
268 try:
269 if self.org_uuid is None:
270 org_dict = self.get_org_list()
271 for org in org_dict:
272 # we set org UUID at the init phase but we can do it only when we have valid credential.
273 if org_dict[org] == self.org_name:
274 self.org_uuid = org
275 self.logger.debug("Setting organization UUID {}".format(self.org_uuid))
276 break
277
278 else:
279 raise vimconn.vimconnException("Vcloud director organization {} not found".format(self.org_name))
280
281 # if well good we require for org details
282 org_details_dict = self.get_org(org_uuid=self.org_uuid)
283
284 # we have two case if we want to initialize VDC ID or VDC name at run time
285 # tenant_name provided but no tenant id
286 print ("Setting vdc from name {} {} {} {}".format(self.tenant_id, self.tenant_name,
287 org_details_dict.has_key('vdcs'), org_details_dict))
288 if self.tenant_id is None and self.tenant_name is not None and org_details_dict.has_key('vdcs'):
289 print ("Setting vdc from name")
290 vdcs_dict = org_details_dict['vdcs']
291 print ("Searching vdc UUUID")
292 for vdc in vdcs_dict:
293 if vdcs_dict[vdc] == self.tenant_name:
294 self.tenant_id = vdc
295 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
296 self.name))
297 break
298 else:
299 raise vimconn.vimconnException("Tenant name indicated but not present in vcloud director.")
300 # case two we have tenant_id but we don't have tenant name so we find and set it.
301 if self.tenant_id is not None and self.tenant_name is None and org_details_dict.has_key('vdcs'):
302 print ("setting vdc from id")
303 vdcs_dict = org_details_dict['vdcs']
304 for vdc in vdcs_dict:
305 if vdc == self.tenant_id:
306 self.tenant_name = vdcs_dict[vdc]
307 self.logger.debug("Setting vdc uuid {} for organization UUID {}".format(self.tenant_id,
308 self.name))
309 break
310 else:
311 raise vimconn.vimconnException("Tenant id indicated but not present in vcloud director")
312 self.logger.debug("Setting organization uuid {}".format(self.org_uuid))
313 except:
314 self.logger.debug("Failed initialize organization UUID for org {}".format(self.org_name))
315 self.logger.debug(traceback.format_exc())
316 self.org_uuid = None
317
318 def new_tenant(self, tenant_name=None, tenant_description=None):
319 """Adds a new tenant to VIM with this name and description
320
321 :param tenant_name:
322 :param tenant_description:
323 :return: returns the tenant identifier
324 """
325 vdc_task = self.create_vdc(vdc_name=tenant_name)
326 if vdc_task is not None:
327 vdc_uuid, value = vdc_task.popitem()
328 self.logger.info("Crated new vdc {} and uuid: {}".format(tenant_name, vdc_uuid))
329 return vdc_uuid
330 else:
331 raise vimconn.vimconnException("Failed create tenant {}".format(tenant_name))
332
333 def delete_tenant(self, tenant_id, ):
334 """Delete a tenant from VIM"""
335 'Returns the tenant identifier'
336 raise vimconn.vimconnNotImplemented("Should have implemented this")
337
338 def get_tenant_list(self, filter_dict={}):
339 '''Obtain tenants of VIM
340 filter_dict can contain the following keys:
341 name: filter by tenant name
342 id: filter by tenant uuid/id
343 <other VIM specific>
344 Returns the tenant list of dictionaries:
345 [{'name':'<name>, 'id':'<id>, ...}, ...]
346
347 '''
348
349 org_dict = self.get_org(self.org_uuid)
350 vdcs_dict = org_dict['vdcs']
351
352 vdclist = []
353 try:
354 for k in vdcs_dict:
355 entry = {'name': vdcs_dict[k], 'id': k}
356 filtered_entry = entry.copy()
357 filtered_dict = set(entry.keys()) - set(filter_dict)
358 for unwanted_key in filtered_dict: del entry[unwanted_key]
359 if filter_dict == entry:
360 vdclist.append(filtered_entry)
361 except:
362 self.logger.debug("Error in get_tenant_list()")
363 self.logger.debug(traceback.format_exc())
364 pass
365
366 return vdclist
367
368 def new_network(self, net_name, net_type, ip_profile=None, shared=False):
369 '''Adds a tenant network to VIM
370 net_name is the name
371 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
372 ip_profile is a dict containing the IP parameters of the network
373 shared is a boolean
374 Returns the network identifier'''
375
376 self.logger.debug(
377 "new_network tenant {} net_type {} ip_profile {} shared {}".format(net_name, net_type, ip_profile, shared))
378
379 isshared = 'false'
380 if shared:
381 isshared = 'true'
382
383 network_uuid = self.create_network(network_name=net_name, isshared=isshared)
384 if network_uuid is not None:
385 return network_uuid
386 else:
387 raise vimconn.vimconnUnexpectedResponse("Failed create a new network {}".format(net_name))
388
389 def get_vcd_network_list(self):
390 """ Method available organization for a logged in tenant
391
392 Returns:
393 The return vca object that letter can be used to connect to vcloud direct as admin
394 """
395
396 self.logger.debug("get_vcd_network_list(): retrieving network list for vcd")
397 vca = self.connect()
398 if not vca:
399 raise vimconn.vimconnConnectionException("self.connect() is failed.")
400
401 vdc = vca.get_vdc(self.tenant_name)
402 vdc_uuid = vdc.get_id().split(":")[3]
403 networks = vca.get_networks(vdc.get_name())
404 network_list = []
405 try:
406 for network in networks:
407 filter_dict = {}
408 netid = network.get_id().split(":")
409 if len(netid) != 4:
410 continue
411
412 filter_dict["name"] = network.get_name()
413 filter_dict["id"] = netid[3]
414 filter_dict["shared"] = network.get_IsShared()
415 filter_dict["tenant_id"] = vdc_uuid
416 if network.get_status() == 1:
417 filter_dict["admin_state_up"] = True
418 else:
419 filter_dict["admin_state_up"] = False
420 filter_dict["status"] = "ACTIVE"
421 filter_dict["type"] = "bridge"
422 network_list.append(filter_dict)
423 self.logger.debug("get_vcd_network_list adding entry {}".format(filter_dict))
424 except:
425 self.logger.debug("Error in get_vcd_network_list")
426 self.logger.debug(traceback.format_exc())
427 pass
428
429 self.logger.debug("get_vcd_network_list returning {}".format(network_list))
430 return network_list
431
432 def get_network_list(self, filter_dict={}):
433 '''Obtain tenant networks of VIM
434 Filter_dict can be:
435 name: network name OR/AND
436 id: network uuid OR/AND
437 shared: boolean OR/AND
438 tenant_id: tenant OR/AND
439 admin_state_up: boolean
440 status: 'ACTIVE'
441
442 [{key : value , key : value}]
443
444 Returns the network list of dictionaries:
445 [{<the fields at Filter_dict plus some VIM specific>}, ...]
446 List can be empty
447 '''
448
449 vca = self.connect()
450 if not vca:
451 raise vimconn.vimconnConnectionException("self.connect() is failed")
452
453 vdc = vca.get_vdc(self.tenant_name)
454 vdcid = vdc.get_id().split(":")[3]
455
456 networks = vca.get_networks(vdc.get_name())
457 network_list = []
458
459 try:
460 for network in networks:
461 filter_entry = {}
462 net_uuid = network.get_id().split(":")
463 if len(net_uuid) != 4:
464 continue
465 else:
466 net_uuid = net_uuid[3]
467 # create dict entry
468 self.logger.debug("Adding {} to a list vcd id {} network {}".format(net_uuid,
469 vdcid,
470 network.get_name()))
471 filter_entry["name"] = network.get_name()
472 filter_entry["id"] = net_uuid
473 filter_entry["shared"] = network.get_IsShared()
474 filter_entry["tenant_id"] = vdcid
475 if network.get_status() == 1:
476 filter_entry["admin_state_up"] = True
477 else:
478 filter_entry["admin_state_up"] = False
479 filter_entry["status"] = "ACTIVE"
480 filter_entry["type"] = "bridge"
481 filtered_entry = filter_entry.copy()
482
483 # we remove all the key : value we dont' care and match only
484 # respected field
485 filtered_dict = set(filter_entry.keys()) - set(filter_dict)
486 for unwanted_key in filtered_dict: del filter_entry[unwanted_key]
487 if filter_dict == filter_entry:
488 network_list.append(filtered_entry)
489 except:
490 self.logger.debug("Error in get_vcd_network_list")
491 self.logger.debug(traceback.format_exc())
492
493 self.logger.debug("Returning {}".format(network_list))
494 return network_list
495
496 def get_network(self, net_id):
497 """Method bbtain network details of net_id VIM network
498 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]"""
499
500 vca = self.connect()
501 if not vca:
502 raise vimconn.vimconnConnectionException("self.connect() is failed")
503
504 vdc = vca.get_vdc(self.tenant_name)
505 vdc_id = vdc.get_id().split(":")[3]
506
507 networks = vca.get_networks(vdc.get_name())
508 filter_dict = {}
509
510 try:
511 for network in networks:
512 vdc_network_id = network.get_id().split(":")
513 if len(vdc_network_id) == 4 and vdc_network_id[3] == net_id:
514 filter_dict["name"] = network.get_name()
515 filter_dict["id"] = vdc_network_id[3]
516 filter_dict["shared"] = network.get_IsShared()
517 filter_dict["tenant_id"] = vdc_id
518 if network.get_status() == 1:
519 filter_dict["admin_state_up"] = True
520 else:
521 filter_dict["admin_state_up"] = False
522 filter_dict["status"] = "ACTIVE"
523 filter_dict["type"] = "bridge"
524 self.logger.debug("Returning {}".format(filter_dict))
525 return filter_dict
526 except:
527 self.logger.debug("Error in get_network")
528 self.logger.debug(traceback.format_exc())
529
530 return filter_dict
531
532 def delete_network(self, net_id):
533 """
534 Method Deletes a tenant network from VIM, provide the network id.
535
536 Returns the network identifier or raise an exception
537 """
538
539 vca = self.connect()
540 if not vca:
541 raise vimconn.vimconnConnectionException("self.connect() for tenant {} is failed".format(self.tenant_name))
542
543 if self.delete_network_action(net_id):
544 return net_id
545 else:
546 raise vimconn.vimconnNotFoundException("Network {} not found".format(net_id))
547
548 def refresh_nets_status(self, net_list):
549 """Get the status of the networks
550 Params: the list of network identifiers
551 Returns a dictionary with:
552 net_id: #VIM id of this network
553 status: #Mandatory. Text with one of:
554 # DELETED (not found at vim)
555 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
556 # OTHER (Vim reported other status not understood)
557 # ERROR (VIM indicates an ERROR status)
558 # ACTIVE, INACTIVE, DOWN (admin down),
559 # BUILD (on building process)
560 #
561 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
562 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
563
564 """
565
566 # for net in net_list:
567 # net['status']
568 # net['error_msg']
569 # net['vim_info']
570
571 # vim vimcon failed == ERROR
572 vca = self.connect()
573 if not vca:
574 raise vimconn.vimconnConnectionException("self.connect() is failed")
575
576 dict_entry = {}
577 try:
578 for net in net_list:
579 status = ''
580 errormsg = ''
581 vcd_network = self.get_vcd_network(network_uuid=net)
582 if vcd_network is not None:
583 if vcd_network['status'] == 1:
584 status = 'ACTIVE'
585 else:
586 status = 'DOWN'
587 else:
588 status = 'DELETED'
589 errormsg = 'network not found'
590 dict_entry['net'] = {'status': status, 'error_msg': errormsg,
591 'vm_info': yaml.safe_dump(vcd_network)}
592 except:
593 self.logger.debug("Error in refresh_nets_status")
594 self.logger.debug(traceback.format_exc())
595
596 return dict_entry
597
598 def get_flavor(self, flavor_id):
599 """Obtain flavor details from the VIM
600 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
601 """
602 return flavorlist[flavor_id]
603
604 def new_flavor(self, flavor_data):
605 """Adds a tenant flavor to VIM
606 flavor_data contains a dictionary with information, keys:
607 name: flavor name
608 ram: memory (cloud type) in MBytes
609 vpcus: cpus (cloud type)
610 extended: EPA parameters
611 - numas: #items requested in same NUMA
612 memory: number of 1G huge pages memory
613 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
614 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
615 - name: interface name
616 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
617 bandwidth: X Gbps; requested guarantee bandwidth
618 vpci: requested virtual PCI address
619 disk: disk size
620 is_public:
621
622
623
624 #TODO to concrete
625 Returns the flavor identifier"""
626
627 # generate a new uuid put to internal dict and return it.
628 flavor_id = uuid.uuid4()
629 flavorlist[str(flavor_id)] = flavor_data
630
631 return str(flavor_id)
632
633 def delete_flavor(self, flavor_id):
634 """Deletes a tenant flavor from VIM identify by its id
635
636 Returns the used id or raise an exception
637 """
638
639 # if key not present it will raise KeyError
640 # TODO check do I need raise any specific exception
641 flavorlist.pop(flavor_id, None)
642 return flavor_id
643
644 def new_image(self, image_dict):
645 '''
646 Adds a tenant image to VIM
647 Returns:
648 200, image-id if the image is created
649 <0, message if there is an error
650 '''
651
652 return self.get_image_id_from_path(image_dict['location'])
653
654 def delete_image(self, image_id):
655 '''Deletes a tenant image from VIM'''
656 '''Returns the HTTP response code and a message indicating details of the success or fail'''
657 raise vimconn.vimconnNotImplemented("Should have implemented this")
658
659 def catalog_exists(self, catalog_name, catalogs):
660 for catalog in catalogs:
661 if catalog.name == catalog_name:
662 return True
663 return False
664
665 def create_vimcatalog(self, vca, catalog_name):
666 """Create Catalog entry in VIM"""
667 task = vca.create_catalog(catalog_name, catalog_name)
668 result = vca.block_until_completed(task)
669 if not result:
670 return False
671 catalogs = vca.get_catalogs()
672 return self.catalog_exists(catalog_name, catalogs)
673
674 def upload_ovf(self, vca, catalog_name, item_name, media_file_name, description='', display_progress=False,
675 chunk_bytes=128 * 1024):
676 """
677 Uploads a OVF file to a vCloud catalog
678
679 :param catalog_name: (str): The name of the catalog to upload the media.
680 :param item_name: (str): The name of the media file in the catalog.
681 :param media_file_name: (str): The name of the local media file to upload.
682 :return: (bool) True if the media file was successfully uploaded, false otherwise.
683 """
684 os.path.isfile(media_file_name)
685 statinfo = os.stat(media_file_name)
686 statinfo.st_size
687
688 # find a catalog entry where we upload OVF.
689 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
690 # status change.
691 # if VCD can parse OVF we upload VMDK file
692 for catalog in vca.get_catalogs():
693 if catalog_name != catalog.name:
694 continue
695 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
696 link.get_rel() == 'add', catalog.get_Link())
697 assert len(link) == 1
698 data = """
699 <UploadVAppTemplateParams name="%s Template" xmlns="http://www.vmware.com/vcloud/v1.5" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1"><Description>%s vApp Template</Description></UploadVAppTemplateParams>
700 """ % (escape(item_name), escape(description))
701 headers = vca.vcloud_session.get_vcloud_headers()
702 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
703 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
704 if response.status_code == requests.codes.created:
705 catalogItem = XmlElementTree.fromstring(response.content)
706 entity = [child for child in catalogItem if
707 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
708 href = entity.get('href')
709 template = href
710 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
711 verify=vca.verify, logger=self.logger)
712
713 if response.status_code == requests.codes.ok:
714 media = mediaType.parseString(response.content, True)
715 link = \
716 filter(lambda link: link.get_rel() == 'upload:default',
717 media.get_Files().get_File()[0].get_Link())[
718 0]
719 headers = vca.vcloud_session.get_vcloud_headers()
720 headers['Content-Type'] = 'Content-Type text/xml'
721 response = Http.put(link.get_href(), data=open(media_file_name, 'rb'), headers=headers,
722 verify=vca.verify, logger=self.logger)
723 if response.status_code != requests.codes.ok:
724 self.logger.debug(
725 "Failed create vApp template for catalog name {} and image {}".format(catalog_name,
726 media_file_name))
727 return False
728
729 # TODO fix this with aync block
730 time.sleep(5)
731
732 self.logger.debug("Failed create vApp template for catalog name {} and image {}".
733 format(catalog_name, media_file_name))
734
735 # uploading VMDK file
736 # check status of OVF upload
737 response = Http.get(template, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
738 logger=self.logger)
739 if response.status_code == requests.codes.ok:
740 media = mediaType.parseString(response.content, True)
741 link = \
742 filter(lambda link: link.get_rel() == 'upload:default',
743 media.get_Files().get_File()[0].get_Link())[
744 0]
745
746 # The OVF file and VMDK must be in a same directory
747 head, tail = os.path.split(media_file_name)
748 filevmdk = head + '/' + os.path.basename(link.get_href())
749
750 os.path.isfile(filevmdk)
751 statinfo = os.stat(filevmdk)
752
753 # TODO debug output remove it
754 # print media.get_Files().get_File()[0].get_Link()[0].get_href()
755 # print media.get_Files().get_File()[1].get_Link()[0].get_href()
756 # print link.get_href()
757
758 # in case first element is pointer to OVF.
759 hrefvmdk = link.get_href().replace("descriptor.ovf", "Cirros-disk1.vmdk")
760
761 f = open(filevmdk, 'rb')
762 bytes_transferred = 0
763 while bytes_transferred < statinfo.st_size:
764 my_bytes = f.read(chunk_bytes)
765 if len(my_bytes) <= chunk_bytes:
766 headers = vca.vcloud_session.get_vcloud_headers()
767 headers['Content-Range'] = 'bytes %s-%s/%s' % (
768 bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
769 headers['Content-Length'] = str(len(my_bytes))
770 response = Http.put(hrefvmdk,
771 headers=headers,
772 data=my_bytes,
773 verify=vca.verify,
774 logger=None)
775 if response.status_code == requests.codes.ok:
776 bytes_transferred += len(my_bytes)
777 self.logger.debug('transferred %s of %s bytes' % (str(bytes_transferred),
778 str(statinfo.st_size)))
779 else:
780 self.logger.debug('file upload failed with error: [%s] %s' % (response.status_code,
781 response.content))
782 return False
783 f.close()
784 return True
785 else:
786 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
787 format(catalog_name, media_file_name))
788 return False
789
790 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
791 return False
792
793 def upload_vimimage(self, vca, catalog_name, media_name, medial_file_name):
794 """Upload media file"""
795 # TODO add named parameters for readbility
796 return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True)
797
798 def get_catalogid(self, catalog_name, catalogs):
799 for catalog in catalogs:
800 if catalog.name == catalog_name:
801 catalog_id = catalog.get_id().split(":")
802 return catalog_id[3]
803 return None
804
805 def get_catalogbyid(self, catalog_id, catalogs):
806 for catalog in catalogs:
807 catalogid = catalog.get_id().split(":")[3]
808 if catalogid == catalog_id:
809 return catalog.name
810 return None
811
812 def get_image_id_from_path(self, path):
813 '''Get the image id from image path in the VIM database'''
814 '''Returns:
815 0,"Image not found" if there are no images with that path
816 1,image-id if there is one image with that path
817 <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.)
818 '''
819
820 vca = self.connect()
821 if not vca:
822 raise vimconn.vimconnConnectionException("self.connect() is failed")
823
824 self.logger.debug("get_image_id_from_path path {}".format(path))
825
826 dirpath, filename = os.path.split(path)
827 flname, file_extension = os.path.splitext(path)
828 if file_extension != '.ovf':
829 self.logger.debug("Wrong file extension {}".format(file_extension))
830 return -1, "Wrong container. vCloud director supports only OVF."
831 catalog_name = os.path.splitext(filename)[0]
832
833 self.logger.debug("File name {} Catalog Name {} file path {}".format(filename, catalog_name, path))
834 self.logger.debug("Catalog name {}".format(catalog_name))
835
836 catalogs = vca.get_catalogs()
837 if len(catalogs) == 0:
838 self.logger.info("Creating new catalog entry {} in vcloud director".format(catalog_name))
839 result = self.create_vimcatalog(vca, catalog_name)
840 if not result:
841 return -1, "Failed create new catalog {} ".format(catalog_name)
842 result = self.upload_vimimage(vca, catalog_name, filename, path)
843 if not result:
844 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
845 return self.get_catalogid(catalog_name, vca.get_catalogs())
846 else:
847 for catalog in catalogs:
848 # search for existing catalog if we find same name we return ID
849 # TODO optimize this
850 if catalog.name == catalog_name:
851 self.logger.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name,
852 self.get_catalogid(
853 catalog_name,
854 catalogs)))
855 return self.get_catalogid(catalog_name, vca.get_catalogs())
856
857 # if we didn't find existing catalog we create a new one.
858 self.logger.debug("Creating new catalog entry".format(catalog_name))
859 result = self.create_vimcatalog(vca, catalog_name)
860 if not result:
861 return -1, "Failed create new catalog {} ".format(catalog_name)
862 result = self.upload_vimimage(vca, catalog_name, filename, path)
863 if not result:
864 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
865
866 return self.get_catalogid(catalog_name, vca.get_catalogs())
867
868 def get_vappid(self, vdc=None, vapp_name=None):
869 """ Method takes vdc object and vApp name and returns vapp uuid or None
870
871 Args:
872 vca: Connector to VCA
873 vdc: The VDC object.
874 vapp_name: is application vappp name identifier
875
876 Returns:
877 The return vApp name otherwise None
878 """
879
880 """ Take vdc object and vApp name and returns vapp uuid or None
881 """
882
883 if vdc is None or vapp_name is None:
884 return None
885 # UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
886 try:
887 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
888 vdc.ResourceEntities.ResourceEntity)
889 if len(refs) == 1:
890 return refs[0].href.split("vapp")[1][1:]
891 except Exception as e:
892 self.logger.exception(e)
893 return False
894 return None
895
896 def check_vapp(self, vdc, vapp_id):
897 """ Take VDC object and vApp ID and return True if given ID in vCloud director
898 otherwise return False
899 """
900
901 """ Method Method returns vApp name from vCD and lookup done by vapp_id.
902
903 Args:
904 vca: Connector to VCA
905 vdc: The VDC object.
906 vappid: vappid is application identifier
907
908 Returns:
909 The return vApp name otherwise None
910 """
911 try:
912 refs = filter(lambda ref:
913 ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
914 vdc.ResourceEntities.ResourceEntity)
915 for ref in refs:
916 vappid = ref.href.split("vapp")[1][1:]
917 # find vapp with respected vapp uuid
918 if vappid == vapp_id:
919 return True
920 except Exception as e:
921 self.logger.exception(e)
922 return False
923 return False
924
925 def get_namebyvappid(self, vca, vdc, vapp_id):
926 """Method returns vApp name from vCD and lookup done by vapp_id.
927
928 Args:
929 vca: Connector to VCA
930 vdc: The VDC object.
931 vapp_id: vappid is application identifier
932
933 Returns:
934 The return vApp name otherwise None
935 """
936
937 try:
938 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
939 vdc.ResourceEntities.ResourceEntity)
940 for ref in refs:
941 # we care only about UUID the rest doesn't matter
942 vappid = ref.href.split("vapp")[1][1:]
943 if vappid == vapp_id:
944 response = Http.get(ref.href, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify,
945 logger=self.logger)
946 tree = XmlElementTree.fromstring(response.content)
947 return tree.attrib['name']
948 except Exception as e:
949 self.logger.exception(e)
950 return None
951 return None
952
953 def new_vminstance(self, name, description, start, image_id, flavor_id, net_list, cloud_config=None):
954 """Adds a VM instance to VIM
955 Params:
956 start: indicates if VM must start or boot in pause mode. Ignored
957 image_id,flavor_id: image and flavor uuid
958 net_list: list of interfaces, each one is a dictionary with:
959 name:
960 net_id: network uuid to connect
961 vpci: virtual vcpi to assign
962 model: interface model, virtio, e2000, ...
963 mac_address:
964 use: 'data', 'bridge', 'mgmt'
965 type: 'virtual', 'PF', 'VF', 'VFnotShared'
966 vim_id: filled/added by this function
967 cloud_config: can be a text script to be passed directly to cloud-init,
968 or an object to inject users and ssh keys with format:
969 key-pairs: [] list of keys to install to the default user
970 users: [{ name, key-pairs: []}] list of users to add with their key-pair
971 #TODO ip, security groups
972 Returns >=0, the instance identifier
973 <0, error_text
974 """
975
976 self.logger.info("Creating new instance for entry".format(name))
977 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
978 format(description, start, image_id, flavor_id, net_list, cloud_config))
979 vca = self.connect()
980 if not vca:
981 raise vimconn.vimconnConnectionException("self.connect() is failed.")
982
983 # if vm already deployed we return existing uuid
984 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
985 if vapp_uuid is not None:
986 return vapp_uuid
987
988 # we check for presence of VDC, Catalog entry and Flavor.
989 vdc = vca.get_vdc(self.tenant_name)
990 if vdc is None:
991 raise vimconn.vimconnUnexpectedResponse(
992 "new_vminstance(): Failed create vApp {}: (Failed reprieve VDC information)".format(name))
993 catalogs = vca.get_catalogs()
994 if catalogs is None:
995 raise vimconn.vimconnUnexpectedResponse(
996 "new_vminstance(): Failed create vApp {}: Failed create vApp {}: (Failed reprieve Catalog information)".format(
997 name))
998
999 flavor = flavorlist[flavor_id]
1000 if catalogs is None:
1001 raise vimconn.vimconnUnexpectedResponse(
1002 "new_vminstance(): Failed create vApp {}: (Failed reprieve Flavor information)".format(name))
1003
1004 # image upload creates template name as catalog name space Template.
1005 templateName = self.get_catalogbyid(image_id, catalogs) + ' Template'
1006 power_on = 'false'
1007 if start:
1008 power_on = 'true'
1009
1010 # client must provide at least one entry in net_list if not we report error
1011 primary_net = net_list[0]
1012 if primary_net is None:
1013 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed network list is empty.".format(name))
1014
1015 primary_net_id = primary_net['net_id']
1016 primary_net_name = self.get_network_name_by_id(primary_net_id)
1017 network_mode = primary_net['use']
1018
1019 # use: 'data', 'bridge', 'mgmt'
1020 # create vApp. Set vcpu and ram based on flavor id.
1021 vapptask = vca.create_vapp(self.tenant_name, name, templateName,
1022 self.get_catalogbyid(image_id, catalogs),
1023 network_name=primary_net_name,
1024 network_mode='bridged',
1025 vm_name=name,
1026 vm_cpus=flavor['vcpus'],
1027 vm_memory=flavor['ram'])
1028
1029 if vapptask is None or vapptask is False:
1030 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): failed deploy vApp {}".format(name))
1031 if type(vapptask) is VappTask:
1032 vca.block_until_completed(vapptask)
1033
1034 # we should have now vapp in undeployed state.
1035 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), name)
1036 if vapp is None:
1037 raise vimconn.vimconnUnexpectedResponse(
1038 "new_vminstance(): Failed failed retrieve vApp {} after we deployed".format(name))
1039
1040 # add first NIC
1041 try:
1042 nicIndex = 0
1043 for net in net_list:
1044 # openmano uses network id in UUID format.
1045 # vCloud Director need a name so we do reverse operation from provided UUID we lookup a name
1046 interface_net_id = net['net_id']
1047 interface_net_name = self.get_network_name_by_id(interface_net_id)
1048 interface_network_mode = net['use']
1049
1050 if primary_net_name is not None:
1051 nets = filter(lambda n: n.name == interface_net_name, vca.get_networks(self.tenant_name))
1052 if len(nets) == 1:
1053 task = vapp.connect_to_network(nets[0].name, nets[0].href)
1054 if type(task) is GenericTask:
1055 vca.block_until_completed(task)
1056 # connect network to VM
1057 # TODO figure out mapping between openmano representation to vCloud director.
1058 # one idea use first nic as managment DHCP all remaining in bridge mode
1059 task = vapp.connect_vms(nets[0].name, connection_index=nicIndex,
1060 connections_primary_index=nicIndex,
1061 ip_allocation_mode='DHCP')
1062 if type(task) is GenericTask:
1063 vca.block_until_completed(task)
1064 nicIndex += 1
1065 except KeyError:
1066 # it might be a case if specific mandatory entry in dict is empty
1067 self.logger.debug("Key error {}".format(KeyError.message))
1068 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1069
1070 # deploy and power on vm
1071 task = vapp.poweron()
1072 if type(task) is TaskType:
1073 vca.block_until_completed(task)
1074 deploytask = vapp.deploy(powerOn='True')
1075 if type(task) is TaskType:
1076 vca.block_until_completed(deploytask)
1077
1078 # check if vApp deployed and if that the case return vApp UUID otherwise -1
1079 vapp_uuid = self.get_vappid(vca.get_vdc(self.tenant_name), name)
1080 if vapp_uuid is not None:
1081 return vapp_uuid
1082 else:
1083 raise vimconn.vimconnUnexpectedResponse("new_vminstance(): Failed create new vm instance {}".format(name))
1084
1085 ##
1086 ##
1087 ## based on current discussion
1088 ##
1089 ##
1090 ## server:
1091 # created: '2016-09-08T11:51:58'
1092 # description: simple-instance.linux1.1
1093 # flavor: ddc6776e-75a9-11e6-ad5f-0800273e724c
1094 # hostId: e836c036-74e7-11e6-b249-0800273e724c
1095 # image: dde30fe6-75a9-11e6-ad5f-0800273e724c
1096 # status: ACTIVE
1097 # error_msg:
1098 # interfaces: …
1099 #
1100 def get_vminstance(self, vim_vm_uuid):
1101 '''Returns the VM instance information from VIM'''
1102
1103 self.logger.debug("Client requesting vm instance {} ".format(vim_vm_uuid))
1104 vca = self.connect()
1105 if not vca:
1106 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1107
1108 vdc = vca.get_vdc(self.tenant_name)
1109 if vdc is None:
1110 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1111
1112 vm_name = self.get_namebyvappid(vca, vdc, vim_vm_uuid)
1113 if vm_name is None:
1114 self.logger.debug("get_vminstance(): Failed to get vApp name by UUID {}".format(vim_vm_uuid))
1115 return None, "Failed to get vApp name by UUID {}".format(vim_vm_uuid)
1116
1117 the_vapp = vca.get_vapp(vdc, vm_name)
1118 vm_info = the_vapp.get_vms_details()
1119
1120 vm_dict = {'description': vm_info[0]['name'], 'status': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1121 'error_msg': vcdStatusCode2manoFormat[the_vapp.me.get_status()],
1122 'vim_info': yaml.safe_dump(the_vapp.get_vms_details()), 'interfaces': []}
1123
1124 # get networks
1125 vm_app_networks = the_vapp.get_vms_network_info()
1126
1127 interfaces = []
1128 try:
1129 org_network_dict = self.get_org(self.org_uuid)['networks']
1130 for vapp_network in vm_app_networks:
1131 for vm_network in vapp_network:
1132 if vm_network['name'] == vm_name:
1133 interface = {}
1134 # interface['vim_info'] = yaml.safe_dump(vm_network)
1135 interface["mac_address"] = vm_network['mac']
1136 for net_uuid in org_network_dict:
1137 if org_network_dict[net_uuid] == vm_network['network_name']:
1138 interface["vim_net_id"] = net_uuid
1139 interface["vim_interface_id"] = vm_network['network_name']
1140 interface['ip_address'] = vm_network['ip']
1141 interfaces.append(interface)
1142 except KeyError:
1143 self.logger.debug("Error in respond {}".format(KeyError.message))
1144 self.logger.debug(traceback.format_exc())
1145
1146 vm_dict['interfaces'] = interfaces
1147
1148 return vm_dict
1149
1150 def delete_vminstance(self, vm__vim_uuid):
1151 """Method poweroff and remove VM instance from vcloud director network.
1152
1153 Args:
1154 vm__vim_uuid: VM UUID
1155
1156 Returns:
1157 Returns the instance identifier
1158 """
1159
1160 self.logger.debug("Client requesting delete vm instance {} ".format(vm__vim_uuid))
1161 vca = self.connect()
1162 if not vca:
1163 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1164
1165 vdc = vca.get_vdc(self.tenant_name)
1166 if vdc is None:
1167 self.logger.debug("delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(
1168 self.tenant_name))
1169 raise vimconn.vimconnException(
1170 "delete_vminstance(): Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1171
1172 try:
1173 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1174 if vapp_name is None:
1175 self.logger.debug("delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1176 return -1, "delete_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid)
1177 else:
1178 self.logger.info("Deleting vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1179
1180 # Delete vApp and wait for status change if task executed and vApp is None.
1181 # We successfully delete vApp from vCloud
1182 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1183 # poweroff vapp / undeploy and delete
1184 power_off_task = vapp.poweroff()
1185 if type(power_off_task) is GenericTask:
1186 vca.block_until_completed(power_off_task)
1187 else:
1188 if not power_off_task:
1189 self.logger.debug("delete_vminstance(): Failed power off VM uuid {} ".format(vm__vim_uuid))
1190
1191 # refresh status
1192 if vapp.me.deployed:
1193 undeploy_task = vapp.undeploy()
1194 if type(undeploy_task) is GenericTask:
1195 retry = 0
1196 while retry <= DELETE_INSTANCE_RETRY:
1197 result = vca.block_until_completed(undeploy_task)
1198 if result:
1199 break
1200 retry += 1
1201 else:
1202 return -1
1203
1204 # delete vapp
1205 vapp = vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name)
1206 if vapp is not None:
1207 delete_task = vapp.delete()
1208 retry = 0
1209 while retry <= DELETE_INSTANCE_RETRY:
1210 task = vapp.delete()
1211 if type(task) is GenericTask:
1212 vca.block_until_completed(delete_task)
1213 if not delete_task:
1214 self.logger.debug("delete_vminstance(): Failed delete uuid {} ".format(vm__vim_uuid))
1215 retry += 1
1216
1217 except:
1218 self.logger.debug(traceback.format_exc())
1219 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1220
1221 if vca.get_vapp(vca.get_vdc(self.tenant_name), vapp_name) is None:
1222 return vm__vim_uuid
1223 else:
1224 raise vimconn.vimconnException("delete_vminstance(): Failed delete vm instance {}".format(vm__vim_uuid))
1225
1226 def refresh_vms_status(self, vm_list):
1227 """Get the status of the virtual machines and their interfaces/ports
1228 Params: the list of VM identifiers
1229 Returns a dictionary with:
1230 vm_id: #VIM id of this Virtual Machine
1231 status: #Mandatory. Text with one of:
1232 # DELETED (not found at vim)
1233 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
1234 # OTHER (Vim reported other status not understood)
1235 # ERROR (VIM indicates an ERROR status)
1236 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
1237 # CREATING (on building process), ERROR
1238 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
1239 #
1240 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
1241 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1242 interfaces:
1243 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
1244 mac_address: #Text format XX:XX:XX:XX:XX:XX
1245 vim_net_id: #network id where this interface is connected
1246 vim_interface_id: #interface/port VIM id
1247 ip_address: #null, or text with IPv4, IPv6 address
1248 """
1249
1250 self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
1251 vca = self.connect()
1252 if not vca:
1253 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1254
1255 vdc = vca.get_vdc(self.tenant_name)
1256 if vdc is None:
1257 raise vimconn.vimconnException("Failed to get a reference of VDC for a tenant {}".format(self.tenant_name))
1258
1259 vms_dict = {}
1260 for vmuuid in vm_list:
1261 vmname = self.get_namebyvappid(vca, vdc, vmuuid)
1262 if vmname is not None:
1263
1264 the_vapp = vca.get_vapp(vdc, vmname)
1265 vm_info = the_vapp.get_vms_details()
1266 vm_status = vm_info[0]['status']
1267
1268 vm_dict = {'status': None, 'error_msg': None, 'vim_info': None, 'interfaces': []}
1269 vm_dict['status'] = vcdStatusCode2manoFormat[the_vapp.me.get_status()]
1270 vm_dict['error_msg'] = vcdStatusCode2manoFormat[the_vapp.me.get_status()]
1271 vm_dict['vim_info'] = yaml.safe_dump(the_vapp.get_vms_details())
1272
1273 # get networks
1274 try:
1275 vm_app_networks = the_vapp.get_vms_network_info()
1276 for vapp_network in vm_app_networks:
1277 for vm_network in vapp_network:
1278 if vm_network['name'] == vmname:
1279 interface = {}
1280 # interface['vim_info'] = yaml.safe_dump(vm_network)
1281 interface["mac_address"] = vm_network['mac']
1282 interface["vim_net_id"] = self.get_network_name_by_id(vm_network['network_name'])
1283 interface["vim_interface_id"] = vm_network['network_name']
1284 interface['ip_address'] = vm_network['ip']
1285 vm_dict["interfaces"].append(interface)
1286 # add a vm to vm dict
1287 vms_dict.setdefault(vmuuid, vm_dict)
1288 except KeyError:
1289 self.logger.debug("Error in respond {}".format(KeyError.message))
1290 self.logger.debug(traceback.format_exc())
1291
1292 return vms_dict
1293
1294 def action_vminstance(self, vm__vim_uuid=None, action_dict=None):
1295 """Send and action over a VM instance from VIM
1296 Returns the vm_id if the action was successfully sent to the VIM"""
1297
1298 self.logger.debug("Received action for vm {} and action dict {}".format(vm__vim_uuid, action_dict))
1299 if vm__vim_uuid is None or action_dict is None:
1300 raise vimconn.vimconnException("Invalid request. VM id or action is None.")
1301
1302 vca = self.connect()
1303 if not vca:
1304 raise vimconn.vimconnConnectionException("self.connect() is failed.")
1305
1306 vdc = vca.get_vdc(self.tenant_name)
1307 if vdc is None:
1308 return -1, "Failed to get a reference of VDC for a tenant {}".format(self.tenant_name)
1309
1310 vapp_name = self.get_namebyvappid(vca, vdc, vm__vim_uuid)
1311 if vapp_name is None:
1312 self.logger.debug("action_vminstance(): Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1313 raise vimconn.vimconnException("Failed to get vm by given {} vm uuid".format(vm__vim_uuid))
1314 else:
1315 self.logger.info("Action_vminstance vApp {} and UUID {}".format(vapp_name, vm__vim_uuid))
1316
1317 try:
1318 the_vapp = vca.get_vapp(vdc, vapp_name)
1319 # TODO fix all status
1320 if "start" in action_dict:
1321 if action_dict["start"] == "rebuild":
1322 the_vapp.deploy(powerOn=True)
1323 else:
1324 vm_info = the_vapp.get_vms_details()
1325 vm_status = vm_info[0]['status']
1326 if vm_status == "Suspended":
1327 the_vapp.poweron()
1328 elif vm_status.status == "Powered off":
1329 the_vapp.poweron()
1330 elif "pause" in action_dict:
1331 pass
1332 ##server.pause()
1333 elif "resume" in action_dict:
1334 pass
1335 ##server.resume()
1336 elif "shutoff" in action_dict or "shutdown" in action_dict:
1337 the_vapp.shutdown()
1338 elif "forceOff" in action_dict:
1339 the_vapp.reset()
1340 elif "terminate" in action_dict:
1341 the_vapp.delete()
1342 # elif "createImage" in action_dict:
1343 # server.create_image()
1344 else:
1345 pass
1346 except:
1347 pass
1348
1349 def get_vminstance_console(self, vm_id, console_type="vnc"):
1350 """
1351 Get a console for the virtual machine
1352 Params:
1353 vm_id: uuid of the VM
1354 console_type, can be:
1355 "novnc" (by default), "xvpvnc" for VNC types,
1356 "rdp-html5" for RDP types, "spice-html5" for SPICE types
1357 Returns dict with the console parameters:
1358 protocol: ssh, ftp, http, https, ...
1359 server: usually ip address
1360 port: the http, ssh, ... port
1361 suffix: extra text, e.g. the http path and query string
1362 """
1363 raise vimconn.vimconnNotImplemented("Should have implemented this")
1364
1365 # NOT USED METHODS in current version
1366
1367 def host_vim2gui(self, host, server_dict):
1368 '''Transform host dictionary from VIM format to GUI format,
1369 and append to the server_dict
1370 '''
1371 raise vimconn.vimconnNotImplemented("Should have implemented this")
1372
1373 def get_hosts_info(self):
1374 '''Get the information of deployed hosts
1375 Returns the hosts content'''
1376 raise vimconn.vimconnNotImplemented("Should have implemented this")
1377
1378 def get_hosts(self, vim_tenant):
1379 '''Get the hosts and deployed instances
1380 Returns the hosts content'''
1381 raise vimconn.vimconnNotImplemented("Should have implemented this")
1382
1383 def get_processor_rankings(self):
1384 '''Get the processor rankings in the VIM database'''
1385 raise vimconn.vimconnNotImplemented("Should have implemented this")
1386
1387 def new_host(self, host_data):
1388 '''Adds a new host to VIM'''
1389 '''Returns status code of the VIM response'''
1390 raise vimconn.vimconnNotImplemented("Should have implemented this")
1391
1392 def new_external_port(self, port_data):
1393 '''Adds a external port to VIM'''
1394 '''Returns the port identifier'''
1395 raise vimconn.vimconnNotImplemented("Should have implemented this")
1396
1397 def new_external_network(self, net_name, net_type):
1398 '''Adds a external network to VIM (shared)'''
1399 '''Returns the network identifier'''
1400 raise vimconn.vimconnNotImplemented("Should have implemented this")
1401
1402 def connect_port_network(self, port_id, network_id, admin=False):
1403 '''Connects a external port to a network'''
1404 '''Returns status code of the VIM response'''
1405 raise vimconn.vimconnNotImplemented("Should have implemented this")
1406
1407 def new_vminstancefromJSON(self, vm_data):
1408 '''Adds a VM instance to VIM'''
1409 '''Returns the instance identifier'''
1410 raise vimconn.vimconnNotImplemented("Should have implemented this")
1411
1412 def get_network_name_by_id(self, network_name=None):
1413 """Method gets vcloud director network named based on supplied uuid.
1414
1415 Args:
1416 network_name: network_id
1417
1418 Returns:
1419 The return network name.
1420 """
1421
1422 vca = self.connect()
1423 if not vca:
1424 raise vimconn.vimconnConnectionException("self.connect() is failed")
1425
1426 if network_name is None:
1427 return None
1428
1429 try:
1430 org_network_dict = self.get_org(self.org_uuid)['networks']
1431 for net_uuid in org_network_dict:
1432 if org_network_dict[net_uuid] == network_name:
1433 return net_uuid
1434 except:
1435 self.logger.debug("Exception in get_network_name_by_id")
1436 self.logger.debug(traceback.format_exc())
1437
1438 return None
1439
1440 def list_org_action(self):
1441 """
1442 Method leverages vCloud director and query for available organization for particular user
1443
1444 Args:
1445 vca - is active VCA connection.
1446 vdc_name - is a vdc name that will be used to query vms action
1447
1448 Returns:
1449 The return XML respond
1450 """
1451
1452 vca = self.connect()
1453 if not vca:
1454 raise vimconn.vimconnConnectionException("self.connect() is failed")
1455
1456 url_list = [vca.host, '/api/org']
1457 vm_list_rest_call = ''.join(url_list)
1458
1459 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1460 response = Http.get(url=vm_list_rest_call,
1461 headers=vca.vcloud_session.get_vcloud_headers(),
1462 verify=vca.verify,
1463 logger=vca.logger)
1464 if response.status_code == requests.codes.ok:
1465 return response.content
1466
1467 return None
1468
1469 def get_org_action(self, org_uuid=None):
1470 """
1471 Method leverages vCloud director and retrieve available object fdr organization.
1472
1473 Args:
1474 vca - is active VCA connection.
1475 vdc_name - is a vdc name that will be used to query vms action
1476
1477 Returns:
1478 The return XML respond
1479 """
1480
1481 vca = self.connect()
1482 if not vca:
1483 raise vimconn.vimconnConnectionException("self.connect() is failed")
1484
1485 if org_uuid is None:
1486 return None
1487
1488 url_list = [vca.host, '/api/org/', org_uuid]
1489 vm_list_rest_call = ''.join(url_list)
1490
1491 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1492 response = Http.get(url=vm_list_rest_call,
1493 headers=vca.vcloud_session.get_vcloud_headers(),
1494 verify=vca.verify,
1495 logger=vca.logger)
1496 if response.status_code == requests.codes.ok:
1497 return response.content
1498
1499 return None
1500
1501 def get_org(self, org_uuid=None):
1502 """
1503 Method retrieves available organization in vCloud Director
1504
1505 Args:
1506 vca - is active VCA connection.
1507 vdc_name - is a vdc name that will be used to query vms action
1508
1509 Returns:
1510 The return dictionary and key for each entry vapp UUID
1511 """
1512
1513 org_dict = {}
1514
1515 vca = self.connect()
1516 if not vca:
1517 raise vimconn.vimconnConnectionException("self.connect() is failed")
1518
1519 if org_uuid is None:
1520 return org_dict
1521
1522 content = self.get_org_action(org_uuid=org_uuid)
1523 try:
1524 vdc_list = {}
1525 network_list = {}
1526 catalog_list = {}
1527 vm_list_xmlroot = XmlElementTree.fromstring(content)
1528 for child in vm_list_xmlroot:
1529 if child.attrib['type'] == 'application/vnd.vmware.vcloud.vdc+xml':
1530 vdc_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1531 org_dict['vdcs'] = vdc_list
1532 if child.attrib['type'] == 'application/vnd.vmware.vcloud.orgNetwork+xml':
1533 network_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1534 org_dict['networks'] = network_list
1535 if child.attrib['type'] == 'application/vnd.vmware.vcloud.catalog+xml':
1536 catalog_list[child.attrib['href'].split("/")[-1:][0]] = child.attrib['name']
1537 org_dict['catalogs'] = catalog_list
1538 except:
1539 pass
1540
1541 return org_dict
1542
1543 def get_org_list(self):
1544 """
1545 Method retrieves available organization in vCloud Director
1546
1547 Args:
1548 vca - is active VCA connection.
1549
1550 Returns:
1551 The return dictionary and key for each entry VDC UUID
1552 """
1553
1554 org_dict = {}
1555 vca = self.connect()
1556 if not vca:
1557 raise vimconn.vimconnConnectionException("self.connect() is failed")
1558
1559 content = self.list_org_action()
1560 try:
1561 vm_list_xmlroot = XmlElementTree.fromstring(content)
1562 for vm_xml in vm_list_xmlroot:
1563 if vm_xml.tag.split("}")[1] == 'Org':
1564 org_uuid = vm_xml.attrib['href'].split('/')[-1:]
1565 org_dict[org_uuid[0]] = vm_xml.attrib['name']
1566 except:
1567 pass
1568
1569 return org_dict
1570
1571 def vms_view_action(self, vdc_name=None):
1572 """ Method leverages vCloud director vms query call
1573
1574 Args:
1575 vca - is active VCA connection.
1576 vdc_name - is a vdc name that will be used to query vms action
1577
1578 Returns:
1579 The return XML respond
1580 """
1581 vca = self.connect()
1582 if vdc_name is None:
1583 return None
1584
1585 url_list = [vca.host, '/api/vms/query']
1586 vm_list_rest_call = ''.join(url_list)
1587
1588 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1589 refs = filter(lambda ref: ref.name == vdc_name and ref.type_ == 'application/vnd.vmware.vcloud.vdc+xml',
1590 vca.vcloud_session.organization.Link)
1591 if len(refs) == 1:
1592 response = Http.get(url=vm_list_rest_call,
1593 headers=vca.vcloud_session.get_vcloud_headers(),
1594 verify=vca.verify,
1595 logger=vca.logger)
1596 if response.status_code == requests.codes.ok:
1597 return response.content
1598
1599 return None
1600
1601 def get_vapp_list(self, vdc_name=None):
1602 """
1603 Method retrieves vApp list deployed vCloud director and returns a dictionary
1604 contains a list of all vapp deployed for queried VDC.
1605 The key for a dictionary is vApp UUID
1606
1607
1608 Args:
1609 vca - is active VCA connection.
1610 vdc_name - is a vdc name that will be used to query vms action
1611
1612 Returns:
1613 The return dictionary and key for each entry vapp UUID
1614 """
1615
1616 vapp_dict = {}
1617 if vdc_name is None:
1618 return vapp_dict
1619
1620 content = self.vms_view_action(vdc_name=vdc_name)
1621 try:
1622 vm_list_xmlroot = XmlElementTree.fromstring(content)
1623 for vm_xml in vm_list_xmlroot:
1624 if vm_xml.tag.split("}")[1] == 'VMRecord':
1625 if vm_xml.attrib['isVAppTemplate'] == 'true':
1626 rawuuid = vm_xml.attrib['container'].split('/')[-1:]
1627 if 'vappTemplate-' in rawuuid[0]:
1628 # vm in format vappTemplate-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1629 # vm and use raw UUID as key
1630 vapp_dict[rawuuid[0][13:]] = vm_xml.attrib
1631 except:
1632 pass
1633
1634 return vapp_dict
1635
1636 def get_vm_list(self, vdc_name=None):
1637 """
1638 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1639 contains a list of all VM's deployed for queried VDC.
1640 The key for a dictionary is VM UUID
1641
1642
1643 Args:
1644 vca - is active VCA connection.
1645 vdc_name - is a vdc name that will be used to query vms action
1646
1647 Returns:
1648 The return dictionary and key for each entry vapp UUID
1649 """
1650 vm_dict = {}
1651
1652 if vdc_name is None:
1653 return vm_dict
1654
1655 content = self.vms_view_action(vdc_name=vdc_name)
1656 try:
1657 vm_list_xmlroot = XmlElementTree.fromstring(content)
1658 for vm_xml in vm_list_xmlroot:
1659 if vm_xml.tag.split("}")[1] == 'VMRecord':
1660 if vm_xml.attrib['isVAppTemplate'] == 'false':
1661 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
1662 if 'vm-' in rawuuid[0]:
1663 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1664 # vm and use raw UUID as key
1665 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
1666 except:
1667 pass
1668
1669 return vm_dict
1670
1671 def get_vapp(self, vdc_name=None, vapp_name=None, isuuid=False):
1672 """
1673 Method retrieves VM's list deployed vCloud director. It returns a dictionary
1674 contains a list of all VM's deployed for queried VDC.
1675 The key for a dictionary is VM UUID
1676
1677
1678 Args:
1679 vca - is active VCA connection.
1680 vdc_name - is a vdc name that will be used to query vms action
1681
1682 Returns:
1683 The return dictionary and key for each entry vapp UUID
1684 """
1685 vm_dict = {}
1686 vca = self.connect()
1687 if not vca:
1688 raise vimconn.vimconnConnectionException("self.connect() is failed")
1689
1690 if vdc_name is None:
1691 return vm_dict
1692
1693 content = self.vms_view_action(vdc_name=vdc_name)
1694 try:
1695 vm_list_xmlroot = XmlElementTree.fromstring(content)
1696 for vm_xml in vm_list_xmlroot:
1697 if vm_xml.tag.split("}")[1] == 'VMRecord':
1698 if isuuid:
1699 # lookup done by UUID
1700 if vapp_name in vm_xml.attrib['container']:
1701 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
1702 if 'vm-' in rawuuid[0]:
1703 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1704 # vm and use raw UUID as key
1705 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
1706 # lookup done by Name
1707 else:
1708 if vapp_name in vm_xml.attrib['name']:
1709 rawuuid = vm_xml.attrib['href'].split('/')[-1:]
1710 if 'vm-' in rawuuid[0]:
1711 vm_dict[rawuuid[0][3:]] = vm_xml.attrib
1712 # vm in format vm-e63d40e7-4ff5-4c6d-851f-96c1e4da86a5 we remove
1713 # vm and use raw UUID as key
1714 except:
1715 pass
1716
1717 return vm_dict
1718
1719 def get_network_action(self, network_uuid=None):
1720 """
1721 Method leverages vCloud director and query network based on network uuid
1722
1723 Args:
1724 vca - is active VCA connection.
1725 network_uuid - is a network uuid
1726
1727 Returns:
1728 The return XML respond
1729 """
1730
1731 vca = self.connect()
1732 if not vca:
1733 raise vimconn.vimconnConnectionException("self.connect() is failed")
1734
1735 if network_uuid is None:
1736 return None
1737
1738 url_list = [vca.host, '/api/network/', network_uuid]
1739 vm_list_rest_call = ''.join(url_list)
1740
1741 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1742 response = Http.get(url=vm_list_rest_call,
1743 headers=vca.vcloud_session.get_vcloud_headers(),
1744 verify=vca.verify,
1745 logger=vca.logger)
1746 if response.status_code == requests.codes.ok:
1747 return response.content
1748
1749 return None
1750
1751 def get_vcd_network(self, network_uuid=None):
1752 """
1753 Method retrieves available network from vCloud Director
1754
1755 Args:
1756 network_uuid - is VCD network UUID
1757
1758 Each element serialized as key : value pair
1759
1760 Following keys available for access. network_configuration['Gateway'}
1761 <Configuration>
1762 <IpScopes>
1763 <IpScope>
1764 <IsInherited>true</IsInherited>
1765 <Gateway>172.16.252.100</Gateway>
1766 <Netmask>255.255.255.0</Netmask>
1767 <Dns1>172.16.254.201</Dns1>
1768 <Dns2>172.16.254.202</Dns2>
1769 <DnsSuffix>vmwarelab.edu</DnsSuffix>
1770 <IsEnabled>true</IsEnabled>
1771 <IpRanges>
1772 <IpRange>
1773 <StartAddress>172.16.252.1</StartAddress>
1774 <EndAddress>172.16.252.99</EndAddress>
1775 </IpRange>
1776 </IpRanges>
1777 </IpScope>
1778 </IpScopes>
1779 <FenceMode>bridged</FenceMode>
1780
1781 Returns:
1782 The return dictionary and key for each entry vapp UUID
1783 """
1784
1785 network_configuration = {}
1786 if network_uuid is None:
1787 return network_uuid
1788
1789 content = self.get_network_action(network_uuid=network_uuid)
1790 try:
1791 vm_list_xmlroot = XmlElementTree.fromstring(content)
1792
1793 network_configuration['status'] = vm_list_xmlroot.get("status")
1794 network_configuration['name'] = vm_list_xmlroot.get("name")
1795 network_configuration['uuid'] = vm_list_xmlroot.get("id").split(":")[3]
1796
1797 for child in vm_list_xmlroot:
1798 if child.tag.split("}")[1] == 'IsShared':
1799 network_configuration['isShared'] = child.text.strip()
1800 if child.tag.split("}")[1] == 'Configuration':
1801 for configuration in child.iter():
1802 tagKey = configuration.tag.split("}")[1].strip()
1803 if tagKey != "":
1804 network_configuration[tagKey] = configuration.text.strip()
1805 return network_configuration
1806 except:
1807 pass
1808
1809 return network_configuration
1810
1811 def delete_network_action(self, network_uuid=None):
1812 """
1813 Method delete given network from vCloud director
1814
1815 Args:
1816 network_uuid - is a network uuid that client wish to delete
1817
1818 Returns:
1819 The return None or XML respond or false
1820 """
1821
1822 vca = self.connect_as_admin()
1823 if not vca:
1824 raise vimconn.vimconnConnectionException("self.connect() is failed")
1825 if network_uuid is None:
1826 return False
1827
1828 url_list = [vca.host, '/api/admin/network/', network_uuid]
1829 vm_list_rest_call = ''.join(url_list)
1830
1831 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1832 response = Http.delete(url=vm_list_rest_call,
1833 headers=vca.vcloud_session.get_vcloud_headers(),
1834 verify=vca.verify,
1835 logger=vca.logger)
1836
1837 if response.status_code == 202:
1838 return True
1839
1840 return False
1841
1842 def create_network(self, network_name=None, parent_network_uuid=None, isshared='true'):
1843 """
1844 Method create network in vCloud director
1845
1846 Args:
1847 network_name - is network name to be created.
1848 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1849 It optional attribute. by default if no parent network indicate the first available will be used.
1850
1851 Returns:
1852 The return network uuid or return None
1853 """
1854
1855 content = self.create_network_rest(network_name=network_name,
1856 parent_network_uuid=parent_network_uuid,
1857 isshared=isshared)
1858 if content is None:
1859 self.logger.debug("Failed create network {}.".format(network_name))
1860 return None
1861
1862 try:
1863 vm_list_xmlroot = XmlElementTree.fromstring(content)
1864 vcd_uuid = vm_list_xmlroot.get('id').split(":")
1865 if len(vcd_uuid) == 4:
1866 self.logger.info("Create new network name: {} uuid: {}".format(network_name, vcd_uuid[3]))
1867 return vcd_uuid[3]
1868 except:
1869 self.logger.debug("Failed create network {}".format(network_name))
1870 return None
1871
1872 def create_network_rest(self, network_name=None, parent_network_uuid=None, isshared='true'):
1873 """
1874 Method create network in vCloud director
1875
1876 Args:
1877 network_name - is network name to be created.
1878 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1879 It optional attribute. by default if no parent network indicate the first available will be used.
1880
1881 Returns:
1882 The return network uuid or return None
1883 """
1884
1885 vca = self.connect_as_admin()
1886 if not vca:
1887 raise vimconn.vimconnConnectionException("self.connect() is failed")
1888 if network_name is None:
1889 return None
1890
1891 url_list = [vca.host, '/api/admin/vdc/', self.tenant_id]
1892 vm_list_rest_call = ''.join(url_list)
1893 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
1894 response = Http.get(url=vm_list_rest_call,
1895 headers=vca.vcloud_session.get_vcloud_headers(),
1896 verify=vca.verify,
1897 logger=vca.logger)
1898
1899 provider_network = None
1900 available_networks = None
1901 add_vdc_rest_url = None
1902
1903 if response.status_code != requests.codes.ok:
1904 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
1905 response.status_code))
1906 return None
1907 else:
1908 try:
1909 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
1910 for child in vm_list_xmlroot:
1911 if child.tag.split("}")[1] == 'ProviderVdcReference':
1912 provider_network = child.attrib.get('href')
1913 # application/vnd.vmware.admin.providervdc+xml
1914 if child.tag.split("}")[1] == 'Link':
1915 if child.attrib.get('type') == 'application/vnd.vmware.vcloud.orgVdcNetwork+xml' \
1916 and child.attrib.get('rel') == 'add':
1917 add_vdc_rest_url = child.attrib.get('href')
1918 except:
1919 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
1920 self.logger.debug("Respond body {}".format(response.content))
1921 return None
1922
1923 # find pvdc provided available network
1924 response = Http.get(url=provider_network,
1925 headers=vca.vcloud_session.get_vcloud_headers(),
1926 verify=vca.verify,
1927 logger=vca.logger)
1928 if response.status_code != requests.codes.ok:
1929 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
1930 response.status_code))
1931 return None
1932
1933 # available_networks.split("/")[-1]
1934
1935 if parent_network_uuid is None:
1936 try:
1937 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
1938 for child in vm_list_xmlroot.iter():
1939 if child.tag.split("}")[1] == 'AvailableNetworks':
1940 for networks in child.iter():
1941 # application/vnd.vmware.admin.network+xml
1942 if networks.attrib.get('href') is not None:
1943 available_networks = networks.attrib.get('href')
1944 break
1945 except:
1946 return None
1947
1948 # either use client provided UUID or search for a first available
1949 # if both are not defined we return none
1950 if parent_network_uuid is not None:
1951 url_list = [vca.host, '/api/admin/network/', parent_network_uuid]
1952 add_vdc_rest_url = ''.join(url_list)
1953
1954 # return response.content
1955 data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
1956 <Description>Openmano created</Description>
1957 <Configuration>
1958 <ParentNetwork href="{1:s}"/>
1959 <FenceMode>{2:s}</FenceMode>
1960 </Configuration>
1961 <IsShared>{3:s}</IsShared>
1962 </OrgVdcNetwork> """.format(escape(network_name), available_networks, "bridged", isshared)
1963
1964 headers = vca.vcloud_session.get_vcloud_headers()
1965 headers['Content-Type'] = 'application/vnd.vmware.vcloud.orgVdcNetwork+xml'
1966 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify, logger=vca.logger)
1967
1968 # if we all ok we respond with content otherwise by default None
1969 if response.status_code == 201:
1970 return response.content
1971 return None
1972
1973 def get_provider_rest(self, vca=None):
1974 """
1975 Method gets provider vdc view from vcloud director
1976
1977 Args:
1978 network_name - is network name to be created.
1979 parent_network_uuid - is parent provider vdc network that will be used for mapping.
1980 It optional attribute. by default if no parent network indicate the first available will be used.
1981
1982 Returns:
1983 The return xml content of respond or None
1984 """
1985
1986 url_list = [vca.host, '/api/admin']
1987 response = Http.get(url=''.join(url_list),
1988 headers=vca.vcloud_session.get_vcloud_headers(),
1989 verify=vca.verify,
1990 logger=vca.logger)
1991
1992 if response.status_code == requests.codes.ok:
1993 return response.content
1994 return None
1995
1996 def create_vdc(self, vdc_name=None):
1997
1998 vdc_dict = {}
1999
2000 xml_content = self.create_vdc_from_tmpl_rest(vdc_name=vdc_name)
2001 if xml_content is not None:
2002 print xml_content
2003 try:
2004 task_resp_xmlroot = XmlElementTree.fromstring(xml_content)
2005 for child in task_resp_xmlroot:
2006 if child.tag.split("}")[1] == 'Owner':
2007 vdc_id = child.attrib.get('href').split("/")[-1]
2008 vdc_dict[vdc_id] = task_resp_xmlroot.get('href')
2009 return vdc_dict
2010 except:
2011 self.logger.debug("Respond body {}".format(xml_content))
2012
2013 return None
2014
2015 def create_vdc_from_tmpl_rest(self, vdc_name=None):
2016 """
2017 Method create vdc in vCloud director based on VDC template.
2018 it uses pre-defined template that must be named openmano
2019
2020 Args:
2021 vdc_name - name of a new vdc.
2022
2023 Returns:
2024 The return xml content of respond or None
2025 """
2026
2027 self.logger.info("Creating new vdc {}".format(vdc_name))
2028 print ("Creating new vdc {}".format(vdc_name))
2029
2030 vca = self.connect()
2031 if not vca:
2032 raise vimconn.vimconnConnectionException("self.connect() is failed")
2033 if vdc_name is None:
2034 return None
2035
2036 url_list = [vca.host, '/api/vdcTemplates']
2037 vm_list_rest_call = ''.join(url_list)
2038 response = Http.get(url=vm_list_rest_call,
2039 headers=vca.vcloud_session.get_vcloud_headers(),
2040 verify=vca.verify,
2041 logger=vca.logger)
2042
2043 # container url to a template
2044 vdc_template_ref = None
2045 try:
2046 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2047 for child in vm_list_xmlroot:
2048 # application/vnd.vmware.admin.providervdc+xml
2049 # we need find a template from witch we instantiate VDC
2050 if child.tag.split("}")[1] == 'VdcTemplate':
2051 if child.attrib.get('type') == 'application/vnd.vmware.admin.vdcTemplate+xml' and child.attrib.get(
2052 'name') == 'openmano':
2053 vdc_template_ref = child.attrib.get('href')
2054 except:
2055 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2056 self.logger.debug("Respond body {}".format(response.content))
2057 return None
2058
2059 # if we didn't found required pre defined template we return None
2060 if vdc_template_ref is None:
2061 return None
2062
2063 try:
2064 # instantiate vdc
2065 url_list = [vca.host, '/api/org/', self.org_uuid, '/action/instantiate']
2066 vm_list_rest_call = ''.join(url_list)
2067 data = """<InstantiateVdcTemplateParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
2068 <Source href="{1:s}"></Source>
2069 <Description>opnemano</Description>
2070 </InstantiateVdcTemplateParams>""".format(vdc_name, vdc_template_ref)
2071 headers = vca.vcloud_session.get_vcloud_headers()
2072 headers['Content-Type'] = 'application/vnd.vmware.vcloud.instantiateVdcTemplateParams+xml'
2073 response = Http.post(url=vm_list_rest_call, headers=headers, data=data, verify=vca.verify,
2074 logger=vca.logger)
2075 # if we all ok we respond with content otherwise by default None
2076 if response.status_code >= 200 and response.status_code < 300:
2077 return response.content
2078 return None
2079 except:
2080 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2081 self.logger.debug("Respond body {}".format(response.content))
2082
2083 return None
2084
2085 def create_vdc_rest(self, vdc_name=None):
2086 """
2087 Method create network in vCloud director
2088
2089 Args:
2090 network_name - is network name to be created.
2091 parent_network_uuid - is parent provider vdc network that will be used for mapping.
2092 It optional attribute. by default if no parent network indicate the first available will be used.
2093
2094 Returns:
2095 The return network uuid or return None
2096 """
2097
2098 self.logger.info("Creating new vdc {}".format(vdc_name))
2099 print ("Creating new vdc {}".format(vdc_name))
2100
2101 vca = self.connect_as_admin()
2102 if not vca:
2103 raise vimconn.vimconnConnectionException("self.connect() is failed")
2104 if vdc_name is None:
2105 return None
2106
2107 url_list = [vca.host, '/api/admin/org/', self.org_uuid]
2108 vm_list_rest_call = ''.join(url_list)
2109 if not (not vca.vcloud_session or not vca.vcloud_session.organization):
2110 response = Http.get(url=vm_list_rest_call,
2111 headers=vca.vcloud_session.get_vcloud_headers(),
2112 verify=vca.verify,
2113 logger=vca.logger)
2114
2115 provider_vdc_ref = None
2116 add_vdc_rest_url = None
2117 available_networks = None
2118
2119 if response.status_code != requests.codes.ok:
2120 self.logger.debug("REST API call {} failed. Return status code {}".format(vm_list_rest_call,
2121 response.status_code))
2122 return None
2123 else:
2124 try:
2125 vm_list_xmlroot = XmlElementTree.fromstring(response.content)
2126 for child in vm_list_xmlroot:
2127 # application/vnd.vmware.admin.providervdc+xml
2128 if child.tag.split("}")[1] == 'Link':
2129 if child.attrib.get('type') == 'application/vnd.vmware.admin.createVdcParams+xml' \
2130 and child.attrib.get('rel') == 'add':
2131 add_vdc_rest_url = child.attrib.get('href')
2132 except:
2133 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2134 self.logger.debug("Respond body {}".format(response.content))
2135 return None
2136
2137 response = self.get_provider_rest(vca=vca)
2138 print response
2139 try:
2140 vm_list_xmlroot = XmlElementTree.fromstring(response)
2141 for child in vm_list_xmlroot:
2142 if child.tag.split("}")[1] == 'ProviderVdcReferences':
2143 for sub_child in child:
2144 provider_vdc_ref = sub_child.attrib.get('href')
2145 except:
2146 self.logger.debug("Failed parse respond for rest api call {}".format(vm_list_rest_call))
2147 self.logger.debug("Respond body {}".format(response))
2148 return None
2149
2150 print "Add vdc {}".format(add_vdc_rest_url)
2151 print "Provider ref {}".format(provider_vdc_ref)
2152 if add_vdc_rest_url is not None and provider_vdc_ref is not None:
2153 data = """ <CreateVdcParams name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5"><Description>{1:s}</Description>
2154 <AllocationModel>ReservationPool</AllocationModel>
2155 <ComputeCapacity><Cpu><Units>MHz</Units><Allocated>2048</Allocated><Limit>2048</Limit></Cpu>
2156 <Memory><Units>MB</Units><Allocated>2048</Allocated><Limit>2048</Limit></Memory>
2157 </ComputeCapacity><NicQuota>0</NicQuota><NetworkQuota>100</NetworkQuota>
2158 <VdcStorageProfile><Enabled>true</Enabled><Units>MB</Units><Limit>20480</Limit><Default>true</Default></VdcStorageProfile>
2159 <ProviderVdcReference
2160 name="Main Provider"
2161 href="{2:s}" />
2162 <UsesFastProvisioning>true</UsesFastProvisioning></CreateVdcParams>""".format(escape(vdc_name),
2163 escape(vdc_name),
2164 provider_vdc_ref)
2165
2166 print data
2167 headers = vca.vcloud_session.get_vcloud_headers()
2168 headers['Content-Type'] = 'application/vnd.vmware.admin.createVdcParams+xml'
2169 response = Http.post(url=add_vdc_rest_url, headers=headers, data=data, verify=vca.verify,
2170 logger=vca.logger)
2171
2172 print response.status_code
2173 print response.content
2174 # if we all ok we respond with content otherwise by default None
2175 if response.status_code == 201:
2176 return response.content
2177 return None