From: Tony Mack Date: Wed, 18 Sep 2013 11:59:14 +0000 (-0400) Subject: instantiate sycn steps with openstack driver. Implement garbage collector and deleters X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=387a73f79ab3fb6b9c2168f9c8975956281d4bfc;p=plstackapi.git instantiate sycn steps with openstack driver. Implement garbage collector and deleters --- diff --git a/planetstack/observer/deleters/network_deleter.py b/planetstack/observer/deleters/network_deleter.py new file mode 100644 index 0000000..51f9fcb --- /dev/null +++ b/planetstack/observer/deleters/network_deleter.py @@ -0,0 +1,17 @@ +from core.models import Network +from deleter import Deleter + +class NetworkDeleter(Deleter): + model='Network' + + def call(self, pk): + network = Network.objects.get(pk=pk) + if (network.router_id) and (network.subnet_id): + self.driver.delete_router_interface(network.router_id, network.subnet_id) + if network.subnet_id: + self.driver.delete_subnet(network.subnet_id) + if network.router_id: + self.driver.delete_router(network.router_id) + if network.network_id: + self.driver.delete_network(network.network_id) + network.delete() diff --git a/planetstack/observer/deleters/network_sliver_deleter.py b/planetstack/observer/deleters/network_sliver_deleter.py new file mode 100644 index 0000000..71ba040 --- /dev/null +++ b/planetstack/observer/deleters/network_sliver_deleter.py @@ -0,0 +1,13 @@ +from core.models import NetworkSliver +from observer.deleter import Deleter + +class NetworkSliverDeleter(Deleter): + model='NetworkSliver' + + def call(self, pk): + network_sliver = NetworkSlivers.objects.get(pk=pk) + # handle openstack delete + + network_sliver.delete() + + diff --git a/planetstack/observer/deleters/site_deleter.py b/planetstack/observer/deleters/site_deleter.py new file mode 100644 index 0000000..bb29c94 --- /dev/null +++ b/planetstack/observer/deleters/site_deleter.py @@ -0,0 +1,11 @@ +from core.models import Site +from observer.delete import Deleter + +class SiteDeleter(Deleter): + model='Site' + + def call(self, pk): + site = Site.objects.get(pk=pk) + if site.tenant_id: + self.driver.delete_tenant(site.tenant_id) + site.delete() diff --git a/planetstack/observer/deleters/slice_deleter.py b/planetstack/observer/deleters/slice_deleter.py index 4cb0a72..6796d7a 100644 --- a/planetstack/observer/deleters/slice_deleter.py +++ b/planetstack/observer/deleters/slice_deleter.py @@ -1,9 +1,22 @@ -#from code.models import Slice +from core.models import Slice +from observer.deleter import Deleter -class SliceDeleter: +class SliceDeleter(Deleter): model='Slice' def call(self, pk): - s = Slice.objects.get(pk=pk) - - # Proceed with delete + slice = Slice.objects.get(pk=pk) + self.driver.delete_router_interface(slice.router_id, slice.subnet_id) + self.driver.delete_subnet(slice.subnet_id) + self.driver.delete_router(slice.router_id) + self.driver.delete_network(slice.network_id) + self.driver.delete_tenant(slice.tenant_id) + # delete external route + subnet = None + subnets = self.driver.shell.quantum.list_subnets()['subnets'] + for snet in subnets: + if snet['id'] == slice.subnet_id: + subnet = snet + if subnet: + self.driver.delete_external_route(subnet) + slice.delete() diff --git a/planetstack/observer/deleters/sliver_deleter.py b/planetstack/observer/deleters/sliver_deleter.py new file mode 100644 index 0000000..d76b533 --- /dev/null +++ b/planetstack/observer/deleters/sliver_deleter.py @@ -0,0 +1,11 @@ +from core.models import Sliver +from observer.deleter import Deleter + +class SliverDeleter(Deleter): + model='Sliver' + + def call(self, pk): + sliver = Sliver.objects.get(pk=pk) + if sliver.instance_id: + self.driver.destroy_instance(sliver.instance_id) + sliver.delete() diff --git a/planetstack/observer/deleters/user_deleter.py b/planetstack/observer/deleters/user_deleter.py new file mode 100644 index 0000000..f250993 --- /dev/null +++ b/planetstack/observer/deleters/user_deleter.py @@ -0,0 +1,11 @@ +from core.models import User +from observer.deleter import Deleter + +class UserDeleter(Deleter): + model='User' + + def call(self, pk): + user = User.objects.get(pk=pk) + if user.kuser_id: + self.driver.delete_user(user.kuser_id) + user.delete() diff --git a/planetstack/observer/event_loop.py b/planetstack/observer/event_loop.py index 671bdc3..492cd9a 100644 --- a/planetstack/observer/event_loop.py +++ b/planetstack/observer/event_loop.py @@ -8,7 +8,8 @@ from datetime import datetime from collections import defaultdict from core.models import * from django.db.models import F, Q -from openstack.manager import OpenStackManager +#from openstack.manager import OpenStackManager +from openstack.driver import OpenStackDriver from util.logger import Logger, logging, logger #from timeout import timeout from planetstack.config import Config @@ -63,6 +64,7 @@ class PlanetStackObserver: # The Condition object that gets signalled by Feefie events self.load_sync_steps() self.event_cond = threading.Condition() + self.driver = OpenStackDriver() def wait_for_event(self, timeout): self.event_cond.acquire() @@ -190,7 +192,7 @@ class PlanetStackObserver: raise StepNotReady def run(self): - if not self.manager.enabled or not self.manager.has_openstack: + if not self.driver.enabled or not self.driver.has_openstack: return while True: @@ -209,7 +211,7 @@ class PlanetStackObserver: for S in self.ordered_steps: start_time=time.time() - sync_step = S() + sync_step = S(driver=self.driver) sync_step.dependencies = self.dependencies[sync_step.name] sync_step.debug_mode = debug_mode diff --git a/planetstack/observer/event_manager.py b/planetstack/observer/event_manager.py index c4215ac..857452b 100644 --- a/planetstack/observer/event_manager.py +++ b/planetstack/observer/event_manager.py @@ -2,7 +2,6 @@ import threading import requests, json from core.models import * -#from openstack.manager import OpenStackManager from planetstack.config import Config from observer.deleters import deleters @@ -19,7 +18,7 @@ def event(func): class EventHandler: # This code is currently not in use. def __init__(self): - pass #self.manager = OpenStackManager() + pass @staticmethod def get_events(): diff --git a/planetstack/observer/steps/garbage_collector.py b/planetstack/observer/steps/garbage_collector.py index f03577c..4b6dfaf 100644 --- a/planetstack/observer/steps/garbage_collector.py +++ b/planetstack/observer/steps/garbage_collector.py @@ -8,4 +8,198 @@ class GarbageCollector(SyncStep): def call(self): pass - + + def run(self): + try: + logger.info('gc start') + #self.sync_roles() + self.gc_tenants() + self.gc_users() + self.gc_user_tenant_roles() + self.gc_slivers() + self.gc_sliver_ips() + self.gc_external_routes() + except: + traceback.print_exc() + + def gc_roles(self): + """ + all role that don't already exist in keystone. Remove keystone roles that + don't exist in planetstack + """ + # sync all roles that don't already in keystone + keystone_roles = self.driver.shell.keystone.roles.findall() + keystone_role_names = [kr.name for kr in keystone_roles] + pending_roles = Role.objects.all() + pending_role_names = [r.role_type for r in pending_roles] + # don't delete roles for now + """ + # delete keystone roles that don't exist in planetstack + for keystone_role in keystone_roles: + if keystone_role.name == 'admin': + continue + if keystone_role.name not in pending_role_names: + try: + self.manager.driver.delete_role({id: keystone_role.id}) + except: + traceback.print_exc() + """ + + def gc_tenants(self): + """ + Remove sites and slices that no don't exist in openstack db if they + have an enacted time (enacted != None). + """ + # get all sites that where enacted != null. We can assume these sites + # have previously been synced and need to be checed for deletion. + sites = Site.objects.filter(enacted__isnull=False) + site_dict = {} + for site in sites: + site_dict[site.login_base] = site + + # get all slices that where enacted != null. We can assume these slices + # have previously been synced and need to be checed for deletion. + slices = Slice.objects.filter(enacted__isnull=False) + slice_dict = {} + for slice in slices: + slice_dict[slice.name] = slice + + # delete keystone tenants that don't have a site record + tenants = self.driver.shell.keystone.tenants.findall() + system_tenants = ['admin','service'] + for tenant in tenants: + if tenant.name in system_tenants: + continue + if tenant.name not in site_dict and tenant.name not in slice_dict: + try: + self.driver.delete_tenant(tenant.id) + logger.info("deleted tenant: %s" % (tenant)) + except: + logger.log_exc("delete tenant failed: %s" % tenant) + + + def gc_users(self): + """ + Remove users that no don't exist in openstack db if they have an + enacted time (enacted != None). + """ + # get all users that where enacted != null. We can assume these users + # have previously been synced and need to be checed for deletion. + users = User.objects.filter(enacted__isnull=False) + user_dict = {} + for user in users: + user_dict[user.kuser_id] = user + + # delete keystone users that don't have a user record + system_users = ['admin', 'nova', 'quantum', 'glance', 'cinder', 'swift', 'service'] + users = self.driver.shell.keystone.users.findall() + for user in users: + if user.name in system_users: + continue + if user.id not in user_dict: + try: + #self.manager.driver.delete_user(user.id) + logger.info("deleted user: %s" % user) + except: + logger.log_exc("delete user failed: %s" % user) + + + def gc_user_tenant_roles(self): + """ + Remove roles that don't exist in openstack db if they have + an enacted time (enacted != None). + """ + # get all site privileges and slice memberships that have been enacted + user_tenant_roles = defaultdict(list) + for site_priv in SitePrivilege.objects.filter(enacted__isnull=False): + user_tenant_roles[(site_priv.user.kuser_id, site_priv.site.tenant_id)].append(site_priv.role.role) + for slice_memb in SliceMembership.objects.filter(enacted__isnull=False): + user_tenant_roles[(slice_memb.user.kuser_id, slice_memb.slice.tenant_id)].append(slice_memb.role.role) + + # Some user tenant role aren't stored in planetstack but they must be preserved. + # Role that fall in this category are + # 1. Never remove a user's role that their home site + # 2. Never remove a user's role at a slice they've created. + # Keep track of all roles that must be preserved. + users = User.objects.all() + preserved_roles = {} + for user in users: + tenant_ids = [s['tenant_id'] for s in user.slices.values()] + tenant_ids.append(user.site.tenant_id) + preserved_roles[user.kuser_id] = tenant_ids + + + # begin removing user tenant roles from keystone. This is stored in the + # Metadata table. + for metadata in self.driver.shell.keystone_db.get_metadata(): + # skip admin roles + if metadata.user_id == self.manager.driver.admin_user.id: + continue + # skip preserved tenant ids + if metadata.user_id in preserved_roles and \ + metadata.tenant_id in preserved_roles[metadata.user_id]: + continue + # get roles for user at this tenant + user_tenant_role_ids = user_tenant_roles.get((metadata.user_id, metadata.tenant_id), []) + + if user_tenant_role_ids: + # The user has roles at the tenant. Check if roles need to + # be updated. + user_keystone_role_ids = metadata.data.get('roles', []) + for role_id in user_keystone_role_ids: + if role_id not in user_tenant_role_ids: + user_keystone_role_ids.pop(user_keystone_role_ids.index(role_id)) + else: + # The user has no roles at this tenant. + metadata.data['roles'] = [] + #session.add(metadata) + logger.info("pruning metadata for %s at %s" % (metadata.user_id, metadata.tenant_id)) + + def gc_slivers(self): + """ + Remove slivers that no don't exist in openstack db if they have + an enacted time (enacted != None). + """ + # get all slivers where enacted != null. We can assume these users + # have previously been synced and need to be checed for deletion. + slivers = Sliver.objects.filter(enacted__isnull=False) + sliver_dict = {} + for sliver in slivers: + sliver_dict[sliver.instance_id] = sliver + + # delete sliver that don't have a sliver record + ctx = self.driver.shell.nova_db.ctx + instances = self.driver.shell.nova_db.instance_get_all(ctx) + for instance in instances: + if instance.uuid not in sliver_dict: + try: + # lookup tenant and update context + tenant = self.driver.shell.keystone.tenants.find(id=instance.project_id) + driver = self.driver.client_driver(tenant=tenant.name) + driver.destroy_instance(instance.uuid) + logger.info("destroyed sliver: %s" % (instance)) + except: + logger.log_exc("destroy sliver failed: %s" % instance) + + + def gc_sliver_ips(self): + """ + Update ips that have changed. + """ + # fill in null ip addresses + slivers = Sliver.objects.filter(ip=None) + for sliver in slivers: + # update connection + self.manager.init_admin(tenant=sliver.slice.name) + servers = self.manager.driver.shell.nova.servers.findall(id=sliver.instance_id) + if not servers: + continue + server = servers[0] + ips = server.addresses.get(sliver.slice.name, []) + if ips and sliver.ip != ips[0]['addr']: + sliver.ip = ips[0]['addr'] + sliver.save() + logger.info("updated sliver ip: %s %s" % (sliver, ips[0])) + + def gc_external_routes(self): + pass diff --git a/planetstack/observer/steps/sync_external_routes.py b/planetstack/observer/steps/sync_external_routes.py index 6c22c8b..cbaf490 100644 --- a/planetstack/observer/steps/sync_external_routes.py +++ b/planetstack/observer/steps/sync_external_routes.py @@ -11,10 +11,10 @@ class SyncExternalRoutes(SyncStep): pass def call(self): - routes = self.manager.driver.get_external_routes() - subnets = self.manager.driver.shell.quantum.list_subnets()['subnets'] + routes = self.driver.get_external_routes() + subnets = self.driver.shell.quantum.list_subnets()['subnets'] for subnet in subnets: try: - self.manager.driver.add_external_route(subnet, routes) + self.driver.add_external_route(subnet, routes) except: logger.log_exc("failed to add external route for subnet %s" % subnet) diff --git a/planetstack/observer/steps/sync_network_slivers.py b/planetstack/observer/steps/sync_network_slivers.py index 9e24fae..4466174 100644 --- a/planetstack/observer/steps/sync_network_slivers.py +++ b/planetstack/observer/steps/sync_network_slivers.py @@ -26,7 +26,7 @@ class SyncNetworkSlivers(OpenStackSyncStep): for sliver in slivers: slivers_by_instance_id[sliver.instance_id] = sliver - ports = self.manager.driver.shell.quantum.list_ports()["ports"] + ports = self.driver.shell.quantum.list_ports()["ports"] for port in ports: if port["id"] in networkSlivers_by_port: # we already have it diff --git a/planetstack/observer/steps/sync_networks.py b/planetstack/observer/steps/sync_networks.py index e64f0a4..f87d241 100644 --- a/planetstack/observer/steps/sync_networks.py +++ b/planetstack/observer/steps/sync_networks.py @@ -38,13 +38,19 @@ class SyncNetworks(OpenStackSyncStep): end = end) network.subnet = cidr network.subnet_id = subnet['id'] + # add subnet as interface to slice's router + self.driver.add_router_interface(router['id'], subnet['id']) + # add external route + self.driver.add_external_route(subnet) def sync_record(self, site): if network.owner and network.owner.creator: try: # update manager context - self.driver.init_caller(network.owner.creator, network.owner.name) + real_driver = self.driver + self.driver = self.driver.client_driver(network.owner.creator, network.owner.name) self.save_network(network) + self.driver = real_driver logger.info("saved network: %s" % (network)) except Exception,e: logger.log_exc("save network failed: %s" % network) diff --git a/planetstack/observer/steps/sync_sliver_ips.py b/planetstack/observer/steps/sync_sliver_ips.py index d69fd5d..d231d13 100644 --- a/planetstack/observer/steps/sync_sliver_ips.py +++ b/planetstack/observer/steps/sync_sliver_ips.py @@ -12,8 +12,8 @@ class SyncSliverIps(OpenStackSyncStep): return slivers def sync_record(self, sliver): - self.manager.init_admin(tenant=sliver.slice.name) - servers = self.manager.driver.shell.nova.servers.findall(id=sliver.instance_id) + driver = self.driver.client_driver(tenant=sliver.slice.name) + servers = driver.shell.nova.servers.findall(id=sliver.instance_id) if not servers: return server = servers[0] diff --git a/planetstack/observer/steps/sync_users.py b/planetstack/observer/steps/sync_users.py index 3f509ef..aa665be 100644 --- a/planetstack/observer/steps/sync_users.py +++ b/planetstack/observer/steps/sync_users.py @@ -28,8 +28,9 @@ class SyncUsers(OpenStackSyncStep): self.driver.delete_user_role(user.kuser_id, user.site.tenant_id, 'admin') if user.public_key: - self.init_caller(user, user.site.login_base) - self.save_key(user.public_key, user.keyname) - self.init_admin() + driver = self.driver.client_driver(caller=user, tenant=user.site.login_base) + key_fields = {'name': user.keyname, + 'public_key': user.public_key} + driver.create_keypair(**key_fields) user.save() diff --git a/planetstack/observer/syncstep.py b/planetstack/observer/syncstep.py index 68e9f99..9f32621 100644 --- a/planetstack/observer/syncstep.py +++ b/planetstack/observer/syncstep.py @@ -28,6 +28,7 @@ class SyncStep: provides -- PlanetStack models sync'd by this step """ dependencies = [] + self.driver = args.get('driver') try: self.soft_deadline = int(self.get_prop('soft_deadline_seconds')) except: diff --git a/planetstack/openstack/driver.py b/planetstack/openstack/driver.py index ce0b883..b6995b8 100644 --- a/planetstack/openstack/driver.py +++ b/planetstack/openstack/driver.py @@ -1,6 +1,14 @@ import commands from planetstack.config import Config -from openstack.client import OpenStackClient + +try: + from openstack.client import OpenStackClient + from openstack.driver import OpenStackDriver + has_openstack = True +except: + has_openstack = False + +manager_enabled = Config().api_nova_enabled class OpenStackDriver: @@ -18,6 +26,21 @@ class OpenStackDriver: else: self.shell = OpenStackClient() + def client_driver(self, caller=None, tenant=None): + if caller: + auth = {'username': caller.email, + 'password': hashlib.md5(caller.password).hexdigest()[:6], + 'tenant': tenant} + client = OpenStackClient(**auth) + else: + client = OpenStackClient(tenant=tenant) + driver = OpenStackDriver(client=client) + return driver + + def admin_driver(self, tenant=None): + client = OpenStackClient(tenant=tenant) + driver = OpenStackDriver(client=client) + def create_role(self, name): roles = self.shell.keystone.roles.findall(name=name) if not roles: