Add scripts that launch/delete devstack vms.

These are for use by jenkins for gating jobs based
off of devstack.

Change-Id: I6688eeae905b3cedd6585a2b8ae88d6e92d38c36
This commit is contained in:
James E. Blair
2011-11-14 11:30:03 -08:00
parent 7209b728a0
commit 8ee0b319ee
8 changed files with 539 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
#!/usr/bin/env python
# Delete a devstack VM.
# Copyright (C) 2011 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.
from libcloud.base import NodeImage, NodeSize, NodeLocation
from libcloud.types import Provider
from libcloud.providers import get_driver
from libcloud.deployment import MultiStepDeployment, ScriptDeployment, SSHKeyDeployment
from libcloud.dns.types import Provider as DnsProvider
from libcloud.dns.types import RecordType
from libcloud.dns.providers import get_driver as dns_get_driver
import libcloud
import os, sys
import getopt
import time
import vmdatabase
CLOUD_SERVERS_DRIVER = os.environ.get('CLOUD_SERVERS_DRIVER','rackspace')
CLOUD_SERVERS_USERNAME = os.environ['CLOUD_SERVERS_USERNAME']
CLOUD_SERVERS_API_KEY = os.environ['CLOUD_SERVERS_API_KEY']
CHANGE = os.environ['GERRIT_CHANGE_NUMBER']
PATCH = os.environ['GERRIT_PATCHSET_NUMBER']
BUILD = os.environ['BUILD_NUMBER']
db = vmdatabase.VMDatabase()
machine = db.getMachine(CHANGE, PATCH, BUILD)
node_name = machine['name']
if CLOUD_SERVERS_DRIVER == 'rackspace':
Driver = get_driver(Provider.RACKSPACE)
conn = Driver(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
node = [n for n in conn.list_nodes() if n.id==str(machine['id'])][0]
dns_provider = dns_get_driver(DnsProvider.RACKSPACE_US)
dns_ctx = dns_provider(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
domain_name = ".".join(node_name.split(".")[-2:])
domain = [z for z in dns_ctx.list_zones() if z.domain == 'openstack.org'][0]
records = [z for z in domain.list_records() if z == node_name]
if records:
records[0].delete()
node.destroy()
db.delMachine(machine['id'])

View File

@@ -0,0 +1,50 @@
#!/bin/bash
# Script that is run on the devstack vm; configures and
# invokes devstack.
# Copyright (C) 2011 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.
set -o errexit
cd workspace
DEST=/opt/stack
# create the destination directory and ensure it is writable by the user
sudo mkdir -p $DEST
if [ ! -w $DEST ]; then
sudo chown `whoami` $DEST
fi
# The workspace has been copied over here by devstack-vm-gate.sh
mv * /opt/stack
cd /opt/stack/devstack
cat <<EOF >localrc
ACTIVE_TIMEOUT=60
BOOT_TIMEOUT=90
ASSOCIATE_TIMEOUT=60
MYSQL_PASSWORD=secret
RABBIT_PASSWORD=secret
ADMIN_PASSWORD=secret
SERVICE_TOKEN=111222333444
ROOTSLEEP=0
ENABLED_SERVICES=g-api,g-reg,key,n-api,n-cpu,n-net,n-sch,mysql,rabbit
SKIP_EXERCISES=swift
EOF
./stack.sh
./exercise.sh

View File

@@ -0,0 +1,67 @@
#!/bin/bash
# Gate commits to several projects on a VM running those projects
# configured by devstack.
# Copyright (C) 2011 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.
set -o xtrace
fi
if [[ ! -e python-novaclient ]]; then
git clone https://github.com/rackspace/python-novaclient.git
fi
for PROJECT in $PROJECTS
do
echo "Setting up $PROJECT"
SHORT_PROJECT=`basename $PROJECT`
if [[ ! -e $SHORT_PROJECT ]]; then
echo " Need to clone"
git clone https://review.openstack.org/p/$PROJECT
fi
cd $SHORT_PROJECT
git remote update
git checkout $GERRIT_BRANCH
git reset --hard remotes/origin/$GERRIT_BRANCH
if [[ $GERRIT_PROJECT == $PROJECT ]]; then
echo " Merging proposed change"
git fetch https://review.openstack.org/p/$PROJECT $GERRIT_REFSPEC
git merge FETCH_HEAD
else
echo " Updating from origin"
git pull --ff-only origin $GERRIT_BRANCH
fi
cd $WORKSPACE
done
python $CI_SCRIPT_DIR/devstack-vm-launch.py || exit $?
. $HOSTNAME.node.sh
rm $HOSTNAME.node.sh
scp -C -q $CI_SCRIPT_DIR/devstack-vm-gate-host.sh $ipAddr:
scp -C -q -r $WORKSPACE/ $ipAddr:workspace
ssh $ipAddr ./devstack-vm-gate-host.sh
RETVAL=$?
if [ $RETVAL = 0 ]; then
echo "Deleting host"
python $CI_SCRIPT_DIR/devstack-vm-delete.py
else
echo "Giving host to developer"
python $CI_SCRIPT_DIR/devstack-vm-give.py
fi

View File

@@ -0,0 +1,71 @@
#!/usr/bin/env python
# Turn over a devstack configured machine to the developer who
# proposed the change that is being tested.
# Copyright (C) 2011 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 os, sys, time
import getopt
import commands
import json
import urllib2
import tempfile
import vmdatabase
CHANGE = os.environ['GERRIT_CHANGE_NUMBER']
PATCH = os.environ['GERRIT_PATCHSET_NUMBER']
BUILD = os.environ['BUILD_NUMBER']
db = vmdatabase.VMDatabase()
machine = db.getMachine(CHANGE, PATCH, BUILD)
stat, out = commands.getstatusoutput("ssh -p 29418 review.openstack.org gerrit query --format=JSON change:%s" % os.environ['GERRIT_CHANGE_NUMBER'])
data = json.loads(out.split('\n')[0])
username = data['owner']['username']
f = urllib2.urlopen('https://launchpad.net/~%s/+sshkeys'%username)
keys = f.read()
tmp = tempfile.NamedTemporaryFile(delete=False)
try:
tmp.write("""#!/bin/bash
chmod u+w ~/.ssh/authorized_keys
cat <<EOF >>~/.ssh/authorized_keys
""")
tmp.write(keys)
tmp.write("\nEOF\n")
tmp.close()
stat, out = commands.getstatusoutput("scp %s %s:/var/tmp/keys.sh" %
(tmp.name, machine['ip']))
if stat:
print out
raise Exception("Unable to copy keys")
stat, out = commands.getstatusoutput("ssh %s /bin/sh /var/tmp/keys.sh" %
machine['ip'])
if stat:
print out
raise Exception("Unable to add keys")
finally:
os.unlink(tmp.name)
db.setMachineUser(machine['id'], username)
print "Added %s to authorized_keys on %s" % (username, machine['ip'])

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python
# Launch a VM for use by devstack.
# Copyright (C) 2011 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.
from libcloud.base import NodeImage, NodeSize, NodeLocation
from libcloud.types import Provider
from libcloud.providers import get_driver
from libcloud.deployment import MultiStepDeployment, ScriptDeployment, SSHKeyDeployment
from libcloud.dns.types import Provider as DnsProvider
from libcloud.dns.types import RecordType
from libcloud.dns.providers import get_driver as dns_get_driver
import libcloud
import os, sys
import getopt
import time
import vmdatabase
CLOUD_SERVERS_DRIVER = os.environ.get('CLOUD_SERVERS_DRIVER','rackspace')
CLOUD_SERVERS_USERNAME = os.environ['CLOUD_SERVERS_USERNAME']
CLOUD_SERVERS_API_KEY = os.environ['CLOUD_SERVERS_API_KEY']
CLOUD_SERVERS_HOST = os.environ.get('CLOUD_SERVERS_HOST', None)
CLOUD_SERVERS_PATH = os.environ.get('CLOUD_SERVERS_PATH', None)
IMAGE_NAME = 'devstack-oneiric'
MIN_RAM = 1024
CHANGE = os.environ['GERRIT_CHANGE_NUMBER']
PATCH = os.environ['GERRIT_PATCHSET_NUMBER']
BUILD = os.environ['BUILD_NUMBER']
db = vmdatabase.VMDatabase()
node_name = 'devstack-%s-%s-%s.slave.openstack.org' % (CHANGE, PATCH, BUILD)
if CLOUD_SERVERS_DRIVER == 'rackspace':
Driver = get_driver(Provider.RACKSPACE)
conn = Driver(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
images = conn.list_images()
sizes = [sz for sz in conn.list_sizes() if sz.ram >= MIN_RAM]
sizes.sort(lambda a,b: cmp(a.ram, b.ram))
size = sizes[0]
image = [img for img in conn.list_images() if img.name==IMAGE_NAME][0]
else:
raise Exception ("Driver not supported")
if CLOUD_SERVERS_DRIVER == 'rackspace':
node = conn.create_node(name=node_name, image=image, size=size)
# A private method, Tomaz Muraus says he's thinking of making it public
node = conn._wait_until_running(node=node, wait_period=3,
timeout=600)
dns_provider = dns_get_driver(DnsProvider.RACKSPACE_US)
dns_ctx = dns_provider(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
domain_name = ".".join(node_name.split(".")[-2:])
domain = [z for z in dns_ctx.list_zones() if z.domain == 'openstack.org'][0]
records = [z for z in domain.list_records() if z == node_name]
if len(records) == 0:
domain.create_record(node_name, RecordType.A, node.public_ip[0])
else:
records[0].update(data=node.public_ip[0])
print "Node ID:", node.id
print "Node IP:", node.public_ip[0]
db.addMachine(node.id, node_name, node.public_ip[0], CHANGE, PATCH, BUILD)
with open("%s.node.sh" % node_name,"w") as node_file:
node_file.write("ipAddr=%s\n" % node.public_ip[0])
node_file.write("nodeId=%s\n" % node.id)

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python
# Remove old devstack VMs that have been given to developers.
# Copyright (C) 2011 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 os, sys, time
import getopt
from libcloud.base import NodeImage, NodeSize, NodeLocation
from libcloud.types import Provider
from libcloud.providers import get_driver
from libcloud.deployment import MultiStepDeployment, ScriptDeployment, SSHKeyDeployment
from libcloud.dns.types import Provider as DnsProvider
from libcloud.dns.types import RecordType
from libcloud.dns.providers import get_driver as dns_get_driver
import libcloud
import vmdatabase
CLOUD_SERVERS_DRIVER = os.environ.get('CLOUD_SERVERS_DRIVER','rackspace')
CLOUD_SERVERS_USERNAME = os.environ['CLOUD_SERVERS_USERNAME']
CLOUD_SERVERS_API_KEY = os.environ['CLOUD_SERVERS_API_KEY']
db = vmdatabase.VMDatabase()
if CLOUD_SERVERS_DRIVER == 'rackspace':
Driver = get_driver(Provider.RACKSPACE)
conn = Driver(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
def delete(machine):
node = [n for n in conn.list_nodes() if n.id==str(machine['id'])][0]
node_name = machine['name']
dns_provider = dns_get_driver(DnsProvider.RACKSPACE_US)
dns_ctx = dns_provider(CLOUD_SERVERS_USERNAME, CLOUD_SERVERS_API_KEY)
domain_name = ".".join(node_name.split(".")[-2:])
domain = [z for z in dns_ctx.list_zones() if z.domain == 'openstack.org'][0]
records = [z for z in domain.list_records() if z == node_name]
if records:
records[0].delete()
node.destroy()
db.delMachine(machine['id'])
now = time.time()
for machine in db.getMachines():
if now-machine['created'] > 24*60*60:
print 'Deleting', machine['name']
delete(machine)

View File

@@ -0,0 +1,75 @@
#!/usr/bin/env python
# Update the base image that is used for devstack VMs.
# Copyright (C) 2011 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 openstack.compute
import commands
import time
USERNAME = os.environ['CLOUD_SERVERS_USERNAME']
API_KEY = os.environ['CLOUD_SERVERS_API_KEY']
SERVER_NAME = 'devstack-oneiric.slave.openstack.org'
IMAGE_NAME = 'devstack-oneiric'
compute = openstack.compute.Compute(username=USERNAME, apikey=API_KEY,
cloud_api='RACKSPACE')
print "Searching for %s server" % SERVER_NAME
node = compute.servers.find(name=SERVER_NAME)
print "Searching for %s image" % IMAGE_NAME
try:
image = compute.images.find(name=IMAGE_NAME)
except openstack.compute.exceptions.NotFound:
image = None
stat, out = commands.getstatusoutput("ssh %s sudo apt-get -y dist-upgrade" %
node.public_ip)
if stat:
print out
raise Exception("Unable to upgrade server")
stat, out = commands.getstatusoutput("ssh %s sudo /etc/init.d/mysql stop" %
node.public_ip)
if stat:
print out
raise Exception("Unable to stop mysql")
stat, out = commands.getstatusoutput("ssh %s sudo /etc/init.d/rabbitmq-server stop" %
node.public_ip)
if stat:
print out
raise Exception("Unable to stop rabbitmq")
if image:
image.delete()
image = compute.images.create(name=IMAGE_NAME, server=node)
last_status = None
while True:
if image.status != last_status:
print
print time.ctime(), image.status,
last_status = image.status
if image.status == u'ACTIVE':
break
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(1)
image = compute.images.get(image.id)

View File

@@ -0,0 +1,57 @@
import sqlite3
import os
import time
class VMDatabase(object):
def __init__(self, path=os.path.expanduser("~/vm.db")):
if not os.path.exists(path):
conn = sqlite3.connect(path)
c = conn.cursor()
c.execute('''create table machines
(id int, name text, ip text, change_number, patch_number, build_number, created int, user text)''')
conn.commit()
c.close()
self.conn = sqlite3.connect(path)
def addMachine(self, mid, name, ip, change, patch, build):
c = self.conn.cursor()
c.execute("insert into machines (id, name, ip, change_number, patch_number, build_number, created) values (?, ?, ?, ?, ?, ?, ?)",
(mid, name, ip, change, patch, build, int(time.time())))
self.conn.commit()
c.close()
def delMachine(self, mid):
c = self.conn.cursor()
c.execute("delete from machines where id=?", (mid,))
self.conn.commit()
c.close()
def setMachineUser(self, mid, user):
c = self.conn.cursor()
c.execute("update machines set user=? where id=?", (user, mid))
self.conn.commit()
c.close()
def getMachines(self):
c = self.conn.cursor()
c.execute("select * from machines")
names = [col[0] for col in c.description]
data = [dict(zip(names, row)) for row in c]
c.close()
return data
def getMachine(self, change, patch, build):
c = self.conn.cursor()
c.execute("select * from machines where change_number=? and patch_number=? and build_number=?", (change, patch, build))
names = [col[0] for col in c.description]
data = [row for row in c]
c.close()
return dict(zip(names, data[0]))
if __name__=='__main__':
db = VMDatabase()
db.addMachine(1, 'foo', '1.2.3.4', 88, 2, 1)
db.setMachineUser(1, 'jeblair')
print db.getMachines()
print db.getMachine(88,2,1)
db.delMachine(1)