4 # Copyright 2017 RIFT.IO Inc
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 from novaclient
import client
as nvclient
22 class NovaDrvAPIVersionException(Exception):
23 def __init__(self
, errors
):
25 super(NovaDrvAPIVersionException
, self
).__init
__("Multiple Exception Received")
28 return self
.__repr
__()
31 msg
= "{} : Following Exception(s) have occured during Nova API discovery".format(self
.__class
__)
32 for n
,e
in enumerate(self
.errors
):
34 msg
+= " {}: {}".format(n
, str(e
))
38 class NovaDriver(object):
40 NovaDriver Class for compute orchestration
42 ### List of supported API versions in prioritized order
43 supported_versions
= ["2.1", "2.0"]
47 region_name
= 'RegionOne',
48 service_type
= 'compute',
51 Constructor for NovaDriver class
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)
60 self
.log
= logging
.getLogger('rwcal.openstack.nova')
61 self
.log
.setLevel(logging
.DEBUG
)
65 self
._sess
_handle
= sess_handle
67 self
._max
_api
_version
= None
68 self
._min
_api
_version
= None
70 #### Attempt to use API versions in prioritized order defined in
71 #### NovaDriver.supported_versions
72 def select_version(version
):
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
,
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
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"]
92 except Exception as e
:
96 self
.log
.info("Nova API v%s selected", version
)
97 return (version
, nvdrv
, max_api_version
, min_api_version
)
100 for v
in NovaDriver
.supported_versions
:
102 (self
._version
, self
._nv
_drv
, self
._max
_api
_version
, self
._min
_api
_version
) = select_version(v
)
103 except Exception as e
:
108 raise NovaDrvAPIVersionException(errors
)
111 def project_id(self
):
112 return self
._sess
_handle
.project_id
115 def nova_endpoint(self
):
116 return self
._nv
_drv
.client
.get_endpoint()
119 def nova_quota(self
):
121 Returns Nova Quota (a dictionary) for project
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
))
128 return quota
.to_dict()
130 def extensions_list(self
):
132 Returns a list of available nova extensions.
136 A list of dictionaries. Each dictionary contains attributes for a single NOVA extension
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
))
143 return [ ext
.to_dict() for ext
in extensions
]
146 def _get_nova_connection(self
):
148 Returns instance of object novaclient.client.Client
153 def _flavor_extra_spec_get(self
, flavor
):
155 Get extra_specs associated with a flavor
157 flavor: Object of novaclient.v2.flavors.Flavor
160 A dictionary of extra_specs (key-value pairs)
163 extra_specs
= flavor
.get_keys()
164 except nvclient
.exceptions
.NotFound
:
166 except Exception as e
:
167 self
.log
.exception("Could not get the EPA attributes for flavor with flavor_id : %s. Exception: %s",
172 def _flavor_get(self
, flavor_id
):
174 Get flavor by flavor_id
176 flavor_id(string): UUID of flavor_id
179 dictionary of flavor parameters
182 flavor
= self
._nv
_drv
.flavors
.get(flavor_id
)
183 except nvclient
.exceptions
.NotFound
:
185 except Exception as e
:
186 self
.log
.exception("Did not find flavor with flavor_id : %s. Exception: %s",flavor_id
, str(e
))
188 response
= flavor
.to_dict()
190 response
['extra_specs'] = self
._flavor
_extra
_spec
_get
(flavor
)
191 except nvclient
.exceptions
.NotFound
:
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
))
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",
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
210 def flavor_get(self
, flavor_id
):
212 Get flavor by flavor_id
214 flavor_id(string): UUID of flavor_id
217 dictionary of flavor parameters
219 return self
._flavor
_get
(flavor_id
)
221 def flavor_find(self
, **kwargs
):
223 Returns list of all flavors (dictionary) matching the filters provided in kwargs
226 A dictionary in following keys
228 "vcpus": Number of vcpus required
230 "disk" : Secondary storage in GB
233 A list of dictionaries. Each dictionary contains attributes for a single flavor instance
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
))
242 for f
in flavor_list
:
244 flavor
['extra_specs'] = self
._flavor
_extra
_spec
_get
(f
)
245 flavor_info
.append(flavor
)
249 def flavor_list(self
):
251 Returns list of all flavors (dictionary per flavor)
256 A list of dictionaries. Each dictionary contains attributes for a single flavor instance
262 flavors
= self
._nv
_drv
.flavors
.list()
263 except Exception as e
:
264 self
.log
.exception("List Flavor operation failed. Exception: %s",str(e
))
267 flavor_info
= [ self
.flavor_get(flv
.id) for flv
in flavors
]
270 def flavor_create(self
, name
, ram
, vcpu
, disk
, extra_specs
):
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
282 flavor_id (string): UUID of flavor created
285 flavor
= self
._nv
_drv
.flavors
.create(name
= name
,
294 except Exception as e
:
295 self
.log
.exception("Create Flavor operation failed. Exception: %s",str(e
))
300 flavor
.set_keys(extra_specs
)
301 except Exception as e
:
302 self
.log
.exception("Set Key operation failed for flavor: %s. Exception: %s",
307 def flavor_delete(self
, flavor_id
):
309 Deletes a flavor identified by flavor_id
312 flavor_id (string): UUID of flavor to be deleted
316 assert flavor_id
== self
._flavor
_get
(flavor_id
)['id']
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",
325 def server_list(self
):
327 Returns a list of available VMs for the project
332 A list of dictionaries. Each dictionary contains attributes associated
338 servers
= self
._nv
_drv
.servers
.list()
339 except Exception as e
:
340 self
.log
.exception("List Server operation failed. Exception: %s", str(e
))
342 server_info
= [ server
.to_dict() for server
in servers
]
345 def _nova_server_get(self
, server_id
):
347 Returns a dictionary of attributes associated with VM identified by service_id
350 server_id (string): UUID of the VM/server for which information is requested
353 A dictionary object with attributes associated with VM identified by server_id
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",
362 return server
.to_dict()
364 def server_get(self
, server_id
):
366 Returns a dictionary of attributes associated with VM identified by service_id
369 server_id (string): UUID of the VM/server for which information is requested
372 A dictionary object with attributes associated with VM identified by server_id
374 return self
._nova
_server
_get
(server_id
)
376 def server_create(self
, **kwargs
):
378 Creates a new VM/server instance
381 A dictionary of following key-value pairs
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
395 server_id (string): UUID of the VM/server created
399 if 'network_list' in kwargs
:
400 for network_id
in kwargs
['network_list']:
401 nics
.append({'net-id': network_id
})
403 if 'port_list' in kwargs
:
404 for port_id
in kwargs
['port_list']:
405 port
= { 'port-id': port_id
['id'] }
409 server
= self
._nv
_drv
.servers
.create(
413 meta
= kwargs
['metadata'] if 'metadata' in kwargs
else None,
414 files
= kwargs
['files'] if 'files' in kwargs
else None,
415 reservation_id
= 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,
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
427 except Exception as e
:
428 self
.log
.exception("Create Server operation failed. Exception: %s", str(e
))
430 return server
.to_dict()['id']
432 def server_delete(self
, server_id
):
434 Deletes a server identified by server_id
437 server_id (string): UUID of the server to be deleted
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",
448 def server_start(self
, server_id
):
450 Starts a server identified by server_id
453 server_id (string): UUID of the server to be started
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",
464 def server_stop(self
, server_id
):
467 server_id (string): UUID of the server to be stopped
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",
478 def server_pause(self
, server_id
):
481 server_id (string): UUID of the server to be paused
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",
492 def server_unpause(self
, server_id
):
495 server_id (string): UUID of the server to be unpaused
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",
507 def server_suspend(self
, server_id
):
510 server_id (string): UUID of the server to be suspended
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",
521 def server_resume(self
, server_id
):
524 server_id (string): UUID of the server to be resumed
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",
535 def server_reboot(self
, server_id
, reboot_type
):
538 server_id (string) : UUID of the server to be rebooted
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",
551 def server_console(self
, server_id
, console_type
= 'novnc'):
554 server_id (string) : UUID of the server to be rebooted
555 console_type(string):
559 A dictionary object response for console information
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",
571 def server_rebuild(self
, server_id
, image_id
):
574 server_id (string) : UUID of the server to be rebooted
575 image_id (string) : UUID of the image to use
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",
587 def server_add_port(self
, server_id
, port_id
):
590 server_id (string): UUID of the server
591 port_id (string): UUID of the port to be attached
596 self
._nv
_drv
.servers
.interface_attach(server_id
,
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
))
605 def server_delete_port(self
, server_id
, port_id
):
608 server_id (string): UUID of the server
609 port_id (string): UUID of the port to be deleted
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
))
620 def floating_ip_list(self
):
625 List of objects of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
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
))
635 def floating_ip_create(self
, pool
):
638 pool (string): Name of the pool (optional)
640 An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
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
))
650 def floating_ip_delete(self
, floating_ip
):
653 floating_ip: An object of floating IP nova class (novaclient.v2.floating_ips.FloatingIP)
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
))
663 def floating_ip_assign(self
, server_id
, floating_ip
, fixed_ip
):
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
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
))
678 def floating_ip_release(self
, server_id
, floating_ip
):
681 server_id (string) : UUID of the server
682 floating_ip (string): IP address string for floating-ip
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
))
692 def volume_list(self
, server_id
):
694 List of volumes attached to the server
699 List of dictionary objects where dictionary is representation of class (novaclient.v2.volumes.Volume)
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
))
707 volume_info
= [v
.to_dict() for v
in volumes
]
711 def group_list(self
):
713 List of Server Affinity and Anti-Affinity Groups
718 List of dictionary objects where dictionary is representation of class (novaclient.v2.server_groups.ServerGroup)
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
))
726 group_info
= [ group
.to_dict() for group
in group_list
]
730 def security_group_list(self
):
732 List of Security Group
736 List of dictionary objects representating novaclient.v2.security_groups.SecurityGroup class
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
))
743 sec_info
= [ sec_group
.to_dict() for sec_group
in sec_groups
]