blob: 705ed67a08a3ab3a71be3369c942f6d48dfcd8e7 [file] [log] [blame]
Sapan Bhatiaf68688d2013-09-03 11:45:46 -04001import os
2import base64
Tony Macka76b8952013-10-05 23:36:30 -04003import traceback
4from collections import defaultdict
Tony Mackae7f30c2013-09-25 12:46:50 -04005from django.db.models import F, Q
Sapan Bhatiaf68688d2013-09-03 11:45:46 -04006from planetstack.config import Config
Tony Macka76b8952013-10-05 23:36:30 -04007from util.logger import Logger, logging
Tony Mack66646d52013-09-24 21:47:12 -04008from observer.openstacksyncstep import OpenStackSyncStep
Tony Macka76b8952013-10-05 23:36:30 -04009from core.models import *
10
11logger = Logger(logfile='observer.log', level=logging.INFO)
Sapan Bhatiaf68688d2013-09-03 11:45:46 -040012
Tony Mack66646d52013-09-24 21:47:12 -040013class GarbageCollector(OpenStackSyncStep):
14 requested_interval = 86400
15 provides=[]
Sapan Bhatiaf68688d2013-09-03 11:45:46 -040016
Tony Mackb0e03bf2013-10-05 20:52:13 -040017 def call(self, **args):
Tony Mack387a73f2013-09-18 07:59:14 -040018 try:
Tony Macke1509e82013-10-09 12:38:04 -040019 #self.gc_roles()
Tony Mack387a73f2013-09-18 07:59:14 -040020 self.gc_tenants()
21 self.gc_users()
22 self.gc_user_tenant_roles()
23 self.gc_slivers()
24 self.gc_sliver_ips()
25 self.gc_external_routes()
26 except:
27 traceback.print_exc()
28
29 def gc_roles(self):
30 """
31 all role that don't already exist in keystone. Remove keystone roles that
32 don't exist in planetstack
33 """
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
40 """
41 # delete keystone roles that don't exist in planetstack
42 for keystone_role in keystone_roles:
43 if keystone_role.name == 'admin':
44 continue
45 if keystone_role.name not in pending_role_names:
46 try:
Tony Macka76b8952013-10-05 23:36:30 -040047 self.driver.delete_role({id: keystone_role.id})
Tony Mack387a73f2013-09-18 07:59:14 -040048 except:
49 traceback.print_exc()
50 """
51
52 def gc_tenants(self):
53 """
54 Remove sites and slices that no don't exist in openstack db if they
55 have an enacted time (enacted != None).
56 """
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)
60 site_dict = {}
61 for site in sites:
62 site_dict[site.login_base] = site
63
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)
67 slice_dict = {}
68 for slice in slices:
69 slice_dict[slice.name] = slice
70
71 # delete keystone tenants that don't have a site record
72 tenants = self.driver.shell.keystone.tenants.findall()
Tony Mackbfe0f8e2013-10-06 11:36:47 -040073 system_tenants = ['admin','service', 'invisible_to_admin']
Tony Mack387a73f2013-09-18 07:59:14 -040074 for tenant in tenants:
75 if tenant.name in system_tenants:
76 continue
77 if tenant.name not in site_dict and tenant.name not in slice_dict:
78 try:
79 self.driver.delete_tenant(tenant.id)
80 logger.info("deleted tenant: %s" % (tenant))
81 except:
82 logger.log_exc("delete tenant failed: %s" % tenant)
83
84
85 def gc_users(self):
86 """
87 Remove users that no don't exist in openstack db if they have an
88 enacted time (enacted != None).
89 """
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)
93 user_dict = {}
94 for user in users:
95 user_dict[user.kuser_id] = user
96
97 # delete keystone users that don't have a user record
Tony Mack9b6dde82013-10-06 11:05:49 -040098 system_users = ['admin', 'nova', 'quantum', 'glance', 'cinder', 'swift', 'service', 'demo']
Tony Mack387a73f2013-09-18 07:59:14 -040099 users = self.driver.shell.keystone.users.findall()
100 for user in users:
101 if user.name in system_users:
102 continue
103 if user.id not in user_dict:
104 try:
Tony Mack9b6dde82013-10-06 11:05:49 -0400105 self.driver.delete_user(user.id)
Tony Mack387a73f2013-09-18 07:59:14 -0400106 logger.info("deleted user: %s" % user)
107 except:
108 logger.log_exc("delete user failed: %s" % user)
109
110
111 def gc_user_tenant_roles(self):
112 """
113 Remove roles that don't exist in openstack db if they have
114 an enacted time (enacted != None).
115 """
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)
Tony Macka76b8952013-10-05 23:36:30 -0400120 for slice_memb in SlicePrivilege.objects.filter(enacted__isnull=False):
Tony Mack387a73f2013-09-18 07:59:14 -0400121 user_tenant_roles[(slice_memb.user.kuser_id, slice_memb.slice.tenant_id)].append(slice_memb.role.role)
122
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()
129 preserved_roles = {}
130 for user in users:
131 tenant_ids = [s['tenant_id'] for s in user.slices.values()]
Tony Macka76b8952013-10-05 23:36:30 -0400132 if user.site:
133 tenant_ids.append(user.site.tenant_id)
Tony Mack387a73f2013-09-18 07:59:14 -0400134 preserved_roles[user.kuser_id] = tenant_ids
135
136
137 # begin removing user tenant roles from keystone. This is stored in the
138 # Metadata table.
139 for metadata in self.driver.shell.keystone_db.get_metadata():
140 # skip admin roles
Tony Macka76b8952013-10-05 23:36:30 -0400141 if metadata.user_id == self.driver.admin_user.id:
Tony Mack387a73f2013-09-18 07:59:14 -0400142 continue
143 # skip preserved tenant ids
144 if metadata.user_id in preserved_roles and \
145 metadata.tenant_id in preserved_roles[metadata.user_id]:
146 continue
147 # get roles for user at this tenant
148 user_tenant_role_ids = user_tenant_roles.get((metadata.user_id, metadata.tenant_id), [])
149
150 if user_tenant_role_ids:
151 # The user has roles at the tenant. Check if roles need to
152 # be updated.
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))
157 else:
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))
162
163 def gc_slivers(self):
164 """
165 Remove slivers that no don't exist in openstack db if they have
166 an enacted time (enacted != None).
167 """
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)
171 sliver_dict = {}
172 for sliver in slivers:
173 sliver_dict[sliver.instance_id] = sliver
174
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:
180 try:
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))
186 except:
187 logger.log_exc("destroy sliver failed: %s" % instance)
188
189
190 def gc_sliver_ips(self):
191 """
192 Update ips that have changed.
193 """
194 # fill in null ip addresses
195 slivers = Sliver.objects.filter(ip=None)
196 for sliver in slivers:
197 # update connection
Tony Macka76b8952013-10-05 23:36:30 -0400198 driver = self.driver.client_driver(tenant=sliver.slice.name)
199 servers = driver.shell.nova.servers.findall(id=sliver.instance_id)
Tony Mack387a73f2013-09-18 07:59:14 -0400200 if not servers:
201 continue
202 server = servers[0]
203 ips = server.addresses.get(sliver.slice.name, [])
204 if ips and sliver.ip != ips[0]['addr']:
205 sliver.ip = ips[0]['addr']
206 sliver.save()
207 logger.info("updated sliver ip: %s %s" % (sliver, ips[0]))
208
209 def gc_external_routes(self):
210 pass
Tony Macke1509e82013-10-09 12:38:04 -0400211
212 def gc_nodes(self):
213 # collect local nodes
214 nodes = Node.objects.all()
215 nodes_dict = {}
216 for node in nodes:
217 nodes_dict[node.name] = node
218
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
224
225 # remove old nodes
226 old_node_names = set(nodes_dict.keys()).difference(compute_nodes_dict.keys())
227 Node.objects.filter(name__in=old_node_names).delete()
228
229 def gc_images(self):
Tony Mack46c2d502013-10-09 13:04:28 -0400230 # collect local images
231 images = Image.objects.all()
232 images_dict = {}
233 for image in images:
234 images_dict[image.name] = image
235
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
241
242 # remove old images
243 old_image_names = set(images_dict.keys()).difference(glance_images_dict.keys())
244 Image.objects.filter(name__in=old_image_names).delete()