Esempio n. 1
0
 def __init__(self, **kwargs):
     # Get the specified AT instance
     logger.debug(f"AnsibleTower instantiated with {kwargs=}")
     instance_name = kwargs.pop("AnsibleTower", None)
     # Validate the AnsibleTower-specific settings
     self._validate_settings(instance_name)
     # get our instance settings
     self.url = settings.ANSIBLETOWER.base_url
     self.uname = settings.ANSIBLETOWER.get("username")
     self.pword = settings.ANSIBLETOWER.get("password")
     self.token = settings.ANSIBLETOWER.get("token")
     self._inventory = (
         kwargs.get("tower_inventory") or settings.ANSIBLETOWER.inventory
     )
     # Init the class itself
     self._construct_params = []
     config = kwargs.get("config", awxkit.config)
     config.base_url = self.url
     root = kwargs.get("root")
     if root is None:
         root = awxkit.api.Api()  # support mock stub for unit tests
     # Prefer token if its set, otherwise use username/password
     # auth paths for the API taken from:
     # https://github.com/ansible/awx/blob/ddb6c5d0cce60779be279b702a15a2fddfcd0724/awxkit/awxkit/cli/client.py#L85-L94
     # unit test mock structure means the root API instance can't be loaded on the same line
     if self.token:
         helpers.emit(auth_type="token")
         logger.info("Using token authentication")
         config.token = self.token
         try:
             root.connection.login(
                 username=None, password=None, token=self.token, auth_type="Bearer"
             )
         except awxkit.exceptions.Unauthorized as err:
             raise exceptions.AuthenticationError(err.args[0])
         versions = root.get().available_versions
         try:
             # lookup the user that authenticated with the token
             # If a username was specified in config, use that instead
             my_username = (
                 self.uname or versions.v2.get().me.get().results[0].username
             )
         except (IndexError, AttributeError):
             # lookup failed for whatever reason
             raise exceptions.ProviderError(
                 provider="AnsibleTower",
                 message="Failed to lookup a username for the given token, please check credentials",
             )
     else:  # dynaconf validators should have checked that either token or password was provided
         helpers.emit(auth_type="password")
         logger.info("Using username and password authentication")
         config.credentials = {
             "default": {"username": self.uname, "password": self.pword}
         }
         config.use_sessions = True
         root.load_session().get()
         versions = root.available_versions
         my_username = self.uname
     self.v2 = versions.v2.get()
     self.username = my_username
Esempio n. 2
0
 def __call__(self, *args, **kwargs):
     try:
         return self.main(*args, **kwargs)
     except Exception as err:
         if not isinstance(err, exceptions.BrokerError):
             err = exceptions.BrokerError(err)
         helpers.emit(return_code=err.error_code, error_message=str(err.message))
         sys.exit(err.error_code)
     helpers.emit(return_code=0)
Esempio n. 3
0
def test_emitter(tmp_file):
    assert not tmp_file.exists()
    helpers.emit.set_file(tmp_file)
    assert tmp_file.exists()
    helpers.emit(test="value", another=5)
    written = json.loads(tmp_file.read_text())
    assert written == {"test": "value", "another": 5}
    helpers.emit({"thing": 13})
    written = json.loads(tmp_file.read_text())
    assert written == {"test": "value", "another": 5, "thing": 13}
Esempio n. 4
0
    def checkout(self):
        """checkout one or more VMs

        :return: Host obj or list of Host objects
        """
        hosts = self._checkout()
        helpers.emit(hosts=[host.to_dict() for host in hosts])
        self._hosts.extend(hosts)
        helpers.update_inventory([host.to_dict() for host in hosts])
        return hosts if not len(hosts) == 1 else hosts[0]
Esempio n. 5
0
def test_emitter(tmp_path):
    out_file = tmp_path / "output.json"
    assert not out_file.exists()
    helpers.emit.set_file(out_file)
    assert out_file.exists()
    helpers.emit(test="value", another=5)
    written = json.loads(out_file.read_text())
    assert written == {"test": "value", "another": 5}
    helpers.emit({"thing": 13})
    written = json.loads(out_file.read_text())
    assert written == {"test": "value", "another": 5, "thing": 13}
Esempio n. 6
0
    def checkout(self, connect=False):
        """checkout one or more VMs

        :param connect: Boolean whether to establish host ssh connection

        :return: Host obj or list of Host objects
        """
        hosts = self._checkout()
        helpers.emit(hosts=[host.to_dict() for host in hosts])
        if connect:
            for host in hosts:
                host.connect()
        self._hosts.extend(hosts)
        helpers.update_inventory([host.to_dict() for host in hosts])
        return hosts if not len(hosts) == 1 else hosts[0]
