root / Classes / OpenStackAccount.m @ e06c24cf
History | View | Annotate | Download (16.3 kB)
1 |
// |
---|---|
2 |
// OpenStackAccount.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 10/1/10. |
6 |
// The OpenStack project is provided under the Apache 2.0 license. |
7 |
// |
8 |
|
9 |
#import "OpenStackAccount.h" |
10 |
#import "Keychain.h" |
11 |
#import "Provider.h" |
12 |
#import "Archiver.h" |
13 |
#import "OpenStackRequest.h" |
14 |
#import "NSObject+Conveniences.h" |
15 |
#import "Server.h" |
16 |
#import "Image.h" |
17 |
#import "Flavor.h" |
18 |
#import "AccountManager.h" |
19 |
#import "LoadBalancer.h" |
20 |
#import "APICallback.h" |
21 |
|
22 |
|
23 |
static NSArray *accounts = nil; |
24 |
static NSMutableDictionary *timers = nil; |
25 |
|
26 |
@implementation OpenStackAccount |
27 |
|
28 |
@synthesize uuid, provider, username, projectId, images, flavors, servers, serversURL, filesURL, cdnURL, manager, rateLimits, |
29 |
lastUsedFlavorId, lastUsedImageId, |
30 |
containerCount, totalBytesUsed, quota, containers, hasBeenRefreshed, flaggedForDelete, |
31 |
loadBalancers, lbProtocols, serversByPublicIP, apiVersion, shared, sharingAccount, pithosLoginURLPrefix, pithosPublicLinkURLPrefix, hostURL; |
32 |
|
33 |
+ (void)initialize { |
34 |
accounts = [[Archiver retrieve:@"accounts"] retain]; |
35 |
if (accounts == nil) { |
36 |
accounts = [[NSArray alloc] init]; |
37 |
[Archiver persist:accounts key:@"accounts"]; |
38 |
} |
39 |
timers = [[NSMutableDictionary alloc] initWithCapacity:[accounts count]]; |
40 |
} |
41 |
|
42 |
- (NSString *)serversKey { |
43 |
return [NSString stringWithFormat:@"%@-servers", self.uuid]; |
44 |
} |
45 |
|
46 |
- (NSMutableDictionary *)servers { |
47 |
if (!serversUnarchived) { |
48 |
servers = [[Archiver retrieve:[self serversKey]] retain]; |
49 |
serversUnarchived = YES; |
50 |
} |
51 |
return servers; |
52 |
} |
53 |
|
54 |
// no sense wasting space by storing sorted arrays, so override the getters to be sure |
55 |
// we at least return something |
56 |
|
57 |
- (NSArray *)sortedImages { |
58 |
return [[self.images allValues] sortedArrayUsingSelector:@selector(compare:)]; |
59 |
} |
60 |
|
61 |
- (NSArray *)sortedFlavors { |
62 |
return [[self.flavors allValues] sortedArrayUsingSelector:@selector(compare:)]; |
63 |
} |
64 |
|
65 |
- (NSArray *)sortedServers { |
66 |
return [[self.servers allValues] sortedArrayUsingSelector:@selector(compare:)]; |
67 |
} |
68 |
|
69 |
- (NSArray *)sortedRateLimits { |
70 |
return [self.rateLimits sortedArrayUsingSelector:@selector(compare:)]; |
71 |
} |
72 |
|
73 |
- (NSArray *)sortedContainers { |
74 |
return [[self.containers allValues] sortedArrayUsingSelector:@selector(compare:)]; |
75 |
} |
76 |
|
77 |
- (NSArray *)pithosSortedContainers { |
78 |
NSMutableArray *pithosSortedContainers = [NSMutableArray array]; |
79 |
if ([self.containers objectForKey:@"pithos"]) |
80 |
[pithosSortedContainers addObject:[self.containers objectForKey:@"pithos"]]; |
81 |
if ([self.containers objectForKey:@"trash"]) |
82 |
[pithosSortedContainers addObject:[self.containers objectForKey:@"trash"]]; |
83 |
|
84 |
NSMutableDictionary *otherContainers = [NSMutableDictionary dictionaryWithDictionary:self.containers]; |
85 |
[otherContainers removeObjectForKey:@"pithos"]; |
86 |
[otherContainers removeObjectForKey:@"trash"]; |
87 |
[pithosSortedContainers addObjectsFromArray:[[otherContainers allValues] sortedArrayUsingSelector:@selector(compare:)]]; |
88 |
return pithosSortedContainers; |
89 |
} |
90 |
|
91 |
- (NSArray *)sortedLoadBalancers { |
92 |
NSMutableArray *allLoadBalancers = [[NSMutableArray alloc] init]; |
93 |
for (NSString *endpoint in self.loadBalancers) { |
94 |
NSDictionary *lbs = [self.loadBalancers objectForKey:endpoint]; |
95 |
if ([lbs isKindOfClass:[LoadBalancer class]]) { |
96 |
NSLog(@"load balancers not persisted properly. replacing."); |
97 |
self.loadBalancers = nil; |
98 |
lbs = nil; |
99 |
[self persist]; |
100 |
} else { |
101 |
for (NSString *key in lbs) { |
102 |
LoadBalancer *lb = [lbs objectForKey:key]; |
103 |
if (![lb.status isEqualToString:@"PENDING_DELETE"]) { |
104 |
[allLoadBalancers addObject:lb]; |
105 |
} |
106 |
} |
107 |
} |
108 |
|
109 |
} |
110 |
NSArray *sortedArray = [NSArray arrayWithArray:[allLoadBalancers sortedArrayUsingSelector:@selector(compare:)]]; |
111 |
[allLoadBalancers release]; |
112 |
return sortedArray; |
113 |
} |
114 |
|
115 |
- (void)setServers:(NSMutableDictionary *)s { |
116 |
if ([servers isEqual:s]) { |
117 |
return; |
118 |
} else { |
119 |
[servers release]; |
120 |
servers = [s retain]; |
121 |
|
122 |
self.serversByPublicIP = [NSMutableDictionary dictionaryWithCapacity:[self.servers count]]; |
123 |
for (Server *server in [self.servers allValues]) { |
124 |
NSArray *ips = [server.addresses objectForKey:@"public"]; |
125 |
for (NSString *ip in ips) { |
126 |
[self.serversByPublicIP setObject:server forKey:ip]; |
127 |
} |
128 |
} |
129 |
|
130 |
[Archiver persist:servers key:[self serversKey]]; |
131 |
} |
132 |
} |
133 |
|
134 |
#pragma mark - |
135 |
#pragma mark Collections API Management |
136 |
|
137 |
- (void)observeGetLimits:(OpenStackRequest *)request { |
138 |
self.rateLimits = [request rateLimits]; |
139 |
[[NSNotificationCenter defaultCenter] removeObserver:getLimitsObserver]; |
140 |
} |
141 |
|
142 |
- (void)refreshCollections { |
143 |
if (!self.manager) { |
144 |
self.manager = [[[AccountManager alloc] init] autorelease]; |
145 |
self.manager.account = self; |
146 |
} |
147 |
|
148 |
[[self.manager authenticate] success:^(OpenStackRequest *request){ |
149 |
|
150 |
if (self.serversURL != nil) { |
151 |
[self.manager getImages]; |
152 |
[self.manager getFlavors]; |
153 |
[self.manager getLimits]; |
154 |
[self.manager getServers]; |
155 |
|
156 |
|
157 |
// handle success; don't worry about failure |
158 |
getLimitsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getLimitsSucceeded" object:self |
159 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
160 |
{ |
161 |
[self performSelectorOnMainThread:@selector(observeGetLimits:) withObject:[notification.userInfo objectForKey:@"request"] waitUntilDone:NO]; |
162 |
}]; |
163 |
} |
164 |
} failure:^(OpenStackRequest *request){ |
165 |
}]; |
166 |
|
167 |
|
168 |
} |
169 |
|
170 |
#pragma mark - |
171 |
#pragma mark Serialization |
172 |
|
173 |
- (void)loadTimer { |
174 |
if (![timers objectForKey:uuid]) { |
175 |
// if (!hasBeenRefreshed) { |
176 |
// [self refreshCollections]; |
177 |
// } |
178 |
/* |
179 |
[NSTimer scheduledTimerWithTimeInterval:4.0 target:self.manager selector:@selector(getServers) userInfo:nil repeats:NO]; |
180 |
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:kOpenStackPollingFrequency * 20 target:self selector:@selector(refreshCollections) userInfo:nil repeats:YES]; |
181 |
[timers setObject:timer forKey:uuid]; |
182 |
*/ |
183 |
} |
184 |
} |
185 |
|
186 |
- (id)copyWithZone:(NSZone *)zone { |
187 |
OpenStackAccount *copy = [[OpenStackAccount allocWithZone:zone] init]; |
188 |
copy.uuid = self.uuid; |
189 |
copy.provider = self.provider; |
190 |
copy.username = self.username; |
191 |
copy.apiKey = self.apiKey; |
192 |
copy.authToken = self.authToken; |
193 |
|
194 |
copy.images = [[[NSMutableDictionary alloc] initWithDictionary:self.images] autorelease]; |
195 |
copy.flavors = [[[NSDictionary alloc] initWithDictionary:self.flavors] autorelease]; |
196 |
/* |
197 |
copy.servers = [[NSMutableDictionary alloc] initWithDictionary:self.servers]; |
198 |
copy.containers = self.containers; |
199 |
copy.loadBalancers = self.loadBalancers; |
200 |
copy.serversByPublicIP = self.serversByPublicIP; |
201 |
*/ |
202 |
|
203 |
copy.serversURL = self.serversURL; |
204 |
copy.filesURL = self.filesURL; |
205 |
copy.hostURL = self.hostURL; |
206 |
copy.cdnURL = self.cdnURL; |
207 |
copy.rateLimits = [[[NSArray alloc] initWithArray:self.rateLimits] autorelease]; |
208 |
copy.lastUsedFlavorId = self.lastUsedFlavorId; |
209 |
copy.lastUsedImageId = self.lastUsedImageId; |
210 |
copy.containerCount = self.containerCount; |
211 |
copy.totalBytesUsed = self.totalBytesUsed; |
212 |
copy.apiVersion = self.apiVersion; |
213 |
manager = [[AccountManager alloc] init]; |
214 |
manager.account = copy; |
215 |
return copy; |
216 |
} |
217 |
|
218 |
- (void)encodeWithCoder: (NSCoder *)coder { |
219 |
[coder encodeObject:uuid forKey:@"uuid"]; |
220 |
[coder encodeObject:provider forKey:@"provider"]; |
221 |
[coder encodeObject:username forKey:@"username"]; |
222 |
|
223 |
[coder encodeObject:serversURL forKey:@"serversURL"]; |
224 |
[coder encodeObject:filesURL forKey:@"filesURL"]; |
225 |
[coder encodeObject:hostURL forKey:@"hostURL"]; |
226 |
[coder encodeObject:cdnURL forKey:@"cdnURL"]; |
227 |
[coder encodeObject:rateLimits forKey:@"rateLimits"]; |
228 |
[coder encodeObject:lastUsedFlavorId forKey:@"lastUsedFlavorId"]; |
229 |
[coder encodeObject:lastUsedImageId forKey:@"lastUsedImageId"]; |
230 |
[coder encodeInt:containerCount forKey:@"containerCount"]; |
231 |
[coder encodeInt:totalBytesUsed forKey:@"totalBytesUsed"]; |
232 |
|
233 |
[coder encodeObject:images forKey:@"images"]; |
234 |
[coder encodeObject:flavors forKey:@"flavors"]; |
235 |
/* |
236 |
[coder encodeObject:servers forKey:@"servers"]; |
237 |
[coder encodeObject:serversByPublicIP forKey:@"serversByPublicIP"]; |
238 |
[coder encodeObject:containers forKey:@"containers"]; |
239 |
[coder encodeObject:loadBalancers forKey:@"loadBalancers"]; |
240 |
*/ |
241 |
|
242 |
[coder encodeObject:apiVersion forKey:@"apiVersion"]; |
243 |
} |
244 |
|
245 |
- (id)decode:(NSCoder *)coder key:(NSString *)key { |
246 |
@try { |
247 |
return [[coder decodeObjectForKey:key] retain]; |
248 |
} |
249 |
@catch (NSException *exception) { |
250 |
return nil; |
251 |
} |
252 |
} |
253 |
|
254 |
- (id)initWithCoder:(NSCoder *)coder { |
255 |
if ((self = [super init])) { |
256 |
uuid = [self decode:coder key:@"uuid"]; |
257 |
provider = [self decode:coder key:@"provider"]; |
258 |
username = [self decode:coder key:@"username"]; |
259 |
|
260 |
images = [self decode:coder key:@"images"]; |
261 |
|
262 |
// make sure images stored aren't corrupt |
263 |
if ([images count] > 0) { |
264 |
for (id obj in [images allValues]) { |
265 |
if (![obj isKindOfClass:[Image class]]) { |
266 |
images = nil; |
267 |
break; |
268 |
} |
269 |
} |
270 |
} |
271 |
|
272 |
flavors = [self decode:coder key:@"flavors"]; |
273 |
servers = [self decode:coder key:@"servers"]; |
274 |
serversByPublicIP = [self decode:coder key:@"serversByPublicIP"]; |
275 |
|
276 |
serversURL = [self decode:coder key:@"serversURL"]; |
277 |
filesURL = [self decode:coder key:@"filesURL"]; |
278 |
hostURL = [self decode:coder key:@"hostURL"]; |
279 |
cdnURL = [self decode:coder key:@"cdnURL"]; |
280 |
rateLimits = [self decode:coder key:@"rateLimits"]; |
281 |
|
282 |
[self loadTimer]; |
283 |
|
284 |
lastUsedFlavorId = [self decode:coder key:@"lastUserFlavorId"]; |
285 |
lastUsedImageId = [self decode:coder key:@"lastUsedImageId"]; |
286 |
|
287 |
containerCount = [coder decodeIntForKey:@"containerCount"]; |
288 |
//totalBytesUsed = [coder decodeIntForKey:@"totalBytesUsed"]; |
289 |
|
290 |
containers = [self decode:coder key:@"containers"]; |
291 |
loadBalancers = [self decode:coder key:@"loadBalancers"]; |
292 |
|
293 |
apiVersion = [self decode:coder key:@"apiVersion"]; |
294 |
if (!apiVersion) { |
295 |
NSString *component = [[[provider.authEndpointURL description] componentsSeparatedByString:@"/"] lastObject]; |
296 |
if ([component isEqualToString:@"v1.1"]) { |
297 |
self.apiVersion = @"1.1"; |
298 |
} else { |
299 |
self.apiVersion = component; |
300 |
} |
301 |
} |
302 |
|
303 |
manager = [[AccountManager alloc] init]; |
304 |
manager.account = self; |
305 |
} |
306 |
return self; |
307 |
} |
308 |
|
309 |
- (id)init { |
310 |
if ((self = [super init])) { |
311 |
uuid = [[NSString alloc] initWithString:[OpenStackAccount stringWithUUID]]; |
312 |
|
313 |
[self loadTimer]; |
314 |
|
315 |
manager = [[AccountManager alloc] init]; |
316 |
manager.account = self; |
317 |
} |
318 |
return self; |
319 |
} |
320 |
|
321 |
+ (NSArray *)accounts { |
322 |
if (accounts == nil) { |
323 |
accounts = [[Archiver retrieve:@"accounts"] retain]; |
324 |
} |
325 |
return accounts; |
326 |
} |
327 |
|
328 |
+ (void)persist:(NSArray *)accountArray { |
329 |
accounts = [[NSArray arrayWithArray:accountArray] retain]; |
330 |
[Archiver persist:accounts key:@"accounts"]; |
331 |
[accounts release]; |
332 |
accounts = nil; |
333 |
} |
334 |
|
335 |
- (void)persist { |
336 |
//return NO; |
337 |
//* |
338 |
if (!flaggedForDelete) { |
339 |
NSMutableArray *accountArr = [NSMutableArray arrayWithArray:[OpenStackAccount accounts]]; |
340 |
|
341 |
BOOL accountPresent = NO; |
342 |
for (int i = 0; i < [accountArr count]; i++) { |
343 |
OpenStackAccount *account = [accountArr objectAtIndex:i]; |
344 |
|
345 |
if ([account.uuid isEqualToString:self.uuid]) { |
346 |
accountPresent = YES; |
347 |
[accountArr replaceObjectAtIndex:i withObject:self]; |
348 |
break; |
349 |
} |
350 |
} |
351 |
|
352 |
if (!accountPresent) { |
353 |
[accountArr insertObject:self atIndex:0]; |
354 |
} |
355 |
|
356 |
[Archiver persist:[NSArray arrayWithArray:accountArr] key:@"accounts"]; |
357 |
[accounts release]; |
358 |
accounts = nil; |
359 |
//return result; |
360 |
} //*/ |
361 |
} |
362 |
|
363 |
// the API key and auth token are stored in the Keychain, so overriding the |
364 |
// getter and setter to abstract the encryption away and make it easy to use |
365 |
|
366 |
- (NSString *)apiKeyKeychainKey { |
367 |
return [NSString stringWithFormat:@"%@-apiKey", self.uuid]; |
368 |
} |
369 |
|
370 |
- (NSString *)apiKey { |
371 |
return [Keychain getStringForKey:[self apiKeyKeychainKey]]; |
372 |
} |
373 |
|
374 |
- (void)setApiKey:(NSString *)newAPIKey { |
375 |
[Keychain setString:newAPIKey forKey:[self apiKeyKeychainKey]]; |
376 |
} |
377 |
|
378 |
- (NSString *)authTokenKeychainKey { |
379 |
return [NSString stringWithFormat:@"%@-authToken", self.uuid]; |
380 |
} |
381 |
|
382 |
- (NSString *)authToken { |
383 |
NSString *authToken = [Keychain getStringForKey:[self authTokenKeychainKey]]; |
384 |
if (!authToken) { |
385 |
authToken = @""; |
386 |
} |
387 |
return authToken; |
388 |
} |
389 |
|
390 |
- (void)setAuthToken:(NSString *)newAuthToken { |
391 |
[Keychain setString:newAuthToken forKey:[self authTokenKeychainKey]]; |
392 |
} |
393 |
|
394 |
- (NSString *)accountNumber { |
395 |
NSString *accountNumber = nil; |
396 |
if (self.serversURL) { |
397 |
NSString *surl = [self.serversURL description]; |
398 |
accountNumber = [[surl componentsSeparatedByString:@"/"] lastObject]; |
399 |
} |
400 |
return accountNumber; |
401 |
} |
402 |
|
403 |
- (NSString *)loadBalancerEndpointForRegion:(NSString *)region { |
404 |
NSString *accountNumber = [self accountNumber]; |
405 |
if ([region isEqualToString:@"DFW"]) { |
406 |
return [NSString stringWithFormat:@"https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber]; |
407 |
} else if ([region isEqualToString:@"ORD"]) { |
408 |
return [NSString stringWithFormat:@"https://ord.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber]; |
409 |
} else { |
410 |
return @""; |
411 |
} |
412 |
} |
413 |
|
414 |
- (NSString *)loadBalancerRegionForEndpoint:(NSString *)endpoint { |
415 |
NSString *component = [[endpoint componentsSeparatedByString:@"."] objectAtIndex:0]; |
416 |
component = [[component componentsSeparatedByString:@"//"] objectAtIndex:1]; |
417 |
return [component uppercaseString]; |
418 |
} |
419 |
|
420 |
- (NSArray *)loadBalancerURLs { |
421 |
NSString *accountNumber = [self accountNumber]; |
422 |
|
423 |
if (accountNumber && [self.provider isRackspace]) { |
424 |
if ([self.provider isRackspaceUS]) { |
425 |
NSString *ord = [NSString stringWithFormat:@"https://ord.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber]; |
426 |
NSString *dfw = [NSString stringWithFormat:@"https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber]; |
427 |
return [NSArray arrayWithObjects:ord, dfw, nil]; |
428 |
} else if ([self.provider isRackspaceUK]) { |
429 |
NSString *lon = [NSString stringWithFormat:@"https://lon.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber]; |
430 |
return [NSArray arrayWithObjects:lon, nil]; |
431 |
} else { |
432 |
return nil; |
433 |
} |
434 |
} else { |
435 |
return nil; |
436 |
} |
437 |
} |
438 |
|
439 |
- (NSArray *)loadBalancerRegions { |
440 |
NSString *accountNumber = [self accountNumber]; |
441 |
|
442 |
if (accountNumber && [self.provider isRackspace]) { |
443 |
if ([self.provider isRackspaceUS]) { |
444 |
return [NSArray arrayWithObjects:@"ORD", @"DFW", nil]; |
445 |
} else if ([self.provider isRackspaceUK]) { |
446 |
return [NSArray arrayWithObjects:@"LON", nil]; |
447 |
} else { |
448 |
return [NSArray array]; |
449 |
} |
450 |
} else { |
451 |
return [NSArray array]; |
452 |
} |
453 |
} |
454 |
|
455 |
#pragma mark - Memory Management |
456 |
|
457 |
- (void)dealloc { |
458 |
NSTimer *timer = [timers objectForKey:uuid]; |
459 |
[timer invalidate]; |
460 |
[timers removeObjectForKey:uuid]; |
461 |
|
462 |
[uuid release]; |
463 |
[manager release]; |
464 |
[provider release]; |
465 |
[username release]; |
466 |
[projectId release]; |
467 |
[flavors release]; |
468 |
[images release]; |
469 |
[servers release]; |
470 |
[hostURL release]; |
471 |
[serversURL release]; |
472 |
[filesURL release]; |
473 |
[cdnURL release]; |
474 |
[rateLimits release]; |
475 |
[containers release]; |
476 |
[loadBalancers release]; |
477 |
[lbProtocols release]; |
478 |
[serversByPublicIP release]; |
479 |
[lastUsedFlavorId release]; |
480 |
[lastUsedImageId release]; |
481 |
[apiVersion release]; |
482 |
[sharingAccount release]; |
483 |
[pithosLoginURLPrefix release]; |
484 |
[pithosPublicLinkURLPrefix release]; |
485 |
|
486 |
[super dealloc]; |
487 |
} |
488 |
|
489 |
@end |