def validate_args(args): if args.version is True: # --version with no second argument print_version_info() exit_success() if args.version and args.command not in ["install", "packages"]: user_error("Cannot specify version number in '{}' command".format( args.command)) if args.hosts: args.hosts = file_or_comma_list(args.hosts) if args.clients: args.clients = file_or_comma_list(args.clients) if args.bootstrap: args.bootstrap = [ strip_user(host_info) for host_info in file_or_comma_list(args.bootstrap) ] if args.hub: args.hub = file_or_comma_list(args.hub) args.command = args.command.strip() if not args.command: user_error("Invalid or missing command") validate_command(args.command, args)
def file_or_single_host(string): assert "," not in string one_list = file_or_comma_list(string) if len(one_list) != 1: user_error( "File '{}' must contain exactly 1 hostname or IP".format(string)) return one_list[0]
def validate_args(args): if args.version is True: # --version with no second argument print_version_info() exit_success() if args.version and args.command not in ["install", "packages"]: user_error("Cannot specify version number in '{}' command".format( args.command)) if "hosts" in args and args.hosts: log.debug("validate_args, hosts in args, args.hosts='{}'".format( args.hosts)) args.hosts = resolve_hosts(args.hosts) if "clients" in args and args.clients: args.clients = resolve_hosts(args.clients) if "bootstrap" in args and args.bootstrap: args.bootstrap = [ strip_user(host_info) for host_info in resolve_hosts(args.bootstrap, private_ips=True) ] if "hub" in args and args.hub: args.hub = resolve_hosts(args.hub) if not args.command: user_error("Invalid or missing command") args.command = args.command.strip() validate_command(args.command, args)
def install_host(host, *, hub=False, package=None, bootstrap=None, version=None, demo=False, call_collect=False, connection=None, edition=None): data = get_info(host, connection=connection) print_info(data) if not package: tags = [] if edition == "enterprise": tags.append("hub" if hub else "agent") tags.append("64" if data["arch"] in ["x86_64", "amd64"] else data["arch"]) extension = None if "package_tags" in data and "msi" in data["package_tags"]: extension = ".msi" elif "dpkg" in data["bin"]: extension = ".deb" elif "rpm" in data["bin"]: extension = ".rpm" releases = Releases(edition) release = releases.default if version: release = releases.pick_version(version) artifacts = release.find(tags, extension) if not artifacts: user_error( "Could not find an appropriate package for host, please use --{}-package" .format("hub" if hub else "client")) artifact = artifacts[-1] package = download_package(artifact.url) scp(package, host, connection=connection) package = basename(package) install_package(host, package, data, connection=connection) data = get_info(host, connection=connection) if data["agent_version"] and len(data["agent_version"]) > 0: print("CFEngine {} was successfully installed on '{}'".format( data["agent_version"], host)) else: print("Installation failed!") sys.exit(1) if bootstrap: bootstrap_host(data, policy_server=bootstrap, connection=connection) if demo: if hub: demo_lib.install_def_json(host, connection=connection, call_collect=call_collect) demo_lib.agent_run(data, connection=connection) demo_lib.disable_password_dialog(host) demo_lib.agent_run(data, connection=connection)
def bootstrap_host(host, policy_server, *, connection=None): print("Bootstrapping: '{}' -> '{}'".format(host, policy_server)) command = "/var/cfengine/bin/cf-agent --bootstrap {}".format(policy_server) output = ssh_sudo(connection, command) if output is None: sys.exit("Bootstrap failed on '{}'".format(host)) if output and "completed successfully" in output: print("Bootstrap successful: '{}' -> '{}'".format(host, policy_server)) else: user_error("Something went wrong while bootstrapping")
def boootstrap_host(host, policy_server): print("Bootstrapping: '{}' -> '{}'".format(host, policy_server)) with connect(host) as connection: command = "/var/cfengine/bin/cf-agent --bootstrap {}".format( policy_server) output = ssh_sudo(connection, command) if output and "completed successfully" in output: print("Bootstrap succesful: '{}' -> '{}'".format( host, policy_server)) else: user_error("Something went wrong while bootstrapping")
def install_host( host, *, hub=False, package=None, bootstrap=None, version=None, demo=False, call_collect=False, connection=None): data = get_info(host, connection=connection) print_info(data) if not package: tags = [] tags.append("hub" if hub else "agent") tags.append("64" if data["arch"] in ["x86_64", "amd64"] else data["arch"]) extension = None if "dpkg" in data["bin"]: extension = ".deb" elif "rpm" in data["bin"]: extension = ".rpm" releases = Releases() release = releases.default if version: release = releases.pick_version(version) artifacts = release.find(tags, extension) if not artifacts: user_error( "Could not find an appropriate package for host, please use --{}-package".format( "hub" if hub else "client")) artifact = artifacts[-1] package = download_package(artifact.url) scp(package, host, connection=connection) package = basename(package) install_package(host, package, data, connection=connection) data = get_info(host, connection=connection) if data["agent_version"] and len(data["agent_version"]) > 0: print( "CFEngine {} was successfully installed on '{}'".format(data["agent_version"], host)) else: print("Installation failed!") sys.exit(1) if bootstrap: bootstrap_host(host, policy_server=bootstrap, connection=connection) if demo: if hub: demo_lib.install_def_json(host, connection=connection, call_collect=call_collect) demo_lib.agent_run(host, connection=connection) demo_lib.disable_password_dialog(host) demo_lib.agent_run(host, connection=connection)
def run_command_with_args(command, args): if command == "info": commands.info(args.hosts, None) elif command == "install": commands.install(args.hub, args.clients, package=args.package, bootstrap=args.bootstrap, hub_package=args.hub_package, client_package=args.client_package, version=args.version, demo=args.demo, call_collect=args.call_collect, edition=args.edition) elif command == "uninstall": all_hosts = ((args.hosts or []) + (args.hub or []) + (args.clients or [])) commands.uninstall(all_hosts) elif command == "packages": commands.packages(tags=args.tags, version=args.version, edition=args.edition) elif command == "run": commands.run(hosts=args.hosts, raw=args.raw, command=args.remote_command) elif command == "sudo": commands.sudo(hosts=args.hosts, raw=args.raw, command=args.remote_command) elif command == "scp": commands.scp(hosts=args.hosts, files=args.args) elif command == "spawn": if args.list_platforms: commands.list_platforms() return if args.init_config: commands.init_cloud_config() return if args.name and "," in args.name: user_error("Group --name may not contain commas") # else if args.role.endswith("s"): # role should be singular args.role = args.role[:-1] commands.spawn(args.platform, args.count, args.role, args.name) elif command == "destroy": group_name = args.name if args.name else None commands.destroy(group_name) else: user_error("Unknown command: '{}'".format(command))
def _verify_package_urls(urls): verified_urls = [] for package_url in urls: if package_url is None: verified_urls.append(None) continue # Throw an error if not valid URL if is_package_url(package_url): verified_urls.append(package_url) else: user_error("Wrong package URL: {}".format(package_url)) return verified_urls
def resolve_hosts(string, single=False, private_ips=False): if is_file_string(string): ret = expand_list_from_file(string) elif is_in_cloud_state(string): ret = get_cloud_hosts(string, private_ips) else: ret = string.split(",") if single: if len(ret) != 1: user_error( "'{}' must contain exactly 1 hostname or IP".format(string)) return ret[0] else: return ret
def bootstrap_host(host_data, policy_server, *, connection=None): host = host_data["ssh_host"] agent = host_data["agent"] print("Bootstrapping: '{}' -> '{}'".format(host, policy_server)) command = "{} --bootstrap {}".format(agent, policy_server) if host_data["os"] == "windows": output = ssh_cmd(connection, command) else: output = ssh_sudo(connection, command) if output is None: sys.exit("Bootstrap failed on '{}'".format(host)) if output and "completed successfully" in output: print("Bootstrap successful: '{}' -> '{}'".format(host, policy_server)) else: user_error("Something went wrong while bootstrapping")
def run_command_with_args(command, args): if command == "info": commands.info(args.hosts, None) elif command == "install": commands.install( args.hub, args.clients, package=args.package, bootstrap=args.bootstrap, hub_package=args.hub_package, client_package=args.client_package, version=args.version, demo=args.demo) elif command == "packages": commands.packages(tags=args.args, version=args.version) else: user_error("Unknown command: '{}'".format(command))
def validate_args(args): if args.version is True: # --version with no second argument print_version_info() exit_success() if args.version and args.command not in ["install", "packages"]: user_error("Cannot specify version number in '{}' command".format(args.command)) if args.hosts: args.hosts = file_or_comma_list(args.hosts) if args.clients: args.clients = file_or_comma_list(args.clients) if args.bootstrap: args.bootstrap = [strip_user(host_info) for host_info in file_or_comma_list(args.bootstrap)] if args.hub: args.hub = file_or_comma_list(args.hub) args.command = args.command.strip() if not args.command: user_error("Invalid or missing command") validate_command(args.command, args)
def uninstall_cfengine(host, data, *, connection=None): print("Uninstalling CFEngine on '{}'".format(host)) if "dpkg" in data["bin"]: run_command(host, "dpkg --remove cfengine-community || true", connection=connection, sudo=True) run_command(host, "dpkg --remove cfengine-nova || true", connection=connection, sudo=True) run_command(host, "dpkg --remove cfengine-nova-hub || true", connection=connection, sudo=True) elif "rpm" in data["bin"]: run_command(host, "rpm --erase cfengine-community || true", connection=connection, sudo=True) run_command(host, "rpm --erase cfengine-nova || true", connection=connection, sudo=True) run_command(host, "rpm --erase cfengine-nova-hub || true", connection=connection, sudo=True) else: user_error("I don't know how to uninstall there!") run_command(host, "pkill -U cfapache || true", connection=connection, sudo=True) run_command(host, "rm -rf /var/cfengine /opt/cfengine", connection=connection, sudo=True)
def resolve_hosts(string, single=False, private_ips=False): log.debug("resolving hosts from '{}'".format(string)) if is_file_string(string): names = expand_list_from_file(string) else: names = string.split(",") ret = [] for name in names: if is_in_cloud_state(name): hosts = get_cloud_hosts(name, private_ips) ret.extend(hosts) log.debug("found in cloud, adding '{}'".format(hosts)) else: ret.append(name) if single: if len(ret) != 1: user_error("'{}' must contain exactly 1 hostname or IP".format(string)) return ret[0] else: return ret
def run_command_with_args(command, args): if command == "info": commands.info(args.hosts, None) elif command == "install": commands.install(args.hub, args.clients, package=args.package, bootstrap=args.bootstrap, hub_package=args.hub_package, client_package=args.client_package, version=args.version, demo=args.demo, call_collect=args.call_collect) elif command == "packages": commands.packages(tags=args.args, version=args.version) elif command == "run": commands.run(hosts=args.hosts, command=" ".join(args.args)) elif command == "sudo": commands.sudo(hosts=args.hosts, command=" ".join(args.args)) elif command == "scp": commands.scp(hosts=args.hosts, files=args.args) else: user_error("Unknown command: '{}'".format(command))
def run_command_with_args(command, args): if command == "info": commands.info(args.hosts, None) elif command == "install": commands.install( args.hub, args.clients, package=args.package, bootstrap=args.bootstrap, hub_package=args.hub_package, client_package=args.client_package, version=args.version, demo=args.demo) elif command == "packages": commands.packages(tags=args.args, version=args.version) elif command == "run": commands.run(hosts=args.hosts, command=" ".join(args.args)) elif command == "sudo": commands.sudo(hosts=args.hosts, command=" ".join(args.args)) elif command == "scp": commands.scp(hosts=args.hosts, files=args.args) else: user_error("Unknown command: '{}'".format(command))
def _download_urls(urls): """Download packages from URLs, replace URLs with filenames Return a new list of packages where URLs are replaced with paths to packages which have been downloaded. Other values, like None and paths to local packages are preserved. """ urls_dir = cf_remote_packages_dir("url_specified") downloaded_urls = [] downloaded_paths = [] paths = [] for package_url in urls: # Skip anything that is not a package url: if package_url is None or not is_package_url(package_url): paths.append(package_url) continue if not os.path.isdir(urls_dir): os.mkdir(urls_dir) # separate name from url and construct path for downloaded file url, name = package_url, get_package_name(package_url) path = os.path.join(urls_dir, name) # replace url with local path to package in list which will be returned paths.append(path) if path in downloaded_paths and url not in downloaded_urls: user_error( f"2 packages with the same name '{name}' from different URLs") download_package(url, path) downloaded_urls.append(url) downloaded_paths.append(path) return paths
def validate_args(args): if args.version is True: # --version with no second argument print_version_info() exit_success() if args.version and args.command not in ["install", "packages"]: user_error("Cannot specify version number in '{}' command".format(command)) if any(x for x in [args.hub, args.bootstrap] if "," in (x or "")): user_error("--hub and --bootstrap do not support lists (cannot contain commas)") if args.hosts: args.hosts = file_or_comma_list(args.hosts) if args.clients: args.clients = file_or_comma_list(args.clients) if args.bootstrap: args.bootstrap = file_or_single_host(args.bootstrap) if args.hub: args.hub = file_or_single_host(args.hub) args.command = args.command.strip() if not args.command: user_error("Invalid or missing command") validate_command(args.command, args)
def validate_command(command, args): if command == "info" and not args.hosts: user_error("Use --hosts to specify remote hosts") if args.bootstrap and command != "install": user_error("--bootstrap can only be used with install command") if command == "install": if args.hosts: user_error("Use --clients and --hub instead of --hosts") if not args.clients and not args.hub: user_error("Specify hosts using --hub and --clients") if args.hub and args.clients and args.package: user_error( "Use --hub-package / --client-package instead to distinguish between hosts") if args.package and (args.hub_package or args.client_package): user_error( "--package cannot be used in combination with --hub-package / --client-package")
def validate_command(command, args): if command in ["install", "packages", "list", "download"]: if args.edition: args.edition = args.edition.lower() if args.edition == "core": args.edition = "community" if args.edition not in ["enterprise", "community"]: user_error("--edition must be either community or enterprise") else: args.edition = "enterprise" if command in ["uninstall"] and not (args.hosts or args.hub or args.clients): user_error("Use --hosts, --hub or --clients to specify remote hosts") if command == "install": if args.call_collect and not args.demo: user_error("--call-collect must be used with --demo") if not args.clients and not args.hub: user_error("Specify hosts using --hub and --clients") if args.hub and args.clients and args.package: user_error( "Use --hub-package / --client-package instead to distinguish between hosts") if args.package and (args.hub_package or args.client_package): user_error( "--package cannot be used in combination with --hub-package / --client-package") if args.package and not is_package_url(args.package): if not os.path.exists(os.path.expanduser(args.package)): user_error(f"Package/directory '{args.package}' does not exist") if args.hub_package and not is_package_url(args.hub_package): if not os.path.isfile(args.hub_package): user_error(f"Hub package '{args.hub_package}' does not exist") if args.client_package and not is_package_url(args.client_package): if not os.path.isfile(args.client_package): user_error(f"Client package '{args.client_package}' does not exist") if command in ["sudo", "run"]: if len(args.remote_command) != 1: user_error("cf-remote sude/run requires exactly 1 command (use quotes)") args.remote_command = args.remote_command[0] if command == "spawn" and not args.list_platforms and not args.init_config: # --list-platforms doesn't require any other options/arguments (TODO: # --provider), but otherwise all have to be given if not args.platform: user_error("--platform needs to be specified") if not args.count: user_error("--count needs to be specified") if not args.role: user_error("--role needs to be specified") if not args.name: user_error("--name needs to be specified") if command == "destroy": if not args.all and not args.name: user_error("One of --all or NAME required for destroy") if command == "deploy": if not args.directory: user_error("Must specify a masterfiles directory") if not args.hub: user_error("Must specify at least one hub") if not os.path.isdir(args.directory): user_error(f"'{args.directory}' is not a directory")
def validate_command(command, args): if command in ["install", "packages"]: if args.edition: args.edition = args.edition.lower() if args.edition == "core": args.edition = "community" if args.edition not in ["enterprise", "community"]: user_error("--edition must be either community or enterprise") else: args.edition = "enterprise" if command in ["uninstall" ] and not (args.hosts or args.hub or args.clients): user_error("Use --hosts, --hub or --clients to specify remote hosts") if command == "install" and (args.call_collect and not args.demo): user_error("--call-collect must be used with --demo") if command == "install": if not args.clients and not args.hub: user_error("Specify hosts using --hub and --clients") if args.hub and args.clients and args.package: user_error( "Use --hub-package / --client-package instead to distinguish between hosts" ) if args.package and (args.hub_package or args.client_package): user_error( "--package cannot be used in combination with --hub-package / --client-package" ) # TODO: Find this automatically if command in ["sudo", "run"]: if len(args.remote_command) != 1: user_error( "cf-remote sude/run requires exactly 1 command (use quotes)") args.remote_command = args.remote_command[0] if command == "spawn" and not args.list_platforms and not args.init_config: # --list-platforms doesn't require any other options/arguments (TODO: # --provider), but otherwise all have to be given if not args.platform: user_error("--platform needs to be specified") if not args.count: user_error("--count needs to be specified") if not args.role: user_error("--role needs to be specified") if not args.name: user_error("--name needs to be specified") if command == "destroy": if not args.all and not args.name: user_error("One of --all or NAME required for destroy")
def file_or_single_host(string): assert "," not in string one_list = file_or_comma_list(string) if len(one_list) != 1: user_error("File '{}' must contain exactly 1 hostname or IP".format(string)) return one_list[0]
def validate_command(command, args): if command in ["info", "sudo", "run"] and not args.hosts: user_error("Use --hosts to specify remote hosts") if args.bootstrap and command != "install": user_error("--bootstrap can only be used with install command") if args.call_collect and not args.demo: user_error("--call-collect must be used with --demo") if command == "install": if args.hosts: user_error("Use --clients and --hub instead of --hosts") if not args.clients and not args.hub: user_error("Specify hosts using --hub and --clients") if args.hub and args.clients and args.package: user_error( "Use --hub-package / --client-package instead to distinguish between hosts") if args.package and (args.hub_package or args.client_package): user_error( "--package cannot be used in combination with --hub-package / --client-package")
def destroy(group_name=None): if os.path.exists(CLOUD_CONFIG_FPATH): creds_data = read_json(CLOUD_CONFIG_FPATH) else: print("Cloud credentials not found at %s" % CLOUD_CONFIG_FPATH) return 1 aws_creds = None try: aws_creds = AWSCredentials(creds_data["aws"]["key"], creds_data["aws"]["secret"]) except KeyError: # missing/incomplete AWS credentials, may not be needed, though pass gcp_creds = None try: gcp_creds = GCPCredentials(creds_data["gcp"]["project_id"], creds_data["gcp"]["service_account_id"], creds_data["gcp"]["key_path"]) except KeyError: # missing/incomplete GCP credentials, may not be needed, though pass if not os.path.exists(CLOUD_STATE_FPATH): print("No saved cloud state info") return 1 vms_info = read_json(CLOUD_STATE_FPATH) to_destroy = [] if group_name: print("Destroying hosts in the '%s' group" % group_name) if not group_name.startswith("@"): group_name = "@" + group_name if group_name not in vms_info: print("Group '%s' not found" % group_name) return 1 region = vms_info[group_name]["meta"]["region"] provider = vms_info[group_name]["meta"]["provider"] if provider == "aws": if aws_creds is None: user_error("Missing/incomplete AWS credentials") return 1 driver = get_cloud_driver(Providers.AWS, aws_creds, region) if provider == "gcp": if gcp_creds is None: user_error("Missing/incomplete GCP credentials") return 1 driver = get_cloud_driver(Providers.GCP, gcp_creds, region) nodes = driver.list_nodes() for name, vm_info in vms_info[group_name].items(): if name == "meta": continue vm_uuid = vm_info["uuid"] vm = VM.get_by_uuid(vm_uuid, nodes=nodes) if vm is not None: to_destroy.append(vm) else: print("VM '%s' not found in the clouds" % vm_uuid) del vms_info[group_name] else: print("Destroying all hosts") for group_name in [ key for key in vms_info.keys() if key.startswith("@") ]: region = vms_info[group_name]["meta"]["region"] provider = vms_info[group_name]["meta"]["provider"] if provider == "aws": if aws_creds is None: user_error("Missing/incomplete AWS credentials") return 1 driver = get_cloud_driver(Providers.AWS, aws_creds, region) if provider == "gcp": if gcp_creds is None: user_error("Missing/incomplete GCP credentials") return 1 driver = get_cloud_driver(Providers.GCP, gcp_creds, region) nodes = driver.list_nodes() for name, vm_info in vms_info[group_name].items(): if name == "meta": continue vm_uuid = vm_info["uuid"] vm = VM.get_by_uuid(vm_uuid, nodes=nodes) if vm is not None: to_destroy.append(vm) else: print("VM '%s' not found in the clouds" % vm_uuid) del vms_info[group_name] destroy_vms(to_destroy) write_json(CLOUD_STATE_FPATH, vms_info) return 0
def run_command_with_args(command, args): if command == "info": return commands.info(args.hosts, None) elif command == "install": if args.trust_keys: trust_keys = args.trust_keys.split(",") else: trust_keys = None return commands.install( args.hub, args.clients, package=args.package, bootstrap=args.bootstrap, hub_package=args.hub_package, client_package=args.client_package, version=args.version, demo=args.demo, call_collect=args.call_collect, edition=args.edition, remote_download=args.remote_download, trust_keys=trust_keys) elif command == "uninstall": all_hosts = ((args.hosts or []) + (args.hub or []) + (args.clients or [])) return commands.uninstall(all_hosts) elif command == "packages": log.warning("packages command is deprecated, please use the new command: download") return commands.download(tags=args.tags, version=args.version, edition=args.edition) elif command == "list": return commands.list_command(tags=args.tags, version=args.version, edition=args.edition) elif command == "download": return commands.download(tags=args.tags, version=args.version, edition=args.edition) elif command == "run": return commands.run(hosts=args.hosts, raw=args.raw, command=args.remote_command) elif command == "sudo": return commands.sudo(hosts=args.hosts, raw=args.raw, command=args.remote_command) elif command == "scp": return commands.scp(hosts=args.hosts, files=args.args) elif command == "spawn": if args.list_platforms: return commands.list_platforms() if args.init_config: return commands.init_cloud_config() if args.name and "," in args.name: user_error("Group --name may not contain commas") if args.aws and args.gcp: user_error("--aws and --gcp cannot be used at the same time") if args.role.endswith("s"): # role should be singular args.role = args.role[:-1] if args.gcp: provider = Providers.GCP else: # AWS is currently also the default provider = Providers.AWS if args.network: user_error("--network not supported for AWS") if args.no_public_ip: user_error("--no-public-ip not supported for AWS") if args.network and (args.network.count("/") != 1): user_error("Invalid network specified, needs to be in the network/subnet format") return commands.spawn(args.platform, args.count, args.role, args.name, provider=provider, size=args.size, network=args.network, public_ip=not args.no_public_ip, extend_group=args.append) elif command == "destroy": group_name = args.name if args.name else None return commands.destroy(group_name) elif command == "deploy": return commands.deploy(args.hub, args.directory) else: user_error("Unknown command: '{}'".format(command))