X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=planetstack%2Fopenstack%2Fdriver.py;h=2edf10ea24d0c84156c511758c349ad08ec64755;hb=90ecab134bf44662886576bea76c48d0aea316b7;hp=e9988e3512e316cdad16b128c62fbebdcae48dd2;hpb=c59fcaf0847bcc583591ae79cfc06e63960f59d8;p=plstackapi.git diff --git a/planetstack/openstack/driver.py b/planetstack/openstack/driver.py index e9988e3..2edf10e 100644 --- a/planetstack/openstack/driver.py +++ b/planetstack/openstack/driver.py @@ -1,29 +1,64 @@ import commands +import hashlib from planetstack.config import Config -from openstack.client import OpenStackClient +from core.models import Controller + +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.controller = None + self.admin_user = None + + def client_driver(self, caller=None, tenant=None, controller=None): + if caller: + auth = {'username': caller.email, + 'password': hashlib.md5(caller.password).hexdigest()[:6], + 'tenant': tenant} + client = OpenStackClient(controller=controller, cacert=self.config.nova_ca_ssl_cert, **auth) + else: + admin_driver = self.admin_driver(tenant=tenant, controller=controller) + client = OpenStackClient(tenant=tenant, controller=admin_driver.controller) + + driver = OpenStackDriver(client=client) + #driver.admin_user = admin_driver.admin_user + #driver.controller = admin_driver.controller + return driver + + def admin_driver(self, tenant=None, controller=None): + if isinstance(controller, int): + controller = Controller.objects.get(id=controller.id) + client = OpenStackClient(tenant=tenant, controller=controller, cacert=self.config.nova_ca_ssl_cert) + driver = OpenStackDriver(client=client) + driver.admin_user = client.keystone.users.find(name=controller.admin_user) + driver.controller = controller + 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): @@ -51,8 +86,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 @@ -74,12 +118,27 @@ class OpenStackDriver: 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 +153,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 +171,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 +216,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 +266,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 +299,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 +324,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 @@ -298,39 +388,85 @@ class OpenStackDriver: 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 availability_zone is None or 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 + 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)