4 from collections import defaultdict
5 from django.db.models import F, Q
6 from planetstack.config import Config
7 from util.logger import Logger, logging
8 from observer.openstacksyncstep import OpenStackSyncStep
9 from core.models import *
11 logger = Logger(logfile='observer.log', level=logging.INFO)
13 class GarbageCollector(OpenStackSyncStep):
14 requested_interval = 86400
17 def call(self, **args):
22 self.gc_user_tenant_roles()
25 self.gc_external_routes()
31 all role that don't already exist in keystone. Remove keystone roles that
32 don't exist in planetstack
34 # sync all roles that don't already in keystone
35 keystone_roles = self.driver.shell.keystone.roles.findall()
36 keystone_role_names = [kr.name for kr in keystone_roles]
37 pending_roles = Role.objects.all()
38 pending_role_names = [r.role_type for r in pending_roles]
39 # don't delete roles for now
41 # delete keystone roles that don't exist in planetstack
42 for keystone_role in keystone_roles:
43 if keystone_role.name == 'admin':
45 if keystone_role.name not in pending_role_names:
47 self.driver.delete_role({id: keystone_role.id})
54 Remove sites and slices that no don't exist in openstack db if they
55 have an enacted time (enacted != None).
57 # get all sites that where enacted != null. We can assume these sites
58 # have previously been synced and need to be checed for deletion.
59 sites = Site.objects.filter(enacted__isnull=False)
62 site_dict[site.login_base] = site
64 # get all slices that where enacted != null. We can assume these slices
65 # have previously been synced and need to be checed for deletion.
66 slices = Slice.objects.filter(enacted__isnull=False)
69 slice_dict[slice.name] = slice
71 # delete keystone tenants that don't have a site record
72 tenants = self.driver.shell.keystone.tenants.findall()
73 system_tenants = ['admin','service', 'invisible_to_admin']
74 for tenant in tenants:
75 if tenant.name in system_tenants:
77 if tenant.name not in site_dict and tenant.name not in slice_dict:
79 self.driver.delete_tenant(tenant.id)
80 logger.info("deleted tenant: %s" % (tenant))
82 logger.log_exc("delete tenant failed: %s" % tenant)
87 Remove users that no don't exist in openstack db if they have an
88 enacted time (enacted != None).
90 # get all users that where enacted != null. We can assume these users
91 # have previously been synced and need to be checed for deletion.
92 users = User.objects.filter(enacted__isnull=False)
95 user_dict[user.kuser_id] = user
97 # delete keystone users that don't have a user record
98 system_users = ['admin', 'nova', 'quantum', 'glance', 'cinder', 'swift', 'service', 'demo']
99 users = self.driver.shell.keystone.users.findall()
101 if user.name in system_users:
103 if user.id not in user_dict:
105 self.driver.delete_user(user.id)
106 logger.info("deleted user: %s" % user)
108 logger.log_exc("delete user failed: %s" % user)
111 def gc_user_tenant_roles(self):
113 Remove roles that don't exist in openstack db if they have
114 an enacted time (enacted != None).
116 # get all site privileges and slice memberships that have been enacted
117 user_tenant_roles = defaultdict(list)
118 for site_priv in SitePrivilege.objects.filter(enacted__isnull=False):
119 user_tenant_roles[(site_priv.user.kuser_id, site_priv.site.tenant_id)].append(site_priv.role.role)
120 for slice_memb in SlicePrivilege.objects.filter(enacted__isnull=False):
121 user_tenant_roles[(slice_memb.user.kuser_id, slice_memb.slice.tenant_id)].append(slice_memb.role.role)
123 # Some user tenant role aren't stored in planetstack but they must be preserved.
124 # Role that fall in this category are
125 # 1. Never remove a user's role that their home site
126 # 2. Never remove a user's role at a slice they've created.
127 # Keep track of all roles that must be preserved.
128 users = User.objects.all()
131 tenant_ids = [s['tenant_id'] for s in user.slices.values()]
133 tenant_ids.append(user.site.tenant_id)
134 preserved_roles[user.kuser_id] = tenant_ids
137 # begin removing user tenant roles from keystone. This is stored in the
139 for metadata in self.driver.shell.keystone_db.get_metadata():
141 if metadata.user_id == self.driver.admin_user.id:
143 # skip preserved tenant ids
144 if metadata.user_id in preserved_roles and \
145 metadata.tenant_id in preserved_roles[metadata.user_id]:
147 # get roles for user at this tenant
148 user_tenant_role_ids = user_tenant_roles.get((metadata.user_id, metadata.tenant_id), [])
150 if user_tenant_role_ids:
151 # The user has roles at the tenant. Check if roles need to
153 user_keystone_role_ids = metadata.data.get('roles', [])
154 for role_id in user_keystone_role_ids:
155 if role_id not in user_tenant_role_ids:
156 user_keystone_role_ids.pop(user_keystone_role_ids.index(role_id))
158 # The user has no roles at this tenant.
159 metadata.data['roles'] = []
160 #session.add(metadata)
161 logger.info("pruning metadata for %s at %s" % (metadata.user_id, metadata.tenant_id))
163 def gc_slivers(self):
165 Remove slivers that no don't exist in openstack db if they have
166 an enacted time (enacted != None).
168 # get all slivers where enacted != null. We can assume these users
169 # have previously been synced and need to be checed for deletion.
170 slivers = Sliver.objects.filter(enacted__isnull=False)
172 for sliver in slivers:
173 sliver_dict[sliver.instance_id] = sliver
175 # delete sliver that don't have a sliver record
176 ctx = self.driver.shell.nova_db.ctx
177 instances = self.driver.shell.nova_db.instance_get_all(ctx)
178 for instance in instances:
179 if instance.uuid not in sliver_dict:
181 # lookup tenant and update context
182 tenant = self.driver.shell.keystone.tenants.find(id=instance.project_id)
183 driver = self.driver.client_driver(tenant=tenant.name)
184 driver.destroy_instance(instance.uuid)
185 logger.info("destroyed sliver: %s" % (instance))
187 logger.log_exc("destroy sliver failed: %s" % instance)
190 def gc_sliver_ips(self):
192 Update ips that have changed.
194 # fill in null ip addresses
195 slivers = Sliver.objects.filter(ip=None)
196 for sliver in slivers:
198 driver = self.driver.client_driver(tenant=sliver.slice.name)
199 servers = driver.shell.nova.servers.findall(id=sliver.instance_id)
203 ips = server.addresses.get(sliver.slice.name, [])
204 if ips and sliver.ip != ips[0]['addr']:
205 sliver.ip = ips[0]['addr']
207 logger.info("updated sliver ip: %s %s" % (sliver, ips[0]))
209 def gc_external_routes(self):
213 # collect local nodes
214 nodes = Node.objects.all()
217 nodes_dict[node.name] = node
219 # collect nova nodes:
220 compute_nodes = self.client.nova.hypervisors.list()
221 compute_nodes_dict = {}
222 for compute_node in compute_nodes:
223 compute_nodes_dict[compute_node.hypervisor_hostname] = compute_node
226 old_node_names = set(nodes_dict.keys()).difference(compute_nodes_dict.keys())
227 Node.objects.filter(name__in=old_node_names).delete()
230 # collect local images
231 images = Image.objects.all()
234 images_dict[image.name] = image
236 # collect glance images
237 glance_images = self.driver.shell.glance.get_images()
238 glance_images_dict = {}
239 for glance_image in glance_images:
240 glance_images_dict[glance_image['name']] = glance_image
243 old_image_names = set(images_dict.keys()).difference(glance_images_dict.keys())
244 Image.objects.filter(name__in=old_image_names).delete()