From e14471dcc6daa4f33bba84ac1d3e7c091ada10ea Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Wed, 18 Nov 2015 14:37:42 -0500 Subject: [PATCH] Update for ansible and puppet apply Shade supports all of the TODO items in here now, so use it. Also, we have ansible playbooks that do the work of running puppet - and since we're on puppet apply now, we can use them. Change-Id: I6f57e9a31bf835ef2e22db1f5531d92e99806cf4 --- launch/dns.py | 15 +++- launch/shade-launch-node.py | 157 +++++++++++++----------------------- launch/utils.py | 133 ------------------------------ 3 files changed, 70 insertions(+), 235 deletions(-) diff --git a/launch/dns.py b/launch/dns.py index eb962692e9..a35ceca935 100755 --- a/launch/dns.py +++ b/launch/dns.py @@ -82,10 +82,21 @@ def print_dns(client, name): server.name, ip4)) -def shade_print_dns(server): +def get_href(server): + if not hasattr(server, 'links'): + return None + for link in server.links: + if link['rel'] == 'self': + return link['href'] + + +def shade_print_dns(cloud, server): ip4 = server.public_v4 ip6 = server.public_v6 - href = utils.get_href(server) + + for raw_server in cloud.nova_client.servers.list(): + if raw_server.id == server.id: + href = get_href(raw_server) print print "Run the following commands to set up DNS:" diff --git a/launch/shade-launch-node.py b/launch/shade-launch-node.py index 812b0ef264..6a7d13ba85 100644 --- a/launch/shade-launch-node.py +++ b/launch/shade-launch-node.py @@ -18,11 +18,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys +import argparse import os +import subprocess +import sys +import tempfile import time import traceback -import argparse import dns import utils @@ -41,8 +43,8 @@ except: pass -def bootstrap_server(server, key, cert, environment, name, - puppetmaster, volume, floating_ip_pool): +def bootstrap_server(server, key, name, volume, keep): + ip = server.public_v4 ssh_kwargs = dict(pkey=key) @@ -79,11 +81,11 @@ def bootstrap_server(server, key, cert, environment, name, 'mount_volume.sh') ssh_client.ssh('bash -x mount_volume.sh') + # This next chunk should really exist as a playbook, but whatev ssh_client.scp(os.path.join(SCRIPT_DIR, '..', 'install_puppet.sh'), 'install_puppet.sh') ssh_client.ssh('bash -x install_puppet.sh') - certname = cert[:(0 - len('.pem'))] shortname = name.split('.')[0] with ssh_client.open('/etc/hosts', 'w') as f: f.write('127.0.0.1 localhost\n') @@ -91,35 +93,38 @@ def bootstrap_server(server, key, cert, environment, name, with ssh_client.open('/etc/hostname', 'w') as f: f.write('%s\n' % (shortname,)) ssh_client.ssh("hostname %s" % (name,)) - ssh_client.ssh("mkdir -p /var/lib/puppet/ssl/certs") - ssh_client.ssh("mkdir -p /var/lib/puppet/ssl/private_keys") - ssh_client.ssh("mkdir -p /var/lib/puppet/ssl/public_keys") - ssh_client.ssh("chown -R puppet:root /var/lib/puppet/ssl") - ssh_client.ssh("chown -R puppet:puppet /var/lib/puppet/ssl/private_keys") - ssh_client.ssh("chmod 0771 /var/lib/puppet/ssl") - ssh_client.ssh("chmod 0755 /var/lib/puppet/ssl/certs") - ssh_client.ssh("chmod 0750 /var/lib/puppet/ssl/private_keys") - ssh_client.ssh("chmod 0755 /var/lib/puppet/ssl/public_keys") - for ssldir in ['/var/lib/puppet/ssl/certs/', - '/var/lib/puppet/ssl/private_keys/', - '/var/lib/puppet/ssl/public_keys/']: - ssh_client.scp(os.path.join(ssldir, cert), - os.path.join(ssldir, cert)) + # Write out the private SSH key we generated + key_file = tempfile.NamedTemporaryFile(delete=not keep) + key.write_private_key(key_file) + key_file.flush() - ssh_client.scp("/var/lib/puppet/ssl/crl.pem", - "/var/lib/puppet/ssl/crl.pem") - ssh_client.scp("/var/lib/puppet/ssl/certs/ca.pem", - "/var/lib/puppet/ssl/certs/ca.pem") + # Write out inventory + inventory_file = tempfile.NamedTemporaryFile(delete=not keep) + inventory_file.write("{host} ansible_host={ip} ansible_user=root".format( + host=server.id, ip=server.interface_ip)) + inventory_file.flush() - (rc, output) = ssh_client.ssh( - "puppet agent " - "--environment %s " - "--server %s " - "--detailed-exitcodes " - "--no-daemonize --verbose --onetime --pluginsync true " - "--certname %s" % (environment, puppetmaster, certname), error_ok=True) - utils.interpret_puppet_exitcodes(rc, output) + ansible_cmd = [ + 'ansible-playbook', + '-i', inventory_file.name, '-l', server.id, + '--private-key={key}'.format(key=key_file.name), + "--ssh-common-args='-o StrictHostKeyChecking=no'", + ] + + # Run the remote puppet apply playbook limited to just this server + # we just created + try: + print subprocess.check_output( + ansible_cmd + [ + os.path.join( + SCRIPT_DIR, '..', 'playbooks', + 'remote_puppet_adhoc.yaml')], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + print "Subprocess failed" + print e.output + raise try: ssh_client.ssh("reboot") @@ -132,37 +137,19 @@ def bootstrap_server(server, key, cert, environment, name, raise -def build_server(cloud, name, image, flavor, cert, environment, - puppetmaster, volume, keep, net_label, - floating_ip_pool, boot_from_volume, - config_drive): +def build_server(cloud, name, image, flavor, + volume, keep, network, boot_from_volume, config_drive): key = None server = None create_kwargs = dict(image=image, flavor=flavor, name=name, - reuse_ips=False, wait=True, config_drive=config_drive) + reuse_ips=False, wait=True, + boot_from_volume=boot_from_volume, + network=network, + config_drive=config_drive) - #TODO: test with rax - #TODO: use shade - if boot_from_volume: - block_mapping = [{ - 'boot_index': '0', - 'delete_on_termination': True, - 'destination_type': 'volume', - 'uuid': image.id, - 'source_type': 'image', - 'volume_size': '50', - }] - create_kwargs['image'] = None - create_kwargs['block_device_mapping_v2'] = block_mapping - - #TODO: use shade - #if net_label: - # nics = [] - # for net in client.networks.list(): - # if net.label == net_label: - # nics.append({'net-id': net.id}) - # create_kwargs['nics'] = nics + if volume: + create_kwargs['volumes'] = [volume] key_name = 'launch-%i' % (time.time()) key = paramiko.RSAKey.generate(2048) @@ -183,20 +170,10 @@ def build_server(cloud, name, image, flavor, cert, environment, try: cloud.delete_keypair(key_name) - # TODO: use shade - if volume: - raise Exception("not implemented") - #vobj = client.volumes.create_server_volume( - # server.id, volume, None) - #if not vobj: - # raise Exception("Couldn't attach volume") - server = cloud.get_openstack_vars(server) - bootstrap_server(server, key, cert, environment, name, - puppetmaster, volume, floating_ip_pool) - print('UUID=%s\nIPV4=%s\nIPV6=%s\n' % (server.id, - server.accessIPv4, - server.accessIPv6)) + bootstrap_server(server, key, name, volume, keep) + print('UUID=%s\nIPV4=%s\nIPV6=%s\n' % ( + server.id, server.public_v4, server.public_v6)) except Exception: try: if keep: @@ -224,14 +201,6 @@ def main(): parser.add_argument("--image", dest="image", default="Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)", help="image name") - parser.add_argument("--environment", dest="environment", - default="production", - help="puppet environment name") - parser.add_argument("--cert", dest="cert", - help="name of signed puppet certificate file (e.g., " - "hostname.example.com.pem)") - parser.add_argument("--server", dest="server", help="Puppetmaster to use.", - default="puppetmaster.openstack.org") parser.add_argument("--volume", dest="volume", help="UUID of volume to attach to the new server.", default=None) @@ -243,24 +212,18 @@ def main(): help="Don't clean up or delete the server on error.", action='store_true', default=False) - parser.add_argument("--net-label", dest="net_label", default='', + parser.add_argument("--verbose", dest="verbose", default=False, + action='store_true', + help="Be verbose about logging cloud actions") + parser.add_argument("--network", dest="network", default=None, help="network label to attach instance to") - parser.add_argument("--fip-pool", dest="floating_ip_pool", default=None, - help="pool to assign floating IP from") parser.add_argument("--config-drive", dest="config_drive", help="Boot with config_drive attached.", action='store_true', - default=False) + default=True) options = parser.parse_args() - if options.cert: - cert = options.cert - else: - cert = options.name + ".pem" - - if not os.path.exists(os.path.join("/var/lib/puppet/ssl/private_keys", - cert)): - raise Exception("Please specify the name of a signed puppet cert.") + shade.simple_logging(debug=options.verbose) cloud_kwargs = {} if options.region: @@ -288,17 +251,11 @@ def main(): print i.name sys.exit(1) - if options.volume: - print "The --volume option does not support cinder; until it does" - print "it should not be used." - sys.exit(1) - - server = build_server(cloud, options.name, image, flavor, cert, - options.environment, options.server, + server = build_server(cloud, options.name, image, flavor, options.volume, options.keep, - options.net_label, options.floating_ip_pool, - options.boot_from_volume, options.config_drive) - dns.shade_print_dns(server) + options.network, options.boot_from_volume, + options.config_drive) + dns.shade_print_dns(cloud, server) # Remove the ansible inventory cache so that next run finds the new # server if os.path.exists('/var/cache/ansible-inventory/ansible-inventory.cache'): diff --git a/launch/utils.py b/launch/utils.py index a41e485583..401b19c2d9 100644 --- a/launch/utils.py +++ b/launch/utils.py @@ -19,16 +19,9 @@ # limitations under the License. import time -import os import traceback import socket -import novaclient -from novaclient.v1_1 import client as Client11 -try: - from v1_0 import client as Client10 -except: - pass import paramiko from sshclient import SSHClient @@ -44,108 +37,6 @@ def iterate_timeout(max_seconds, purpose): raise Exception("Timeout waiting for %s" % purpose) -def get_client(provider): - args = [provider.nova_username, provider.nova_api_key, - provider.nova_project_id, provider.nova_auth_url] - kwargs = {} - if provider.nova_service_type: - kwargs['service_type'] = provider.nova_service_type - if provider.nova_service_name: - kwargs['service_name'] = provider.nova_service_name - if provider.nova_service_region: - kwargs['region_name'] = provider.nova_service_region - if provider.nova_api_version == '1.0': - Client = Client10.Client - elif provider.nova_api_version == '1.1': - Client = Client11.Client - else: - raise Exception("API version not supported") - if provider.nova_rax_auth: - os.environ['NOVA_RAX_AUTH'] = '1' - client = Client(*args, **kwargs) - return client - -extension_cache = {} - - -def get_extensions(client): - global extension_cache - cache = extension_cache.get(client) - if cache: - return cache - try: - resp, body = client.client.get('/extensions') - extensions = [x['alias'] for x in body['extensions']] - except novaclient.exceptions.NotFound: - extensions = [] - extension_cache[client] = extensions - return extensions - - -def get_flavor(client, min_ram): - flavors = [f for f in client.flavors.list() if f.ram >= min_ram] - flavors.sort(lambda a, b: cmp(a.ram, b.ram)) - return flavors[0] - - -def get_public_ip(server, version=4, floating_ip_pool=None): - for addr in server.addresses.get('Ext-Net', []): - if addr.get('version') == version: # OVH - return addr['addr'] - if 'os-floating-ips' in get_extensions(server.manager.api): - for addr in server.manager.api.floating_ips.list(): - if addr.instance_id == server.id: - return addr.ip - # We don't have one - so add one please - new_ip = server.manager.api.floating_ips.create(pool=floating_ip_pool) - server.add_floating_ip(new_ip) - for addr in server.manager.api.floating_ips.list(): - if addr.instance_id == server.id: - return addr.ip - for addr in server.addresses.get('public', []): - if type(addr) == type(u''): # Rackspace/openstack 1.0 - return addr - if addr['version'] == version: # Rackspace/openstack 1.1 - return addr['addr'] - for addr in server.addresses.get('private', []): - # HP Cloud - if addr['version'] == version and not addr['addr'].startswith('10.'): - return addr['addr'] - return None - - -def get_href(server): - if not hasattr(server, 'links'): - return None - for link in server.links: - if link['rel'] == 'self': - return link['href'] - - -def add_public_ip(server): - ip = server.manager.api.floating_ips.create() - server.add_floating_ip(ip) - for count in iterate_timeout(600, "ip to be added"): - try: - newip = ip.manager.get(ip.id) - except: - print "Unable to get ip details, will retry" - traceback.print_exc() - time.sleep(5) - continue - - if newip.instance_id == server.id: - print 'ip has been added' - return - - -def add_keypair(client, name): - key = paramiko.RSAKey.generate(2048) - public_key = key.get_name() + ' ' + key.get_base64() - kp = client.keypairs.create(name, public_key) - return key, kp - - def wait_for_resource(wait_resource): last_progress = None last_status = None @@ -190,30 +81,6 @@ def ssh_connect(ip, username, connect_kwargs={}, timeout=60): return None -def delete_server(server): - try: - if 'os-floating-ips' in get_extensions(server.manager.api): - for addr in server.manager.api.floating_ips.list(): - if addr.instance_id == server.id: - server.remove_floating_ip(addr) - addr.delete() - except: - print "Unable to remove floating IP" - traceback.print_exc() - - try: - if 'os-keypairs' in get_extensions(server.manager.api): - for kp in server.manager.api.keypairs.list(): - if kp.name == server.key_name: - kp.delete() - except: - print "Unable to delete keypair" - traceback.print_exc() - - print "Deleting server", server.id - server.delete() - - def interpret_puppet_exitcodes(rc, output): if rc == 0: # success