diff options
author | Andy Doan <andy.doan@linaro.org> | 2016-04-20 15:02:48 -0500 |
---|---|---|
committer | Andy Doan <andy.doan@linaro.org> | 2016-04-20 15:06:14 -0500 |
commit | dd45d54ab74c265513805d309041235c167114fe (patch) | |
tree | 233082748ff9764cc23ee1dd52a61d2852d86712 | |
parent | 8cc18c6814ad0ab8489c9b85f280003bee3bba8e (diff) |
colo: create a simple tool for the devcloud
This creates a simple command members of dev-cloud-admins can use to
manage the devcloud
Change-Id: I002f9b39a7a36a8eca307c2d2ae738c3b735d880
-rw-r--r-- | aus-colo-servers.yml | 3 | ||||
-rwxr-xr-x | roles/devcloud-admin/files/devcloud-admin.sh | 11 | ||||
-rwxr-xr-x | roles/devcloud-admin/files/devcloud.py | 140 | ||||
-rw-r--r-- | roles/devcloud-admin/files/sudoers | 2 | ||||
-rw-r--r-- | roles/devcloud-admin/tasks/main.yml | 14 | ||||
-rw-r--r-- | roles/devcloud-admin/templates/devcloud-novarc | 10 |
6 files changed, 179 insertions, 1 deletions
diff --git a/aus-colo-servers.yml b/aus-colo-servers.yml index ea30759a..31446ad5 100644 --- a/aus-colo-servers.yml +++ b/aus-colo-servers.yml @@ -2,12 +2,13 @@ --- - name: Configure firewall/router hosts: aus-colo.linaro.org + become: yes vars_files: - "{{secrets_dir}}/host_vars/{{inventory_hostname}}" - "reservations.yml" roles: - role: colo-router - become: yes + - {role: devcloud-admin, tags: [devcloud]} - name: Configure our x86-1 top-of-rack servers hosts: diff --git a/roles/devcloud-admin/files/devcloud-admin.sh b/roles/devcloud-admin/files/devcloud-admin.sh new file mode 100755 index 00000000..895244d4 --- /dev/null +++ b/roles/devcloud-admin/files/devcloud-admin.sh @@ -0,0 +1,11 @@ +#!/bin/sh -e + +if [ `id -u` -ne 0 ] ; then + # This has to be exectuted as root + exec sudo $0 $* +fi + +. /srv/devcloud/novarc +. /srv/devcloud/bin/activate + +/srv/devcloud/devcloud.py $* diff --git a/roles/devcloud-admin/files/devcloud.py b/roles/devcloud-admin/files/devcloud.py new file mode 100755 index 00000000..3b86a04c --- /dev/null +++ b/roles/devcloud-admin/files/devcloud.py @@ -0,0 +1,140 @@ +#!/usr/bin/python3 + +import argparse +import configparser +import json +import logging +import os +import re +import subprocess +import tempfile +import time + +IP_CONF = '/etc/iptables.conf' + +logging.basicConfig(level=logging.WARN) +log = logging.getLogger() + + +def _find_colo_ip(server): + out = subprocess.check_output( + ['openstack', 'server', 'show', '-f', 'json', server]) + props = json.loads(out.decode()).get('properties') + m = re.match('COLO_IP=\'(\S+)\'', props) + if m: + return m.groups()[0] + return None + + +def _assign_public_ip(server, ip): + log.debug('Finding public IP for instance') + cp = configparser.ConfigParser() + cp.read([IP_CONF]) + mappings = cp['public/private mappings'] + for pub, priv in mappings.items(): + priv = priv.split('#')[0].strip() + if not priv: + log.info('Mapping %s -> %s', ip, pub) + mappings[pub] = '%s # devcloud - %s' % (ip, server) + with open(IP_CONF, 'w') as f: + cp.write(f, True) + subprocess.check_call( + ['/usr/local/bin/manage_iptables.py', 'sync']) + return pub + + +def _unmap_ip(public_ip): + log.info('Removing public ip mapping for: %s', public_ip) + cp = configparser.ConfigParser() + cp.read([IP_CONF]) + cp['public/private mappings'][public_ip] = ' ' + with open(IP_CONF, 'w') as f: + cp.write(f, True) + subprocess.check_call(['/usr/local/bin/manage_iptables.py', 'sync']) + + +def _start_instance(server, flavor, image, sshkey): + log.info('Starting openstack instance: %s %s %s', server, flavor, image) + with tempfile.NamedTemporaryFile('w', delete=False) as f: + f.write('#!/bin/sh -ex\n') + f.write('echo "%s" >> /home/admin/.ssh/authorized_keys\n' % sshkey) + f.write('systemctl enable systemd-timesyncd\n') + f.write('systemctl start systemd-timesyncd\n') + + try: + subprocess.check_call( + ['openstack', 'server', 'create', '--user-data', f.name, + '--key-name', 'andy', '--image', image, + '--flavor', flavor, server]) + finally: + os.unlink(f.name) + + +def _wait_for_ip(server): + for _ in range(5): + out = subprocess.check_output( + ['openstack', 'server', 'show', '-f', 'json', server]) + addresses = json.loads(out.decode()).get('addresses') + if addresses: + for x in addresses.split(','): + if x.startswith('public='): + return x[7:] + log.info('Waiting for openstack instance IP...') + time.sleep(4) + raise RuntimeError('Unable to find IP for instance') + + +def create(args): + if os.path.exists(args.sshkey): + args.sshkey = open(args.sshkey).read() + + _start_instance(args.name, args.flavor, args.image, args.sshkey) + try: + ip = _wait_for_ip(args.name) + log.debug('internal ip: %s', ip) + ip = _assign_public_ip(args.name, ip) + if not ip: + raise RuntimeError('Unable to find available public IP') + subprocess.check_call(['openstack', 'server', 'set', '--property', + 'COLO_IP=%s' % ip, args.name]) + except: + delete(args) + + +def delete(args): + ip = _find_colo_ip(args.name) + if ip: + _unmap_ip(ip) + else: + log.warn('No public IP address found?') + log.info('Deleting openstack instance') + subprocess.check_call(['openstack', 'server', 'delete', args.name]) + + +if __name__ == '__main__': + images = ('rp-debian-jessie', 'rp-centos7') + flavors = ('m1.small', 'm1.medium', 'm1.large', 'm1.xlarge') + + parser = argparse.ArgumentParser(description='Manage devcloud instances') + parser.add_argument('--log', default='INFO', + choices=('WARN', 'INFO', 'DEBUG'), + help='Logging level to use. Default=%(default)s') + + cmds = parser.add_subparsers(title='Commands') + p = cmds.add_parser('create-instance') + p.set_defaults(func=create) + p.add_argument('--name', '-n', required=True) + p.add_argument('--image', '-i', required=True, choices=images) + p.add_argument('--flavor', '-f', required=True, choices=flavors) + p.add_argument('--sshkey', '-k', required=True, + help='Users ssh key(s) can be file or string') + + p = cmds.add_parser('delete-instance') + p.set_defaults(func=delete) + p.add_argument('--name', '-n', required=True) + + args = parser.parse_args() + log.setLevel(getattr(logging, args.log)) + + if getattr(args, 'func', None): + args.func(args) diff --git a/roles/devcloud-admin/files/sudoers b/roles/devcloud-admin/files/sudoers new file mode 100644 index 00000000..f4053e95 --- /dev/null +++ b/roles/devcloud-admin/files/sudoers @@ -0,0 +1,2 @@ +# DO NOT EDIT! MANAGED BY ANSIBLE +%dev-cloud-admins ALL=(ALL:ALL) /usr/local/bin/devcloud-admin diff --git a/roles/devcloud-admin/tasks/main.yml b/roles/devcloud-admin/tasks/main.yml new file mode 100644 index 00000000..cf84d4ea --- /dev/null +++ b/roles/devcloud-admin/tasks/main.yml @@ -0,0 +1,14 @@ +- name: Install openstack command line into virtualenv + pip: name=python-openstackclient virtualenv=/srv/devcloud + +- name: Install devcloud.py script + copy: src=devcloud.py dest=/srv/devcloud/ mode=0500 + +- name: Install devcloud novarc + template: src=devcloud-novarc dest=/srv/devcloud/novarc mode=0500 + +- name: Install devcloud wrapper + copy: src=devcloud-admin.sh dest=/usr/local/bin/devcloud-admin mode=0555 + +- name: Install sudoers rule + copy: src=sudoers dest=/etc/sudoers.d/devcloud mode=0440 diff --git a/roles/devcloud-admin/templates/devcloud-novarc b/roles/devcloud-admin/templates/devcloud-novarc new file mode 100644 index 00000000..77eefb1a --- /dev/null +++ b/roles/devcloud-admin/templates/devcloud-novarc @@ -0,0 +1,10 @@ +#!/bin/bash + +export OS_AUTH_URL=http://r3-m1-c4:5000/v2.0 + +export OS_TENANT_ID={{devcloud_tenant_id}} +export OS_TENANT_NAME="{{devcloud_tenant_name}}" +export OS_PROJECT_NAME="{{devcloud_tenant_name}}" +export OS_USERNAME="admin" + +export OS_PASSWORD="{{devcloud_os_password}}" |