#!/usr/bin/env python3 # 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 json import os.path import time from ansible.module_utils.basic import AnsibleModule # noqa DOCUMENTATION = ''' --- module: virtualbmc_domain short_description: Manages domains in VirtualBMC. ''' RETRIES = 60 INTERVAL = 0.5 def _vbmc_command(module, args): cmd = [module.params["vbmc_path"], "--no-daemon"] if module.params["log_directory"]: log_file = os.path.join(module.params["log_directory"], "vbmc-%s.log" % module.params["domain"]) cmd += ["--log-file", log_file] cmd += args result = module.run_command(cmd, check_rc=True) rc, out, err = result return out def _get_domain(module, allow_missing=False): domain_name = module.params["domain"] if allow_missing: # Use a list to avoid failing if the domain does not exist. domains = _vbmc_command(module, ["list", "-f", "json"]) domains = json.loads(domains) # vbmc list returns a list of dicts. Transform into a dict of dicts # keyed by domain name. domains = {d["Domain name"]: d for d in domains} try: return domains[domain_name] except KeyError: return None else: domain = _vbmc_command(module, ["show", domain_name, "-f", "json"]) domain = json.loads(domain) domain = {field["Property"]: field["Value"] for field in domain} return domain def _add_domain(module): domain_name = module.params["domain"] args = [ "add", domain_name, "--address", module.params["ipmi_address"], "--port", str(module.params["ipmi_port"]), "--username", module.params["ipmi_username"], "--password", module.params["ipmi_password"], ] if module.params["libvirt_uri"]: args += ["--libvirt-uri", module.params["libvirt_uri"]] _vbmc_command(module, args) def _wait_for_existence(module, exists): """Wait for the domain to exist or cease existing.""" for _ in range(RETRIES): domain = _get_domain(module, allow_missing=True) if (exists and domain) or (not exists and not domain): return time.sleep(INTERVAL) action = "added" if exists else "deleted" module.fail_json(msg="Timed out waiting for domain %s to be %s" % (module.params['domain'], action)) def _wait_for_status(module, status): """Wait for the domain to reach a particular status.""" for _ in range(RETRIES): domain = _get_domain(module) if domain["status"] == status: return time.sleep(INTERVAL) module.fail_json(msg="Timed out waiting for domain %s to reach status " "%s" % (module.params['domain'], status)) def _virtualbmc_domain(module): """Configure a VirtualBMC domain.""" changed = False domain_name = module.params["domain"] # Even if the domain is present in VBMC, we can't guarantee that it's # configured correctly. It's easiest to delete and re-add it; this should # involve minimal downtime. domain = _get_domain(module, allow_missing=True) if domain and domain["Status"] == "running": if not module.check_mode: module.debug("Stopping domain %s" % domain_name) _vbmc_command(module, ["stop", domain_name]) _wait_for_status(module, "down") changed = True if domain: if not module.check_mode: module.debug("Deleting domain %s" % domain_name) _vbmc_command(module, ["delete", domain_name]) _wait_for_existence(module, False) changed = True if module.params['state'] == 'present': if not module.check_mode: module.debug("Adding domain %s" % domain_name) _add_domain(module) _wait_for_existence(module, True) module.debug("Starting domain %s" % domain_name) _vbmc_command(module, ["start", domain_name]) _wait_for_status(module, "running") changed = True return {"changed": changed} def main(): module = AnsibleModule( argument_spec=dict( domain=dict(type='str', required=True), ipmi_address=dict(type='str', required=True), ipmi_port=dict(type='int', required=True), ipmi_username=dict(type='str', required=True), ipmi_password=dict(type='str', required=True, no_log=True), libvirt_uri=dict(type='str'), log_directory=dict(type='str'), state=dict(type=str, default='present', choices=['present', 'absent']), vbmc_path=dict(type='str'), ), supports_check_mode=True, ) result = _virtualbmc_domain(module) module.exit_json(**result) if __name__ == '__main__': main()