#!/usr/bin/python3 # Copyright (c) 2017 StackHPC Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from ansible.module_utils.basic import * from ansible.module_utils.openstack import * # Store a list of import errors to report to the user. IMPORT_ERRORS = [] try: import openstack except Exception as e: IMPORT_ERRORS.append(e) DOCUMENTATION = """ module: os_ironic_inspector_rule short_description: Create or destroy an Ironic Inspector rule. author: "Mark Goddard " extends_documentation_fragment: openstack description: - Create or destroy an Ironic inspector rule. options: state: description: - State of the rule choices: ["present", "absent"] uuid: description: - Globally unique identifier for the rule. required: false description: description: - Description for the rule. required: false conditions: description: - List of conditions that must be met in order to apply the rule. required: true actions: description: - List of actions to be taken when the conditions are met. required: true """ EXAMPLES = """ # Ensure that an inspector rule exists. os_ironic_inspector_rule: cloud: "openstack" state: present uuid: "d44666e1-35b3-4f6b-acb0-88ab7052da69" description: Set IPMI username in driver_info if not set conditions: - field: "node://driver_info.ipmi_username" op: "is-empty" actions: - action: "set-attribute" path: "driver_info/ipmi_username" value: "root" """ def _get_client(module, cloud): """Return an Ironic inspector client.""" return cloud.baremetal_introspection def _ensure_rule_present(module, client): """Ensure that an inspector rule is present.""" if module.params['uuid']: response = client.get('/rules/{}'.format(module.params['uuid'])) if not response.ok: if response.status_code != 404: module.fail_json(msg="Failed retrieving Inspector rule %s: %s" % (module.params['uuid'], repr(e))) else: rule = response.json() # Check whether the rule differs from the request. keys = ('conditions', 'actions', 'description') for key in keys: expected = module.params[key] if key == 'conditions': # Rules returned from the API include default values in the # conditions that may not be in the requested rule. Apply # defaults to allow the comparison to succeed. expected = copy.deepcopy(expected) for condition in expected: condition.setdefault('invert', False) condition.setdefault('multiple', 'any') if rule[key] != expected: break else: # Nothing to do - rule exists and is as requested. return False # Rule differs - delete it before recreating. _ensure_rule_absent(module, client) rule = { "conditions": module.params['conditions'], "actions": module.params['actions'], "description": module.params['description'], "uuid": module.params['uuid'], } response = client.post("/rules", json=rule) if not response.ok: module.fail_json(msg="Failed creating Inspector rule %s: %s" % (module.params['uuid'], response.text)) return True def _ensure_rule_absent(module, client): """Ensure that an inspector rule is absent.""" if not module.params['uuid']: module.fail_json(msg="UUID is required to ensure rules are absent") response = client.delete("/rules/{}".format(module.params['uuid'])) if not response.ok: # If the rule does not exist, no problem and no change. if response.status_code == 404: return False module.fail_json(msg="Failed retrieving Inspector rule %s: %s" % (module.params['uuid'], response.text)) return True def main(): argument_spec = openstack_full_argument_spec( conditions=dict(type='list', required=True), actions=dict(type='list', required=True), description=dict(required=False), uuid=dict(required=False), state=dict(required=False, default='present', choices=['present', 'absent']), ) module_kwargs = openstack_module_kwargs() module = AnsibleModule(argument_spec, **module_kwargs) # Fail if there were any exceptions when importing modules. if IMPORT_ERRORS: module.fail_json(msg="Import errors: %s" % ", ".join([repr(e) for e in IMPORT_ERRORS])) sdk, cloud = openstack_cloud_from_module(module) try: client = _get_client(module, cloud) if module.params["state"] == "present": changed = _ensure_rule_present(module, client) else: changed = _ensure_rule_absent(module, client) except Exception as e: module.fail_json(msg="Failed to configure Ironic Inspector rule: %s" % repr(e)) else: module.exit_json(changed=changed) if __name__ == '__main__': main()