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