Revert "Full Juju Charm support"
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rift / rwcal / openstack / nova / nova_drv.py
1 #!/usr/bin/python
2
3 #
4 # Copyright 2017 RIFT.IO Inc
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 import logging
19 from novaclient import client as nvclient
20
21
22 class NovaDrvAPIVersionException(Exception):
23 def __init__(self, errors):
24 self.errors = errors
25 super(NovaDrvAPIVersionException, self).__init__("Multiple Exception Received")
26
27 def __str__(self):
28 return self.__repr__()
29
30 def __repr__(self):
31 msg = "{} : Following Exception(s) have occured during Nova API discovery".format(self.__class__)
32 for n,e in enumerate(self.errors):
33 msg += "\n"
34 msg += " {}: {}".format(n, str(e))
35 return msg
36
37
38 class NovaDriver(object):
39 """
40 NovaDriver Class for compute orchestration
41 """
42 ### List of supported API versions in prioritized order
43 supported_versions = ["2.1", "2.0"]
44
45 def __init__(self,
46 sess_handle,
47 region_name = 'RegionOne',
48 service_type = 'compute',
49 logger = None):
50 """
51 Constructor for NovaDriver class
52 Arguments:
53 sess_handle (instance of class SessionDriver)
54 region_name (string): Region Name
55 service_type(string): Type of service in service catalog
56 logger (instance of logging.Logger)
57 """
58
59 if logger is None:
60 self.log = logging.getLogger('rwcal.openstack.nova')
61 self.log.setLevel(logging.DEBUG)
62 else:
63 self.log = logger
64
65 self._sess_handle = sess_handle
66
67 self._max_api_version = None
68 self._min_api_version = None
69
70 #### Attempt to use API versions in prioritized order defined in
71 #### NovaDriver.supported_versions
72 def select_version(version):
73 try:
74 self.log.info("Attempting to use Nova v%s APIs", version)
75 nvdrv = nvclient.Client(version=version,
76 region_name = region_name,
77 service_type = service_type,
78 session = self._sess_handle.session,
79 logger = self.log)
80
81 api_version = 'v' + nvdrv.versions.api_version.get_string()
82 nova_version_list = nvdrv.versions.list()
83 max_api_version, min_api_version = None, None
84
85 for v in nova_version_list:
86 version_dict = v.to_dict()
87 if api_version == version_dict["id"]:
88 max_api_version = version_dict["version"] # Max version supported is stored in version field.
89 min_api_version = version_dict["min_version"]
90 break
91
92 except Exception as e:
93 self.log.info(str(e))
94 raise
95 else:
96 self.log.info("Nova API v%s selected", version)
97 return (version, nvdrv, max_api_version, min_api_version)
98
99 errors = []
100 for v in NovaDriver.supported_versions:
101 try:
102 (self._version, self._nv_drv, self._max_api_version, self._min_api_version) = select_version(v)
103 except Exception as e:
104 errors.append(e)
105 else:
106 break
107 else:
108 raise NovaDrvAPIVersionException(errors)
109
110 @property
111 def project_id(self):
112 return self._sess_handle.project_id
113
114 @property
115 def nova_endpoint(self):
116 return self._nv_drv.client.get_endpoint()
117
118 @property
119 def nova_quota(self):
120 """
121 Returns Nova Quota (a dictionary) for project
122 """
123 try:
124 quota = self._nv_drv.quotas.get(self.project_id)
125 except Exception as e:
126 self.log.exception("Get Nova quota operation failed. Exception: %s", str(e))
127 raise
128 return quota.to_dict()
129
130 def extensions_list(self):
131 """
132 Returns a list of available nova extensions.
133 Arguments:
134 None
135 Returns:
136 A list of dictionaries. Each dictionary contains attributes for a single NOVA extension
137 """
138 try:
139 extensions = self._nv_drv.list_extensions.show_all()
140 except Exception as e:
141 self.log.exception("List extension operation failed. Exception: %s", str(e))
142 raise
143 return [ ext.to_dict() for ext in extensions ]
144
145
146 def _get_nova_connection(self):
147 """
148 Returns instance of object novaclient.client.Client
149 Use for DEBUG ONLY
150 """
151 return self._nv_drv
152
153 def _flavor_extra_spec_get(self, flavor):
154 """
155 Get extra_specs associated with a flavor
156 Arguments:
157 flavor: Object of novaclient.v2.flavors.Flavor
158
159 Returns:
160 A dictionary of extra_specs (key-value pairs)
161 """
162 try:
163 extra_specs = flavor.get_keys()
164 except nvclient.exceptions.NotFound:
165 return None
166 except Exception as e:
167 self.log.exception("Could not get the EPA attributes for flavor with flavor_id : %s. Exception: %s",
168 flavor.id, str(e))
169 raise
170 return extra_specs
171
172 def _flavor_get(self, flavor_id):
173 """
174 Get flavor by flavor_id
175 Arguments:
176 flavor_id(string): UUID of flavor_id
177
178 Returns:
179 dictionary of flavor parameters
180 """
181 try:
182 flavor = self._nv_drv.flavors.get(flavor_id)
183 except nvclient.exceptions.NotFound:
184 return None
185 except Exception as e:
186 self.log.exception("Did not find flavor with flavor_id : %s. Exception: %s",flavor_id, str(e))
187 raise
188 response = flavor.to_dict()
189 try:
190 response['extra_specs'] = self._flavor_extra_spec_get(flavor)
191 except nvclient.exceptions.NotFound:
192 pass
193 except Exception as e:
194 self.log.exception("Did not find extra_specs in flavor with flavor_id : %s. Exception: %s",flavor_id, str(e))
195 raise
196 return response
197
198 try:
199 extra_specs = flavor.get_keys()
200 except Exception as e:
201 self.log.exception("Could not get the EPA attributes for flavor with flavor_id : %s. Exception: %s",
202 flavor_id, str(e))
203 raise
204
205 response = flavor.to_dict()
206 assert 'extra_specs' not in response, "Key extra_specs present as flavor attribute"
207 response['extra_specs'] = extra_specs
208 return response
209
210 def flavor_get(self, flavor_id):
211 """
212 Get flavor by flavor_id
213 Arguments:
214 flavor_id(string): UUID of flavor_id
215
216 Returns:
217 dictionary of flavor parameters
218 """
219 return self._flavor_get(flavor_id)
220
221 def flavor_find(self, **kwargs):
222 """
223 Returns list of all flavors (dictionary) matching the filters provided in kwargs
224
225 Arguments:
226 A dictionary in following keys
227 {
228 "vcpus": Number of vcpus required
229 "ram" : Memory in MB
230 "disk" : Secondary storage in GB
231 }
232 Returns:
233 A list of dictionaries. Each dictionary contains attributes for a single flavor instance
234 """
235 try:
236 flavor_list = self._nv_drv.flavors.findall(**kwargs)
237 except Exception as e:
238 self.log.exception("Find Flavor operation failed. Exception: %s",str(e))
239 raise
240
241 flavor_info = list()
242 for f in flavor_list:
243 flavor = f.to_dict()
244 flavor['extra_specs'] = self._flavor_extra_spec_get(f)
245 flavor_info.append(flavor)
246
247 return flavor_info
248
249 def flavor_list(self):
250 """
251 Returns list of all flavors (dictionary per flavor)
252
253 Arguments:
254 None
255 Returns:
256 A list of dictionaries. Each dictionary contains attributes for a single flavor instance
257 """
258 flavors = []
259 flavor_info = []
260
261 try:
262 flavors = self._nv_drv.flavors.list()
263 except Exception as e:
264 self.log.exception("List Flavor operation failed. Exception: %s",str(e))
265 raise
266 if flavors:
267 flavor_info = [ self.flavor_get(flv.id) for flv in flavors ]
268 return flavor_info
269
270 def flavor_create(self, name, ram, vcpu, disk, extra_specs):
271 """
272 Create a new flavor
273
274 Arguments:
275 name (string): Name of the new flavor
276 ram (int) : Memory in MB
277 vcpus (int) : Number of VCPUs
278 disk (int) : Secondary storage size in GB
279 extra_specs (dictionary): EPA attributes dictionary
280
281 Returns:
282 flavor_id (string): UUID of flavor created
283 """
284 try:
285 flavor = self._nv_drv.flavors.create(name = name,
286 ram = ram,
287 vcpus = vcpu,
288 disk = disk,
289 flavorid = 'auto',
290 ephemeral = 0,
291 swap = 0,
292 rxtx_factor = 1.0,
293 is_public = True)
294 except Exception as e:
295 self.log.exception("Create Flavor operation failed. Exception: %s",str(e))
296 raise
297
298 if extra_specs:
299 try:
300 flavor.set_keys(extra_specs)
301 except Exception as e:
302 self.log.exception("Set Key operation failed for flavor: %s. Exception: %s",
303 flavor.id, str(e))
304 raise
305 return flavor.id
306
307 def flavor_delete(self, flavor_id):
308 """
309 Deletes a flavor identified by flavor_id
310
311 Arguments:
312 flavor_id (string): UUID of flavor to be deleted
313
314 Returns: None
315 """
316 assert flavor_id == self._flavor_get(flavor_id)['id']
317 try:
318 self._nv_drv.flavors.delete(flavor_id)
319 except Exception as e:
320 self.log.exception("Delete flavor operation failed for flavor: %s. Exception: %s",
321 flavor_id, str(e))
322 raise
323
324
325 def server_list(self):
326 """
327 Returns a list of available VMs for the project
328
329 Arguments: None
330
331 Returns:
332 A list of dictionaries. Each dictionary contains attributes associated
333 with individual VM
334 """
335 servers = []
336 server_info = []
337 try:
338 servers = self._nv_drv.servers.list()
339 except Exception as e:
340 self.log.exception("List Server operation failed. Exception: %s", str(e))
341 raise
342 server_info = [ server.to_dict() for server in servers]
343 return server_info
344
345 def _nova_server_get(self, server_id):
346 """
347 Returns a dictionary of attributes associated with VM identified by service_id
348
349 Arguments:
350 server_id (string): UUID of the VM/server for which information is requested
351
352 Returns:
353 A dictionary object with attributes associated with VM identified by server_id
354 """
355 try:
356 server = self._nv_drv.servers.get(server = server_id)
357 except Exception as e:
358 self.log.exception("Get Server operation failed for server_id: %s. Exception: %s",
359 server_id, str(e))
360 raise
361 else:
362 return server.to_dict()
363
364 def server_get(self, server_id):
365 """
366 Returns a dictionary of attributes associated with VM identified by service_id
367
368 Arguments:
369 server_id (string): UUID of the VM/server for which information is requested
370
371 Returns:
372 A dictionary object with attributes associated with VM identified by server_id
373 """
374 return self._nova_server_get(server_id)
375
376 def server_create(self, **kwargs):
377 """
378 Creates a new VM/server instance
379
380 Arguments:
381 A dictionary of following key-value pairs
382 {
383 server_name(string) : Name of the VM/Server
384 flavor_id (string) : UUID of the flavor to be used for VM
385 image_id (string) : UUID of the image to be used VM/Server instance,
386 This could be None if volumes (with images) are being used
387 network_list(List) : A List of network_ids. A port will be created in these networks
388 port_list (List) : A List of port-ids. These ports will be added to VM.
389 metadata (dict) : A dictionary of arbitrary key-value pairs associated with VM/server
390 userdata (string) : A script which shall be executed during first boot of the VM
391 availability_zone (string) : A name of the availability zone where instance should be launched
392 scheduler_hints (string) : Openstack scheduler_hints to be passed to nova scheduler
393 }
394 Returns:
395 server_id (string): UUID of the VM/server created
396
397 """
398 nics = []
399 if 'network_list' in kwargs:
400 for network_id in kwargs['network_list']:
401 nics.append({'net-id': network_id})
402
403 if 'port_list' in kwargs:
404 for port_id in kwargs['port_list']:
405 port = { 'port-id': port_id['id'] }
406 nics.append(port)
407
408 try:
409 server = self._nv_drv.servers.create(
410 kwargs['name'],
411 kwargs['image_id'],
412 kwargs['flavor_id'],
413 meta = kwargs['metadata'] if 'metadata' in kwargs else None,
414 files = kwargs['files'] if 'files' in kwargs else None,
415 reservation_id = None,
416 min_count = None,
417 max_count = None,
418 userdata = kwargs['userdata'] if 'userdata' in kwargs else None,
419 security_groups = kwargs['security_groups'] if 'security_groups' in kwargs else None,
420 availability_zone = kwargs['availability_zone'].name if 'availability_zone' in kwargs else None,
421 block_device_mapping_v2 = kwargs['block_device_mapping_v2'] if 'block_device_mapping_v2' in kwargs else None,
422 nics = nics,
423 scheduler_hints = kwargs['scheduler_hints'] if 'scheduler_hints' in kwargs else None,
424 config_drive = kwargs['config_drive'] if 'config_drive' in kwargs else None
425 )
426
427 except Exception as e:
428 self.log.exception("Create Server operation failed. Exception: %s", str(e))
429 raise
430 return server.to_dict()['id']
431
432 def server_delete(self, server_id):
433 """
434 Deletes a server identified by server_id
435
436 Arguments:
437 server_id (string): UUID of the server to be deleted
438
439 Returns: None
440 """
441 try:
442 self._nv_drv.servers.delete(server_id)
443 except Exception as e:
444 self.log.exception("Delete server operation failed for server_id: %s. Exception: %s",
445 server_id, str(e))
446 raise
447
448 def server_start(self, server_id):
449 """
450 Starts a server identified by server_id
451
452 Arguments:
453 server_id (string): UUID of the server to be started
454
455 Returns: None
456 """
457 try:
458 self._nv_drv.servers.start(server_id)
459 except Exception as e:
460 self.log.exception("Start Server operation failed for server_id : %s. Exception: %s",
461 server_id, str(e))
462 raise
463
464 def server_stop(self, server_id):
465 """
466 Arguments:
467 server_id (string): UUID of the server to be stopped
468
469 Returns: None
470 """
471 try:
472 self._nv_drv.servers.stop(server_id)
473 except Exception as e:
474 self.log.exception("Stop Server operation failed for server_id : %s. Exception: %s",
475 server_id, str(e))
476 raise
477
478 def server_pause(self, server_id):
479 """
480 Arguments:
481 server_id (string): UUID of the server to be paused
482
483 Returns: None
484 """
485 try:
486 self._nv_drv.servers.pause(server_id)
487 except Exception as e:
488 self.log.exception("Pause Server operation failed for server_id : %s. Exception: %s",
489 server_id, str(e))
490 raise
491
492 def server_unpause(self, server_id):
493 """
494 Arguments:
495 server_id (string): UUID of the server to be unpaused
496
497 Returns: None
498 """
499 try:
500 self._nv_drv.servers.unpause(server_id)
501 except Exception as e:
502 self.log.exception("Resume Server operation failed for server_id : %s. Exception: %s",
503 server_id, str(e))
504 raise
505
506
507 def server_suspend(self, server_id):
508 """
509 Arguments:
510 server_id (string): UUID of the server to be suspended
511
512 Returns: None
513 """
514 try:
515 self._nv_drv.servers.suspend(server_id)
516 except Exception as e:
517 self.log.exception("Suspend Server operation failed for server_id : %s. Exception: %s",
518 server_id, str(e))
519
520
521 def server_resume(self, server_id):
522 """
523 Arguments:
524 server_id (string): UUID of the server to be resumed
525
526 Returns: None
527 """
528 try:
529 self._nv_drv.servers.resume(server_id)
530 except Exception as e:
531 self.log.exception("Resume Server operation failed for server_id : %s. Exception: %s",
532 server_id, str(e))
533 raise
534
535 def server_reboot(self, server_id, reboot_type):
536 """
537 Arguments:
538 server_id (string) : UUID of the server to be rebooted
539 reboot_type(string):
540 'SOFT': Soft Reboot
541 'HARD': Hard Reboot
542 Returns: None
543 """
544 try:
545 self._nv_drv.servers.reboot(server_id, reboot_type)
546 except Exception as e:
547 self.log.exception("Reboot Server operation failed for server_id: %s. Exception: %s",
548 server_id, str(e))
549 raise
550
551 def server_console(self, server_id, console_type = 'novnc'):
552 """
553 Arguments:
554 server_id (string) : UUID of the server to be rebooted
555 console_type(string):
556 'novnc',
557 'xvpvnc'
558 Returns:
559 A dictionary object response for console information
560 """
561 try:
562 console_info = self._nv_drv.servers.get_vnc_console(server_id, console_type)
563 except Exception as e:
564 # TODO: This error keeps repeating incase there is no console available
565 # So reduced level from exception to warning
566 self.log.warning("Server Get-Console operation failed for server_id: %s. Exception: %s",
567 server_id, str(e))
568 raise e
569 return console_info
570
571 def server_rebuild(self, server_id, image_id):
572 """
573 Arguments:
574 server_id (string) : UUID of the server to be rebooted
575 image_id (string) : UUID of the image to use
576 Returns: None
577 """
578
579 try:
580 self._nv_drv.servers.rebuild(server_id, image_id)
581 except Exception as e:
582 self.log.exception("Rebuild Server operation failed for server_id: %s. Exception: %s",
583 server_id, str(e))
584 raise
585
586
587 def server_add_port(self, server_id, port_id):
588 """
589 Arguments:
590 server_id (string): UUID of the server
591 port_id (string): UUID of the port to be attached
592
593 Returns: None
594 """
595 try:
596 self._nv_drv.servers.interface_attach(server_id,
597 port_id,
598 net_id = None,
599 fixed_ip = None)
600 except Exception as e:
601 self.log.exception("Server Port Add operation failed for server_id : %s, port_id : %s. Exception: %s",
602 server_id, port_id, str(e))
603 raise
604
605 def server_delete_port(self, server_id, port_id):
606 """
607 Arguments:
608 server_id (string): UUID of the server
609 port_id (string): UUID of the port to be deleted
610 Returns: None
611
612 """
613 try:
614 self._nv_drv.servers.interface_detach(server_id, port_id)
615 except Exception as e:
616 self.log.exception("Server Port Delete operation failed for server_id : %s, port_id : %s. Exception: %s",
617 server_id, port_id, str(e))
618 raise
619
620 def floating_ip_list(self):
621 """
622 Arguments:
623 None
624 Returns:
625 List of objects of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
626 """
627 try:
628 ip_list = self._nv_drv.floating_ips.list()
629 except Exception as e:
630 self.log.exception("Floating IP List operation failed. Exception: %s", str(e))
631 raise
632
633 return ip_list
634
635 def floating_ip_create(self, pool):
636 """
637 Arguments:
638 pool (string): Name of the pool (optional)
639 Returns:
640 An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
641 """
642 try:
643 floating_ip = self._nv_drv.floating_ips.create(pool)
644 except Exception as e:
645 self.log.exception("Floating IP Create operation failed. Exception: %s", str(e))
646 raise
647
648 return floating_ip
649
650 def floating_ip_delete(self, floating_ip):
651 """
652 Arguments:
653 floating_ip: An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
654 Returns:
655 None
656 """
657 try:
658 floating_ip = self._nv_drv.floating_ips.delete(floating_ip)
659 except Exception as e:
660 self.log.exception("Floating IP Delete operation failed. Exception: %s", str(e))
661 raise
662
663 def floating_ip_assign(self, server_id, floating_ip, fixed_ip):
664 """
665 Arguments:
666 server_id (string) : UUID of the server
667 floating_ip (string): IP address string for floating-ip
668 fixed_ip (string) : IP address string for the fixed-ip with which floating ip will be associated
669 Returns:
670 None
671 """
672 try:
673 self._nv_drv.servers.add_floating_ip(server_id, floating_ip, fixed_ip)
674 except Exception as e:
675 self.log.exception("Assign Floating IP operation failed. Exception: %s", str(e))
676 raise
677
678 def floating_ip_release(self, server_id, floating_ip):
679 """
680 Arguments:
681 server_id (string) : UUID of the server
682 floating_ip (string): IP address string for floating-ip
683 Returns:
684 None
685 """
686 try:
687 self._nv_drv.servers.remove_floating_ip(server_id, floating_ip)
688 except Exception as e:
689 self.log.exception("Release Floating IP operation failed. Exception: %s", str(e))
690 raise
691
692 def volume_list(self, server_id):
693 """
694 List of volumes attached to the server
695
696 Arguments:
697 None
698 Returns:
699 List of dictionary objects where dictionary is representation of class (novaclient.v2.volumes.Volume)
700 """
701 try:
702 volumes = self._nv_drv.volumes.get_server_volumes(server_id=server_id)
703 except Exception as e:
704 self.log.exception("Get volume information failed. Exception: %s", str(e))
705 raise
706
707 volume_info = [v.to_dict() for v in volumes]
708 return volume_info
709
710
711 def group_list(self):
712 """
713 List of Server Affinity and Anti-Affinity Groups
714
715 Arguments:
716 None
717 Returns:
718 List of dictionary objects where dictionary is representation of class (novaclient.v2.server_groups.ServerGroup)
719 """
720 try:
721 group_list = self._nv_drv.server_groups.list()
722 except Exception as e:
723 self.log.exception("Server Group List operation failed. Exception: %s", str(e))
724 raise
725
726 group_info = [ group.to_dict() for group in group_list ]
727 return group_info
728
729
730 def security_group_list(self):
731 """
732 List of Security Group
733 Arguments:
734 None
735 Returns:
736 List of dictionary objects representating novaclient.v2.security_groups.SecurityGroup class
737 """
738 try:
739 sec_groups = self._nv_drv.security_groups.list()
740 except Exception as e:
741 self.log.exception("Security Group List operation failed. Exception: %s", str(e))
742 raise
743 sec_info = [ sec_group.to_dict() for sec_group in sec_groups]
744 return sec_info
745