256 lines
8.2 KiB
Python
256 lines
8.2 KiB
Python
#!/usr/bin/env python
|
|
|
|
# Copyright (C) 2011-2013 OpenStack Foundation
|
|
#
|
|
# 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 novaclient.client
|
|
import time
|
|
import paramiko
|
|
import socket
|
|
import logging
|
|
from sshclient import SSHClient
|
|
|
|
import nodedb
|
|
|
|
log = logging.getLogger("nodepool.utils")
|
|
|
|
|
|
def iterate_timeout(max_seconds, purpose):
|
|
start = time.time()
|
|
count = 0
|
|
while (time.time() < start + max_seconds):
|
|
count += 1
|
|
yield count
|
|
time.sleep(2)
|
|
raise Exception("Timeout waiting for %s" % purpose)
|
|
|
|
|
|
def get_client(provider):
|
|
args = ['1.1', provider.username, provider.password,
|
|
provider.project_id, provider.auth_url]
|
|
kwargs = {}
|
|
if provider.service_type:
|
|
kwargs['service_type'] = provider.service_type
|
|
if provider.service_name:
|
|
kwargs['service_name'] = provider.service_name
|
|
if provider.region_name:
|
|
kwargs['region_name'] = provider.region_name
|
|
client = novaclient.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):
|
|
if 'os-floating-ips' in get_extensions(server.manager.api):
|
|
log.debug('Server %s using floating ips' % server.id)
|
|
for addr in server.manager.api.floating_ips.list():
|
|
if addr.instance_id == server.id:
|
|
return addr.ip
|
|
log.debug('Server %s not using floating ips, addresses: %s' %
|
|
(server.id, server.addresses))
|
|
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', []):
|
|
# HPcloud
|
|
if (addr['version'] == version and version == 4):
|
|
quad = map(int, addr['addr'].split('.'))
|
|
if quad[0] == 10:
|
|
continue
|
|
if quad[0] == 192 and quad[1] == 168:
|
|
continue
|
|
if quad[0] == 172 and (16 <= quad[1] <= 31):
|
|
continue
|
|
return addr['addr']
|
|
return None
|
|
|
|
|
|
def add_public_ip(server):
|
|
ip = server.manager.api.floating_ips.create()
|
|
log.debug('Created floading IP %s for server %s' % (ip, server.id))
|
|
server.add_floating_ip(ip)
|
|
for count in iterate_timeout(600, "ip to be added"):
|
|
try:
|
|
newip = ip.manager.get(ip.id)
|
|
except Exception:
|
|
log.exception('Unable to get IP details for server %s, '
|
|
'will retry' % (server.id))
|
|
continue
|
|
|
|
if newip.instance_id == server.id:
|
|
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, timeout=3600):
|
|
last_progress = None
|
|
last_status = None
|
|
for count in iterate_timeout(timeout, "waiting for %s" % wait_resource):
|
|
try:
|
|
resource = wait_resource.manager.get(wait_resource.id)
|
|
except:
|
|
log.exception('Unable to list resources, while waiting for %s '
|
|
'will retry' % wait_resource)
|
|
continue
|
|
|
|
# In Rackspace v1.0, there is no progress attribute while queued
|
|
if hasattr(resource, 'progress'):
|
|
if (last_progress != resource.progress
|
|
or last_status != resource.status):
|
|
log.debug('Status of %s: %s %s' % (resource, resource.status,
|
|
resource.progress))
|
|
last_progress = resource.progress
|
|
elif last_status != resource.status:
|
|
log.debug('Status of %s: %s' % (resource, resource.status))
|
|
last_status = resource.status
|
|
if resource.status == 'ACTIVE':
|
|
return resource
|
|
|
|
|
|
def ssh_connect(ip, username, connect_kwargs={}, timeout=60):
|
|
# HPcloud may return errno 111 for about 30 seconds after adding the IP
|
|
for count in iterate_timeout(timeout, "ssh access"):
|
|
try:
|
|
client = SSHClient(ip, username, **connect_kwargs)
|
|
break
|
|
except socket.error, e:
|
|
if e[0] != 111:
|
|
log.exception('Exception while testing ssh access:')
|
|
|
|
out = client.ssh("test ssh access", "echo access okay")
|
|
if "access okay" in out:
|
|
return client
|
|
return None
|
|
|
|
|
|
def create_server(client, hostname, base_image, flavor, add_key=False):
|
|
create_kwargs = dict(image=base_image, flavor=flavor, name=hostname)
|
|
|
|
key = None
|
|
# hpcloud can't handle dots in keypair names
|
|
if add_key:
|
|
key_name = hostname.split('.')[0]
|
|
if 'os-keypairs' in get_extensions(client):
|
|
key, kp = add_keypair(client, key_name)
|
|
create_kwargs['key_name'] = key_name
|
|
|
|
server = client.servers.create(**create_kwargs)
|
|
return server, key
|
|
|
|
|
|
def create_image(client, server, image_name):
|
|
# TODO: fix novaclient so it returns an image here
|
|
# image = server.create_image(name)
|
|
uuid = server.manager.create_image(server, image_name)
|
|
image = client.images.get(uuid)
|
|
return image
|
|
|
|
|
|
def delete_server(client, 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:
|
|
log.exception('Unable to remove floating IP from server %s' %
|
|
server.id)
|
|
|
|
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:
|
|
log.exception('Unable to delete keypair from server %s' % server.id)
|
|
|
|
log.debug('Deleting server %s' % server.id)
|
|
server.delete()
|
|
for count in iterate_timeout(3600, "waiting for server %s deletion" %
|
|
server.id):
|
|
try:
|
|
client.servers.get(server.id)
|
|
except novaclient.exceptions.NotFound:
|
|
return
|
|
|
|
|
|
def delete_node(client, node):
|
|
node.state = nodedb.DELETE
|
|
if node.external_id:
|
|
try:
|
|
server = client.servers.get(node.external_id)
|
|
delete_server(client, server)
|
|
except novaclient.exceptions.NotFound:
|
|
pass
|
|
|
|
node.delete()
|
|
|
|
|
|
def delete_image(client, image):
|
|
server = None
|
|
if image.server_external_id:
|
|
try:
|
|
server = client.servers.get(image.server_external_id)
|
|
except novaclient.exceptions.NotFound:
|
|
log.warning('Image server id %s not found' %
|
|
image.server_external_id)
|
|
|
|
if server:
|
|
delete_server(client, server)
|
|
|
|
remote_image = None
|
|
if image.external_id:
|
|
try:
|
|
remote_image = client.images.get(image.external_id)
|
|
except novaclient.exceptions.NotFound:
|
|
log.warning('Image id %s not found' % image.external_id)
|
|
|
|
if remote_image:
|
|
log.debug('Deleting image %s' % remote_image.id)
|
|
remote_image.delete()
|
|
|
|
image.delete()
|