summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2018-06-08 19:40:11 +0300
committerLars Wirzenius <liw@liw.fi>2018-06-08 21:45:43 +0300
commitd8cd2c15e34f32f4a869672dd65b7eb2a2163e50 (patch)
tree85ed0d58e65fd02496996e0c94d00b240302bbb7
parent0bb2263495c16fc9a4b46fe36f9205db8174f920 (diff)
downloadhetznertool-d8cd2c15e34f32f4a869672dd65b7eb2a2163e50.tar.gz
Change: support settings that are per-context
This allows me to use hetznertool for personal stuff as well as work stuff.
-rwxr-xr-xhetznertool195
1 files changed, 122 insertions, 73 deletions
diff --git a/hetznertool b/hetznertool
index 919536e..3608177 100755
--- a/hetznertool
+++ b/hetznertool
@@ -8,24 +8,23 @@ import subprocess
import yaml
-CONFIG_FILENAME = os.path.expanduser('~/.config/hetznertool/hetznertool.yaml')
+CONFIG_DIR = os.path.expanduser('~/.config/hetznertool')
+DEFAULT_PROFILE = 'hetznertool'
default_config = {
- 'dnszone-dir': os.path.expanduser('~/qvarnlabs/code/dnszone'),
- 'dnszone-file': 'db.h',
- 'ansible-inventory-dir': '.',
'ssh-key': None,
'ns1': 'root@ns1.qvarnlabs.net',
}
def main():
- config = read_config()
+ profile = os.environ.get('HETZNERTOOL_PROFILE', DEFAULT_PROFILE)
+ config = read_config(profile)
parser = create_parser(config)
args = vars(parser.parse_args())
func = args['func']
- func(args)
+ func(config, args)
def hcloud(*args):
@@ -61,8 +60,8 @@ def parse_server_line(line):
}
-def dns_name(context, name):
- return '{}-{}.h.qvarnlabs.eu'.format(context, name)
+def dns_name(context, name, domain):
+ return '{}-{}.{}'.format(context, name, domain)
class ServerSpecification(dict):
@@ -100,49 +99,101 @@ class ServerSpecification(dict):
-def create_func(args):
+def create_func(config, args):
spec = ServerSpecification()
spec.from_file(args['specfile'])
- use_context(args['context'])
+ context = args['context']
+ use_context(context)
+
for name in spec.get_servers():
server = spec.get_server(name)
- print('creating {} in {}'.format(name, args['context']))
- hcloud(
- 'server', 'create',
- '--name', name,
- '--image', server['image'],
- '--type', server['type'],
- '--ssh-key', args['ssh_key'],
- )
- update_zone_file(args)
- write_inventory_files(args)
-
-
-def list_func(args):
+ print('creating {} in {}'.format(name, context))
+ if args['act']:
+ hcloud(
+ 'server', 'create',
+ '--name', name,
+ '--image', server['image'],
+ '--type', server['type'],
+ '--ssh-key', args['ssh_key'],
+ )
+ if args['act']:
+ zonedir = get_zonedir_for_context(config, context)
+ zonefile = get_zonefile_for_context(config, context)
+ kick = get_kick_for_context(config, context)
+ domain = get_domain_for_context(config, context)
+ style = get_style_for_context(config, context)
+ update_zone_file(style, domain, kick, args, zonedir, zonefile)
+ write_inventory_files(config, args)
+
+
+def list_func(config, args):
contexts = list_contexts()
for context in contexts:
use_context(context)
+ domain = get_domain_for_context(config, context)
+ assert domain is not None
for info in list_servers():
- domain = dns_name(context, info['name'])
- print(domain, info['ipv4'])
+ name = dns_name(context, info['name'], domain)
+ print(name, info['ipv4'])
+
+
+def get_config_for_context(config, context):
+ return config.get('contexts', {}).get(context, {})
+
+
+def get_domain_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('domain')
+
+
+def get_zonedir_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('dnszone-dir')
+
+
+def get_zonefile_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('dnszone-file')
-def delete_func(args):
- use_context(args['context'])
+def get_inventorydir_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('ansible-inventory-dir')
+
+
+def get_kick_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('kick_bind9')
+
+
+def get_style_for_context(config, context):
+ cc = get_config_for_context(config, context)
+ return cc.get('style')
+
+
+def delete_func(config, args):
+ context = args['context']
+ use_context(context)
+ domain = get_domain_for_context(config, context)
for info in list_servers():
- domain = dns_name(args['context'], info['name'])
+ name = dns_name(args['context'], info['name'], domain)
print(
'deleting {} ({} in {})'.format(
- domain, info['name'], args['context']))
- hcloud('server', 'delete', info['name'])
- update_zone_file(args)
- write_inventory_files(args)
+ name, info['name'], args['context']))
+ if args['act']:
+ hcloud('server', 'delete', info['name'])
+ if args['act']:
+ zonedir = get_zonedir_for_context(config, context)
+ zonefile = get_zonefile_for_context(config, context)
+ kick = get_kick_for_context(config, context)
+ style = get_style_for_context(config, context)
+ update_zone_file(style, domain, kick, args, zonedir, zonefile)
+ write_inventory_files(config, args)
-def update_zone_file(args):
- dirname = args['dnszone_dir']
- basename = args['dnszone_file']
+
+def update_zone_file(style, domain, kick, args, dirname, basename):
filename = os.path.join(dirname, basename)
serial_name = os.path.join(dirname, 'serial')
@@ -150,24 +201,33 @@ def update_zone_file(args):
subprocess.check_call(['git', 'pull'], cwd=dirname)
- serial = int(open(serial_name).readline().strip())
- serial += 1
-
- with open(filename, 'w') as f:
- write_zone(f, serial)
-
- open(serial_name, 'w').write('{}\n'.format(serial))
+ if style == 'qvarnlabs':
+ serial = int(open(serial_name).readline().strip())
+ serial += 1
+ open(serial_name, 'w').write('{}\n'.format(serial))
+ with open(filename, 'w') as f:
+ write_qvarnlabs_zone(f, serial)
+ elif style == 'dns-api':
+ serial = None
+ with open(filename, 'w') as f:
+ write_dnsapi_zone(f, domain)
+ else:
+ assert 0
+ filenames = [basename]
+ if serial is not None:
+ filename.append(serial)
subprocess.check_call(
- ['git', 'commit', '-m', 'automatic zone update', basename, 'serial'],
+ ['git', 'commit', '-m', 'automatic zone update'] + filenames,
cwd=dirname)
subprocess.check_call(['git', 'push'], cwd=dirname)
- kick_bind9(args['ns1'], filename, basename)
+ if kick:
+ kick_bind9(args['ns1'], filename, basename)
-def write_zone(stream, serial):
+def write_qvarnlabs_zone(stream, serial):
stream.write('''
$TTL 30
$ORIGIN h.qvarnlabs.eu.
@@ -187,10 +247,19 @@ $ORIGIN h.qvarnlabs.eu.
'{}-{} IN A {}\n'.format(context, info['name'], info['ipv4']))
-def write_inventory_files(args):
- dirname = args['ansible_inventory_dir']
+def write_dnsapi_zone(stream, domain):
+ for context in list_contexts():
+ use_context(context)
+ for info in list_servers():
+ stream.write(
+ '+{}-{}.{}:{}:60\n'.format(
+ context, info['name'], domain, info['ipv4']))
+
+
+def write_inventory_files(config, args):
for context in list_contexts():
use_context(context)
+ dirname = get_inventorydir_for_context(config, context)
filename = os.path.join(dirname, 'hosts.{}'.format(context))
print('Writing Ansible inventory file {}'.format(filename))
with open(filename, 'w') as f:
@@ -207,9 +276,9 @@ def kick_bind9(ssh_target, filename, basename):
-def read_config():
+def read_config(profile):
config = copy.deepcopy(default_config)
- filename = CONFIG_FILENAME
+ filename = os.path.join(CONFIG_DIR, '{}.yaml'.format(profile))
if os.path.exists(filename):
with open(filename) as f:
config.update(yaml.safe_load(f))
@@ -224,6 +293,8 @@ def create_parser(config):
create = factory.add_parser('create')
create.add_argument(
+ '-n', '--no-act', dest='act', default=True, action='store_false')
+ create.add_argument(
'context', help='hcloud context to create VMs in')
create.add_argument(
'specfile', help='file to read VM specifications from')
@@ -234,18 +305,6 @@ def create_parser(config):
default=config['ssh-key'],
help='create VM so it allow login via ssh key uploaded as KEYNAME')
create.add_argument(
- '--ansible-inventory-dir',
- metavar='DIR',
- required='ansible-inventory-dir' not in config,
- default=config['ansible-inventory-dir'],
- help='create Ansible inventory files in DIR')
- create.add_argument(
- '--dnszone-dir', default=config['dnszone-dir'],
- metavar='DIR', help='write DNS zone directory into DIR')
- create.add_argument(
- '--dnszone-file', default=config['dnszone-file'],
- metavar='FILE', help='write DNS zone directory into FILE')
- create.add_argument(
'--ns1', default=config['ns1'],
required='ns1' not in config,
metavar='USER@ADDRESS',
@@ -255,19 +314,9 @@ def create_parser(config):
delete = factory.add_parser('delete')
delete.add_argument(
- 'context', help='hcloud context to delete all VMs from')
- delete.add_argument(
- '--ansible-inventory-dir',
- metavar='DIR',
- required='ansible-inventory-dir' not in config,
- default=config['ansible-inventory-dir'],
- help='create Ansible inventory files in DIR')
+ '-n', '--no-act', dest='act', default=True, action='store_false')
delete.add_argument(
- '--dnszone-dir', default=config['dnszone-dir'],
- metavar='DIR', help='write DNS zone directory into DIR')
- delete.add_argument(
- '--dnszone-file', default=config['dnszone-file'],
- metavar='FILE', help='write DNS zone directory into FILE')
+ 'context', help='hcloud context to delete all VMs from')
delete.add_argument(
'--ns1', default=config['ns1'],
required='ns1' not in config,