root / snf-cyclades-app / synnefo / api / management / commands / cyclades-astakos-migrate-013.py @ 5ec446aa
History | View | Annotate | Download (9.3 kB)
1 | 629acc65 | Giorgos Korfiatis | # Copyright 2012, 2013 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.api.util import get_existing_users |
45 | a682eaea | Kostas Papadimitriou | from synnefo.lib.utils import case_unique |
46 | a682eaea | Kostas Papadimitriou | from synnefo.db.models import Network, VirtualMachine |
47 | b47b110d | Kostas Papadimitriou | from synnefo.userdata.models import PublicKeyPair |
48 | a682eaea | Kostas Papadimitriou | |
49 | 04a1b675 | Christos Stavrakakis | from snf_django.lib import astakos |
50 | a682eaea | Kostas Papadimitriou | |
51 | a682eaea | Kostas Papadimitriou | def warn(*msgs): |
52 | a682eaea | Kostas Papadimitriou | print "WARNING: %s" % ' '.join(msgs) |
53 | a682eaea | Kostas Papadimitriou | |
54 | 890c2065 | Sofia Papagiannaki | get_displayname = functools.partial(astakos.get_displayname, |
55 | 18c4414d | Giorgos Korfiatis | settings.CYCLADES_SERVICE_TOKEN, |
56 | 2a7276e7 | Sofia Papagiannaki | url=settings.ASTAKOS_URL.replace('im/authenticate',
|
57 | 2a7276e7 | Sofia Papagiannaki | 'service/api/user_catalogs'))
|
58 | a682eaea | Kostas Papadimitriou | get_user_uuid = functools.partial(astakos.get_user_uuid, |
59 | 18c4414d | Giorgos Korfiatis | settings.CYCLADES_SERVICE_TOKEN, |
60 | 2a7276e7 | Sofia Papagiannaki | url=settings.ASTAKOS_URL.replace('im/authenticate',
|
61 | 2a7276e7 | Sofia Papagiannaki | 'service/api/user_catalogs'))
|
62 | a682eaea | Kostas Papadimitriou | |
63 | a682eaea | Kostas Papadimitriou | def _validate_db_state(usernames): |
64 | a682eaea | Kostas Papadimitriou | |
65 | a682eaea | Kostas Papadimitriou | usernames = filter(bool, usernames) |
66 | a682eaea | Kostas Papadimitriou | invalid_case_users = case_unique(usernames) |
67 | a682eaea | Kostas Papadimitriou | if invalid_case_users:
|
68 | a682eaea | Kostas Papadimitriou | invalid_case_users.append(invalid_case_users[0].lower())
|
69 | a682eaea | Kostas Papadimitriou | raise CommandError("Duplicate case insensitive user identifiers exist %r" % invalid_case_users) |
70 | a682eaea | Kostas Papadimitriou | |
71 | a682eaea | Kostas Papadimitriou | uuidusers = filter(lambda uid:'@' in uid or uid == None, usernames) |
72 | a682eaea | Kostas Papadimitriou | if len(uuidusers) != len(usernames): |
73 | a682eaea | Kostas Papadimitriou | warn('It seems that mixed uuid/email user identifiers exist in database.')
|
74 | a682eaea | Kostas Papadimitriou | return False |
75 | a682eaea | Kostas Papadimitriou | |
76 | a682eaea | Kostas Papadimitriou | return True |
77 | a682eaea | Kostas Papadimitriou | |
78 | a682eaea | Kostas Papadimitriou | |
79 | a682eaea | Kostas Papadimitriou | @transaction.commit_manually
|
80 | a682eaea | Kostas Papadimitriou | def delete_user(username, only_stats=True, dry=True): |
81 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__exact=username) |
82 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__exact=username) |
83 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__exact=username) |
84 | a682eaea | Kostas Papadimitriou | |
85 | a682eaea | Kostas Papadimitriou | if not len(list(itertools.ifilter(bool, map(lambda q: q.count(), [vms, |
86 | a682eaea | Kostas Papadimitriou | networks, |
87 | a682eaea | Kostas Papadimitriou | keys])))): |
88 | a682eaea | Kostas Papadimitriou | print "No entries exist for '%s'" % username |
89 | a682eaea | Kostas Papadimitriou | return -1 |
90 | a682eaea | Kostas Papadimitriou | |
91 | a682eaea | Kostas Papadimitriou | if only_stats:
|
92 | a682eaea | Kostas Papadimitriou | print "The following entries will be deleted if you decide to remove this user" |
93 | a682eaea | Kostas Papadimitriou | print "%d Virtual Machines" % vms.exclude(operstate='DESTROYED').count() |
94 | a682eaea | Kostas Papadimitriou | print "%d Destroyed Virtual Machines" % vms.filter(operstate='DESTROYED').count() |
95 | a682eaea | Kostas Papadimitriou | print "%d Networks" % networks.count() |
96 | a682eaea | Kostas Papadimitriou | print "%d PublicKeyPairs" % keys.count() |
97 | a682eaea | Kostas Papadimitriou | return
|
98 | a682eaea | Kostas Papadimitriou | |
99 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
100 | a682eaea | Kostas Papadimitriou | o.delete() |
101 | a682eaea | Kostas Papadimitriou | |
102 | a682eaea | Kostas Papadimitriou | for key in keys: |
103 | a682eaea | Kostas Papadimitriou | key.delete() |
104 | a682eaea | Kostas Papadimitriou | |
105 | a682eaea | Kostas Papadimitriou | if dry:
|
106 | a682eaea | Kostas Papadimitriou | print "Skipping database commit." |
107 | a682eaea | Kostas Papadimitriou | transaction.rollback() |
108 | a682eaea | Kostas Papadimitriou | else:
|
109 | a682eaea | Kostas Papadimitriou | transaction.commit() |
110 | a682eaea | Kostas Papadimitriou | print "User entries removed." |
111 | a682eaea | Kostas Papadimitriou | |
112 | a682eaea | Kostas Papadimitriou | |
113 | a682eaea | Kostas Papadimitriou | @transaction.commit_on_success
|
114 | a682eaea | Kostas Papadimitriou | def merge_user(username): |
115 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__iexact=username) |
116 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__iexact=username) |
117 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__iexact=username) |
118 | a682eaea | Kostas Papadimitriou | |
119 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
120 | a682eaea | Kostas Papadimitriou | o.userid = username.lower() |
121 | a682eaea | Kostas Papadimitriou | o.save() |
122 | a682eaea | Kostas Papadimitriou | |
123 | a682eaea | Kostas Papadimitriou | for key in keys: |
124 | a682eaea | Kostas Papadimitriou | key.user = username.lower() |
125 | a682eaea | Kostas Papadimitriou | key.save() |
126 | a682eaea | Kostas Papadimitriou | |
127 | ec0c9d21 | Kostas Papadimitriou | |
128 | a682eaea | Kostas Papadimitriou | def migrate_user(username, uuid): |
129 | a682eaea | Kostas Papadimitriou | """
|
130 | a682eaea | Kostas Papadimitriou | Warn: no transaction handling. Consider wrapping within another function.
|
131 | a682eaea | Kostas Papadimitriou | """
|
132 | a682eaea | Kostas Papadimitriou | vms = VirtualMachine.objects.filter(userid__exact=username) |
133 | a682eaea | Kostas Papadimitriou | networks = Network.objects.filter(userid__exact=username) |
134 | a682eaea | Kostas Papadimitriou | keys = PublicKeyPair.objects.filter(user__exact=username) |
135 | a682eaea | Kostas Papadimitriou | |
136 | a682eaea | Kostas Papadimitriou | for o in itertools.chain(vms, networks): |
137 | ec0c9d21 | Kostas Papadimitriou | o.userid = uuid or o.userid
|
138 | a682eaea | Kostas Papadimitriou | o.save() |
139 | a682eaea | Kostas Papadimitriou | |
140 | a682eaea | Kostas Papadimitriou | for key in keys: |
141 | a682eaea | Kostas Papadimitriou | key.user = uuid |
142 | a682eaea | Kostas Papadimitriou | key.save() |
143 | a682eaea | Kostas Papadimitriou | |
144 | a682eaea | Kostas Papadimitriou | |
145 | a682eaea | Kostas Papadimitriou | @transaction.commit_manually
|
146 | a682eaea | Kostas Papadimitriou | def migrate_users(usernames, dry=True): |
147 | a682eaea | Kostas Papadimitriou | usernames = filter(bool, usernames) |
148 | a682eaea | Kostas Papadimitriou | count = 0
|
149 | a682eaea | Kostas Papadimitriou | for u in usernames: |
150 | a682eaea | Kostas Papadimitriou | if not '@' in u: |
151 | a682eaea | Kostas Papadimitriou | warn('Skipping %s. It doesn\'t seem to be an email' % u)
|
152 | a682eaea | Kostas Papadimitriou | continue
|
153 | a682eaea | Kostas Papadimitriou | |
154 | a682eaea | Kostas Papadimitriou | try:
|
155 | a682eaea | Kostas Papadimitriou | uuid = get_user_uuid(u) |
156 | a682eaea | Kostas Papadimitriou | print "%s -> %s" % (u, uuid) |
157 | ec0c9d21 | Kostas Papadimitriou | if not uuid: |
158 | ec0c9d21 | Kostas Papadimitriou | raise Exception("No uuid for %s" % u) |
159 | a682eaea | Kostas Papadimitriou | migrate_user(u, uuid) |
160 | a682eaea | Kostas Papadimitriou | count += 1
|
161 | a682eaea | Kostas Papadimitriou | except Exception, e: |
162 | a682eaea | Kostas Papadimitriou | print "ERROR: User id migration failed (%s)" % e |
163 | a682eaea | Kostas Papadimitriou | |
164 | a682eaea | Kostas Papadimitriou | if dry:
|
165 | a682eaea | Kostas Papadimitriou | print "Skipping database commit." |
166 | a682eaea | Kostas Papadimitriou | transaction.rollback() |
167 | a682eaea | Kostas Papadimitriou | else:
|
168 | a682eaea | Kostas Papadimitriou | transaction.commit() |
169 | a682eaea | Kostas Papadimitriou | print "Migrated %d users" % count |
170 | a682eaea | Kostas Papadimitriou | |
171 | a682eaea | Kostas Papadimitriou | |
172 | a682eaea | Kostas Papadimitriou | class Command(NoArgsCommand): |
173 | a682eaea | Kostas Papadimitriou | help = "Quotas migration helper"
|
174 | a682eaea | Kostas Papadimitriou | |
175 | a682eaea | Kostas Papadimitriou | option_list = BaseCommand.option_list + ( |
176 | a682eaea | Kostas Papadimitriou | make_option('--strict',
|
177 | a682eaea | Kostas Papadimitriou | dest='strict',
|
178 | a682eaea | Kostas Papadimitriou | action="store_false",
|
179 | a682eaea | Kostas Papadimitriou | default=True,
|
180 | a682eaea | Kostas Papadimitriou | help="Exit on warnings."),
|
181 | a682eaea | Kostas Papadimitriou | make_option('--validate-db',
|
182 | a682eaea | Kostas Papadimitriou | dest='validate',
|
183 | a682eaea | Kostas Papadimitriou | action="store_true",
|
184 | a682eaea | Kostas Papadimitriou | default=True,
|
185 | ec0c9d21 | Kostas Papadimitriou | help=("Check if cyclades database contents are valid for "
|
186 | ec0c9d21 | Kostas Papadimitriou | "migration.")),
|
187 | a682eaea | Kostas Papadimitriou | make_option('--migrate-users',
|
188 | a682eaea | Kostas Papadimitriou | dest='migrate_users',
|
189 | a682eaea | Kostas Papadimitriou | action="store_true",
|
190 | a682eaea | Kostas Papadimitriou | default=False,
|
191 | ec0c9d21 | Kostas Papadimitriou | help=("Convert emails to uuids for all users stored in "
|
192 | ec0c9d21 | Kostas Papadimitriou | "database.")),
|
193 | a682eaea | Kostas Papadimitriou | make_option('--merge-user',
|
194 | a682eaea | Kostas Papadimitriou | dest='merge_user',
|
195 | a682eaea | Kostas Papadimitriou | default=False,
|
196 | a682eaea | Kostas Papadimitriou | help="Merge case insensitive duplicates of a user."),
|
197 | a682eaea | Kostas Papadimitriou | make_option('--delete-user',
|
198 | a682eaea | Kostas Papadimitriou | dest='delete_user',
|
199 | a682eaea | Kostas Papadimitriou | action='store',
|
200 | a682eaea | Kostas Papadimitriou | default=False,
|
201 | a682eaea | Kostas Papadimitriou | help="Delete user entries."),
|
202 | a682eaea | Kostas Papadimitriou | make_option('--user-entries',
|
203 | a682eaea | Kostas Papadimitriou | dest='user_entries',
|
204 | a682eaea | Kostas Papadimitriou | action='store',
|
205 | a682eaea | Kostas Papadimitriou | default=False,
|
206 | a682eaea | Kostas Papadimitriou | help="Display user summary."),
|
207 | a682eaea | Kostas Papadimitriou | make_option('--dry',
|
208 | a682eaea | Kostas Papadimitriou | dest='dry',
|
209 | a682eaea | Kostas Papadimitriou | action="store_true",
|
210 | a682eaea | Kostas Papadimitriou | default=False,
|
211 | a682eaea | Kostas Papadimitriou | help="Do not commit database changes. Do not communicate "
|
212 | a682eaea | Kostas Papadimitriou | "with quotaholder"),
|
213 | a682eaea | Kostas Papadimitriou | make_option('--user',
|
214 | a682eaea | Kostas Papadimitriou | dest='user',
|
215 | a682eaea | Kostas Papadimitriou | action="store",
|
216 | a682eaea | Kostas Papadimitriou | default=False,)
|
217 | a682eaea | Kostas Papadimitriou | ) |
218 | a682eaea | Kostas Papadimitriou | |
219 | a682eaea | Kostas Papadimitriou | def resolve_conflicts(self, options): |
220 | a682eaea | Kostas Papadimitriou | conflicting = map(options.get, ['migrate_users', |
221 | a682eaea | Kostas Papadimitriou | 'merge_user'])
|
222 | a682eaea | Kostas Papadimitriou | if len(filter(bool, conflicting)) > 1: |
223 | a682eaea | Kostas Papadimitriou | raise CommandError('You can use only one of --validate,' |
224 | ec0c9d21 | Kostas Papadimitriou | '--migrate-users')
|
225 | a682eaea | Kostas Papadimitriou | |
226 | a682eaea | Kostas Papadimitriou | def handle(self, *args, **options): |
227 | a682eaea | Kostas Papadimitriou | self.resolve_conflicts(options)
|
228 | a682eaea | Kostas Papadimitriou | self.strict = options.get('strict') |
229 | a682eaea | Kostas Papadimitriou | self.dry = options.get('dry') |
230 | a682eaea | Kostas Papadimitriou | |
231 | a682eaea | Kostas Papadimitriou | if options.get('validate') and not options.get('merge_user') and not \ |
232 | a682eaea | Kostas Papadimitriou | options.get('delete_user') and not options.get('user_entries'): |
233 | a682eaea | Kostas Papadimitriou | usernames = get_existing_users() |
234 | a682eaea | Kostas Papadimitriou | _validate_db_state(usernames) |
235 | a682eaea | Kostas Papadimitriou | |
236 | a682eaea | Kostas Papadimitriou | if options.get('migrate_users'): |
237 | a682eaea | Kostas Papadimitriou | migrate_users(usernames, dry=self.dry)
|
238 | a682eaea | Kostas Papadimitriou | |
239 | a682eaea | Kostas Papadimitriou | if options.get('merge_user'): |
240 | a682eaea | Kostas Papadimitriou | merge_user(options.get('merge_user'))
|
241 | a682eaea | Kostas Papadimitriou | print "Merge finished." |
242 | a682eaea | Kostas Papadimitriou | |
243 | a682eaea | Kostas Papadimitriou | if options.get('delete_user'): |
244 | a682eaea | Kostas Papadimitriou | entries = delete_user(options.get('delete_user'), only_stats=True) |
245 | a682eaea | Kostas Papadimitriou | if entries == -1: |
246 | a682eaea | Kostas Papadimitriou | return
|
247 | a682eaea | Kostas Papadimitriou | |
248 | a682eaea | Kostas Papadimitriou | confirm = raw_input("Type 'yes of course' if you are sure you want" |
249 | a682eaea | Kostas Papadimitriou | " to remove those entries: ")
|
250 | a682eaea | Kostas Papadimitriou | if not confirm == 'yes of course': |
251 | a682eaea | Kostas Papadimitriou | return
|
252 | a682eaea | Kostas Papadimitriou | else:
|
253 | a682eaea | Kostas Papadimitriou | delete_user(options.get('delete_user'), only_stats=False, |
254 | a682eaea | Kostas Papadimitriou | dry=self.dry)
|
255 | a682eaea | Kostas Papadimitriou | |
256 | a682eaea | Kostas Papadimitriou | if options.get('user_entries'): |
257 | a682eaea | Kostas Papadimitriou | delete_user(options.get('user_entries')) |