import commands from planetstack.config import Config from openstack.client import OpenStackClient class OpenStackDriver: 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) if client: self.shell = client else: self.shell = OpenStackClient() def create_role(self, name): roles = self.shell.keystone.roles.findall(name=name) if not roles: role = self.shell.keystone.roles.create(name) else: role = roles[0] return role def delete_role(self, filter): roles = self.shell.keystone.roles.findall(**filter) for role in roles: self.shell.keystone.roles.delete(role) return 1 def create_tenant(self, tenant_name, enabled, description): """Create keystone tenant. Suggested fields: name, description, enabled""" tenants = self.shell.keystone.tenants.findall(name=tenant_name) if not tenants: fields = {'tenant_name': tenant_name, 'enabled': enabled, 'description': description} tenant = self.shell.keystone.tenants.create(**fields) else: tenant = tenants[0] # always give the admin user the admin role to any tenant created # by the driver. self.add_user_role(self.admin_user.id, tenant.id, 'admin') return tenant def update_tenant(self, id, **kwds): 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 create_user(self, name, email, password, enabled): users = self.shell.keystone.users.findall(email=email) if not users: fields = {'name': name, 'email': email, 'password': password, 'enabled': enabled} user = self.shell.keystone.users.create(**fields) else: user = users[0] return user 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 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) role_found = False user_roles = user.list_roles(tenant.id) for user_role in user_roles: if user_role.name == role.name: role_found = True if not role_found: tenant.add_user(user, role) return 1 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) role_found = False user_roles = user.list_roles(tenant.id) for user_role in user_roles: if user_role.name == role.name: role_found = True if role_found: tenant.remove_user(user, role) return 1 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 routers: router = routers[0] else: router = self.shell.quantum.create_router({'router': {'name': name}})['router'] # add router to external network if set_gateway: nets = self.shell.quantum.list_networks()['networks'] for net in nets: if net['router:external'] == True: self.shell.quantum.add_gateway_router(router['id'], {'network_id': net['id']}) return router def delete_router(self, id): routers = self.shell.quantum.list_routers(id=id)['routers'] for router in routers: self.shell.quantum.delete_router(router['id']) # remove router form external network #nets = self.shell.quantum.list_networks()['networks'] #for net in nets: # if net['router:external'] == True: # self.shell.quantum.remove_gateway_router(router['id']) def add_router_interface(self, router_id, subnet_id): router = self.shell.quantum.show_router(router_id)['router'] subnet = self.shell.quantum.show_subnet(subnet_id)['subnet'] if router and subnet: self.shell.quantum.add_interface_router(router_id, {'subnet_id': subnet_id}) def delete_router_interface(self, router_id, subnet_id): router = self.shell.quantum.show_router(router_id) subnet = self.shell.quantum.show_subnet(subnet_id) if router and subnet: self.shell.quantum.remove_interface_router(router_id, {'subnet_id': subnet_id}) def create_network(self, name): 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'] return net def delete_network(self, id): nets = self.shell.quantum.list_networks()['networks'] for net in nets: if net['id'] == id: # delete_all ports self.delete_network_ports(net['id']) # delete all subnets: for subnet_id in net['subnets']: self.delete_subnet(subnet_id) self.shell.quantum.delete_network(net['id']) return 1 def delete_network_ports(self, network_id): ports = self.shell.quantum.list_ports()['ports'] for port in ports: if port['network_id'] == network_id: self.shell.quantum.delete_port(port['id']) return 1 def delete_subnet_ports(self, subnet_id): ports = self.shell.quantum.list_ports()['ports'] for port in ports: delete = False for fixed_ip in port['fixed_ips']: if fixed_ip['subnet_id'] == subnet_id: delete=True break if delete: self.shell.quantum.delete_port(port['id']) return 1 def create_subnet(self, name, network_id, cidr_ip, ip_version, start, end): #nets = self.shell.quantum.list_networks(name=network_name)['networks'] #if not nets: # raise Exception, "No such network: %s" % network_name #net = nets[0] subnet = None subnets = self.shell.quantum.list_subnets()['subnets'] for snet in subnets: if snet['cidr'] == cidr_ip and snet['network_id'] == network_id: subnet = snet if not subnet: 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}} 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 return subnet def update_subnet(self, id, fields): return self.shell.quantum.update_subnet(id, fields) def delete_subnet(self, id): #return self.shell.quantum.delete_subnet(id=id) # inefficient but fault tolerant subnets = self.shell.quantum.list_subnets()['subnets'] for subnet in subnets: if subnet['id'] == id: self.delete_subnet_ports(subnet['id']) self.shell.quantum.delete_subnet(id) self.delete_external_route(subnet) return 1 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'] subnet_id = subnet['id'] # 1. Find the port associated with the subnet's gateway # 2. Find the router associated with that port # 3. Find the port associated with this router and on the external net # 4. Set up route to the subnet through the port from step 3 ip_address = None for port in ports: for fixed_ip in port['fixed_ips']: if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_ip: gw_port = port router_id = gw_port['device_id'] router = self.shell.quantum.show_router(router_id)['router'] 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: # 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 def delete_external_route(self, subnet): ports = self.shell.quantum.list_ports()['ports'] gw_ip = subnet['gateway_ip'] subnet_id = subnet['id'] # 1. Find the port associated with the subnet's gateway # 2. Find the router associated with that port # 3. Find the port associated with this router and on the external net # 4. Set up route to the subnet through the port from step 3 ip_address = None for port in ports: for fixed_ip in port['fixed_ips']: if fixed_ip['subnet_id'] == subnet_id and fixed_ip['ip_address'] == gw_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 ip_address: cmd = "route delete -net %s" % (subnet['cidr']) commands.getstatusoutput(cmd) return 1 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=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 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=[], networks=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 files = {} if pubkeys: files['/root/.ssh/authorized_keys'] = "\n".join(pubkeys) hints = {} availability_zone = None if hostname: availability_zone = 'nova:%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, scheduler_hints=hints, availability_zone=availability_zone, networks=networks) return server def destroy_instance(self, id): servers = self.shell.nova.servers.findall(id=id) for server in servers: self.shell.nova.servers.delete(server) def update_instance_metadata(self, id, metadata): servers = self.shell.nova.servers.findall(id=id) for server in servers: self.shell.nova.servers.set_meta(server, metadata) # note: set_meta() returns a broken Server() object. Don't try to # print it in the shell or it will fail in __repr__. def delete_instance_metadata(self, id, metadata): # note: metadata is a dict. Only the keys matter, not the values. servers = self.shell.nova.servers.findall(id=id) for server in servers: self.shell.nova.servers.delete_meta(server, metadata)