| """ |
| Management of Cinder resources |
| """ |
| |
| import ast |
| import logging |
| |
| log = logging.getLogger(__name__) |
| |
| |
| def __virtual__(): |
| return 'cinderv3' if 'cinderv3.volume_list' in __salt__ else False # noqa |
| |
| |
| def _cinder_call(fname, *args, **kwargs): |
| return __salt__['cinderv3.{}'.format(fname)](*args, **kwargs) |
| |
| |
| def _resource_present(resource, name, cloud_name, **kwargs): |
| try: |
| method_name = '{}_get_details'.format(resource) |
| exact_resource = _cinder_call( |
| method_name, name, cloud_name=cloud_name, **kwargs |
| )[resource] |
| except Exception as e: |
| if 'ResourceNotFound' in repr(e): |
| try: |
| method_name = '{}_create'.format(resource) |
| resp = _cinder_call( |
| method_name, name=name, cloud_name=cloud_name, **kwargs |
| ) |
| except Exception as e: |
| log.exception('Cinder {0} create failed with {1}'. |
| format(resource, e)) |
| return _failed('create', name, resource) |
| return _succeeded('create', name, resource, resp) |
| elif 'MultipleResourcesFound' in repr(e): |
| return _failed('find', name, resource) |
| else: |
| raise |
| |
| to_update = {} |
| if to_update: |
| for key in kwargs: |
| if key not in exact_resource or kwargs[key] != exact_resource[key]: |
| to_update[key] = kwargs[key] |
| try: |
| method_name = '{}_update'.format(resource) |
| resp = _cinder_call( |
| method_name, name, cloud_name=cloud_name, **to_update |
| ) |
| except Exception as e: |
| log.exception('Cinder {0} update failed with {1}'.format(resource, e)) |
| return _failed('update', name, resource) |
| return _succeeded('update', name, resource, resp) |
| return _succeeded('no_changes', name, resource) |
| |
| |
| def _resource_absent(resource, name, cloud_name, connection_params=None): |
| try: |
| method_name = '{}_get_details'.format(resource) |
| _cinder_call( |
| method_name, name, cloud_name=cloud_name, |
| connection_params=connection_params |
| )[resource] |
| except Exception as e: |
| if 'ResourceNotFound' in repr(e): |
| return _succeeded('absent', name, resource) |
| if 'MultipleResourcesFound' in repr(e): |
| return _failed('find', name, resource) |
| try: |
| method_name = '{}_delete'.format(resource) |
| _cinder_call( |
| method_name, name, cloud_name=cloud_name, |
| connection_params=connection_params |
| ) |
| except Exception as e: |
| log.error('Cinder delete {0} failed with {1}'.format(resource, e)) |
| return _failed('delete', name, resource) |
| return _succeeded('delete', name, resource) |
| |
| |
| def service_enabled(name, binary, cloud_name, connection_params=None): |
| """Ensures that the service is enabled on the host |
| |
| :param name: name of a host where service is running |
| :param binary: name of the service have to be run |
| :param connection_params: dictionary with salt internal connection params |
| """ |
| changes = {} |
| ret = [] |
| |
| services = _cinder_call('service_list', host=name, binary=binary, cloud_name=cloud_name, |
| connection_params=connection_params)['services'] |
| |
| disabled_service = [s for s in services if s['status'] == 'disabled'] |
| |
| if len(disabled_service): |
| for service in disabled_service: |
| changes = _cinder_call('service_update', service['host'], binary, 'enable', cloud_name=cloud_name, |
| connection_params=connection_params) |
| ret.append(changes) |
| return _succeeded('update', name, binary, {'changes':ret}) |
| return _succeeded('no_changes', name, binary) |
| |
| |
| def service_disabled(name, binary, cloud_name, disabled_reason=None, connection_params=None): |
| """Ensures that the service is disabled on the host |
| |
| :param name: name of a host where service is running |
| :param binary: name of the service have to be disabled |
| :param connection_params: dictionary with salt internal connection params |
| """ |
| kwargs = {} |
| ret = [] |
| |
| if disabled_reason: |
| kwargs['disabled_reason'] = disabled_reason |
| |
| services = _cinder_call('service_list', host=name, binary=binary, cloud_name=cloud_name, |
| connection_params=connection_params)['services'] |
| |
| enabled_services = [s for s in services if s['status'] == 'enabled'] |
| |
| if len(enabled_services): |
| for service in enabled_services: |
| changes = _cinder_call('service_update', service['host'], binary, 'disable', |
| cloud_name=cloud_name, connection_params=connection_params, |
| **kwargs) |
| ret.append(changes) |
| return _succeeded('update', name, binary, {'changes':ret}) |
| return _succeeded('no_changes', name, binary) |
| |
| |
| def volume_type_present(name, cloud_name, **kwargs): |
| return _resource_present('volume_type', name, cloud_name, **kwargs) |
| |
| |
| def volume_type_absent(name, cloud_name, connection_params=None): |
| return _resource_absent('volume_type', name, cloud_name, connection_params) |
| |
| |
| def volume_present(name, cloud_name, size, **kwargs): |
| kwargs.update({ |
| 'size': size, |
| }) |
| return _resource_present('volume', name, cloud_name, **kwargs) |
| |
| |
| def volume_absent(name, cloud_name, connection_params=None): |
| return _resource_absent('volume', name, cloud_name, connection_params) |
| |
| |
| def volume_type_key_present(name=None, key=None, value=None, cloud_name=None, |
| connection_params=None): |
| """ |
| Ensures that the extra specs are present on a volume type. |
| """ |
| keys = "{u'" + key + "': u'" + value + "'}" |
| keys = ast.literal_eval(keys) |
| signal_create = _cinder_call('keys_volume_type_set', name, keys=keys, |
| cloud_name=cloud_name, |
| connection_params=connection_params) |
| if signal_create["result"] is True: |
| ret = { |
| 'name': name, |
| 'changes': keys, |
| 'result': True, |
| 'comment': 'Volume type "{0}" was updated'.format(name) |
| } |
| else: |
| ret = { |
| 'name': name, |
| 'changes': {}, |
| 'result': False, |
| 'comment': signal_create.get("comment") |
| } |
| return ret |
| |
| |
| def _succeeded(op, name, resource, changes=None): |
| msg_map = { |
| 'create': '{0} {1} created', |
| 'delete': '{0} {1} removed', |
| 'update': '{0} {1} updated', |
| 'no_changes': '{0} {1} is in desired state', |
| 'absent': '{0} {1} not present', |
| 'resources_moved': '{1} resources were moved from {0}', |
| } |
| changes_dict = { |
| 'name': name, |
| 'result': True, |
| 'comment': msg_map[op].format(resource, name), |
| 'changes': changes or {}, |
| } |
| return changes_dict |
| |
| def _failed(op, name, resource): |
| msg_map = { |
| 'create': '{0} {1} failed to create', |
| 'delete': '{0} {1} failed to delete', |
| 'update': '{0} {1} failed to update', |
| 'find': '{0} {1} found multiple {0}', |
| 'resources_moved': 'failed to move {1} from {0}', |
| } |
| changes_dict = { |
| 'name': name, |
| 'result': False, |
| 'comment': msg_map[op].format(resource, name), |
| 'changes': {}, |
| } |
| return changes_dict |