4 # Copyright 2016 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.
21 from . import session
as sess_drv
22 from . import keystone
as ks_drv
23 from . import nova
as nv_drv
24 from . import neutron
as nt_drv
25 from . import glance
as gl_drv
26 from . import ceilometer
as ce_drv
27 from . import cinder
as ci_drv
28 from . import utils
as drv_utils
31 import keystoneclient
.exceptions
as KeystoneExceptions
34 class ValidationError(Exception):
38 class DriverUtilities(object):
40 Class with utility method
42 def __init__(self
, driver
):
44 Constructor of DriverUtilities class
46 driver: Object of OpenstackDriver
48 self
.flavor_utils
= drv_utils
.FlavorUtils(driver
)
49 self
.network_utils
= drv_utils
.NetworkUtils(driver
)
50 self
.image_utils
= drv_utils
.ImageUtils(driver
)
51 self
.compute_utils
= drv_utils
.ComputeUtils(driver
)
55 return self
.flavor_utils
59 return self
.compute_utils
63 return self
.network_utils
67 return self
.image_utils
70 class OpenstackDriver(object):
72 Driver for openstack nova, neutron, glance, keystone, swift, cinder services
74 def __init__(self
, logger
= None, **kwargs
):
76 OpenstackDriver Driver constructor
78 logger: (instance of logging.Logger)
79 kwargs: A dictionary of
81 username (string) : Username for project/tenant.
82 password (string) : Password
83 auth_url (string) : Keystone Authentication URL.
84 project (string) : Openstack project name
85 mgmt_network(string, optional) : Management network name. Each VM created with this cloud-account will
86 have a default interface into management network.
87 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
88 user_domain : Domain name for user
89 project_domain : Domain name for project
95 self
.log
= logging
.getLogger('rwcal.openstack.driver')
96 self
.log
.setLevel(logging
.DEBUG
)
100 args
= dict(auth_url
= kwargs
['auth_url'],
101 username
= kwargs
['username'],
102 password
= kwargs
['password'],
103 project_name
= kwargs
['project'],
104 project_domain_name
= kwargs
['project_domain'] if 'project_domain' in kwargs
else None,
105 user_domain_name
= kwargs
['user_domain'] if 'user_domain' in kwargs
else None,)
107 cert_validate
= kwargs
['cert_validate'] if 'cert_validate' in kwargs
else False
108 region
= kwargs
['region_name'] if 'region_name' in kwargs
else False
109 mgmt_network
= kwargs
['mgmt_network'] if 'mgmt_network' in kwargs
else None
111 discover
= ks_drv
.KeystoneVersionDiscover(kwargs
['auth_url'], logger
= self
.log
)
112 (major
, minor
) = discover
.get_version()
114 self
.sess_drv
= sess_drv
.SessionDriver(auth_method
= 'password',
115 version
= str(major
),
116 cert_validate
= cert_validate
,
120 self
.ks_drv
= ks_drv
.KeystoneDriver(str(major
),
124 self
.nova_drv
= nv_drv
.NovaDriver(self
.sess_drv
,
125 region_name
= region
,
128 self
.neutron_drv
= nt_drv
.NeutronDriver(self
.sess_drv
,
129 region_name
= region
,
132 self
.glance_drv
= gl_drv
.GlanceDriver(self
.sess_drv
,
133 region_name
= region
,
136 self
.cinder_drv
= ci_drv
.CinderDriver(self
.sess_drv
,
137 region_name
= region
,
140 self
.ceilo_drv
= ce_drv
.CeilometerDriver(self
.sess_drv
,
141 region_name
= region
,
144 self
.utils
= DriverUtilities(self
)
146 self
._mgmt
_network
= mgmt_network
148 self
._cache
= dict(neutron
= dict(),
152 self
.build_resource_cache()
155 def nova_cache(self
):
156 return self
._cache
['nova']
159 def neutron_cache(self
):
160 return self
._cache
['neutron']
163 def glance_cache(self
):
164 return self
._cache
['glance']
167 def cinder_cache(self
):
168 return self
._cache
['cinder']
170 def build_resource_cache(self
):
171 self
.build_network_resource_cache()
172 self
.build_nova_resource_cache()
173 self
.build_cinder_resource_cache()
174 self
.build_glance_resource_cache()
176 def _cache_populate(self
, method
, datatype
, *args
, **kwargs
):
178 rsp
= method(*args
, **kwargs
)
179 except Exception as e
:
180 self
.log
.exception("Exception %s occured during execution of %s",
186 def _build_nova_security_group_list(self
):
187 self
.log
.info("Building Nova security group cache")
188 self
.nova_cache
['security_groups'] = self
._cache
_populate
(self
.nova_drv
.security_group_list
,
190 return self
.nova_cache
['security_groups']
192 def _build_nova_affinity_group_list(self
):
193 self
.log
.info("Building Nova affinity/anti-affinity group cache")
194 self
.nova_cache
['affinity_groups'] = self
._cache
_populate
(self
.nova_server_group_list
,
196 return self
.nova_cache
['affinity_groups']
198 def _build_neutron_security_group_list(self
):
199 self
.log
.info("Discovering neutron security group")
200 self
.neutron_cache
['security_groups'] = self
._cache
_populate
(self
.neutron_security_group_list
,
202 return self
.neutron_cache
['security_groups']
204 def _build_neutron_subnet_prefix_list(self
):
205 self
.log
.info("Discovering subnet prefix pools")
206 self
.neutron_cache
['subnet_pool'] = self
._cache
_populate
(self
.neutron_subnetpool_list
,
208 return self
.neutron_cache
['subnet_pool']
210 def _get_neutron_mgmt_network(self
):
211 if self
._mgmt
_network
:
212 self
.log
.info("Discovering management network %s", self
._mgmt
_network
)
213 network_list
= self
._cache
_populate
(self
.neutron_drv
.network_get
,
215 **{'network_name':self
._mgmt
_network
})
217 self
.neutron_cache
['mgmt_net'] = network_list
['id']
219 raise KeyError("Error")
222 def _build_glance_image_list(self
):
223 self
.log
.info("Discovering images")
224 self
.glance_cache
['images'] = self
._cache
_populate
(self
.glance_image_list
,
227 return self
.glance_cache
['images']
229 def _build_cinder_volume_list(self
):
230 self
.log
.info("Discovering volumes")
231 vollist
= self
.cinder_volume_list()
232 self
.cinder_cache
['volumes'] = self
._cache
_populate
(self
.cinder_volume_list
,
234 return self
.cinder_cache
['volumes']
236 def build_nova_resource_cache(self
):
237 self
.log
.info("Building nova resource cache")
238 self
._build
_nova
_security
_group
_list
()
239 self
._build
_nova
_affinity
_group
_list
()
242 def build_network_resource_cache(self
):
243 self
.log
.info("Building network resource cache")
244 self
._get
_neutron
_mgmt
_network
()
245 self
._build
_neutron
_security
_group
_list
()
246 self
._build
_neutron
_subnet
_prefix
_list
()
248 def build_cinder_resource_cache(self
):
249 self
.log
.info("Building cinder resource cache")
250 self
._build
_cinder
_volume
_list
()
252 def build_glance_resource_cache(self
):
253 self
.log
.info("Building glance resource cache")
254 self
._build
_glance
_image
_list
()
258 def _nova_affinity_group(self
):
259 if 'affinity_groups' in self
.nova_cache
:
260 return self
.nova_cache
['affinity_groups']
262 return self
._build
_nova
_affinity
_group
_list
()
265 def _nova_security_groups(self
):
266 if 'security_groups' in self
.nova_cache
:
267 return self
.nova_cache
['security_groups']
269 return self
._build
_nova
_security
_group
_list
()
272 def mgmt_network(self
):
273 return self
._mgmt
_network
276 def _mgmt_network_id(self
):
277 if 'mgmt_net' in self
.neutron_cache
:
278 return self
.neutron_cache
['mgmt_net']
283 def _neutron_security_groups(self
):
284 if 'security_groups' in self
.neutron_cache
:
285 return self
.neutron_cache
['security_groups']
287 return self
._build
_neutron
_security
_group
_list
()
290 def _neutron_subnet_prefix_pool(self
):
291 if 'subnet_pool' in self
.neutron_cache
:
292 return self
.neutron_cache
['subnet_pool']
294 return self
._build
_neutron
_subnet
_prefix
_list
()
297 def _glance_image_list(self
):
298 if 'images' in self
.glance_cache
:
299 return self
.glance_cache
['images']
301 return self
._build
_glance
_image
_list
()
304 def _cinder_volume_list(self
):
305 if 'volumes' in self
.cinder_cache
:
306 return self
.cinder_cache
['volumes']
308 return self
._build
_cinder
_volume
_list
()
310 def validate_account_creds(self
):
312 self
.sess_drv
.invalidate_auth_token()
313 self
.sess_drv
.auth_token
314 self
.build_resource_cache()
315 except KeystoneExceptions
.AuthorizationFailure
as e
:
316 self
.log
.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e
))
317 raise ValidationError("Invalid Credentials: "+ str(e
))
318 except Exception as e
:
319 self
.log
.error("Could not connect to Openstack. Exception: %s", str(e
))
320 raise ValidationError("Connection Error: "+ str(e
))
323 def glance_image_create(self
, **kwargs
):
324 if not 'disk_format' in kwargs
:
325 kwargs
['disk_format'] = 'qcow2'
326 if not 'container_format' in kwargs
:
327 kwargs
['container_format'] = 'bare'
328 if not 'min_disk' in kwargs
:
329 kwargs
['min_disk'] = 0
330 if not 'min_ram' in kwargs
:
331 kwargs
['min_ram'] = 0
332 return self
.glance_drv
.image_create(**kwargs
)
334 def glance_image_upload(self
, image_id
, fd
):
335 self
.glance_drv
.image_upload(image_id
, fd
)
337 def glance_image_add_location(self
, image_id
, location
):
338 self
.glance_drv
.image_add_location(image_id
, location
)
340 def glance_image_update(self
, image_id
, remove_props
= None, **kwargs
):
341 self
.glance_drv
.image_update(image_id
, remove_props
=remove_props
, **kwargs
)
343 def glance_image_delete(self
, image_id
):
344 self
.glance_drv
.image_delete(image_id
)
346 def glance_image_list(self
):
347 return self
.glance_drv
.image_list()
349 def glance_image_get(self
, image_id
):
350 return self
.glance_drv
.image_get(image_id
)
352 def nova_flavor_list(self
):
353 return self
.nova_drv
.flavor_list()
355 def nova_flavor_find(self
, **kwargs
):
356 return self
.nova_drv
.flavor_find(**kwargs
)
358 def nova_flavor_create(self
, name
, ram
, vcpus
, disk
, epa_specs
= dict()):
359 return self
.nova_drv
.flavor_create(name
,
363 extra_specs
= epa_specs
)
365 def nova_flavor_delete(self
, flavor_id
):
366 self
.nova_drv
.flavor_delete(flavor_id
)
368 def nova_flavor_get(self
, flavor_id
):
369 return self
.nova_drv
.flavor_get(flavor_id
)
371 def nova_server_create(self
, **kwargs
):
372 if 'security_groups' not in kwargs
:
373 kwargs
['security_groups'] = [ s
['name'] for s
in self
._nova
_security
_groups
]
374 return self
.nova_drv
.server_create(**kwargs
)
376 def nova_server_add_port(self
, server_id
, port_id
):
377 self
.nova_drv
.server_add_port(server_id
, port_id
)
379 def nova_server_delete_port(self
, server_id
, port_id
):
380 self
.nova_drv
.server_delete_port(server_id
, port_id
)
382 def nova_server_start(self
, server_id
):
383 self
.nova_drv
.server_start(server_id
)
385 def nova_server_stop(self
, server_id
):
386 self
.nova_drv
.server_stop(server_id
)
388 def nova_server_delete(self
, server_id
):
389 self
.nova_drv
.server_delete(server_id
)
391 def nova_server_reboot(self
, server_id
):
392 self
.nova_drv
.server_reboot(server_id
, reboot_type
='HARD')
394 def nova_server_rebuild(self
, server_id
, image_id
):
395 self
.nova_drv
.server_rebuild(server_id
, image_id
)
397 def nova_floating_ip_list(self
):
398 return self
.nova_drv
.floating_ip_list()
400 def nova_floating_ip_create(self
, pool
= None):
401 return self
.nova_drv
.floating_ip_create(pool
)
403 def nova_floating_ip_delete(self
, floating_ip
):
404 self
.nova_drv
.floating_ip_delete(floating_ip
)
406 def nova_floating_ip_assign(self
, server_id
, floating_ip
, fixed_ip
):
407 self
.nova_drv
.floating_ip_assign(server_id
, floating_ip
, fixed_ip
)
409 def nova_floating_ip_release(self
, server_id
, floating_ip
):
410 self
.nova_drv
.floating_ip_release(server_id
, floating_ip
)
412 def nova_server_list(self
):
413 return self
.nova_drv
.server_list()
415 def nova_server_get(self
, server_id
):
416 return self
.nova_drv
.server_get(server_id
)
418 def nova_server_console(self
, server_id
):
419 return self
.nova_drv
.server_console(server_id
)
421 def nova_server_group_list(self
):
422 return self
.nova_drv
.group_list()
424 def nova_volume_list(self
, server_id
):
425 return self
.nova_drv
.volume_list(server_id
)
427 def neutron_network_list(self
):
428 return self
.neutron_drv
.network_list()
430 def neutron_network_get(self
, network_id
):
431 return self
.neutron_drv
.network_get(network_id
=network_id
)
433 def neutron_network_create(self
, **kwargs
):
434 return self
.neutron_drv
.network_create(**kwargs
)
436 def neutron_network_delete(self
, network_id
):
437 self
.neutron_drv
.network_delete(network_id
)
439 def neutron_subnet_list(self
):
440 return self
.neutron_drv
.subnet_list(**{})
442 def neutron_subnet_get(self
, subnet_id
):
443 return self
.neutron_drv
.subnet_get(subnet_id
)
445 def neutron_subnet_create(self
, **kwargs
):
446 return self
.neutron_drv
.subnet_create(**kwargs
)
448 def netruon_subnet_delete(self
, subnet_id
):
449 self
.neutron_drv
.subnet_delete(subnet_id
)
451 def neutron_subnetpool_list(self
):
452 return self
.neutron_drv
.subnetpool_list()
454 def netruon_subnetpool_by_name(self
, pool_name
):
455 pool_list
= self
.neutron_drv
.subnetpool_list(**{'name': pool_name
})
461 def neutron_port_list(self
, **kwargs
):
462 return self
.neutron_drv
.port_list(**kwargs
)
464 def neutron_port_get(self
, port_id
):
465 return self
.neutron_drv
.port_get(port_id
)
467 def neutron_port_create(self
, **kwargs
):
468 port_id
= self
.neutron_drv
.port_create([kwargs
])[0]
469 if 'vm_id' in kwargs
:
470 self
.nova_server_add_port(kwargs
['vm_id'], port_id
)
473 def neutron_multi_port_create(self
, ports
):
474 return self
.neutron_drv
.port_create(ports
)
476 def neutron_security_group_list(self
):
477 return self
.neutron_drv
.security_group_list(**{})
479 def neutron_security_group_by_name(self
, group_name
):
480 group_list
= self
.neutron_drv
.security_group_list(**{'name': group_name
})
486 def neutron_port_delete(self
, port_id
):
487 self
.neutron_drv
.port_delete(port_id
)
489 def ceilo_meter_endpoint(self
):
490 return self
.ceilo_drv
.endpoint
492 def ceilo_meter_list(self
):
493 return self
.ceilo_drv
.meters
495 def ceilo_nfvi_metrics(self
, vim_id
):
496 """Returns a dict of NFVI metrics for a given VM
499 vim_id - the VIM ID of the VM to retrieve the metrics for
502 A dict of NFVI metrics
505 return self
.ceilo_drv
.nfvi_metrics(vim_id
)
507 def ceilo_alarm_list(self
):
508 """Returns a list of ceilometer alarms"""
509 return self
.ceilo_drv
.client
.alarms
.list()
511 def ceilo_alarm_create(self
,
524 """Create a new Alarm
527 name - the name of the alarm
528 meter - the name of the meter to measure
529 statistic - the type of statistic used to trigger the alarm
530 ('avg', 'min', 'max', 'count', 'sum')
531 operation - the relational operator that, combined with the
532 threshold value, determines when the alarm is
533 triggered ('lt', 'le', 'eq', 'ge', 'gt')
534 threshold - the value of the statistic that will trigger the
536 period - the duration (seconds) over which to evaluate the
538 evaluations - the number of samples of the meter statistic to
539 collect when evaluating the threshold
540 severity - a measure of the urgency or importance of the alarm
541 ('low', 'moderate', 'critical')
542 repeat - a flag that indicates whether the alarm should be
543 triggered once (False) or repeatedly while the alarm
544 condition is true (True)
545 enabled - a flag that indicates whether the alarm is enabled
546 (True) or disabled (False)
547 actions - a dict specifying the URLs for webhooks. The dict can
548 have up to 3 keys: 'insufficient_data', 'alarm',
549 'ok'. Each key is associated with a list of URLs to
550 webhooks that will be invoked when one of the 3
552 kwargs - an arbitrary dict of keyword arguments that are
553 passed to the ceilometer client
556 ok_actions
= actions
.get('ok') if actions
is not None else None
557 alarm_actions
= actions
.get('alarm') if actions
is not None else None
558 insufficient_data_actions
= actions
.get('insufficient_data') if actions
is not None else None
560 return self
.ceilo_drv
.client
.alarms
.create(name
=name
,
563 comparison_operator
=operation
,
566 evaluation_periods
=evaluations
,
568 repeat_actions
=repeat
,
570 ok_actions
=ok_actions
,
571 alarm_actions
=alarm_actions
,
572 insufficient_data_actions
=insufficient_data_actions
,
575 def ceilo_alarm_update(self
, alarm_id
, **kwargs
):
576 """Updates an existing alarm
579 alarm_id - the identifier of the alarm to update
580 kwargs - a dict of the alarm attributes to update
583 return self
.ceilo_drv
.client
.alarms
.update(alarm_id
, **kwargs
)
585 def ceilo_alarm_delete(self
, alarm_id
):
586 self
.ceilo_drv
.client
.alarms
.delete(alarm_id
)
588 def cinder_volume_list(self
):
589 return self
.cinder_drv
.volume_list()
591 def cinder_volume_get(self
,vol_id
):
592 return self
.cinder_drv
.volume_get(vol_id
)
594 def cinder_volume_set_metadata(self
, volumeid
, metadata
):
595 return self
.cinder_drv
.volume_set_metadata(volumeid
, metadata
)
597 def cinder_volume_delete_metadata(self
, volumeid
, metadata
):
598 return self
.cinder_drv
.volume_delete_metadata(volumeid
, metadata
)