import commands
+import hashlib
from planetstack.config import Config
-from openstack.client import OpenStackClient
+from core.models import Deployment
+
+try:
+ from openstack.client import OpenStackClient
+ has_openstack = True
+except:
+ has_openstack = False
+
+manager_enabled = Config().api_nova_enabled
class OpenStackDriver:
- def __init__(self, config = None, client=None):
+ def __init__(self, config = None, client=None):
if config:
self.config = Config(config)
else:
- self.config = Config()
-
- self.admin_client = OpenStackClient()
- self.admin_user = self.admin_client.keystone.users.find(name=self.admin_client.keystone.username)
+ self.config = Config()
if client:
self.shell = client
- else:
- self.shell = OpenStackClient()
- def create_role(self, name):
+ self.enabled = manager_enabled
+ self.has_openstack = has_openstack
+ self.deployment = None
+ self.admin_user = None
+
+ def client_driver(self, caller=None, tenant=None, deployment=None):
+ admin_driver = self.admin_driver(tenant=tenant, deployment=deployment)
+ if caller:
+ auth = {'username': caller.email,
+ 'password': hashlib.md5(caller.password).hexdigest()[:6],
+ 'tenant': tenant}
+ client = OpenStackClient(deployment=admin_driver.deployment, **auth)
+ else:
+ client = OpenStackClient(tenant=tenant, deployment=admin_driver.deployment)
+
+ driver = OpenStackDriver(client=client)
+ driver.admin_user = admin_driver.admin_user
+ driver.deployment = admin_driver.deployment
+ return driver
+
+ def admin_driver(self, tenant=None, deployment=None):
+ deployment = Deployment.objects.get(name=deployment)
+ client = OpenStackClient(tenant=tenant, deployment=deployment)
+ driver = OpenStackDriver(client=client)
+ driver.admin_user = client.keystone.users.find(name=deployment.admin_user)
+ driver.deployment = deployment
+ return driver
+
+ def create_role(self, name):
roles = self.shell.keystone.roles.findall(name=name)
- if not roles:
+ roles_title = self.shell.keystone.roles.findall(name=name.title())
+ roles_found = roles + roles_title
+ if not roles_found:
role = self.shell.keystone.roles.create(name)
else:
- role = roles[0]
+ role = roles_found[0]
return role
def delete_role(self, filter):
return self.shell.keystone.tenants.update(id, **kwds)
def delete_tenant(self, id):
+ ctx = self.shell.nova_db.ctx
tenants = self.shell.keystone.tenants.findall(id=id)
for tenant in tenants:
+ # nova does not automatically delete the tenant's instances
+ # so we manually delete instances before deleteing the tenant
+ instances = self.shell.nova_db.instance_get_all_by_filters(ctx,
+ {'project_id': tenant.id}, 'id', 'asc')
+ client = OpenStackClient(tenant=tenant.name)
+ driver = OpenStackDriver(client=client)
+ for instance in instances:
+ driver.destroy_instance(instance.id)
self.shell.keystone.tenants.delete(tenant)
return 1
def delete_user(self, id):
users = self.shell.keystone.users.findall(id=id)
for user in users:
+ # delete users keys
+ keys = self.shell.nova.keypairs.findall()
+ for key in keys:
+ self.shell.nova.keypairs.delete(key)
self.shell.keystone.users.delete(user)
- return 1
+ return 1
+
+ def get_admin_role(self):
+ role = None
+ for admin_role_name in ['admin', 'Admin']:
+ roles = self.shell.keystone.roles.findall(name=admin_role_name)
+ if roles:
+ role = roles[0]
+ break
+ return role
def add_user_role(self, kuser_id, tenant_id, role_name):
user = self.shell.keystone.users.find(id=kuser_id)
tenant = self.shell.keystone.tenants.find(id=tenant_id)
- role = self.shell.keystone.roles.find(name=role_name)
+ # admin role can be lowercase or title. Look for both
+ role = None
+ if role_name.lower() == 'admin':
+ role = self.get_admin_role()
+ else:
+ # look up non admin role or force exception when admin role isnt found
+ role = self.shell.keystone.roles.find(name=role_name)
role_found = False
user_roles = user.list_roles(tenant.id)
def delete_user_role(self, kuser_id, tenant_id, role_name):
user = self.shell.keystone.users.find(id=kuser_id)
tenant = self.shell.keystone.tenants.find(id=tenant_id)
- role = self.shell.keystone.roles.find(name=role_name)
+ # admin role can be lowercase or title. Look for both
+ role = None
+ if role_name.lower() == 'admin':
+ role = self.get_admin_role()
+ else:
+ # look up non admin role or force exception when admin role isnt found
+ role = self.shell.keystone.roles.find(name=role_name)
role_found = False
user_roles = user.list_roles(tenant.id)
return 1
- def update_user(self, id, **kwds):
- return self.shell.keystone.users.update(id, **kwds)
+ def update_user(self, id, fields):
+ if 'password' in fields:
+ self.shell.keystone.users.update_password(id, fields['password'])
+ if 'enabled' in fields:
+ self.shell.keystone.users.update_enabled(id, fields['enabled'])
+ return 1
def create_router(self, name, set_gateway=True):
routers = self.shell.quantum.list_routers(name=name)['routers']
if router and subnet:
self.shell.quantum.remove_interface_router(router_id, {'subnet_id': subnet_id})
- def create_network(self, name):
+ def create_network(self, name, shared=False):
nets = self.shell.quantum.list_networks(name=name)['networks']
if nets:
net = nets[0]
else:
- net = self.shell.quantum.create_network({'network': {'name': name}})['network']
+ net = self.shell.quantum.create_network({'network': {'name': name, 'shared': shared}})['network']
return net
def delete_network(self, id):
for snet in subnets:
if snet['cidr'] == cidr_ip and snet['network_id'] == network_id:
subnet = snet
-
+
if not subnet:
+ # HACK: Add metadata route -- Neutron does not reliably supply this
+ metadata_ip = cidr_ip.replace("0/24", "3")
+
allocation_pools = [{'start': start, 'end': end}]
subnet = {'subnet': {'name': name,
'network_id': network_id,
'ip_version': ip_version,
'cidr': cidr_ip,
- 'dns_nameservers': ['8.8.8.8', '8.8.4.4'],
- 'allocation_pools': allocation_pools}}
+ #'dns_nameservers': ['8.8.8.8', '8.8.4.4'],
+ 'host_routes': [{'destination':'169.254.169.254/32','nexthop':metadata_ip}],
+ 'gateway_ip': None,
+ 'allocation_pools': allocation_pools}}
subnet = self.shell.quantum.create_subnet(subnet)['subnet']
- self.add_external_route(subnet)
- # TODO: Add route to external network
- # e.g. # route add -net 10.0.3.0/24 dev br-ex gw 10.100.0.5
+ # self.add_external_route(subnet)
+
return subnet
def update_subnet(self, id, fields):
self.delete_external_route(subnet)
return 1
- def add_external_route(self, subnet):
+ def get_external_routes(self):
+ status, output = commands.getstatusoutput('route')
+ routes = output.split('\n')[3:]
+ return routes
+
+ def add_external_route(self, subnet, routes=[]):
+ if not routes:
+ routes = self.get_external_routes()
+
ports = self.shell.quantum.list_ports()['ports']
gw_ip = subnet['gateway_ip']
gw_port = port
router_id = gw_port['device_id']
router = self.shell.quantum.show_router(router_id)['router']
- ext_net = router['external_gateway_info']['network_id']
- for port in ports:
- if port['device_id'] == router_id and port['network_id'] == ext_net:
- ip_address = port['fixed_ips'][0]['ip_address']
+ if router and router.get('external_gateway_info'):
+ ext_net = router['external_gateway_info']['network_id']
+ for port in ports:
+ if port['device_id'] == router_id and port['network_id'] == ext_net:
+ ip_address = port['fixed_ips'][0]['ip_address']
if ip_address:
- cmd = "route add -net %s dev br-ex gw %s" % (subnet['cidr'], ip_address)
- commands.getstatusoutput(cmd)
+ # check if external route already exists
+ route_exists = False
+ if routes:
+ for route in routes:
+ if subnet['cidr'] in route and ip_address in route:
+ route_exists = True
+ if not route_exists:
+ cmd = "route add -net %s dev br-ex gw %s" % (subnet['cidr'], ip_address)
+ s, o = commands.getstatusoutput(cmd)
+ #print cmd, "\n", s, o
return 1
return 1
- def create_keypair(self, name, key):
+ def create_keypair(self, name, public_key):
keys = self.shell.nova.keypairs.findall(name=name)
if keys:
key = keys[0]
+ # update key
+ if key.public_key != public_key:
+ self.delete_keypair(key.id)
+ key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
else:
- key = self.shell.nova.keypairs.create(name=name, public_key=key)
+ key = self.shell.nova.keypairs.create(name=name, public_key=public_key)
return key
def delete_keypair(self, id):
keys = self.shell.nova.keypairs.findall(id=id)
for key in keys:
self.shell.nova.keypairs.delete(key)
- return 1
+ return 1
+
+ def get_private_networks(self, tenant=None):
+ if not tenant:
+ tenant = self.shell.nova.tenant
+ tenant = self.shell.keystone.tenants.find(name=tenant)
+ search_opts = {"tenant_id": tenant.id, "shared": False}
+ private_networks = self.shell.quantum.list_networks(**search_opts)
+ return private_networks
+
+ def get_shared_networks(self):
+ search_opts = {"shared": True}
+ shared_networks = self.shell.quantum.list_networks(**search_opts)
+ return shared_networks
+
+ def get_network_subnet(self, network_id):
+ subnet_id = None
+ subnet = None
+ if network_id:
+ os_networks = self.shell.quantum.list_networks(id=network_id)["networks"]
+ if os_networks:
+ os_network = os_networks[0]
+ if os_network['subnets']:
+ subnet_id = os_network['subnets'][0]
+ os_subnets = self.shell.quantum.list_subnets(id=subnet_id)['subnets']
+ if os_subnets:
+ subnet = os_subnets[0]['cidr']
+
+ return (subnet_id, subnet)
+
+ def spawn_instance(self, name, key_name=None, availability_zone=None, hostname=None, image_id=None, security_group=None, pubkeys=[], nics=None, metadata=None, userdata=None, flavor_name=None):
+ if not flavor_name:
+ flavor_name = self.config.nova_default_flavor
- def spawn_instance(self, name, key_name=None, hostname=None, image_id=None, security_group=None, pubkeys=[]):
- flavor_name = self.config.nova_default_flavor
flavor = self.shell.nova.flavors.find(name=flavor_name)
- #if not image:
- # image = self.config.nova_default_imave
+
if not security_group:
- security_group = self.config.nova_default_security_group
+ security_group = self.config.nova_default_security_group
- #authorized_keys = "\n".join(pubkeys)
- #files = {'/root/.ssh/authorized_keys': authorized_keys}
files = {}
-
+ #if pubkeys:
+ # files["/root/.ssh/authorized_keys"] = "\n".join(pubkeys).encode('base64')
hints = {}
- availability_zone = None
+
+ # determine availability zone and compute host
+ availability_zone_filter = None
+ if not availability_zone:
+ availability_zone_filter = 'nova'
+ else:
+ availability_zone_filter = availability_zone
if hostname:
- availability_zone = 'nova:%s' % hostname
+ availability_zone_filter += ':%s' % hostname.split('.')[0]
+
server = self.shell.nova.servers.create(
name=name,
key_name = key_name,
flavor=flavor.id,
image=image_id,
security_group = security_group,
- files=files,
+ #files = files,
scheduler_hints=hints,
- availability_zone=availability_zone)
+ availability_zone=availability_zone_filter,
+ nics=nics,
+ networks=nics,
+ meta=metadata,
+ userdata=userdata)
return server
-
+
def destroy_instance(self, id):
- servers = self.shell.nova.servers.findall(id=id)
+ if (self.shell.nova.tenant=="admin"):
+ # findall() is implemented as a list() followed by a python search of the
+ # list. Since findall() doesn't accept "all_tenants", we do this using
+ # list() ourselves. This allows us to delete an instance as admin.
+ servers = self.shell.nova.servers.list(search_opts={"all_tenants": True})
+ else:
+ servers = self.shell.nova.servers.list()
for server in servers:
- self.shell.nova.servers.delete(server)
+ if server.id == id:
+ result=self.shell.nova.servers.delete(server)
def update_instance_metadata(self, id, metadata):
servers = self.shell.nova.servers.findall(id=id)