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 portchain
as port_drv
29 from . import utils
as drv_utils
32 import keystoneclient
.exceptions
as KeystoneExceptions
35 class ValidationError(Exception):
39 class DriverUtilities(object):
41 Class with utility method
43 def __init__(self
, driver
):
45 Constructor of DriverUtilities class
47 driver: Object of OpenstackDriver
49 self
.flavor_utils
= drv_utils
.FlavorUtils(driver
)
50 self
.network_utils
= drv_utils
.NetworkUtils(driver
)
51 self
.image_utils
= drv_utils
.ImageUtils(driver
)
52 self
.compute_utils
= drv_utils
.ComputeUtils(driver
)
56 return self
.flavor_utils
60 return self
.compute_utils
64 return self
.network_utils
68 return self
.image_utils
71 class OpenstackDriver(object):
73 Driver for openstack nova, neutron, glance, keystone, swift, cinder services
75 def __init__(self
, logger
= None, **kwargs
):
77 OpenstackDriver Driver constructor
79 logger: (instance of logging.Logger)
80 kwargs: A dictionary of
82 username (string) : Username for project/tenant.
83 password (string) : Password
84 auth_url (string) : Keystone Authentication URL.
85 project (string) : Openstack project name
86 mgmt_network(string, optional) : Management network name. Each VM created with this cloud-account will
87 have a default interface into management network.
88 cert_validate (boolean, optional) : In case of SSL/TLS connection if certificate validation is required or not.
89 user_domain : Domain name for user
90 project_domain : Domain name for project
96 self
.log
= logging
.getLogger('rwcal.openstack.driver')
97 self
.log
.setLevel(logging
.DEBUG
)
101 args
= dict(auth_url
= kwargs
['auth_url'],
102 username
= kwargs
['username'],
103 password
= kwargs
['password'],
104 project_name
= kwargs
['project'],
105 project_domain_name
= kwargs
['project_domain'] if 'project_domain' in kwargs
else None,
106 user_domain_name
= kwargs
['user_domain'] if 'user_domain' in kwargs
else None,)
108 cert_validate
= kwargs
['cert_validate'] if 'cert_validate' in kwargs
else False
109 region
= kwargs
['region_name'] if 'region_name' in kwargs
else False
110 mgmt_network
= kwargs
['mgmt_network'] if 'mgmt_network' in kwargs
else None
112 discover
= ks_drv
.KeystoneVersionDiscover(kwargs
['auth_url'], logger
= self
.log
)
113 (major
, minor
) = discover
.get_version()
115 self
.sess_drv
= sess_drv
.SessionDriver(auth_method
= 'password',
116 version
= str(major
),
117 cert_validate
= cert_validate
,
121 self
.ks_drv
= ks_drv
.KeystoneDriver(str(major
),
125 self
.nova_drv
= nv_drv
.NovaDriver(self
.sess_drv
,
126 region_name
= region
,
129 self
.neutron_drv
= nt_drv
.NeutronDriver(self
.sess_drv
,
130 region_name
= region
,
133 self
.glance_drv
= gl_drv
.GlanceDriver(self
.sess_drv
,
134 region_name
= region
,
137 self
.cinder_drv
= ci_drv
.CinderDriver(self
.sess_drv
,
138 region_name
= region
,
141 self
.ceilo_drv
= ce_drv
.CeilometerDriver(self
.sess_drv
,
142 region_name
= region
,
145 self
.portchain_drv
= port_drv
.L2PortChainDriver(self
.sess_drv
,
148 self
.utils
= DriverUtilities(self
)
150 self
._mgmt
_network
= mgmt_network
152 self
._cache
= dict(neutron
= dict(),
156 self
.build_resource_cache()
159 def nova_cache(self
):
160 return self
._cache
['nova']
163 def neutron_cache(self
):
164 return self
._cache
['neutron']
167 def glance_cache(self
):
168 return self
._cache
['glance']
171 def cinder_cache(self
):
172 return self
._cache
['cinder']
174 def build_resource_cache(self
):
175 self
.build_network_resource_cache()
176 self
.build_nova_resource_cache()
177 self
.build_cinder_resource_cache()
178 self
.build_glance_resource_cache()
180 def _cache_populate(self
, method
, datatype
, *args
, **kwargs
):
182 rsp
= method(*args
, **kwargs
)
183 except Exception as e
:
184 self
.log
.exception("Exception %s occured during execution of %s",
190 def _build_nova_security_group_list(self
):
191 self
.log
.info("Building Nova security group cache")
192 self
.nova_cache
['security_groups'] = self
._cache
_populate
(self
.nova_drv
.security_group_list
,
194 return self
.nova_cache
['security_groups']
196 def _build_nova_affinity_group_list(self
):
197 self
.log
.info("Building Nova affinity/anti-affinity group cache")
198 self
.nova_cache
['affinity_groups'] = self
._cache
_populate
(self
.nova_server_group_list
,
200 return self
.nova_cache
['affinity_groups']
202 def _build_neutron_security_group_list(self
):
203 self
.log
.info("Discovering neutron security group")
204 self
.neutron_cache
['security_groups'] = self
._cache
_populate
(self
.neutron_security_group_list
,
206 return self
.neutron_cache
['security_groups']
208 def _build_neutron_subnet_prefix_list(self
):
209 self
.log
.info("Discovering subnet prefix pools")
210 self
.neutron_cache
['subnet_pool'] = self
._cache
_populate
(self
.neutron_subnetpool_list
,
212 return self
.neutron_cache
['subnet_pool']
214 def _get_neutron_mgmt_network(self
):
215 if self
._mgmt
_network
:
216 self
.log
.info("Discovering management network %s", self
._mgmt
_network
)
217 network_list
= self
._cache
_populate
(self
.neutron_drv
.network_get
,
219 **{'network_name':self
._mgmt
_network
})
221 self
.neutron_cache
['mgmt_net'] = network_list
['id']
223 raise KeyError("Error")
226 def _build_glance_image_list(self
):
227 self
.log
.info("Discovering images")
228 self
.glance_cache
['images'] = self
._cache
_populate
(self
.glance_image_list
,
230 return self
.glance_cache
['images']
233 def build_nova_resource_cache(self
):
234 self
.log
.info("Building nova resource cache")
235 self
._build
_nova
_security
_group
_list
()
236 self
._build
_nova
_affinity
_group
_list
()
239 def build_network_resource_cache(self
):
240 self
.log
.info("Building network resource cache")
241 self
._get
_neutron
_mgmt
_network
()
242 self
._build
_neutron
_security
_group
_list
()
243 self
._build
_neutron
_subnet
_prefix
_list
()
245 def build_cinder_resource_cache(self
):
249 def build_glance_resource_cache(self
):
250 self
.log
.info("Building glance resource cache")
251 self
._build
_glance
_image
_list
()
255 def _nova_affinity_group(self
):
256 if 'affinity_groups' in self
.nova_cache
:
257 return self
.nova_cache
['affinity_groups']
259 return self
._build
_nova
_affinity
_group
_list
()
262 def _nova_security_groups(self
):
263 if 'security_groups' in self
.nova_cache
:
264 return self
.nova_cache
['security_groups']
266 return self
._build
_nova
_security
_group
_list
()
269 def mgmt_network(self
):
270 return self
._mgmt
_network
273 def _mgmt_network_id(self
):
274 if 'mgmt_net' in self
.neutron_cache
:
275 return self
.neutron_cache
['mgmt_net']
280 def _neutron_security_groups(self
):
281 if 'security_groups' in self
.neutron_cache
:
282 return self
.neutron_cache
['security_groups']
284 return self
._build
_neutron
_security
_group
_list
()
287 def _neutron_subnet_prefix_pool(self
):
288 if 'subnet_pool' in self
.neutron_cache
:
289 return self
.neutron_cache
['subnet_pool']
291 return self
._build
_neutron
_subnet
_prefix
_list
()
294 def _glance_image_list(self
):
295 if 'images' in self
.glance_cache
:
296 return self
.glance_cache
['images']
298 return self
._build
_glance
_image
_list
()
300 def validate_account_creds(self
):
302 self
.sess_drv
.invalidate_auth_token()
303 self
.sess_drv
.auth_token
304 self
.build_resource_cache()
305 except KeystoneExceptions
.AuthorizationFailure
as e
:
306 self
.log
.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e
))
307 raise ValidationError("Invalid Credentials: "+ str(e
))
308 except Exception as e
:
309 self
.log
.error("Could not connect to Openstack. Exception: %s", str(e
))
310 raise ValidationError("Connection Error: "+ str(e
))
313 def glance_image_create(self
, **kwargs
):
314 if not 'disk_format' in kwargs
:
315 kwargs
['disk_format'] = 'qcow2'
316 if not 'container_format' in kwargs
:
317 kwargs
['container_format'] = 'bare'
318 if not 'min_disk' in kwargs
:
319 kwargs
['min_disk'] = 0
320 if not 'min_ram' in kwargs
:
321 kwargs
['min_ram'] = 0
322 return self
.glance_drv
.image_create(**kwargs
)
324 def glance_image_upload(self
, image_id
, fd
):
325 self
.glance_drv
.image_upload(image_id
, fd
)
327 def glance_image_add_location(self
, image_id
, location
):
328 self
.glance_drv
.image_add_location(image_id
, location
)
330 def glance_image_update(self
, image_id
, remove_props
= None, **kwargs
):
331 self
.glance_drv
.image_update(image_id
, remove_props
=remove_props
, **kwargs
)
333 def glance_image_delete(self
, image_id
):
334 self
.glance_drv
.image_delete(image_id
)
336 def glance_image_list(self
):
337 return self
.glance_drv
.image_list()
339 def glance_image_get(self
, image_id
):
340 return self
.glance_drv
.image_get(image_id
)
342 def nova_flavor_list(self
):
343 return self
.nova_drv
.flavor_list()
345 def nova_flavor_find(self
, **kwargs
):
346 return self
.nova_drv
.flavor_find(**kwargs
)
348 def nova_flavor_create(self
, name
, ram
, vcpus
, disk
, epa_specs
= dict()):
349 return self
.nova_drv
.flavor_create(name
,
353 extra_specs
= epa_specs
)
355 def nova_flavor_delete(self
, flavor_id
):
356 self
.nova_drv
.flavor_delete(flavor_id
)
358 def nova_flavor_get(self
, flavor_id
):
359 return self
.nova_drv
.flavor_get(flavor_id
)
361 def nova_server_create(self
, **kwargs
):
362 if 'security_groups' not in kwargs
:
363 kwargs
['security_groups'] = [ s
['name'] for s
in self
._nova
_security
_groups
]
364 return self
.nova_drv
.server_create(**kwargs
)
366 def nova_server_add_port(self
, server_id
, port_id
):
367 self
.nova_drv
.server_add_port(server_id
, port_id
)
369 def nova_server_delete_port(self
, server_id
, port_id
):
370 self
.nova_drv
.server_delete_port(server_id
, port_id
)
372 def nova_server_start(self
, server_id
):
373 self
.nova_drv
.server_start(server_id
)
375 def nova_server_stop(self
, server_id
):
376 self
.nova_drv
.server_stop(server_id
)
378 def nova_server_delete(self
, server_id
):
379 self
.nova_drv
.server_delete(server_id
)
381 def nova_server_reboot(self
, server_id
):
382 self
.nova_drv
.server_reboot(server_id
, reboot_type
='HARD')
384 def nova_server_rebuild(self
, server_id
, image_id
):
385 self
.nova_drv
.server_rebuild(server_id
, image_id
)
387 def nova_floating_ip_list(self
):
388 return self
.nova_drv
.floating_ip_list()
390 def nova_floating_ip_create(self
, pool
= None):
391 return self
.nova_drv
.floating_ip_create(pool
)
393 def nova_floating_ip_delete(self
, floating_ip
):
394 self
.nova_drv
.floating_ip_delete(floating_ip
)
396 def nova_floating_ip_assign(self
, server_id
, floating_ip
, fixed_ip
):
397 self
.nova_drv
.floating_ip_assign(server_id
, floating_ip
, fixed_ip
)
399 def nova_floating_ip_release(self
, server_id
, floating_ip
):
400 self
.nova_drv
.floating_ip_release(server_id
, floating_ip
)
402 def nova_server_list(self
):
403 return self
.nova_drv
.server_list()
405 def nova_server_get(self
, server_id
):
406 return self
.nova_drv
.server_get(server_id
)
408 def nova_server_console(self
, server_id
):
409 return self
.nova_drv
.server_console(server_id
)
411 def nova_server_group_list(self
):
412 return self
.nova_drv
.group_list()
414 def nova_volume_list(self
, server_id
):
415 return self
.nova_drv
.volume_list(server_id
)
417 def neutron_network_list(self
):
418 return self
.neutron_drv
.network_list()
420 def neutron_network_get(self
, network_id
):
421 return self
.neutron_drv
.network_get(network_id
=network_id
)
423 def neutron_network_create(self
, **kwargs
):
424 return self
.neutron_drv
.network_create(**kwargs
)
426 def neutron_network_delete(self
, network_id
):
427 self
.neutron_drv
.network_delete(network_id
)
429 def neutron_subnet_list(self
):
430 return self
.neutron_drv
.subnet_list(**{})
432 def neutron_subnet_get(self
, subnet_id
):
433 return self
.neutron_drv
.subnet_get(subnet_id
)
435 def neutron_subnet_create(self
, **kwargs
):
436 return self
.neutron_drv
.subnet_create(**kwargs
)
438 def netruon_subnet_delete(self
, subnet_id
):
439 self
.neutron_drv
.subnet_delete(subnet_id
)
441 def neutron_subnetpool_list(self
):
442 return self
.neutron_drv
.subnetpool_list()
444 def netruon_subnetpool_by_name(self
, pool_name
):
445 pool_list
= self
.neutron_drv
.subnetpool_list(**{'name': pool_name
})
451 def neutron_port_list(self
, **kwargs
):
452 return self
.neutron_drv
.port_list(**kwargs
)
454 def neutron_port_get(self
, port_id
):
455 return self
.neutron_drv
.port_get(port_id
)
457 def neutron_port_create(self
, **kwargs
):
458 port_id
= self
.neutron_drv
.port_create([kwargs
])[0]
459 if 'vm_id' in kwargs
:
460 self
.nova_server_add_port(kwargs
['vm_id'], port_id
)
463 def neutron_multi_port_create(self
, ports
):
464 return self
.neutron_drv
.port_create(ports
)
466 def neutron_security_group_list(self
):
467 return self
.neutron_drv
.security_group_list(**{})
469 def neutron_security_group_by_name(self
, group_name
):
470 group_list
= self
.neutron_drv
.security_group_list(**{'name': group_name
})
476 def neutron_port_delete(self
, port_id
):
477 self
.neutron_drv
.port_delete(port_id
)
479 def ceilo_meter_endpoint(self
):
480 return self
.ceilo_drv
.endpoint
482 def ceilo_meter_list(self
):
483 return self
.ceilo_drv
.meters
485 def ceilo_nfvi_metrics(self
, vim_id
):
486 """Returns a dict of NFVI metrics for a given VM
489 vim_id - the VIM ID of the VM to retrieve the metrics for
492 A dict of NFVI metrics
495 return self
.ceilo_drv
.nfvi_metrics(vim_id
)
497 def ceilo_alarm_list(self
):
498 """Returns a list of ceilometer alarms"""
499 return self
.ceilo_drv
.client
.alarms
.list()
501 def ceilo_alarm_create(self
,
514 """Create a new Alarm
517 name - the name of the alarm
518 meter - the name of the meter to measure
519 statistic - the type of statistic used to trigger the alarm
520 ('avg', 'min', 'max', 'count', 'sum')
521 operation - the relational operator that, combined with the
522 threshold value, determines when the alarm is
523 triggered ('lt', 'le', 'eq', 'ge', 'gt')
524 threshold - the value of the statistic that will trigger the
526 period - the duration (seconds) over which to evaluate the
528 evaluations - the number of samples of the meter statistic to
529 collect when evaluating the threshold
530 severity - a measure of the urgency or importance of the alarm
531 ('low', 'moderate', 'critical')
532 repeat - a flag that indicates whether the alarm should be
533 triggered once (False) or repeatedly while the alarm
534 condition is true (True)
535 enabled - a flag that indicates whether the alarm is enabled
536 (True) or disabled (False)
537 actions - a dict specifying the URLs for webhooks. The dict can
538 have up to 3 keys: 'insufficient_data', 'alarm',
539 'ok'. Each key is associated with a list of URLs to
540 webhooks that will be invoked when one of the 3
542 kwargs - an arbitrary dict of keyword arguments that are
543 passed to the ceilometer client
546 ok_actions
= actions
.get('ok') if actions
is not None else None
547 alarm_actions
= actions
.get('alarm') if actions
is not None else None
548 insufficient_data_actions
= actions
.get('insufficient_data') if actions
is not None else None
550 return self
.ceilo_drv
.client
.alarms
.create(name
=name
,
553 comparison_operator
=operation
,
556 evaluation_periods
=evaluations
,
558 repeat_actions
=repeat
,
560 ok_actions
=ok_actions
,
561 alarm_actions
=alarm_actions
,
562 insufficient_data_actions
=insufficient_data_actions
,
565 def ceilo_alarm_update(self
, alarm_id
, **kwargs
):
566 """Updates an existing alarm
569 alarm_id - the identifier of the alarm to update
570 kwargs - a dict of the alarm attributes to update
573 return self
.ceilo_drv
.client
.alarms
.update(alarm_id
, **kwargs
)
575 def ceilo_alarm_delete(self
, alarm_id
):
576 self
.ceilo_drv
.client
.alarms
.delete(alarm_id
)
578 def create_port_chain(self
,name
,port_lists
):
582 for index
,port_pair
in enumerate(port_lists
):
584 ingress_port
,egress_port
= port_pair
585 #Disable security group and port security for the port
586 self
.neutron_drv
.port_update(ingress_port
,no_security_groups
=True,port_security_enabled
=False)
587 if ingress_port
!= egress_port
:
588 self
.neutron_drv
.port_update(egress_port
,no_security_groups
=True,port_security_enabled
=False)
590 ppair_id
= self
.portchain_drv
.create_port_pair(name
+'ppair'+str(index
),ingress_port
,egress_port
)
591 ppair_list
.append(ppair_id
)
592 # Create port pair group
593 ppgrp_id
= self
.portchain_drv
.create_port_pair_group(name
+'_ppgrp_'+str(index
),ppair_list
)
594 ppgrp_list
.append(ppgrp_id
)
596 port_chain_id
= self
.portchain_drv
.create_port_chain(name
,ppgrp_list
)
599 def delete_port_chain(self
,port_chain_id
):
602 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
603 port_chain
= result
.json()
604 self
.log
.debug("Port chain result is %s", port_chain
)
605 port_pair_groups
= port_chain
["port_chain"]["port_pair_groups"]
606 self
.portchain_drv
.delete_port_chain(port_chain_id
)
608 # Get port pairs and delete port pair groups
610 self
.log
.debug("Port pair groups during delete is %s", port_pair_groups
)
611 for port_pair_group_id
in port_pair_groups
:
612 result
= self
.portchain_drv
.get_port_pair_group(port_pair_group_id
)
613 port_pair_group
= result
.json()
614 self
.log
.debug("Port pair group result is %s", port_pair_group
)
615 port_pairs
.extend(port_pair_group
["port_pair_group"]["port_pairs"])
616 self
.portchain_drv
.delete_port_pair_group(port_pair_group_id
)
618 self
.log
.debug("Port pairs during delete is %s",port_pairs
)
620 for port_pair_id
in port_pairs
:
621 self
.portchain_drv
.delete_port_pair(port_pair_id
)
623 except Exception as e
:
624 self
.log
.error("Error while delete port chain with id %s, exception %s", port_chain_id
,str(e
))
626 def update_port_chain(self
,port_chain_id
,flow_classifier_list
):
627 result
= self
.portchain_drv
.get_port_chain(port_chain_id
)
628 result
.raise_for_status()
629 port_chain
= result
.json()['port_chain']
630 new_flow_classifier_list
= list()
631 if port_chain
and port_chain
['flow_classifiers']:
632 new_flow_classifier_list
.extend(port_chain
['flow_classifiers'])
633 new_flow_classifier_list
.extend(flow_classifier_list
)
634 port_chain_id
= self
.portchain_drv
.update_port_chain(port_chain
['id'],flow_classifiers
=new_flow_classifier_list
)
637 def create_flow_classifer(self
,classifier_name
,classifier_dict
):
638 "Create flow classifier"
639 flow_classifier_id
= self
.portchain_drv
.create_flow_classifier(classifier_name
,classifier_dict
)
640 return flow_classifier_id
642 def delete_flow_classifier(self
,classifier_id
):
643 "Create flow classifier"
645 self
.portchain_drv
.delete_flow_classifier(classifier_id
)
646 except Exception as e
:
647 self
.log
.error("Error while deleting flow classifier with id %s, exception %s", classifier_id
,str(e
))
649 def get_port_chain_list(self
):
650 result
= self
.portchain_drv
.get_port_chain_list()
651 port_chain_list
= result
.json()
652 if 'port_chains' in port_chain_list
:
653 return port_chain_list
['port_chains']
655 def cinder_volume_list(self
):
656 return self
.cinder_drv
.volume_list()
658 def cinder_volume_get(self
,vol_id
):
659 return self
.cinder_drv
.volume_get(vol_id
)
661 def cinder_volume_set_metadata(self
, volumeid
, metadata
):
662 return self
.cinder_drv
.volume_set_metadata(volumeid
, metadata
)
664 def cinder_volume_delete_metadata(self
, volumeid
, metadata
):
665 return self
.cinder_drv
.volume_delete_metadata(volumeid
, metadata
)