root / snf-cyclades-app / synnefo / api / management / commands / cyclades-astakos-migrate-013.py @ d443e1dd
History | View | Annotate | Download (9.4 kB)
1 | a682eaea | Kostas Papadimitriou | # Copyright 2012 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | a682eaea | Kostas Papadimitriou | #
|
3 | a682eaea | Kostas Papadimitriou | # Redistribution and use in source and binary forms, with or
|
4 | a682eaea | Kostas Papadimitriou | # without modification, are permitted provided that the following
|
5 | a682eaea | Kostas Papadimitriou | # conditions are met:
|
6 | a682eaea | Kostas Papadimitriou | #
|
7 | a682eaea | Kostas Papadimitriou | # 1. Redistributions of source code must retain the above
|
8 | a682eaea | Kostas Papadimitriou | # copyright notice, this list of conditions and the following
|
9 | a682eaea | Kostas Papadimitriou | # disclaimer.
|
10 | a682eaea | Kostas Papadimitriou | #
|
11 | a682eaea | Kostas Papadimitriou | # 2. Redistributions in binary form must reproduce the above
|
12 | a682eaea | Kostas Papadimitriou | # copyright notice, this list of conditions and the following
|
13 | a682eaea | Kostas Papadimitriou | # disclaimer in the documentation and/or other materials
|
14 | a682eaea | Kostas Papadimitriou | # provided with the distribution.
|
15 | a682eaea | Kostas Papadimitriou | #
|
16 | a682eaea | Kostas Papadimitriou | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | a682eaea | Kostas Papadimitriou | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | a682eaea | Kostas Papadimitriou | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | a682eaea | Kostas Papadimitriou | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | a682eaea | Kostas Papadimitriou | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | a682eaea | Kostas Papadimitriou | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | a682eaea | Kostas Papadimitriou | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | a682eaea | Kostas Papadimitriou | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | a682eaea | Kostas Papadimitriou | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | a682eaea | Kostas Papadimitriou | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | a682eaea | Kostas Papadimitriou | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | a682eaea | Kostas Papadimitriou | # POSSIBILITY OF SUCH DAMAGE.
|
28 | a682eaea | Kostas Papadimitriou | #
|
29 | a682eaea | Kostas Papadimitriou | # The views and conclusions contained in the software and
|
30 | a682eaea | Kostas Papadimitriou | # documentation are those of the authors and should not be
|
31 | a682eaea | Kostas Papadimitriou | # interpreted as representing official policies, either expressed
|
32 | a682eaea | Kostas Papadimitriou | # or implied, of GRNET S.A.
|
33 | a682eaea | Kostas Papadimitriou | |
34 | a682eaea | Kostas Papadimitriou | import itertools |
35 | a682eaea | Kostas Papadimitriou | import warnings |
36 | a682eaea | Kostas Papadimitriou | import functools |
37 | a682eaea | Kostas Papadimitriou | |
38 | a682eaea | Kostas Papadimitriou | from optparse import make_option |
39 | a682eaea | Kostas Papadimitriou | |
40 | a682eaea | Kostas Papadimitriou | from django.core.management.base import NoArgsCommand, CommandError, BaseCommand |
41 | a682eaea | Kostas Papadimitriou | from django.db import transaction |
42 | a682eaea | Kostas Papadimitriou | from django.conf import settings |
43 | a682eaea | Kostas Papadimitriou | |
44 | a682eaea | Kostas Papadimitriou | from synnefo.quotas import get_quota_holder |
45 | a682eaea | Kostas Papadimitriou | from synnefo.api.util import get_existing_users |
46 | a682eaea | Kostas Papadimitriou | from synnefo.lib.utils import case_unique |
47 | a682eaea | Kostas Papadimitriou | from synnefo.db.models import Network, VirtualMachine |
48 | a682eaea | Kostas Papadimitriou | from synnefo.ui.userdata.models import PublicKeyPair |
49 | a682eaea | Kostas Papadimitriou | |
50 | a682eaea | Kostas Papadimitriou | from synnefo.lib import astakos |
51 | a682eaea | Kostas Papadimitriou | |
52 | a682eaea | Kostas Papadimitriou | def warn(*msgs): |
53 | a682eaea | Kostas Papadimitriou | print "WARNING: %s" % ' '.join(msgs) |
54 | a682eaea | Kostas Papadimitriou | |
55 | 890c2065 | Sofia Papagiannaki | get_displayname = functools.partial(astakos.get_displayname, |
56 | a682eaea | Kostas Papadimitriou | settings.CYCLADES_ASTAKOS_SERVICE_TOKEN, |
57 | 2a7276e7 | Sofia Papagiannaki | url=settings.ASTAKOS_URL.replace('im/authenticate',
|
58 | 2a7276e7 | Sofia Papagiannaki | 'service/api/user_catalogs'))
|
59 | a682eaea | Kostas Papadimitriou | get_user_uuid = functools.partial(astakos.get_user_uuid, |
60 | a682eaea | Kostas Papadimitriou | settings.CYCLADES_ASTAKOS_SERVICE_TOKEN, |
61 | 2a7276e7 | Sofia Papagiannaki | url=settings.ASTAKOS_URL.replace('im/authenticate',
|
62 | 2a7276e7 | Sofia Papagiannaki | 'service/api/user_catalogs'))
|
63 | a682eaea | Kostas Papadimitriou | |
64 | a682eaea | Kostas Papadimitriou | def _validate_db_state(usernames): |
65 | a682eaea | Kostas Papadimitriou | |
66 | a682eaea | Kostas Papadimitriou | usernames = filter(bool, usernames) |
67 | a682eaea | Kostas Papadimitriou | invalid_case_users = case_unique(usernames) |
68 | a682eaea | Kostas Papadimitriou | if invalid_case_users:
|
69 | a682eaea | Kostas Papadimitriou | invalid_case_users.append(invalid_case_users[0].lower())
|
70 | a682eaea | Kostas Papadimitriou | raise CommandError("Duplicate case insensitive user identifiers exist %r" % invalid_case_users) |
71 | a682eaea | Kostas Papadimitriou | |
72 | a682eaea | Kostas Papadimitriou | uuidusers = filter(lambda uid:'@' in uid or uid == None, usernames) |
73 | a682eaea | Kostas Papadimitriou | if len(uuidusers) != len(usernames): |
74 | a682eaea | Kostas Papadimitriou | warn('It seems that mixed uuid/email user identifiers exist in database.')
|
75 | a682eaea | Kostas Papadimitriou | return False |
76 | a682eaea | Kostas Papadimitriou | |
77 | a682eaea | Kostas Papadimitriou | return True |
78 | a682eaea | Kostas Papadimitriou | |
79 | a682eaea | Kostas Papadimitriou | |
80 | a682eaea | Kostas Papadimitriou | @transaction.commit_manually
|
81 | a682eaea | Kostas Papadimitriou | def delete_user(username, only_stats=True, dry=True): |
82 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__exact=username) |
83 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__exact=username) |
84 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__exact=username) |
85 | a682eaea | Kostas Papadimitriou | |
86 | a682eaea | Kostas Papadimitriou | if not len(list(itertools.ifilter(bool, map(lambda q: q.count(), [vms, |
87 | a682eaea | Kostas Papadimitriou | networks, |
88 | a682eaea | Kostas Papadimitriou | keys])))): |
89 | a682eaea | Kostas Papadimitriou | print "No entries exist for '%s'" % username |
90 | a682eaea | Kostas Papadimitriou | return -1 |
91 | a682eaea | Kostas Papadimitriou | |
92 | a682eaea | Kostas Papadimitriou | if only_stats:
|
93 | a682eaea | Kostas Papadimitriou | print "The following entries will be deleted if you decide to remove this user" |
94 | a682eaea | Kostas Papadimitriou | print "%d Virtual Machines" % vms.exclude(operstate='DESTROYED').count() |
95 | a682eaea | Kostas Papadimitriou | print "%d Destroyed Virtual Machines" % vms.filter(operstate='DESTROYED').count() |
96 | a682eaea | Kostas Papadimitriou | print "%d Networks" % networks.count() |
97 | a682eaea | Kostas Papadimitriou | print "%d PublicKeyPairs" % keys.count() |
98 | a682eaea | Kostas Papadimitriou | return
|
99 | a682eaea | Kostas Papadimitriou | |
100 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
101 | a682eaea | Kostas Papadimitriou | o.delete() |
102 | a682eaea | Kostas Papadimitriou | |
103 | a682eaea | Kostas Papadimitriou | for key in keys: |
104 | a682eaea | Kostas Papadimitriou | key.delete() |
105 | a682eaea | Kostas Papadimitriou | |
106 | a682eaea | Kostas Papadimitriou | if dry:
|
107 | a682eaea | Kostas Papadimitriou | print "Skipping database commit." |
108 | a682eaea | Kostas Papadimitriou | transaction.rollback() |
109 | a682eaea | Kostas Papadimitriou | else:
|
110 | a682eaea | Kostas Papadimitriou | transaction.commit() |
111 | a682eaea | Kostas Papadimitriou | print "User entries removed." |
112 | a682eaea | Kostas Papadimitriou | |
113 | a682eaea | Kostas Papadimitriou | |
114 | a682eaea | Kostas Papadimitriou | @transaction.commit_on_success
|
115 | a682eaea | Kostas Papadimitriou | def merge_user(username): |
116 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__iexact=username) |
117 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__iexact=username) |
118 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__iexact=username) |
119 | a682eaea | Kostas Papadimitriou | |
120 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
121 | a682eaea | Kostas Papadimitriou | o.userid = username.lower() |
122 | a682eaea | Kostas Papadimitriou | o.save() |
123 | a682eaea | Kostas Papadimitriou | |
124 | a682eaea | Kostas Papadimitriou | for key in keys: |
125 | a682eaea | Kostas Papadimitriou | key.user = username.lower() |
126 | a682eaea | Kostas Papadimitriou | key.save() |
127 | a682eaea | Kostas Papadimitriou | |
128 | ec0c9d21 | Kostas Papadimitriou | |
129 | a682eaea | Kostas Papadimitriou | def migrate_user(username, uuid): |
130 | a682eaea | Kostas Papadimitriou | """
|
131 | a682eaea | Kostas Papadimitriou | Warn: no transaction handling. Consider wrapping within another function.
|
132 | a682eaea | Kostas Papadimitriou | """
|
133 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__exact=username) |
134 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__exact=username) |
135 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__exact=username) |
136 | a682eaea | Kostas Papadimitriou | |
137 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
138 | ec0c9d21 | Kostas Papadimitriou | o.userid = uuid or o.userid
|
139 | a682eaea | Kostas Papadimitriou | o.save() |
140 | a682eaea | Kostas Papadimitriou | |
141 | a682eaea | Kostas Papadimitriou | for key in keys: |
142 | a682eaea | Kostas Papadimitriou | key.user = uuid |
143 | a682eaea | Kostas Papadimitriou | key.save() |
144 | a682eaea | Kostas Papadimitriou | |
145 | a682eaea | Kostas Papadimitriou | |
146 | a682eaea | Kostas Papadimitriou | @transaction.commit_manually
|
147 | a682eaea | Kostas Papadimitriou | def migrate_users(usernames, dry=True): |
148 | a682eaea | Kostas Papadimitriou | usernames = filter(bool, usernames) |
149 | a682eaea | Kostas Papadimitriou | count = 0
|
150 | a682eaea | Kostas Papadimitriou | for u in usernames: |
151 | a682eaea | Kostas Papadimitriou | if not '@' in u: |
152 | a682eaea | Kostas Papadimitriou | warn('Skipping %s. It doesn\'t seem to be an email' % u)
|
153 | a682eaea | Kostas Papadimitriou | continue
|
154 | a682eaea | Kostas Papadimitriou | |
155 | a682eaea | Kostas Papadimitriou | try:
|
156 | a682eaea | Kostas Papadimitriou | uuid = get_user_uuid(u) |
157 | a682eaea | Kostas Papadimitriou | print "%s -> %s" % (u, uuid) |
158 | ec0c9d21 | Kostas Papadimitriou | if not uuid: |
159 | ec0c9d21 | Kostas Papadimitriou | raise Exception("No uuid for %s" % u) |
160 | a682eaea | Kostas Papadimitriou | migrate_user(u, uuid) |
161 | a682eaea | Kostas Papadimitriou | count += 1
|
162 | a682eaea | Kostas Papadimitriou | except Exception, e: |
163 | a682eaea | Kostas Papadimitriou | print "ERROR: User id migration failed (%s)" % e |
164 | a682eaea | Kostas Papadimitriou | |
165 | a682eaea | Kostas Papadimitriou | if dry:
|
166 | a682eaea | Kostas Papadimitriou | print "Skipping database commit." |
167 | a682eaea | Kostas Papadimitriou | transaction.rollback() |
168 | a682eaea | Kostas Papadimitriou | else:
|
169 | a682eaea | Kostas Papadimitriou | transaction.commit() |
170 | a682eaea | Kostas Papadimitriou | print "Migrated %d users" % count |
171 | a682eaea | Kostas Papadimitriou | |
172 | a682eaea | Kostas Papadimitriou | |
173 | a682eaea | Kostas Papadimitriou | class Command(NoArgsCommand): |
174 | a682eaea | Kostas Papadimitriou | help = "Quotas migration helper"
|
175 | a682eaea | Kostas Papadimitriou | |
176 | a682eaea | Kostas Papadimitriou | option_list = BaseCommand.option_list + ( |
177 | a682eaea | Kostas Papadimitriou | make_option('--strict',
|
178 | a682eaea | Kostas Papadimitriou | dest='strict',
|
179 | a682eaea | Kostas Papadimitriou | action="store_false",
|
180 | a682eaea | Kostas Papadimitriou | default=True,
|
181 | a682eaea | Kostas Papadimitriou | help="Exit on warnings."),
|
182 | a682eaea | Kostas Papadimitriou | make_option('--validate-db',
|
183 | a682eaea | Kostas Papadimitriou | dest='validate',
|
184 | a682eaea | Kostas Papadimitriou | action="store_true",
|
185 | a682eaea | Kostas Papadimitriou | default=True,
|
186 | ec0c9d21 | Kostas Papadimitriou | help=("Check if cyclades database contents are valid for "
|
187 | ec0c9d21 | Kostas Papadimitriou | "migration.")),
|
188 | a682eaea | Kostas Papadimitriou | make_option('--migrate-users',
|
189 | a682eaea | Kostas Papadimitriou | dest='migrate_users',
|
190 | a682eaea | Kostas Papadimitriou | action="store_true",
|
191 | a682eaea | Kostas Papadimitriou | default=False,
|
192 | ec0c9d21 | Kostas Papadimitriou | help=("Convert emails to uuids for all users stored in "
|
193 | ec0c9d21 | Kostas Papadimitriou | "database.")),
|
194 | a682eaea | Kostas Papadimitriou | make_option('--merge-user',
|
195 | a682eaea | Kostas Papadimitriou | dest='merge_user',
|
196 | a682eaea | Kostas Papadimitriou | default=False,
|
197 | a682eaea | Kostas Papadimitriou | help="Merge case insensitive duplicates of a user."),
|
198 | a682eaea | Kostas Papadimitriou | make_option('--delete-user',
|
199 | a682eaea | Kostas Papadimitriou | dest='delete_user',
|
200 | a682eaea | Kostas Papadimitriou | action='store',
|
201 | a682eaea | Kostas Papadimitriou | default=False,
|
202 | a682eaea | Kostas Papadimitriou | help="Delete user entries."),
|
203 | a682eaea | Kostas Papadimitriou | make_option('--user-entries',
|
204 | a682eaea | Kostas Papadimitriou | dest='user_entries',
|
205 | a682eaea | Kostas Papadimitriou | action='store',
|
206 | a682eaea | Kostas Papadimitriou | default=False,
|
207 | a682eaea | Kostas Papadimitriou | help="Display user summary."),
|
208 | a682eaea | Kostas Papadimitriou | make_option('--dry',
|
209 | a682eaea | Kostas Papadimitriou | dest='dry',
|
210 | a682eaea | Kostas Papadimitriou | action="store_true",
|
211 | a682eaea | Kostas Papadimitriou | default=False,
|
212 | a682eaea | Kostas Papadimitriou | help="Do not commit database changes. Do not communicate "
|
213 | a682eaea | Kostas Papadimitriou | "with quotaholder"),
|
214 | a682eaea | Kostas Papadimitriou | make_option('--user',
|
215 | a682eaea | Kostas Papadimitriou | dest='user',
|
216 | a682eaea | Kostas Papadimitriou | action="store",
|
217 | a682eaea | Kostas Papadimitriou | default=False,)
|
218 | a682eaea | Kostas Papadimitriou | ) |
219 | a682eaea | Kostas Papadimitriou | |
220 | a682eaea | Kostas Papadimitriou | def resolve_conflicts(self, options): |
221 | a682eaea | Kostas Papadimitriou | conflicting = map(options.get, ['migrate_users', |
222 | a682eaea | Kostas Papadimitriou | 'merge_user'])
|
223 | a682eaea | Kostas Papadimitriou | if len(filter(bool, conflicting)) > 1: |
224 | a682eaea | Kostas Papadimitriou | raise CommandError('You can use only one of --validate,' |
225 | ec0c9d21 | Kostas Papadimitriou | '--migrate-users')
|
226 | a682eaea | Kostas Papadimitriou | |
227 | a682eaea | Kostas Papadimitriou | def handle(self, *args, **options): |
228 | a682eaea | Kostas Papadimitriou | self.resolve_conflicts(options)
|
229 | a682eaea | Kostas Papadimitriou | self.strict = options.get('strict') |
230 | a682eaea | Kostas Papadimitriou | self.dry = options.get('dry') |
231 | a682eaea | Kostas Papadimitriou | |
232 | a682eaea | Kostas Papadimitriou | if options.get('validate') and not options.get('merge_user') and not \ |
233 | a682eaea | Kostas Papadimitriou | options.get('delete_user') and not options.get('user_entries'): |
234 | a682eaea | Kostas Papadimitriou | usernames = get_existing_users() |
235 | a682eaea | Kostas Papadimitriou | _validate_db_state(usernames) |
236 | a682eaea | Kostas Papadimitriou | |
237 | a682eaea | Kostas Papadimitriou | if options.get('migrate_users'): |
238 | a682eaea | Kostas Papadimitriou | migrate_users(usernames, dry=self.dry)
|
239 | a682eaea | Kostas Papadimitriou | |
240 | a682eaea | Kostas Papadimitriou | if options.get('merge_user'): |
241 | a682eaea | Kostas Papadimitriou | merge_user(options.get('merge_user'))
|
242 | a682eaea | Kostas Papadimitriou | print "Merge finished." |
243 | a682eaea | Kostas Papadimitriou | |
244 | a682eaea | Kostas Papadimitriou | if options.get('delete_user'): |
245 | a682eaea | Kostas Papadimitriou | entries = delete_user(options.get('delete_user'), only_stats=True) |
246 | a682eaea | Kostas Papadimitriou | if entries == -1: |
247 | a682eaea | Kostas Papadimitriou | return
|
248 | a682eaea | Kostas Papadimitriou | |
249 | a682eaea | Kostas Papadimitriou | confirm = raw_input("Type 'yes of course' if you are sure you want" |
250 | a682eaea | Kostas Papadimitriou | " to remove those entries: ")
|
251 | a682eaea | Kostas Papadimitriou | if not confirm == 'yes of course': |
252 | a682eaea | Kostas Papadimitriou | return
|
253 | a682eaea | Kostas Papadimitriou | else:
|
254 | a682eaea | Kostas Papadimitriou | delete_user(options.get('delete_user'), only_stats=False, |
255 | a682eaea | Kostas Papadimitriou | dry=self.dry)
|
256 | a682eaea | Kostas Papadimitriou | |
257 | a682eaea | Kostas Papadimitriou | if options.get('user_entries'): |
258 | a682eaea | Kostas Papadimitriou | delete_user(options.get('user_entries')) |