X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=planetstack%2Fopenstack%2Fdriver.py;h=c3e1f356e201f0d3a4aa42fbed710d1cd30f6f23;hb=refs%2Fheads%2Fobserver3.0;hp=eba424a5768e1270a181fac54a125ec1f279d740;hpb=732913407cc255ab864e39522585e1b98e654af1;p=plstackapi.git diff --git a/planetstack/openstack/driver.py b/planetstack/openstack/driver.py index eba424a..c3e1f35 100644 --- a/planetstack/openstack/driver.py +++ b/planetstack/openstack/driver.py @@ -1,33 +1,63 @@ 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 -has_openstack = False 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() - if has_openstack: - self.admin_user = self.admin_client.keystone.users.find(name=self.admin_client.keystone.username) - else: - self.admin_user = None + 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): @@ -55,8 +85,17 @@ class OpenStackDriver: 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 @@ -73,13 +112,32 @@ class OpenStackDriver: 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) @@ -94,7 +152,13 @@ class OpenStackDriver: 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) @@ -106,8 +170,12 @@ class OpenStackDriver: 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'] @@ -147,12 +215,12 @@ class OpenStackDriver: 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): @@ -197,19 +265,23 @@ class OpenStackDriver: 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): @@ -226,7 +298,15 @@ class OpenStackDriver: 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'] @@ -243,14 +323,23 @@ class OpenStackDriver: 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 @@ -282,51 +371,93 @@ class OpenStackDriver: 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 spawn_instance(self, name, key_name=None, hostname=None, image_id=None, security_group=None, pubkeys=[]): + 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, hostname=None, image_id=None, security_group=None, pubkeys=[], nics=None, metadata=None, userdata=None): 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 if hostname: - availability_zone = 'nova:%s' % hostname + availability_zone = 'nova:%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, + 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)