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