Merge "Add a shade launch-node"
This commit is contained in:
commit
1189cf74fe
@ -18,21 +18,19 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
|
|
||||||
|
def get_client():
|
||||||
|
#TODO use shade
|
||||||
NOVA_USERNAME = os.environ['OS_USERNAME']
|
NOVA_USERNAME = os.environ['OS_USERNAME']
|
||||||
NOVA_PASSWORD = os.environ['OS_PASSWORD']
|
NOVA_PASSWORD = os.environ['OS_PASSWORD']
|
||||||
NOVA_URL = os.environ['OS_AUTH_URL']
|
NOVA_URL = os.environ['OS_AUTH_URL']
|
||||||
NOVA_PROJECT_ID = os.environ['OS_TENANT_NAME']
|
NOVA_PROJECT_ID = os.environ['OS_TENANT_NAME']
|
||||||
NOVA_REGION_NAME = os.environ['OS_REGION_NAME']
|
NOVA_REGION_NAME = os.environ['OS_REGION_NAME']
|
||||||
|
|
||||||
SCRIPT_DIR = os.path.dirname(sys.argv[0])
|
|
||||||
|
|
||||||
|
|
||||||
def get_client():
|
|
||||||
args = [NOVA_USERNAME, NOVA_PASSWORD, NOVA_PROJECT_ID, NOVA_URL]
|
args = [NOVA_USERNAME, NOVA_PASSWORD, NOVA_PROJECT_ID, NOVA_URL]
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
kwargs['region_name'] = NOVA_REGION_NAME
|
kwargs['region_name'] = NOVA_REGION_NAME
|
||||||
@ -84,6 +82,45 @@ def print_dns(client, name):
|
|||||||
server.name, ip4))
|
server.name, ip4))
|
||||||
|
|
||||||
|
|
||||||
|
def shade_print_dns(server):
|
||||||
|
ip4 = server.public_v4
|
||||||
|
ip6 = server.public_v6
|
||||||
|
href = utils.get_href(server)
|
||||||
|
|
||||||
|
print
|
||||||
|
print "Run the following commands to set up DNS:"
|
||||||
|
print
|
||||||
|
print ". ~root/rackdns-venv/bin/activate"
|
||||||
|
print
|
||||||
|
print (
|
||||||
|
"rackdns rdns-create --name %s \\\n"
|
||||||
|
" --data %s \\\n"
|
||||||
|
" --server-href %s \\\n"
|
||||||
|
" --ttl 3600" % (
|
||||||
|
server.name, ip6, href))
|
||||||
|
print
|
||||||
|
print (
|
||||||
|
"rackdns rdns-create --name %s \\\n"
|
||||||
|
" --data %s \\\n"
|
||||||
|
" --server-href %s \\\n"
|
||||||
|
" --ttl 3600" % (
|
||||||
|
server.name, ip4, href))
|
||||||
|
print
|
||||||
|
print ". ~root/ci-launch/openstack-rs-nova.sh"
|
||||||
|
print
|
||||||
|
print (
|
||||||
|
"rackdns record-create --name %s \\\n"
|
||||||
|
" --type AAAA --data %s \\\n"
|
||||||
|
" --ttl 3600 openstack.org" % (
|
||||||
|
server.name, ip6))
|
||||||
|
print
|
||||||
|
print (
|
||||||
|
"rackdns record-create --name %s \\\n"
|
||||||
|
" --type A --data %s \\\n"
|
||||||
|
" --ttl 3600 openstack.org" % (
|
||||||
|
server.name, ip4))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("name", help="server name")
|
parser.add_argument("name", help="server name")
|
||||||
|
297
launch/shade-launch-node.py
Normal file
297
launch/shade-launch-node.py
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Launch a new OpenStack project infrastructure node.
|
||||||
|
|
||||||
|
# Copyright (C) 2011-2012 OpenStack LLC.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
# implied.
|
||||||
|
#
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
import dns
|
||||||
|
import utils
|
||||||
|
|
||||||
|
import os_client_config
|
||||||
|
import paramiko
|
||||||
|
import shade
|
||||||
|
|
||||||
|
SCRIPT_DIR = os.path.dirname(sys.argv[0])
|
||||||
|
|
||||||
|
try:
|
||||||
|
# This unactionable warning does not need to be printed over and over.
|
||||||
|
import requests.packages.urllib3
|
||||||
|
requests.packages.urllib3.disable_warnings()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def bootstrap_server(server, key, cert, environment, name,
|
||||||
|
puppetmaster, volume, floating_ip_pool):
|
||||||
|
ip = server.public_v4
|
||||||
|
ssh_kwargs = dict(pkey=key)
|
||||||
|
|
||||||
|
print 'Public IP', ip
|
||||||
|
for username in ['root', 'ubuntu', 'centos', 'admin']:
|
||||||
|
ssh_client = utils.ssh_connect(ip, username, ssh_kwargs, timeout=600)
|
||||||
|
if ssh_client:
|
||||||
|
break
|
||||||
|
|
||||||
|
if not ssh_client:
|
||||||
|
raise Exception("Unable to log in via SSH")
|
||||||
|
|
||||||
|
# cloud-init puts the "please log in as user foo" message and
|
||||||
|
# subsequent exit() in root's authorized_keys -- overwrite it with
|
||||||
|
# a normal version to get root login working again.
|
||||||
|
if username != 'root':
|
||||||
|
ssh_client.ssh("sudo cp ~/.ssh/authorized_keys"
|
||||||
|
" ~root/.ssh/authorized_keys")
|
||||||
|
ssh_client.ssh("sudo chmod 644 ~root/.ssh/authorized_keys")
|
||||||
|
ssh_client.ssh("sudo chown root.root ~root/.ssh/authorized_keys")
|
||||||
|
|
||||||
|
ssh_client = utils.ssh_connect(ip, 'root', ssh_kwargs, timeout=600)
|
||||||
|
|
||||||
|
if server.public_v6:
|
||||||
|
ssh_client.ssh('ping6 -c5 -Q 0x10 review.openstack.org '
|
||||||
|
'|| ping6 -c5 -Q 0x10 wiki.openstack.org')
|
||||||
|
|
||||||
|
ssh_client.scp(os.path.join(SCRIPT_DIR, '..', 'make_swap.sh'),
|
||||||
|
'make_swap.sh')
|
||||||
|
ssh_client.ssh('bash -x make_swap.sh')
|
||||||
|
|
||||||
|
if volume:
|
||||||
|
ssh_client.scp(os.path.join(SCRIPT_DIR, '..', 'mount_volume.sh'),
|
||||||
|
'mount_volume.sh')
|
||||||
|
ssh_client.ssh('bash -x mount_volume.sh')
|
||||||
|
|
||||||
|
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'))]
|
||||||
|
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))
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
(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)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ssh_client.ssh("reboot")
|
||||||
|
except Exception as e:
|
||||||
|
# Some init system kill the connection too fast after reboot.
|
||||||
|
# Deal with it by ignoring ssh errors when rebooting.
|
||||||
|
if e.rc == -1:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def build_server(cloud, name, image, flavor, cert, environment,
|
||||||
|
puppetmaster, volume, keep, net_label,
|
||||||
|
floating_ip_pool, boot_from_volume):
|
||||||
|
key = None
|
||||||
|
server = None
|
||||||
|
|
||||||
|
create_kwargs = dict(image=image, flavor=flavor, name=name,
|
||||||
|
reuse_ips=False, wait=True)
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
key_name = 'launch-%i' % (time.time())
|
||||||
|
key = paramiko.RSAKey.generate(2048)
|
||||||
|
public_key = key.get_name() + ' ' + key.get_base64()
|
||||||
|
cloud.create_keypair(key_name, public_key)
|
||||||
|
create_kwargs['key_name'] = key_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
server = cloud.create_server(**create_kwargs)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
cloud.delete_keypair(key_name)
|
||||||
|
except Exception:
|
||||||
|
print "Exception encountered deleting keypair:"
|
||||||
|
traceback.print_exc()
|
||||||
|
raise
|
||||||
|
|
||||||
|
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))
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
if keep:
|
||||||
|
print "Server failed to build, keeping as requested."
|
||||||
|
else:
|
||||||
|
cloud.delete_server(server.id, delete_ips=True)
|
||||||
|
except Exception:
|
||||||
|
print "Exception encountered deleting server:"
|
||||||
|
traceback.print_exc()
|
||||||
|
# Raise the important exception that started this
|
||||||
|
raise
|
||||||
|
|
||||||
|
return server
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("name", help="server name")
|
||||||
|
parser.add_argument("--cloud", dest="cloud", required=True,
|
||||||
|
help="cloud name")
|
||||||
|
parser.add_argument("--region", dest="region",
|
||||||
|
help="cloud region")
|
||||||
|
parser.add_argument("--flavor", dest="flavor", default='1GB',
|
||||||
|
help="name (or substring) of flavor")
|
||||||
|
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)
|
||||||
|
parser.add_argument("--boot-from-volume", dest="boot_from_volume",
|
||||||
|
help="Create a boot volume for the server and use it.",
|
||||||
|
action='store_true',
|
||||||
|
default=False)
|
||||||
|
parser.add_argument("--keep", dest="keep",
|
||||||
|
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='',
|
||||||
|
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")
|
||||||
|
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.")
|
||||||
|
|
||||||
|
cloud_kwargs = {}
|
||||||
|
if options.region:
|
||||||
|
cloud_kwargs['region_name'] = options.region
|
||||||
|
cloud_config = os_client_config.OpenStackConfig().get_one_cloud(
|
||||||
|
options.cloud, **cloud_kwargs)
|
||||||
|
|
||||||
|
cloud = shade.OpenStackCloud(cloud_config)
|
||||||
|
|
||||||
|
flavor = cloud.get_flavor(options.flavor)
|
||||||
|
if flavor:
|
||||||
|
print "Found flavor", flavor.name
|
||||||
|
else:
|
||||||
|
print "Unable to find matching flavor; flavor list:"
|
||||||
|
for i in cloud.list_flavors():
|
||||||
|
print i.name
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
image = cloud.get_image_exclude(options.image, 'deprecated')
|
||||||
|
if image:
|
||||||
|
print "Found image", image.name
|
||||||
|
else:
|
||||||
|
print "Unable to find matching image; image list:"
|
||||||
|
for i in cloud.list_images():
|
||||||
|
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,
|
||||||
|
options.volume, options.keep,
|
||||||
|
options.net_label, options.floating_ip_pool,
|
||||||
|
options.boot_from_volume)
|
||||||
|
dns.shade_print_dns(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'):
|
||||||
|
os.unlink('/var/cache/ansible-inventory/ansible-inventory.cache')
|
||||||
|
os.system('/usr/local/bin/expand-groups.sh')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -115,6 +115,8 @@ def get_public_ip(server, version=4, floating_ip_pool=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_href(server):
|
def get_href(server):
|
||||||
|
if not hasattr(server, 'links'):
|
||||||
|
return None
|
||||||
for link in server.links:
|
for link in server.links:
|
||||||
if link['rel'] == 'self':
|
if link['rel'] == 'self':
|
||||||
return link['href']
|
return link['href']
|
||||||
|
Loading…
Reference in New Issue
Block a user