Add support for tsocks
This commit is contained in:
parent
c674a02a1c
commit
df681e23dc
1 changed files with 247 additions and 188 deletions
|
@ -19,7 +19,7 @@ import argcomplete
|
||||||
# To determine if openstack thinks that a given object exists already
|
# To determine if openstack thinks that a given object exists already
|
||||||
def exists_in_openstack(output_error: tuple) -> bool:
|
def exists_in_openstack(output_error: tuple) -> bool:
|
||||||
output = output_error[0]
|
output = output_error[0]
|
||||||
#error = output_error[1]
|
# error = output_error[1]
|
||||||
# FIXME: This is a dumb way to check that an object exists, we should check the error in addition to this
|
# FIXME: This is a dumb way to check that an object exists, we should check the error in addition to this
|
||||||
try:
|
try:
|
||||||
json.loads(output)
|
json.loads(output)
|
||||||
|
@ -29,12 +29,11 @@ def exists_in_openstack(output_error: tuple) -> bool:
|
||||||
|
|
||||||
|
|
||||||
def get_dns_records(addresses: dict, fqdn: str) -> str:
|
def get_dns_records(addresses: dict, fqdn: str) -> str:
|
||||||
|
|
||||||
def get_tabs(string: str) -> str:
|
def get_tabs(string: str) -> str:
|
||||||
num_tabs: int = int(len(string.expandtabs()) / len(f"\t".expandtabs()))
|
num_tabs: int = int(len(string.expandtabs()) / len(f"\t".expandtabs()))
|
||||||
tabs = str()
|
tabs = str()
|
||||||
for _ in range(0, num_tabs):
|
for _ in range(0, num_tabs):
|
||||||
tabs += '\t'
|
tabs += "\t"
|
||||||
return tabs
|
return tabs
|
||||||
|
|
||||||
v6_record = str()
|
v6_record = str()
|
||||||
|
@ -42,7 +41,7 @@ def get_dns_records(addresses: dict, fqdn: str) -> str:
|
||||||
first = True
|
first = True
|
||||||
tabs = get_tabs(f"{fqdn}.\t")
|
tabs = get_tabs(f"{fqdn}.\t")
|
||||||
for address in addresses:
|
for address in addresses:
|
||||||
if address.find('.') != -1:
|
if address.find(".") != -1:
|
||||||
if first:
|
if first:
|
||||||
v4_record = f"{fqdn}.\tA\t{address}\n"
|
v4_record = f"{fqdn}.\tA\t{address}\n"
|
||||||
first = False
|
first = False
|
||||||
|
@ -57,7 +56,7 @@ def get_dns_records(addresses: dict, fqdn: str) -> str:
|
||||||
# This function is responsible for collecting all neccessary information from the user, and provide feedback if something is missing
|
# This function is responsible for collecting all neccessary information from the user, and provide feedback if something is missing
|
||||||
def get_options() -> dict:
|
def get_options() -> dict:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
subparsers = parser.add_subparsers(title='subcommands', dest='command')
|
subparsers = parser.add_subparsers(title="subcommands", dest="command")
|
||||||
|
|
||||||
# Used with the command "create"
|
# Used with the command "create"
|
||||||
create_parser = subparsers.add_parser("create")
|
create_parser = subparsers.add_parser("create")
|
||||||
|
@ -83,16 +82,14 @@ def get_options() -> dict:
|
||||||
)
|
)
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
"--sgroup-policy",
|
"--sgroup-policy",
|
||||||
dest='sgroup_policy',
|
dest="sgroup_policy",
|
||||||
help="Set security group policy for machine",
|
help="Set security group policy for machine",
|
||||||
choices=[
|
choices=["affinity", "anti-affinity", "soft-affinity", "soft-anti-affinity"],
|
||||||
'affinity', 'anti-affinity', 'soft-affinity', 'soft-anti-affinity'
|
|
||||||
],
|
|
||||||
default="anti-affinity",
|
default="anti-affinity",
|
||||||
)
|
)
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
"--volume-size",
|
"--volume-size",
|
||||||
dest='volume_size',
|
dest="volume_size",
|
||||||
help="Set volume size in GB for machine",
|
help="Set volume size in GB for machine",
|
||||||
default="100",
|
default="100",
|
||||||
)
|
)
|
||||||
|
@ -100,24 +97,29 @@ def get_options() -> dict:
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
"-k",
|
"-k",
|
||||||
"--key",
|
"--key",
|
||||||
help=
|
help="SSH key to use, either the path to a public ssh key OR the name of an existing key in openstack",
|
||||||
"SSH key to use, either the path to a public ssh key OR the name of an existing key in openstack",
|
|
||||||
required=True,
|
required=True,
|
||||||
)
|
)
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
"--fqdn",
|
"--fqdn", help="fqdn of server, MUST follow name standard", required=True
|
||||||
help="fqdn of server, MUST follow name standard",
|
)
|
||||||
required=True)
|
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
'--format',
|
"--format",
|
||||||
choices=['bind', 'json', 'shell', 'table', 'value', 'yaml'],
|
choices=["bind", "json", "shell", "table", "value", "yaml"],
|
||||||
default="json",
|
default="json",
|
||||||
)
|
)
|
||||||
create_parser.add_argument(
|
create_parser.add_argument(
|
||||||
"--namestandard",
|
"--namestandard",
|
||||||
help="Name of name standard",
|
help="Name of name standard",
|
||||||
default='default',
|
default="default",
|
||||||
dest='namestandard',
|
dest="namestandard",
|
||||||
|
)
|
||||||
|
create_parser.add_argument(
|
||||||
|
"--with-tsocks",
|
||||||
|
help="Run with tsocks",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
dest="with_tsocks",
|
||||||
)
|
)
|
||||||
|
|
||||||
delete_parser = subparsers.add_parser("delete")
|
delete_parser = subparsers.add_parser("delete")
|
||||||
|
@ -129,36 +131,43 @@ def get_options() -> dict:
|
||||||
delete_parser.add_argument(
|
delete_parser.add_argument(
|
||||||
"--preserve-port",
|
"--preserve-port",
|
||||||
action=argparse.BooleanOptionalAction,
|
action=argparse.BooleanOptionalAction,
|
||||||
dest='preserve_port',
|
dest="preserve_port",
|
||||||
help="Keep the configured port in openstack",
|
help="Keep the configured port in openstack",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
delete_parser.add_argument(
|
delete_parser.add_argument(
|
||||||
"--preserve-cosmos-config",
|
"--preserve-cosmos-config",
|
||||||
action=argparse.BooleanOptionalAction,
|
action=argparse.BooleanOptionalAction,
|
||||||
dest='preserve_cosmos_overlay',
|
dest="preserve_cosmos_overlay",
|
||||||
help="Keep the overlay for this server around",
|
help="Keep the overlay for this server around",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
delete_parser.add_argument(
|
delete_parser.add_argument(
|
||||||
"--preserve-disk",
|
"--preserve-disk",
|
||||||
action=argparse.BooleanOptionalAction,
|
action=argparse.BooleanOptionalAction,
|
||||||
dest='preserve_disk',
|
dest="preserve_disk",
|
||||||
help="Keep the configured disk in openstack",
|
help="Keep the configured disk in openstack",
|
||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
delete_parser.add_argument(
|
delete_parser.add_argument(
|
||||||
"--namestandard",
|
"--namestandard",
|
||||||
help="Name of name standard",
|
help="Name of name standard",
|
||||||
default='default',
|
default="default",
|
||||||
dest='namestandard',
|
dest="namestandard",
|
||||||
|
)
|
||||||
|
delete_parser.add_argument(
|
||||||
|
"--with-tsocks",
|
||||||
|
help="Run with tsocks",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
dest="with_tsocks",
|
||||||
)
|
)
|
||||||
show_parser = subparsers.add_parser("show")
|
show_parser = subparsers.add_parser("show")
|
||||||
show_parser.add_argument(
|
show_parser.add_argument(
|
||||||
'--format',
|
"--format",
|
||||||
choices=['bind', 'json', 'shell', 'table', 'value', 'yaml'],
|
choices=["bind", "json", "shell", "table", "value", "yaml"],
|
||||||
default='json',
|
default="json",
|
||||||
dest='format',
|
dest="format",
|
||||||
)
|
)
|
||||||
show_parser.add_argument(
|
show_parser.add_argument(
|
||||||
"--network",
|
"--network",
|
||||||
|
@ -173,8 +182,15 @@ def get_options() -> dict:
|
||||||
show_parser.add_argument(
|
show_parser.add_argument(
|
||||||
"--namestandard",
|
"--namestandard",
|
||||||
help="Name of name standard",
|
help="Name of name standard",
|
||||||
default='default',
|
default="default",
|
||||||
dest='namestandard',
|
dest="namestandard",
|
||||||
|
)
|
||||||
|
show_parser.add_argument(
|
||||||
|
"--with-tsocks",
|
||||||
|
help="Run with tsocks",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
dest="with_tsocks",
|
||||||
)
|
)
|
||||||
|
|
||||||
argcomplete.autocomplete(parser)
|
argcomplete.autocomplete(parser)
|
||||||
|
@ -182,163 +198,183 @@ def get_options() -> dict:
|
||||||
|
|
||||||
# This is an opinionated name standard that we follow, and WILL rely on for various automation tasks
|
# This is an opinionated name standard that we follow, and WILL rely on for various automation tasks
|
||||||
fqdn = args.fqdn
|
fqdn = args.fqdn
|
||||||
domain, environment, function, hostname, instance, location, number, service = parse_fqdn(
|
domain, environment, function, hostname, instance, location, number, service = (
|
||||||
fqdn, args.namestandard)
|
parse_fqdn(fqdn, args.namestandard)
|
||||||
|
)
|
||||||
|
|
||||||
# We will allways put a server in i server group, you can override the default with the --sgroup switch
|
# We will allways put a server in i server group, you can override the default with the --sgroup switch
|
||||||
if ('sgroup' not in args) or (args.sgroup == ""):
|
if ("sgroup" not in args) or (args.sgroup == ""):
|
||||||
# if it is not set, we will construct a security group name based on other information
|
# if it is not set, we will construct a security group name based on other information
|
||||||
sgroup = fqdn.replace('.', '-') + "-sgroup"
|
sgroup = fqdn.replace(".", "-").replace(number, "") + "sgroup"
|
||||||
else:
|
else:
|
||||||
sgroup = args.sgroup
|
sgroup = args.sgroup
|
||||||
|
|
||||||
# These options MUST match what is used in main function, or there WILL be dragons
|
# These options MUST match what is used in main function, or there WILL be dragons
|
||||||
options: dict = {
|
options: dict = {
|
||||||
'command': args.command,
|
"command": args.command,
|
||||||
'domain': domain,
|
"domain": domain,
|
||||||
'environment': environment,
|
"environment": environment,
|
||||||
'fqdn': fqdn,
|
"fqdn": fqdn,
|
||||||
'function': function,
|
"function": function,
|
||||||
'hostname': hostname,
|
"hostname": hostname,
|
||||||
'instance': instance,
|
"instance": instance,
|
||||||
'location': location,
|
"location": location,
|
||||||
'namestandard': args.namestandard,
|
"namestandard": args.namestandard,
|
||||||
'number': number,
|
"number": number,
|
||||||
'service': service,
|
"service": service,
|
||||||
'sgroup': sgroup
|
"sgroup": sgroup,
|
||||||
|
"with_tsocks": args.with_tsocks,
|
||||||
}
|
}
|
||||||
if args.command == 'create':
|
if args.command == "create":
|
||||||
options['format'] = args.format
|
options["format"] = args.format
|
||||||
options['flavor'] = args.flavor
|
options["flavor"] = args.flavor
|
||||||
options['image'] = args.image
|
options["image"] = args.image
|
||||||
options['key'] = args.key
|
options["key"] = args.key
|
||||||
options['network'] = args.network
|
options["network"] = args.network
|
||||||
options['sgroup_policy'] = args.sgroup_policy
|
options["sgroup_policy"] = args.sgroup_policy
|
||||||
options['volume_size'] = args.volume_size
|
options["volume_size"] = args.volume_size
|
||||||
if args.command == 'delete':
|
if args.command == "delete":
|
||||||
options['preserve_port'] = args.preserve_port
|
options["preserve_port"] = args.preserve_port
|
||||||
options['preserve_cosmos_overlay'] = args.preserve_cosmos_overlay
|
options["preserve_cosmos_overlay"] = args.preserve_cosmos_overlay
|
||||||
options['preserve_disk'] = args.preserve_disk
|
options["preserve_disk"] = args.preserve_disk
|
||||||
if args.command == 'show':
|
if args.command == "show":
|
||||||
options['format'] = args.format
|
options["format"] = args.format
|
||||||
|
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
def parse_fqdn(fqdn: str, namestandard: str) -> tuple:
|
def parse_fqdn(fqdn: str, namestandard: str) -> tuple:
|
||||||
if namestandard == 'drive':
|
if namestandard == "drive":
|
||||||
return parse_drive_fqdn(fqdn)
|
return parse_drive_fqdn(fqdn)
|
||||||
else:
|
else:
|
||||||
return parse_default_fqdn(fqdn)
|
return parse_default_fqdn(fqdn)
|
||||||
|
|
||||||
|
|
||||||
def parse_default_fqdn(fqdn: str) -> tuple:
|
def parse_default_fqdn(fqdn: str) -> tuple:
|
||||||
domain = '.'.join([x for x in fqdn.split('.')[:1]])
|
domain = ".".join([x for x in fqdn.split(".")[:1]])
|
||||||
hostname = fqdn.split('.')[0]
|
hostname = fqdn.split(".")[0]
|
||||||
instance, location, environment, function, number = hostname.split('-')
|
instance, location, environment, function, number = hostname.split("-")
|
||||||
service = fqdn.split('.')[1]
|
service = fqdn.split(".")[1]
|
||||||
return domain, environment, function, hostname, instance, location, number, service
|
return domain, environment, function, hostname, instance, location, number, service
|
||||||
|
|
||||||
|
|
||||||
def parse_drive_fqdn(fqdn: str) -> tuple:
|
def parse_drive_fqdn(fqdn: str) -> tuple:
|
||||||
type_regex = r'-*[1-9]*\..*'
|
type_regex = r"-*[1-9]*\..*"
|
||||||
server_type = re.sub(type_regex, '', fqdn)
|
server_type = re.sub(type_regex, "", fqdn)
|
||||||
|
|
||||||
env_regex = r'.*(pilot|test).*'
|
env_regex = r".*(pilot|test).*"
|
||||||
environment = re.sub(env_regex, r'\1', fqdn)
|
environment = re.sub(env_regex, r"\1", fqdn)
|
||||||
domain = f'drive.{environment}.sunet.se'
|
domain = f"drive.{environment}.sunet.se"
|
||||||
customer = 'common'
|
customer = "common"
|
||||||
|
|
||||||
if environment not in ['pilot', 'test']:
|
if environment not in ["pilot", "test"]:
|
||||||
environment = 'prod'
|
environment = "prod"
|
||||||
domain = 'drive.sunet.se'
|
domain = "drive.sunet.se"
|
||||||
|
|
||||||
if server_type in ["backup", "intern-db", "node", "redis", "script"]:
|
if server_type in ["backup", "intern-db", "node", "redis", "script"]:
|
||||||
customer = fqdn.split('.')[1]
|
customer = fqdn.split(".")[1]
|
||||||
if customer == 'drive':
|
if customer == "drive":
|
||||||
customer = 'common'
|
customer = "common"
|
||||||
elif server_type in ['gss', 'lookup']:
|
elif server_type in ["gss", "lookup"]:
|
||||||
customer = server_type
|
customer = server_type
|
||||||
elif server_type == 'gssbackup':
|
elif server_type == "gssbackup":
|
||||||
customer = 'gss'
|
customer = "gss"
|
||||||
elif server_type == 'lookupbackup':
|
elif server_type == "lookupbackup":
|
||||||
customer = 'lookup'
|
customer = "lookup"
|
||||||
else:
|
else:
|
||||||
customer = 'common'
|
customer = "common"
|
||||||
hostname = fqdn.split('.')[0]
|
hostname = fqdn.split(".")[0]
|
||||||
number = re.sub(r'[-a-z.]', '', hostname)
|
number = re.sub(r"[-a-z.]", "", hostname)
|
||||||
location = 'sto4'
|
location = "sto4"
|
||||||
if number == '3':
|
if number == "3":
|
||||||
location = 'sto3'
|
location = "sto3"
|
||||||
if number == '1':
|
if number == "1":
|
||||||
location = 'dco'
|
location = "dco"
|
||||||
return domain, environment, server_type, hostname, customer, location, number, 'drive'
|
return (
|
||||||
|
domain,
|
||||||
|
environment,
|
||||||
|
server_type,
|
||||||
|
hostname,
|
||||||
|
customer,
|
||||||
|
location,
|
||||||
|
number,
|
||||||
|
"drive",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def run_in_openstack(outer_command: list[str], rc_file: str) -> tuple:
|
def run_in_openstack(
|
||||||
|
outer_command: list[str], rc_file: str, with_tsocks: bool
|
||||||
|
) -> tuple:
|
||||||
# This function runs the specified command with openstack cli using tsocks.
|
# This function runs the specified command with openstack cli using tsocks.
|
||||||
# FIXME: We should support disabling tsocks with a commandline switch
|
# FIXME: We should support disabling tsocks with a commandline switch
|
||||||
def run_command(inner_command: list[str]) -> tuple:
|
def run_command(inner_command: list[str]) -> tuple:
|
||||||
proc = subprocess.Popen(inner_command,
|
proc = subprocess.Popen(
|
||||||
stdin=subprocess.PIPE,
|
inner_command,
|
||||||
stdout=subprocess.PIPE,
|
stdin=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
text=True)
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
output_error = proc.communicate()
|
output_error = proc.communicate()
|
||||||
proc.wait()
|
proc.wait()
|
||||||
return output_error
|
return output_error
|
||||||
|
|
||||||
outer_command.insert(0, "openstack")
|
outer_command.insert(0, "openstack")
|
||||||
outer_command.insert(0, 'tsocks')
|
if with_tsocks:
|
||||||
|
outer_command.insert(0, "tsocks")
|
||||||
|
|
||||||
# This is where we actually use the openstack openrc file, and we source it before running the command
|
# This is where we actually use the openstack openrc file, and we source it before running the command
|
||||||
string_cmd = f"bash -c 'source {rc_file} &&"
|
string_cmd = f"bash -c 'source {rc_file} &&"
|
||||||
for cmd in outer_command:
|
for cmd in outer_command:
|
||||||
string_cmd += ' ' + cmd
|
string_cmd += " " + cmd
|
||||||
string_cmd += "'"
|
string_cmd += "'"
|
||||||
full_command = shlex.split(string_cmd)
|
full_command = shlex.split(string_cmd)
|
||||||
return run_command(full_command)
|
return run_command(full_command)
|
||||||
|
|
||||||
|
|
||||||
def setup_key(in_key: str, rc_file: str) -> str:
|
def setup_key(in_key: str, rc_file: str, with_tsocks: bool) -> str:
|
||||||
if type(in_key) == type(tuple()):
|
if type(in_key) == type(tuple()):
|
||||||
key = in_key[0]
|
key = in_key[0]
|
||||||
else:
|
else:
|
||||||
key = in_key
|
key = in_key
|
||||||
|
|
||||||
# We deal with public keys first, if the supplied option is a filename we will use that to create a new keypair in openstack if neccessary
|
# We deal with public keys first, if the supplied option is a filename we will use that to create a new keypair in openstack if neccessary
|
||||||
# If the key is NOT a file on the local machine, we assume that it is a name of a pre existing key
|
# If the key is NOT a file on the local machine, we assume that it is a name of a pre existing key
|
||||||
def keypair_existence(key_name: str) -> bool:
|
def keypair_existence(key_name: str) -> bool:
|
||||||
show_keypair_command = ['keypair', 'show', '-f', 'json', key_name]
|
show_keypair_command = ["keypair", "show", "-f", "json", key_name]
|
||||||
return exists_in_openstack(
|
return exists_in_openstack(
|
||||||
run_in_openstack(show_keypair_command, rc_file))
|
run_in_openstack(show_keypair_command, rc_file, with_tsocks)
|
||||||
|
)
|
||||||
|
|
||||||
if isfile(key):
|
if isfile(key):
|
||||||
key_name = str(os.environ.get('USER')) + '-sshkey'
|
key_name = str(os.environ.get("USER")) + "-sshkey"
|
||||||
pub_key = key
|
pub_key = key
|
||||||
create_keypair_command = [
|
create_keypair_command = [
|
||||||
'keypair', 'create', '--public-key', pub_key, key_name
|
"keypair",
|
||||||
|
"create",
|
||||||
|
"--public-key",
|
||||||
|
pub_key,
|
||||||
|
key_name,
|
||||||
]
|
]
|
||||||
keypair_exists = keypair_existence(key_name)
|
keypair_exists = keypair_existence(key_name)
|
||||||
if not keypair_exists:
|
if not keypair_exists:
|
||||||
run_in_openstack(create_keypair_command, rc_file)
|
run_in_openstack(create_keypair_command, rc_file, with_tsocks)
|
||||||
else:
|
else:
|
||||||
key_name = key
|
key_name = key
|
||||||
|
|
||||||
keypair_exists = keypair_existence(key_name)
|
keypair_exists = keypair_existence(key_name)
|
||||||
if not keypair_exists:
|
if not keypair_exists:
|
||||||
# We cannot proceed without a public key
|
# We cannot proceed without a public key
|
||||||
print(
|
print('{"ERROR": "No keypair exists and I could not create a new one :("}')
|
||||||
'{"ERROR": "No keypair exists and I could not create a new one :("}'
|
|
||||||
)
|
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
return key_name
|
return key_name
|
||||||
|
|
||||||
|
|
||||||
def setup_rc_file(options: dict) -> str:
|
def setup_rc_file(options: dict) -> str:
|
||||||
config_basepath = path_join(path_join(os.environ["HOME"], ".config"),
|
config_basepath = path_join(path_join(os.environ["HOME"], ".config"), "sunet")
|
||||||
"sunet")
|
|
||||||
rc_file = path_join(
|
rc_file = path_join(
|
||||||
config_basepath,
|
config_basepath,
|
||||||
f"app-cred-{options['service']}-{options['location']}-{options['environment']}-openrc.sh"
|
f"app-cred-{options['service']}-{options['location']}-{options['environment']}-openrc.sh",
|
||||||
)
|
)
|
||||||
|
|
||||||
if not isfile(rc_file):
|
if not isfile(rc_file):
|
||||||
|
@ -349,21 +385,22 @@ def setup_rc_file(options: dict) -> str:
|
||||||
return rc_file
|
return rc_file
|
||||||
|
|
||||||
|
|
||||||
def show_server(fqdn: str, rc_file: str, format: str = 'json') -> tuple:
|
def show_server(
|
||||||
show_server_command = ['server', 'show', '-f', format, fqdn]
|
fqdn: str, rc_file: str, with_tsocks: bool, format: str = "json"
|
||||||
return run_in_openstack(show_server_command, rc_file)
|
) -> tuple:
|
||||||
|
show_server_command = ["server", "show", "-f", format, fqdn]
|
||||||
|
return run_in_openstack(show_server_command, rc_file, with_tsocks)
|
||||||
|
|
||||||
|
|
||||||
def wait_for_server(fqdn: str,
|
def wait_for_server(
|
||||||
rc_file: str,
|
fqdn: str, rc_file: str, network: str, with_tsocks: bool, timeout=360
|
||||||
network: str,
|
) -> bool:
|
||||||
timeout=360) -> bool:
|
|
||||||
then = time.time()
|
then = time.time()
|
||||||
now = then
|
now = then
|
||||||
while then >= (now - timeout):
|
while then >= (now - timeout):
|
||||||
try:
|
try:
|
||||||
output, _ = show_server(fqdn, rc_file)
|
output, _ = show_server(fqdn, rc_file, with_tsocks)
|
||||||
json.loads(output)['addresses'][network]
|
json.loads(output)["addresses"][network]
|
||||||
return True
|
return True
|
||||||
except KeyError:
|
except KeyError:
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
@ -377,128 +414,145 @@ def wait_for_server(fqdn: str,
|
||||||
def create(options: dict) -> int:
|
def create(options: dict) -> int:
|
||||||
# All of these can be set by command line arguments, but some have defaults and as such are optional.
|
# All of these can be set by command line arguments, but some have defaults and as such are optional.
|
||||||
# At this point they MUST be set after parsing args, otherwise there WILL be bugs
|
# At this point they MUST be set after parsing args, otherwise there WILL be bugs
|
||||||
key: str = options['key']
|
key: str = options["key"]
|
||||||
dns_record: bool = False
|
dns_record: bool = False
|
||||||
format: str = options['format']
|
format: str = options["format"]
|
||||||
if format == 'bind':
|
if format == "bind":
|
||||||
format = 'json'
|
format = "json"
|
||||||
dns_record = True
|
dns_record = True
|
||||||
flavor: str = options['flavor']
|
flavor: str = options["flavor"]
|
||||||
fqdn: str = options['fqdn']
|
fqdn: str = options["fqdn"]
|
||||||
hostname: str = options['hostname']
|
hostname: str = options["hostname"]
|
||||||
image: str = options['image']
|
image: str = options["image"]
|
||||||
network: str = options['network']
|
network: str = options["network"]
|
||||||
rc_file: str = options['rc_file']
|
rc_file: str = options["rc_file"]
|
||||||
sgroup: str = options['sgroup']
|
sgroup: str = options["sgroup"]
|
||||||
sgroup_policy: str = options['sgroup_policy']
|
sgroup_policy: str = options["sgroup_policy"]
|
||||||
volume_size: str = options['volume_size']
|
volume_size: str = options["volume_size"]
|
||||||
|
with_tsocks: bool = options["with_tsocks"]
|
||||||
key_name: str = setup_key(key, rc_file)
|
key_name: str = setup_key(key, rc_file, with_tsocks)
|
||||||
|
|
||||||
# FIXME: we don't support setting the portname, we just use the fqdn and append -port to it
|
# FIXME: we don't support setting the portname, we just use the fqdn and append -port to it
|
||||||
port_name = options['fqdn'].replace('.', '-') + '-port'
|
port_name = options["fqdn"].replace(".", "-") + "-port"
|
||||||
|
|
||||||
# Internal helper functions
|
# Internal helper functions
|
||||||
def sgroup_show(sgroup: str) -> tuple:
|
def sgroup_show(sgroup: str) -> tuple:
|
||||||
show_sgroup_command = ['server', 'group', 'show', '-f', 'json', sgroup]
|
show_sgroup_command = ["server", "group", "show", "-f", "json", sgroup]
|
||||||
return run_in_openstack(show_sgroup_command, rc_file)
|
return run_in_openstack(show_sgroup_command, rc_file, with_tsocks)
|
||||||
|
|
||||||
# Check if server exists
|
# Check if server exists
|
||||||
server_output_error = show_server(fqdn, rc_file)
|
server_output_error = show_server(fqdn, rc_file, with_tsocks)
|
||||||
server_exists = exists_in_openstack(server_output_error)
|
server_exists = exists_in_openstack(server_output_error)
|
||||||
# We will bail out early if the server allready exists, and just print out what we know about it
|
# We will bail out early if the server allready exists, and just print out what we know about it
|
||||||
if server_exists:
|
if server_exists:
|
||||||
if dns_record:
|
if dns_record:
|
||||||
addresses = json.loads(
|
addresses = json.loads(server_output_error[0])["addresses"][network]
|
||||||
server_output_error[0])['addresses'][network]
|
|
||||||
print(get_dns_records(addresses, fqdn))
|
print(get_dns_records(addresses, fqdn))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if format == 'json':
|
if format == "json":
|
||||||
print(server_output_error[0])
|
print(server_output_error[0])
|
||||||
else:
|
else:
|
||||||
print(show_server(fqdn, rc_file, format)[0])
|
print(show_server(fqdn, rc_file, with_tsocks, format)[0])
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Server group section
|
# Server group section
|
||||||
sgroup_exists = exists_in_openstack(sgroup_show(sgroup))
|
sgroup_exists = exists_in_openstack(sgroup_show(sgroup))
|
||||||
if not sgroup_exists:
|
if not sgroup_exists:
|
||||||
create_sgroup_command = [
|
create_sgroup_command = [
|
||||||
'server', 'group', 'create', '--policy', sgroup_policy, sgroup
|
"server",
|
||||||
|
"group",
|
||||||
|
"create",
|
||||||
|
"--policy",
|
||||||
|
sgroup_policy,
|
||||||
|
sgroup,
|
||||||
]
|
]
|
||||||
run_in_openstack(create_sgroup_command, rc_file)
|
run_in_openstack(create_sgroup_command, rc_file, with_tsocks)
|
||||||
sgroup_output, _ = sgroup_show(sgroup)
|
sgroup_output, _ = sgroup_show(sgroup)
|
||||||
sgroup_object = json.loads(sgroup_output)
|
sgroup_object = json.loads(sgroup_output)
|
||||||
sgroup_id = sgroup_object['id']
|
sgroup_id = sgroup_object["id"]
|
||||||
|
|
||||||
# Port section
|
# Port section
|
||||||
show_port_command = ['port', 'show', '-f', 'json', port_name]
|
show_port_command = ["port", "show", "-f", "json", port_name]
|
||||||
port_exists = exists_in_openstack(
|
port_exists = exists_in_openstack(
|
||||||
run_in_openstack(show_port_command, rc_file))
|
run_in_openstack(show_port_command, rc_file, with_tsocks)
|
||||||
|
)
|
||||||
if not port_exists:
|
if not port_exists:
|
||||||
create_port_command = [
|
create_port_command = ["port", "create", "--network", network, port_name]
|
||||||
'port', 'create', '--network', network, port_name
|
run_in_openstack(create_port_command, rc_file, with_tsocks)
|
||||||
]
|
|
||||||
run_in_openstack(create_port_command, rc_file)
|
|
||||||
|
|
||||||
# Finaly set up is done, and we can create server
|
# Finaly set up is done, and we can create server
|
||||||
create_server_command = [
|
create_server_command = [
|
||||||
'server', 'create', '--port', port_name, '--flavor', flavor, '--image',
|
"server",
|
||||||
image, '--boot-from-volume', volume_size, '--key-name', key_name,
|
"create",
|
||||||
'--hint', f'group={sgroup_id}', fqdn
|
"--port",
|
||||||
|
port_name,
|
||||||
|
"--flavor",
|
||||||
|
flavor,
|
||||||
|
"--image",
|
||||||
|
image,
|
||||||
|
"--boot-from-volume",
|
||||||
|
volume_size,
|
||||||
|
"--key-name",
|
||||||
|
key_name,
|
||||||
|
"--hint",
|
||||||
|
f"group={sgroup_id}",
|
||||||
|
fqdn,
|
||||||
]
|
]
|
||||||
run_in_openstack(create_server_command, rc_file)
|
run_in_openstack(create_server_command, rc_file, with_tsocks)
|
||||||
timeout = 360
|
timeout = 360
|
||||||
succeed = wait_for_server(fqdn, rc_file, network, timeout=timeout)
|
succeed = wait_for_server(fqdn, rc_file, network, with_tsocks, timeout=timeout)
|
||||||
if not succeed:
|
if not succeed:
|
||||||
print(f"Wait for server: {fqdn} timed out in {timeout}")
|
print(f"Wait for server: {fqdn} timed out in {timeout}")
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
if dns_record:
|
if dns_record:
|
||||||
output, _ = show_server(fqdn, rc_file)
|
output, _ = show_server(fqdn, rc_file, with_tsocks)
|
||||||
addresses = json.loads(output)['addresses'][network]
|
addresses = json.loads(output)["addresses"][network]
|
||||||
print(get_dns_records(addresses, fqdn))
|
print(get_dns_records(addresses, fqdn))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(show_server(fqdn, rc_file, format))
|
print(show_server(fqdn, rc_file, with_tsocks, format))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def delete(options: dict) -> int:
|
def delete(options: dict) -> int:
|
||||||
fqdn = options['fqdn']
|
fqdn = options["fqdn"]
|
||||||
rc_file = options['rc_file']
|
rc_file = options["rc_file"]
|
||||||
preserve_port = options['preserve_port']
|
preserve_port = options["preserve_port"]
|
||||||
preserve_cosmos_overlay = options['preserve_cosmos_overlay']
|
preserve_cosmos_overlay = options["preserve_cosmos_overlay"]
|
||||||
preserve_disk = options['preserve_disk']
|
preserve_disk = options["preserve_disk"]
|
||||||
|
with_tsocks = options["with_tsocks"]
|
||||||
|
|
||||||
# Check if server exists
|
# Check if server exists
|
||||||
server_output_error = show_server(fqdn, rc_file)
|
server_output_error = show_server(fqdn, rc_file, with_tsocks)
|
||||||
server_exists = exists_in_openstack(server_output_error)
|
server_exists = exists_in_openstack(server_output_error)
|
||||||
# We will bail out early if the server does not allready exists
|
# We will bail out early if the server does not allready exists
|
||||||
if not server_exists:
|
if not server_exists:
|
||||||
print(f"No server called {fqdn} exists.")
|
print(f"No server called {fqdn} exists.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
delete_server_command = ['server', 'delete', fqdn]
|
delete_server_command = ["server", "delete", fqdn]
|
||||||
run_in_openstack(delete_server_command, rc_file)
|
run_in_openstack(delete_server_command, rc_file, with_tsocks)
|
||||||
print(f"Deleted server: {fqdn}")
|
print(f"Deleted server: {fqdn}")
|
||||||
|
|
||||||
if not preserve_disk:
|
if not preserve_disk:
|
||||||
server_object = json.loads(server_output_error[0])
|
server_object = json.loads(server_output_error[0])
|
||||||
for disk in server_object['volumes_attached']:
|
for disk in server_object["volumes_attached"]:
|
||||||
delete_disk_command = ['volume', 'delete', disk['id']]
|
delete_disk_command = ["volume", "delete", disk["id"]]
|
||||||
run_in_openstack(delete_disk_command, rc_file)
|
run_in_openstack(delete_disk_command, rc_file, with_tsocks)
|
||||||
|
|
||||||
# Port section
|
# Port section
|
||||||
if not preserve_port:
|
if not preserve_port:
|
||||||
# FIXME: we don't support setting the portname, we just use the fqdn and append -port to it
|
# FIXME: we don't support setting the portname, we just use the fqdn and append -port to it
|
||||||
port_name = options['fqdn'].replace('.', '-') + '-port'
|
port_name = options["fqdn"].replace(".", "-") + "-port"
|
||||||
show_port_command = ['port', 'show', '-f', 'json', port_name]
|
show_port_command = ["port", "show", "-f", "json", port_name]
|
||||||
port_exists = exists_in_openstack(
|
port_exists = exists_in_openstack(
|
||||||
run_in_openstack(show_port_command, rc_file))
|
run_in_openstack(show_port_command, rc_file, with_tsocks)
|
||||||
|
)
|
||||||
if port_exists:
|
if port_exists:
|
||||||
delete_port_command = ['port', 'delete', port_name]
|
delete_port_command = ["port", "delete", port_name]
|
||||||
run_in_openstack(delete_port_command, rc_file)
|
run_in_openstack(delete_port_command, rc_file, with_tsocks)
|
||||||
print(f"Deleted port: {port_name}")
|
print(f"Deleted port: {port_name}")
|
||||||
dir = os.path.dirname(os.path.abspath(__file__))
|
dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
overlay = path_join(dir, fqdn)
|
overlay = path_join(dir, fqdn)
|
||||||
|
@ -509,29 +563,34 @@ def delete(options: dict) -> int:
|
||||||
|
|
||||||
|
|
||||||
def show(options: dict) -> None:
|
def show(options: dict) -> None:
|
||||||
if options['format'] == 'bind':
|
if options["format"] == "bind":
|
||||||
# We assume that only public addresses go in dns
|
# We assume that only public addresses go in dns
|
||||||
addresses = json.loads(
|
addresses = json.loads(
|
||||||
show_server(options['fqdn'],
|
show_server(options["fqdn"], options["rc_file"], options["with_tsocks"])[0]
|
||||||
options['rc_file'])[0])['addresses']['public']
|
)["addresses"]["public"]
|
||||||
print(get_dns_records(addresses, options['fqdn']))
|
print(get_dns_records(addresses, options["fqdn"]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
show_server(options['fqdn'], options['rc_file'],
|
show_server(
|
||||||
options['format']))
|
options["fqdn"],
|
||||||
|
options["rc_file"],
|
||||||
|
options["with_tsocks"],
|
||||||
|
options["format"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
# Make sure we have openstack app credentials matching the naming scheme <service>-<location>-<environment>
|
# Make sure we have openstack app credentials matching the naming scheme <service>-<location>-<environment>
|
||||||
options = get_options()
|
options = get_options()
|
||||||
options['rc_file'] = setup_rc_file(options)
|
options["rc_file"] = setup_rc_file(options)
|
||||||
command = options['command']
|
command = options["command"]
|
||||||
if command == 'create':
|
if command == "create":
|
||||||
return create(options)
|
return create(options)
|
||||||
if command == 'delete':
|
if command == "delete":
|
||||||
return delete(options)
|
return delete(options)
|
||||||
if command == 'show':
|
if command == "show":
|
||||||
show(options)
|
show(options)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
Loading…
Add table
Reference in a new issue