Esempio n. 7
0
 def _act(self, provider, method, checkout=False):
     """Perform a general action against a provider's method"""
     provider_inst = provider(**self._kwargs)
     helpers.emit({
         "provider": provider_inst.__class__.__name__,
         "action": method,
         "arguments": self._kwargs
     })
     result = getattr(provider_inst, method)(**self._kwargs)
     logger.debug(result)
     if result and checkout:
         return provider_inst.construct_host(
             provider_params=result, host_classes=self.host_classes, **self._kwargs
         )
     else:
         return result
Esempio n. 8
0
def execute(ctx, background, nick, output_format, artifacts, args_file, **kwargs):
    """Execute an arbitrary provider action
    COMMAND: broker execute --workflow "workflow-name" --workflow-arg1 something
    or
    COMMAND: broker execute --nick "nickname"

    :param ctx: clicks context object

    :param background: run a new broker subprocess to carry out command

    :param nick: shortcut for arguments saved in settings.yaml, passed in as a string

    :param output_format: change the format of the output to one of the choice options

    :param artifacts: AnsibleTower provider specific option for choosing what to return

    :param args_file: this broker argument wil be replaced with the contents of the file passed in
    """
    broker_args = helpers.clean_dict(kwargs)
    if nick:
        broker_args["nick"] = nick
    if artifacts:
        broker_args["artifacts"] = artifacts
    if args_file:
        broker_args["args_file"] = args_file
    # if additional arguments were passed, include them in the broker args
    # strip leading -- characters
    broker_args.update(
        {
            (key[2:] if key.startswith("--") else key): val
            for key, val in zip(ctx.args[::2], ctx.args[1::2])
        }
    )
    broker_args = helpers.resolve_file_args(broker_args)
    if background:
        helpers.fork_broker()
    broker_inst = VMBroker(**broker_args)
    result = broker_inst.execute()
    helpers.emit({"output": result})
    if output_format == "raw":
        print(result)
    elif output_format == "log":
        logger.info(result)
    elif output_format == "yaml":
        print(helpers.yaml_format(result))
Esempio n. 9
0
def inventory(details, sync, filter):
    """Get a list of all VMs you've checked out showing hostname and local id
    hostname pulled from list of dictionaries
    """
    if sync:
        VMBroker.sync_inventory(provider=sync)
    logger.info("Pulling local inventory")
    inventory = helpers.load_inventory(filter=filter)
    emit_data = []
    for num, host in enumerate(inventory):
        emit_data.append(host)
        if details:
            logger.info(
                f"{num}: {host['hostname'] or host['name']}, Details: {helpers.yaml_format(host)}"
            )
        else:
            logger.info(f"{num}: {host['hostname'] or host['name']}")
    helpers.emit({"inventory": emit_data})
Esempio n. 10
0
    def checkout(self):
        """checkout one or more VMs

        :return: Host obj or list of Host objects
        """
        hosts = self._checkout()
        err, to_emit = None, []
        for host in hosts:
            if not isinstance(host, exceptions.ProviderError):
                to_emit.append(host.to_dict())
            else:
                err = host
                hosts.remove(host)
        helpers.emit(hosts=[host.to_dict() for host in hosts])
        self._hosts.extend(hosts)
        helpers.update_inventory([host.to_dict() for host in hosts])
        if err:
            raise err
        return hosts if not len(hosts) == 1 else hosts[0]
Esempio n. 11
0
     )
 payload = {"extra_vars": str(kwargs)}
 if self.inventory:
     payload["inventory"] = self.inventory
 else:
     logger.info(
         "No inventory specified, Ansible Tower will use a default.")
 logger.debug(
     f"Launching {subject}: {url_parser.urljoin(self.url, str(target.url))}\n"
     f"{payload=}")
 job = target.launch(payload=payload)
 job_number = job.url.rstrip("/").split("/")[-1]
 job_api_url = url_parser.urljoin(self.url, str(job.url))
 job_ui_url = url_parser.urljoin(self.url,
                                 f"/#/{subject}s/{job_number}")
 helpers.emit(api_url=job_api_url, ui_url=job_ui_url)
 logger.info("Waiting for job: \n"
             f"API: {job_api_url}\n"
             f"UI: {job_ui_url}")
 job.wait_until_completed(
     timeout=settings.ANSIBLETOWER.workflow_timeout)
 if not job.status == "successful":
     message_data = {
         f"{subject.capitalize()} Status": job.status,
         "Reason(s)": self._get_failure_messages(job),
         "URL": job_ui_url
     }
     helpers.emit(message_data)
     raise exceptions.ProviderError(provider="AnsibleTower",
                                    message=message_data["Reason(s)"])
 if (artifacts := kwargs.get("artifacts")):