sync images
[plstackapi.git] / planetstack / observer / steps / garbage_collector.py
1 import os
2 import base64
3 import traceback
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 *
10
11 logger = Logger(logfile='observer.log', level=logging.INFO)
12
13 class GarbageCollector(OpenStackSyncStep):
14     requested_interval = 86400
15     provides=[]
16
17     def call(self, **args):
18         try:
19             #self.gc_roles()
20             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:
47                     self.driver.delete_role({id: keystone_role.id})
48                 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()
73         system_tenants = ['admin','service', 'invisible_to_admin']
74         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
98         system_users = ['admin', 'nova', 'quantum', 'glance', 'cinder', 'swift', 'service', 'demo']
99         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:
105                     self.driver.delete_user(user.id)
106                     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)
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)  
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()]
132             if user.site:
133                 tenant_ids.append(user.site.tenant_id) 
134             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
141             if metadata.user_id == self.driver.admin_user.id:
142                 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
198             driver = self.driver.client_driver(tenant=sliver.slice.name)
199             servers = driver.shell.nova.servers.findall(id=sliver.instance_id)
200             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
211
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):
230         # 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()