blob: 20db81e9f60e4b3dd2877bb12b7ad339d63eb120 [file] [log] [blame]
bayramov325fa1c2016-09-08 01:42:46 -07001# -*- 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'''
25vimconn_vmware implementation an Abstract class in order to interact with VMware vCloud Director.
26mbayramov@vmware.com
27'''
28import os
29import requests
30
31
32from xml.etree import ElementTree as ET
33
34from pyvcloud import Http
35from pyvcloud.vcloudair import VCA
36from pyvcloud.schema.vcd.v1_5.schemas.vcloud import sessionType, organizationType, \
37 vAppType, organizationListType, vdcType, catalogType, queryRecordViewType, \
38 networkType, vcloudType, taskType, diskType, vmsType, vdcTemplateListType, mediaType
39from xml.sax.saxutils import escape
40
41import logging
42import json
43import vimconn
44import time
45import uuid
46import httplib
47
48
49__author__="Mustafa Bayramov"
50__date__ ="$26-Aug-2016 11:09:29$"
51
52
53#Error variables
54HTTP_Bad_Request = 400
55HTTP_Unauthorized = 401
56HTTP_Not_Found = 404
57HTTP_Method_Not_Allowed = 405
58HTTP_Request_Timeout = 408
59HTTP_Conflict = 409
60HTTP_Not_Implemented = 501
61HTTP_Service_Unavailable = 503
62HTTP_Internal_Server_Error = 500
63
64class vimconnException(Exception):
65 '''Common and base class Exception for all vimconnector exceptions'''
66 def __init__(self, message, http_code=HTTP_Bad_Request):
67 Exception.__init__(self, message)
68 self.http_code = http_code
69
70class vimconnConnectionException(vimconnException):
71 '''Connectivity error with the VIM'''
72 def __init__(self, message, http_code=HTTP_Service_Unavailable):
73 vimconnException.__init__(self, message, http_code)
74
75class vimconnUnexpectedResponse(vimconnException):
76 '''Get an wrong response from VIM'''
77 def __init__(self, message, http_code=HTTP_Service_Unavailable):
78 vimconnException.__init__(self, message, http_code)
79
80class vimconnAuthException(vimconnException):
81 '''Invalid credentials or authorization to perform this action over the VIM'''
82 def __init__(self, message, http_code=HTTP_Unauthorized):
83 vimconnException.__init__(self, message, http_code)
84
85class vimconnNotFoundException(vimconnException):
86 '''The item is not found at VIM'''
87 def __init__(self, message, http_code=HTTP_Not_Found):
88 vimconnException.__init__(self, message, http_code)
89
90class vimconnConflictException(vimconnException):
91 '''There is a conflict, e.g. more item found than one'''
92 def __init__(self, message, http_code=HTTP_Conflict):
93 vimconnException.__init__(self, message, http_code)
94
95class vimconnNotImplemented(vimconnException):
96 '''The method is not implemented by the connected'''
97 def __init__(self, message, http_code=HTTP_Not_Implemented):
98 vimconnException.__init__(self, message, http_code)
99
100
101flavorlist = {}
102
103class vimconnector():
104 '''Vmware VIM Connector base class
105 '''
106 def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="ERROR", config={}):
107 self.id = uuid
108 self.name = name
109 self.url = url
110 self.url_admin = url_admin
111 self.tenant_id = tenant_id
112 self.tenant_name = tenant_name
113 self.user = user
114 self.passwd = passwd
115 self.config = config
116 self.logger = logging.getLogger('mano.vim.vmware')
117
118 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
119 ch = logging.StreamHandler()
120 ch.setLevel(log_level)
121 ch.setFormatter(formatter)
122 self.logger.addHandler(ch)
123 self.logger.setLevel( getattr(logging, log_level))
124
125# self.logger = logging.getLogger('mano.vim.vmware')
126
127 self.logger.debug("Vmware tenant from VIM filter: '%s'", user)
128 self.logger.debug("Vmware tenant from VIM filter: '%s'", passwd)
129
130 if not url:
131 raise TypeError, 'url param can not be NoneType'
132
133 if not self.url_admin: #try to use normal url
134 self.url_admin = self.url
135
136 self.vcaversion = '5.6'
137
138 print "Calling constructor with following paramters"
139 print " UUID: {} ".format(uuid)
140 print " name: {} ".format(name)
141 print " tenant_id: {} ".format(tenant_id)
142 print " tenant_name: {} ".format(tenant_name)
143 print " url: {} ".format(url)
144 print " url_admin: {} ".format(url_admin)
145 print " user: {} ".format(user)
146 print " passwd: {} ".format(passwd)
147 print " debug: {} ".format(log_level)
148
149 def __getitem__(self,index):
150 if index=='tenant_id':
151 return self.tenant_id
152 if index=='tenant_name':
153 return self.tenant_name
154 elif index=='id':
155 return self.id
156 elif index=='name':
157 return self.name
158 elif index=='user':
159 return self.user
160 elif index=='passwd':
161 return self.passwd
162 elif index=='url':
163 return self.url
164 elif index=='url_admin':
165 return self.url_admin
166 elif index=="config":
167 return self.config
168 else:
169 raise KeyError("Invalid key '%s'" %str(index))
170
171 def __setitem__(self,index, value):
172 if index=='tenant_id':
173 self.tenant_id = value
174 if index=='tenant_name':
175 self.tenant_name = value
176 elif index=='id':
177 self.id = value
178 elif index=='name':
179 self.name = value
180 elif index=='user':
181 self.user = value
182 elif index=='passwd':
183 self.passwd = value
184 elif index=='url':
185 self.url = value
186 elif index=='url_admin':
187 self.url_admin = value
188 else:
189 raise KeyError("Invalid key '%s'" %str(index))
190
191 def connect(self):
192
193 service_type = 'standalone'
194 version = '5.6'
195
196 self.logger.debug("Logging in to a VCA '%s'", self.name)
197
198 vca = VCA(host=self.url, username=self.user, service_type=service_type, version=version, verify=False, log=True)
199 result = vca.login(password=self.passwd, org=self.name)
200 if not result:
201 raise KeyError("Can't connect to a vCloud director.")
202 result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
203 if result is True:
204 self.logger.debug("Successfully logged to a VCA '%s'", self.name)
205
206 # vca = VCA(host='172.16.254.206', username=self.user, service_type='standalone', version='5.6', verify=False, log=True)
207 # vca.login(password=self.passwd, org=self.name)
208 # vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
209
210 # if not result:
211 # result = vca.login(token=vca.token, org=self.name, org_url=vca.vcloud_session.org_url)
212 # if not result:
213 # raise KeyError("Can't connect to a vcloud director")
214 # else:
215 # print "Logged to VCA via existing token"
216 # else:
217 # print "Logged to VCA"
218
219 return vca
220
221
222 def new_tenant(self,tenant_name,tenant_description):
223 '''Adds a new tenant to VIM with this name and description,
224 returns the tenant identifier'''
225 raise vimconnNotImplemented( "Should have implemented this" )
226
227 def delete_tenant(self,tenant_id,):
228 '''Delete a tenant from VIM'''
229 '''Returns the tenant identifier'''
230 raise vimconnNotImplemented( "Should have implemented this" )
231
232 def get_tenant_list(self, filter_dict={}):
233 '''Obtain tenants of VIM
234 filter_dict can contain the following keys:
235 name: filter by tenant name
236 id: filter by tenant uuid/id
237 <other VIM specific>
238 Returns the tenant list of dictionaries:
239 [{'name':'<name>, 'id':'<id>, ...}, ...]
240 '''
241 raise vimconnNotImplemented( "Should have implemented this" )
242
243 def new_network(self,net_name, net_type, ip_profile=None, shared=False):
244 '''Adds a tenant network to VIM
245 net_name is the name
246 net_type can be 'bridge','data'.'ptp'. TODO: this need to be revised
247 ip_profile is a dict containing the IP parameters of the network
248 shared is a boolean
249 Returns the network identifier'''
250
251 self.logger.debug("Vmware tenant from VIM filter: '%s'", net_name)
252 self.logger.debug("Vmware tenant from VIM filter: '%s'", net_type)
253 self.logger.debug("Vmware tenant from VIM filter: '%s'", ip_profile)
254 self.logger.debug("Vmware tenant from VIM filter: '%s'", shared)
255
256 raise vimconnNotImplemented( "Should have implemented this" )
257
258 def get_network_list(self, filter_dict={}):
259 '''Obtain tenant networks of VIM
260 Filter_dict can be:
261 name: network name
262 id: network uuid
263 shared: boolean
264 tenant_id: tenant
265 admin_state_up: boolean
266 status: 'ACTIVE'
267 Returns the network list of dictionaries:
268 [{<the fields at Filter_dict plus some VIM specific>}, ...]
269 List can be empty
270 '''
271
272 vca = self.connect()
273 if not vca:
274 raise vimconn.vimconnConnectionException("self.connect() is failed")
275
276 vdc = vca.get_vdc(self.tenant_name)
277 vdcid = vdc.get_id().split(":")
278
279 networks = vca.get_networks(vdc.get_name())
280 network_list = []
281 for network in networks:
282 filter_dict = {}
283 netid = network.get_id().split(":")
284 self.logger.debug ("Adding {} to a list".format(netid[3]))
285 self.logger.debug ("VDC ID {} to a list".format(vdcid[3]))
286 self.logger.debug ("Network {} to a list".format(network.get_name()))
287
288 filter_dict["name"] = network.get_name()
289 filter_dict["id"] = netid[3]
290 filter_dict["shared"] = network.get_IsShared()
291 filter_dict["tenant_id"] = vdcid[3]
292 if network.get_status() == 1:
293 filter_dict["admin_state_up"] = True
294 else:
295 filter_dict["admin_state_up"] = False
296 filter_dict["status"] = "ACTIVE"
297 filter_dict["type"] = "bridge"
298 network_list.append(filter_dict)
299
300 self.logger.debug("Returning {}".format(network_list))
301 return network_list
302
303 def get_network(self, net_id):
304 '''Obtain network details of net_id VIM network'
305 Return a dict with the fields at filter_dict (see get_network_list) plus some VIM specific>}, ...]'''
306 raise vimconnNotImplemented( "Should have implemented this" )
307
308 def delete_network(self, net_id):
309 '''Deletes a tenant network from VIM, provide the network id.
310 Returns the network identifier or raise an exception'''
311 raise vimconnNotImplemented( "Should have implemented this" )
312
313 def refresh_nets_status(self, net_list):
314 '''Get the status of the networks
315 Params: the list of network identifiers
316 Returns a dictionary with:
317 net_id: #VIM id of this network
318 status: #Mandatory. Text with one of:
319 # DELETED (not found at vim)
320 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
321 # OTHER (Vim reported other status not understood)
322 # ERROR (VIM indicates an ERROR status)
323 # ACTIVE, INACTIVE, DOWN (admin down),
324 # BUILD (on building process)
325 #
326 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
327 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
328
329 '''
330 raise vimconnNotImplemented( "Should have implemented this" )
331
332 def get_flavor(self, flavor_id):
333 '''Obtain flavor details from the VIM
334 Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
335 '''
336
337 print " get_flavor contains {}".format(flavor_id)
338
339 vca = self.connect()
340 if not vca:
341 raise vimconn.vimconnConnectionException("self.connect() is failed.")
342
343 def new_flavor(self, flavor_data):
344 '''Adds a tenant flavor to VIM
345 flavor_data contains a dictionary with information, keys:
346 name: flavor name
347 ram: memory (cloud type) in MBytes
348 vpcus: cpus (cloud type)
349 extended: EPA parameters
350 - numas: #items requested in same NUMA
351 memory: number of 1G huge pages memory
352 paired-threads|cores|threads: number of paired hyperthreads, complete cores OR individual threads
353 interfaces: # passthrough(PT) or SRIOV interfaces attached to this numa
354 - name: interface name
355 dedicated: yes|no|yes:sriov; for PT, SRIOV or only one SRIOV for the physical NIC
356 bandwidth: X Gbps; requested guarantee bandwidth
357 vpci: requested virtual PCI address
358 disk: disk size
359 is_public:
360
361
362
363 #TODO to concrete
364 Returns the flavor identifier'''
365
366 flavor_uuid = uuid.uuid4()
367 flavorlist[flavor_uuid] = flavor_data
368
369 print " new_flavor contains {}".format(flavor_data)
370 print " flavor list contains {}".format(flavorlist)
371
372 return flavor_uuid
373
374 def delete_flavor(self, flavor_id):
375 '''Deletes a tenant flavor from VIM identify by its id
376 Returns the used id or raise an exception'''
377 raise vimconnNotImplemented( "Should have implemented this" )
378
379 def new_image(self,image_dict):
380 '''
381 Adds a tenant image to VIM
382 Returns:
383 200, image-id if the image is created
384 <0, message if there is an error
385 '''
386
387 print " ################################################################### "
388 print " new_image contains {}".format(image_dict)
389 print " ################################################################### "
390
391
392 def delete_image(self, image_id):
393 '''Deletes a tenant image from VIM'''
394 '''Returns the HTTP response code and a message indicating details of the success or fail'''
395
396 print " ################################################################### "
397 print " delete_image contains {}".format(image_id)
398 print " ################################################################### "
399
400 raise vimconnNotImplemented( "Should have implemented this" )
401
402 def catalog_exists(self, catalog_name, catalogs):
403 for catalog in catalogs:
404 if catalog.name == catalog_name:
405 return True
406 return False
407
408 def create_vimcatalog(self, vca, catalog_name):
409 """Create Catalog entry in VIM"""
410 task = vca.create_catalog(catalog_name, catalog_name)
411 result = vca.block_until_completed(task)
412 if not result:
413 return False
414 catalogs = vca.get_catalogs()
415 return self.catalog_exists(catalog_name, catalogs)
416
417
418 def upload_ovf(self, vca, catalog_name, item_name, media_file_name, description='', display_progress=False,
419 chunk_bytes=128 * 1024):
420 """
421 Uploads a OVF file to a vCloud catalog
422
423 :param catalog_name: (str): The name of the catalog to upload the media.
424 :param item_name: (str): The name of the media file in the catalog.
425 :param media_file_name: (str): The name of the local media file to upload.
426 :return: (bool) True if the media file was successfully uploaded, false otherwise.
427 """
428 os.path.isfile(media_file_name)
429 statinfo = os.stat(media_file_name)
430 statinfo.st_size
431
432 # find a catalog entry where we upload OVF.
433 # create vApp Template and check the status if vCD able to read OVF it will respond with appropirate
434 # status change.
435 # if VCD can parse OVF we upload VMDK file
436 for catalog in vca.get_catalogs():
437 if catalog_name != catalog.name:
438 continue
439 link = filter(lambda link: link.get_type() == "application/vnd.vmware.vcloud.media+xml" and
440 link.get_rel() == 'add', catalog.get_Link())
441 assert len(link) == 1
442 data = """
443 <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>
444 """ % (escape(item_name), escape(description))
445 headers = vca.vcloud_session.get_vcloud_headers()
446 headers['Content-Type'] = 'application/vnd.vmware.vcloud.uploadVAppTemplateParams+xml'
447 response = Http.post(link[0].get_href(), headers=headers, data=data, verify=vca.verify, logger=self.logger)
448 if response.status_code == requests.codes.created:
449 catalogItem = ET.fromstring(response.content)
450 entity = [child for child in catalogItem if
451 child.get("type") == "application/vnd.vmware.vcloud.vAppTemplate+xml"][0]
452 href = entity.get('href')
453 template = href
454 response = Http.get(href, headers=vca.vcloud_session.get_vcloud_headers(),
455 verify=vca.verify, logger=self.logger)
456
457 if response.status_code == requests.codes.ok:
458 media = mediaType.parseString(response.content, True)
459 link = filter(lambda link: link.get_rel() == 'upload:default', media.get_Files().get_File()[0].get_Link())[0]
460 headers = vca.vcloud_session.get_vcloud_headers()
461 headers['Content-Type'] = 'Content-Type text/xml'
462 response = Http.put(link.get_href(), data=open(media_file_name, 'rb'), headers=headers, verify=vca.verify,logger=self.logger)
463 if response.status_code != requests.codes.ok:
464 self.logger.debug("Failed create vApp template for catalog name {} and image {}".format(catalog_name, media_file_name))
465 return False
466
467 time.sleep(5)
468
469 self.logger.debug("Failed create vApp template for catalog name {} and image {}".
470 format(catalog_name, media_file_name))
471
472 # uploading VMDK file
473 # check status of OVF upload
474 response = Http.get(template, headers=vca.vcloud_session.get_vcloud_headers(), verify=vca.verify, logger=self.logger)
475 if response.status_code == requests.codes.ok:
476 media = mediaType.parseString(response.content, True)
477 link = filter(lambda link: link.get_rel() == 'upload:default', media.get_Files().get_File()[0].get_Link())[0]
478
479 # The OVF file and VMDK must be in a same directory
480 head, tail = os.path.split(media_file_name)
481 filevmdk = head + '/' + os.path.basename(link.get_href())
482
483 os.path.isfile(filevmdk)
484 statinfo = os.stat(filevmdk)
485
486 # TODO debug output remove it
487 #print media.get_Files().get_File()[0].get_Link()[0].get_href()
488 #print media.get_Files().get_File()[1].get_Link()[0].get_href()
489 #print link.get_href()
490
491 # in case first element is pointer to OVF.
492 hrefvmdk = link.get_href().replace("descriptor.ovf","Cirros-disk1.vmdk")
493
494 f = open(filevmdk, 'rb')
495 bytes_transferred = 0
496 while bytes_transferred < statinfo.st_size:
497 my_bytes = f.read(chunk_bytes)
498 if len(my_bytes) <= chunk_bytes:
499 headers = vca.vcloud_session.get_vcloud_headers()
500 headers['Content-Range'] = 'bytes %s-%s/%s' % (bytes_transferred, len(my_bytes) - 1, statinfo.st_size)
501 headers['Content-Length'] = str(len(my_bytes))
502 response = Http.put(hrefvmdk, headers=headers, data=my_bytes, verify=vca.verify,logger=None)
503 if response.status_code == requests.codes.ok:
504 bytes_transferred += len(my_bytes)
505 self.logger.debug('transferred %s of %s bytes' % (str(bytes_transferred),
506 str(statinfo.st_size)))
507 else:
508 self.logger.debug('file upload failed with error: [%s] %s' % (response.status_code,
509 response.content))
510 return False
511 f.close()
512 return True
513 else:
514 self.logger.debug("Failed retrieve vApp template for catalog name {} for OVF {}".
515 format(catalog_name, media_file_name))
516 return False
517
518 self.logger.debug("Failed retrieve catalog name {} for OVF file {}".format(catalog_name, media_file_name))
519 return False
520
521 def upload_vimimage(self,vca, catalog_name, media_name, medial_file_name):
522 """Upload media file"""
523 return self.upload_ovf(vca, catalog_name, media_name.split(".")[0], medial_file_name, medial_file_name, True)
524
525 def get_catalogid(self, catalog_name, catalogs):
526 for catalog in catalogs:
527 if catalog.name == catalog_name:
528 print catalog.name
529 catalog_id = catalog.get_id().split(":")
530 return catalog_id[3]
531 return None
532
533 def get_catalogbyid(self, catalog_id, catalogs):
534 for catalog in catalogs:
535 catalogid = catalog.get_id().split(":")[3]
536 if catalogid == catalog_id:
537 return catalog.name
538 return None
539
540 def get_image_id_from_path(self, path):
541 '''Get the image id from image path in the VIM database'''
542 '''Returns:
543 0,"Image not found" if there are no images with that path
544 1,image-id if there is one image with that path
545 <0,message if there was an error (Image not found, error contacting VIM, more than 1 image with that path, etc.)
546 '''
547
548 vca = self.connect()
549 if not vca:
550 raise vimconn.vimconnConnectionException("self.connect() is failed")
551
552 self.logger.debug("get_image_id_from_path path {}".format(path))
553
554 dirpath, filename = os.path.split(path)
555 flname, file_extension = os.path.splitext(path)
556 if file_extension != '.ovf':
557 self.logger.debug("Wrong file extension {}".format(file_extension))
558 return -1, "Wrong container. vCloud director supports only OVF."
559 catalog_name = os.path.splitext(filename)[0]
560
561 self.logger.debug("File name {} Catalog Name {} file path {}".format(filename, catalog_name, path))
562 self.logger.debug("Catalog name {}".format(catalog_name))
563
564 catalogs = vca.get_catalogs()
565 if len(catalogs) == 0:
566 self.logger.info("Creating new catalog entry {} in vcloud director".format(catalog_name))
567 result = self.create_vimcatalog(vca, catalog_name)
568 if not result:
569 return -1, "Failed create new catalog {} ".format(catalog_name)
570 result = self.upload_vimimage(vca, catalog_name, filename, path)
571 if not result:
572 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
573 return self.get_catalogid(catalog_name, vca.get_catalogs())
574 else:
575 for catalog in catalogs:
576 # search for existing catalog if we find same name we return ID
577 # TODO optimize this
578 if catalog.name == catalog_name:
579 self.logger.debug("Found existing catalog entry for {} catalog id {}".format(catalog_name, self.get_catalogid(catalog_name, catalogs)))
580 return self.get_catalogid(catalog_name, vca.get_catalogs())
581
582 # if we didn't find existing catalog we create a new one.
583 self.logger.debug("Creating new catalog entry".format(catalog_name))
584 result = self.create_vimcatalog(vca, catalog_name)
585 if not result:
586 return -1, "Failed create new catalog {} ".format(catalog_name)
587 result = self.upload_vimimage(vca, catalog_name, filename, path)
588 if not result:
589 return -1, "Failed create vApp template for catalog {} ".format(catalog_name)
590
591 return self.get_catalogid(catalog_name, vca.get_catalogs())
592
593 def get_vappid(self, vdc, vapp_name):
594 """ Take vdc object and vApp name and returns vapp uuid or None
595 """
596 #UUID has following format https://host/api/vApp/vapp-30da58a3-e7c7-4d09-8f68-d4c8201169cf
597
598 try:
599 refs = filter(lambda ref: ref.name == vapp_name and ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
600 vdc.ResourceEntities.ResourceEntity)
601
602 if len(refs) == 1:
603 return refs[0].href.split("vapp")[1][1:]
604 except:
605 return None
606 return None
607
608 def get_vappbyid(self, vdc, vapp_id):
609 refs = filter(lambda ref: ref.type_ == 'application/vnd.vmware.vcloud.vApp+xml',
610 vdc.ResourceEntities.ResourceEntity)
611 for ref in refs:
612 print ref.href
613
614 if len(refs) == 1:
615 return refs[0].href.split("vapp")[1][1:]
616
617 def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None):
618 """Adds a VM instance to VIM
619 Params:
620 start: indicates if VM must start or boot in pause mode. Ignored
621 image_id,flavor_id: image and flavor uuid
622 net_list: list of interfaces, each one is a dictionary with:
623 name:
624 net_id: network uuid to connect
625 vpci: virtual vcpi to assign
626 model: interface model, virtio, e2000, ...
627 mac_address:
628 use: 'data', 'bridge', 'mgmt'
629 type: 'virtual', 'PF', 'VF', 'VFnotShared'
630 vim_id: filled/added by this function
631 cloud_config: can be a text script to be passed directly to cloud-init,
632 or an object to inject users and ssh keys with format:
633 key-pairs: [] list of keys to install to the default user
634 users: [{ name, key-pairs: []}] list of users to add with their key-pair
635 #TODO ip, security groups
636 Returns >=0, the instance identifier
637 <0, error_text
638 """
639
640 self.logger.info("Creating new instance for entry".format(name))
641 self.logger.debug("desc {} boot {} image_id: {} flavor_id: {} net_list: {} cloud_config {}".
642 format(description, start, image_id, flavor_id, net_list, cloud_config))
643 vca = self.connect()
644 if not vca:
645 raise vimconn.vimconnConnectionException("self.connect() is failed.")
646
647 # TODO following attribute need be featched from flavor / OVF file must contain same data.
648 # task = self.vca.create_vapp(vdc_name, vapp_name, template, catalog,
649 # vm_name=vm_name,
650 # vm_cpus=cpu,
651 # vm_memory=memory)
652 #
653
654 catalogs = vca.get_catalogs()
655 #image upload creates template name as catalog name space Template.
656 templateName = self.get_catalogbyid(image_id, catalogs) + ' Template'
657 task = vca.create_vapp(self.tenant_name, name, templateName, self.get_catalogbyid(image_id,catalogs), vm_name=name)
658 if task is False:
659 return -1, " Failed deploy vApp {}".format(name)
660
661 result = vca.block_until_completed(task)
662 if result:
663 vappID = self.get_vappid(vca.get_vdc(self.tenant_name), name)
664 if vappID is None:
665 return -1, " Failed featch UUID for vApp {}".format(name)
666 else:
667 return vappID
668
669 return -1, " Failed create vApp {}".format(name)
670
671 def get_vminstance(self,vm_id):
672 '''Returns the VM instance information from VIM'''
673
674 vca = self.connect()
675 if not vca:
676 raise vimconn.vimconnConnectionException("self.connect() is failed.")
677
678
679 raise vimconnNotImplemented( "Should have implemented this" )
680
681 def delete_vminstance(self, vm_id):
682 '''Removes a VM instance from VIM'''
683 '''Returns the instance identifier'''
684
685 print " ###### {} ".format(vm_id)
686
687 vca = self.connect()
688 if not vca:
689 raise vimconn.vimconnConnectionException("self.connect() is failed.")
690
691 thevdc = vca.get_vdc(self.tenant_name)
692 self.get_vappid(vca.get_vdc(self.tenant_name), name)
693
694
695
696
697 def refresh_vms_status(self, vm_list):
698 '''Get the status of the virtual machines and their interfaces/ports
699 Params: the list of VM identifiers
700 Returns a dictionary with:
701 vm_id: #VIM id of this Virtual Machine
702 status: #Mandatory. Text with one of:
703 # DELETED (not found at vim)
704 # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
705 # OTHER (Vim reported other status not understood)
706 # ERROR (VIM indicates an ERROR status)
707 # ACTIVE, PAUSED, SUSPENDED, INACTIVE (not running),
708 # CREATING (on building process), ERROR
709 # ACTIVE:NoMgmtIP (Active but any of its interface has an IP address
710 #
711 error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
712 vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
713 interfaces:
714 - vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
715 mac_address: #Text format XX:XX:XX:XX:XX:XX
716 vim_net_id: #network id where this interface is connected
717 vim_interface_id: #interface/port VIM id
718 ip_address: #null, or text with IPv4, IPv6 address
719 '''
720 raise vimconnNotImplemented( "Should have implemented this" )
721
722 def action_vminstance(self, vm_id, action_dict):
723 '''Send and action over a VM instance from VIM
724 Returns the vm_id if the action was successfully sent to the VIM'''
725 raise vimconnNotImplemented( "Should have implemented this" )
726
727 def get_vminstance_console(self,vm_id, console_type="vnc"):
728 '''
729 Get a console for the virtual machine
730 Params:
731 vm_id: uuid of the VM
732 console_type, can be:
733 "novnc" (by default), "xvpvnc" for VNC types,
734 "rdp-html5" for RDP types, "spice-html5" for SPICE types
735 Returns dict with the console parameters:
736 protocol: ssh, ftp, http, https, ...
737 server: usually ip address
738 port: the http, ssh, ... port
739 suffix: extra text, e.g. the http path and query string
740 '''
741 raise vimconnNotImplemented( "Should have implemented this" )
742
743#NOT USED METHODS in current version
744
745 def host_vim2gui(self, host, server_dict):
746 '''Transform host dictionary from VIM format to GUI format,
747 and append to the server_dict
748 '''
749 raise vimconnNotImplemented( "Should have implemented this" )
750
751 def get_hosts_info(self):
752 '''Get the information of deployed hosts
753 Returns the hosts content'''
754 raise vimconnNotImplemented( "Should have implemented this" )
755
756 def get_hosts(self, vim_tenant):
757 '''Get the hosts and deployed instances
758 Returns the hosts content'''
759 raise vimconnNotImplemented( "Should have implemented this" )
760
761 def get_processor_rankings(self):
762 '''Get the processor rankings in the VIM database'''
763 raise vimconnNotImplemented( "Should have implemented this" )
764
765 def new_host(self, host_data):
766 '''Adds a new host to VIM'''
767 '''Returns status code of the VIM response'''
768 raise vimconnNotImplemented( "Should have implemented this" )
769
770 def new_external_port(self, port_data):
771 '''Adds a external port to VIM'''
772 '''Returns the port identifier'''
773 raise vimconnNotImplemented( "Should have implemented this" )
774
775 def new_external_network(self,net_name,net_type):
776 '''Adds a external network to VIM (shared)'''
777 '''Returns the network identifier'''
778 raise vimconnNotImplemented( "Should have implemented this" )
779
780 def connect_port_network(self, port_id, network_id, admin=False):
781 '''Connects a external port to a network'''
782 '''Returns status code of the VIM response'''
783 raise vimconnNotImplemented( "Should have implemented this" )
784
785 def new_vminstancefromJSON(self, vm_data):
786 '''Adds a VM instance to VIM'''
787 '''Returns the instance identifier'''
788 raise vimconnNotImplemented( "Should have implemented this" )
789