Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions src/cfengine_cli/cfengine_wrapper/arg_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,41 @@


def parse_wrapper_args(subp: argparse._SubParsersAction):

sp = subp.add_parser(
"save", help="Save host(s) with a group name to use in other commands"
)
sp.add_argument(
"--role",
help="Role of the hosts",
choices=["hub", "hubs", "client", "clients"],
required=True,
)
sp.add_argument(
"--name",
help="Name of the group of hosts (can be used in other commands)",
required=True,
)
sp.add_argument(
"--hosts",
"-H",
help="SSH usernames and IPs for SSH and CFEngine in the form of user@ip",
required=True,
)

sp = subp.add_parser(
"setup-code", help="Fetches a new setup-code for mission-portal login"
Comment thread
SimonThalvorsen marked this conversation as resolved.
)
sp.add_argument(
"--hub",
"-H",
help="Hub from which to fetch new setup-code",
type=str,
default=None,
)

subp.add_parser("build", help="Build a policy set from a CFEngine Build project\n\
A wrapper arount the cfbs `build`-function.")

sp = subp.add_parser("deploy", help="Deploy policy-set (masterfiles) to hub\n\
A wrapper around the cf-remote `deploy`-function with some added niceties.")
sp.add_argument("--hub", help="Hub(s) to deploy to", type=str)
Expand Down Expand Up @@ -101,14 +133,19 @@ def parse_wrapper_args(subp: argparse._SubParsersAction):

report_parser = subp.add_parser(
"report",
help="Run the agent and hub commands necessary to get new reporting data",
help="Refresh reporting data",
)
report_parser.add_argument(
"--host",
"--run-agent",
action="store_true",
help="Runs the agent on the chosen host(s) before collecting report data.",
)
report_parser.add_argument(
"--hub",
"-H",
type=str,
default=None,
help="Select which installation to use by name/IP (e.g. 'local' or '192.168.56.90'). "
"If omitted and multiple installations of cf-agent+cf-hub are found, you'll be prompted.",
help="Only refresh one hub specified by name/IP (e.g. 'local' or '192.168.56.90') and accompanying clients",
)

run_parser = subp.add_parser(
Expand Down
83 changes: 72 additions & 11 deletions src/cfengine_cli/cfengine_wrapper/cfengine_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
from cfbs.commands import build_command
from cf_remote.commands import deploy as deploy_command
from cf_remote.commands import destroy as destroy_command
from cf_remote.commands import save as save_command
from cf_remote.remote import run_command, transfer_file

from cfengine_cli.utils import UserError
from cfengine_cli.cfengine_wrapper.cfengine_objects import (
Executable,
_ensure_default_agent_flags,
ensure_default_agent_flags,
)
from cfengine_cli.cfengine_wrapper.cfengine_utils import (
extract_agent_file,
prompt_two_options,
prompt_yes_no,
require_executable,
require_installation,
select_report_targets,
)

_DEFAULT_CFENGINE_INPUTS_DIR = "/var/cfengine/inputs"
Expand Down Expand Up @@ -113,7 +114,7 @@ def _resolve_command_for_agent(agent: Executable, command: str) -> str:
if agent.name != "cf-agent":
return command

command = _ensure_default_agent_flags(command)
command = ensure_default_agent_flags(command)
file_arg = extract_agent_file(command)
if not file_arg:
return command
Expand All @@ -131,14 +132,74 @@ def _resolve_command_for_agent(agent: Executable, command: str) -> str:
# ---------------------------------------------------------------------------


def report(target: str | None = None) -> int: # TODO? ENT-14122
installation = require_installation(target)
rc = installation.agent.run("-KIf update.cf", "-KI")
if rc != 0:
return rc
return installation.hub.run(
"--query rebase -H 127.0.0.1", "--query delta -H 127.0.0.1"
)
def save(hosts: str, role: str, name: str) -> int: # TODO: Add to existing group
return save_command(hosts=hosts, role=role, name=name)


def _refresh_agent(agent: Executable) -> int:
try:
return agent.run("-KIf update.cf", "-KI")
except (Exception, SystemExit) as e:
logging.error(f"Skipping {agent.label}: {e}")
return 1


def _query_hub_delta(hub: Executable, client_ips: list[str]) -> int:
"""
Ask a hub to recompute delta report data for itself and
for every client bootstrapped to it.
"""
try:
queries = ["--query delta -H 127.0.0.1"] + [
f"--query delta -H {ip}" for ip in client_ips
]
return hub.run(*queries)
except (Exception, SystemExit) as e:
logging.error(f"Skipping hub {hub.label}: {e}")
return 1


def report(
target: str | None = None,
run_agent: bool = False,
) -> int:
errors = 0
hubs, clients = select_report_targets(target)

hub_agent_failed = {}
if run_agent:
for hub in hubs:
rc = _refresh_agent(hub.agent)
hub_agent_failed[hub.location] = rc != 0
if rc != 0:
logging.error(f"Agent run failed on {hub.agent.label}")
errors += 1

for agent in clients:
rc = _refresh_agent(agent)
if rc != 0:
logging.error(f"Refresh failed on {agent.label})")
errors += 1

for hub in hubs:
if run_agent and hub_agent_failed[hub.location]:
logging.warning(
f"Agent run failed for {hub.location}, some data may be stale."
)
client_ips = [client.location.split("@", 1)[1] for client in clients]
rc = _query_hub_delta(hub.hub, client_ips)
if rc != 0:
logging.error(f"Hub refresh failed on {hub.label})")
errors += 1

if errors > 0:
logging.error(f"Encountered {errors}.")
return errors


def setup_code(target: str | None = None) -> int:
hub = require_executable("cf-hub", target)
return hub.run("--new-setup-code")


def run(*args, target: str | None = None) -> int:
Expand Down
11 changes: 4 additions & 7 deletions src/cfengine_cli/cfengine_wrapper/cfengine_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import os


def _ensure_default_agent_flags(command: str) -> str:
def ensure_default_agent_flags(command: str) -> str:
"""
cf-agent needs -K (no-lock), -I (inform), and -f (specify
file) to actually run against a policy file the way people expect.
Expand Down Expand Up @@ -52,17 +52,14 @@ def label(self) -> str:
return self.location

def run(self, *commands) -> int:
rc = 0
errors = 0
for command in commands:
rc = self._run_one(command)
if rc != 0:
return rc
return rc
errors += 1
return errors

def _run_one(self, command: str) -> int:
if self.name == "cf-agent":
command = _ensure_default_agent_flags(command)

if self.is_local:
args = [self.path] + command.split()
# cf-agent picks its workdir based on privilege: as root it uses
Expand Down
Loading