Expanded open file functionality to use available apps.
[pithos-ios] / Classes / OpenStackAccount.m
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;
32
33 + (void)initialize {
34     accounts = [Archiver retrieve:@"accounts"];
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]];
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 *)sortedLoadBalancers {
78     NSMutableArray *allLoadBalancers = [[NSMutableArray alloc] init];
79     for (NSString *endpoint in self.loadBalancers) {
80         NSDictionary *lbs = [self.loadBalancers objectForKey:endpoint];
81         if ([lbs isKindOfClass:[LoadBalancer class]]) {
82             NSLog(@"load balancers not persisted properly.  replacing.");
83             self.loadBalancers = nil;
84             lbs = nil;
85             [self persist];
86         } else {
87             for (NSString *key in lbs) {
88                 LoadBalancer *lb = [lbs objectForKey:key];
89                 if (![lb.status isEqualToString:@"PENDING_DELETE"]) {
90                     [allLoadBalancers addObject:lb];
91                 }
92             }
93         }
94         
95     }
96     NSArray *sortedArray = [NSArray arrayWithArray:[allLoadBalancers sortedArrayUsingSelector:@selector(compare:)]];
97     [allLoadBalancers release];
98     return sortedArray;
99 }
100
101 - (void)setServers:(NSMutableDictionary *)s {
102     if ([servers isEqual:s]) {
103         return;
104     } else {
105         [servers release];
106         servers = [s retain];
107         
108         self.serversByPublicIP = [NSMutableDictionary dictionaryWithCapacity:[self.servers count]];
109         for (Server *server in [self.servers allValues]) {
110             NSArray *ips = [server.addresses objectForKey:@"public"];
111             for (NSString *ip in ips) {
112                 [self.serversByPublicIP setObject:server forKey:ip];
113             }
114         }
115         
116         [Archiver persist:servers key:[self serversKey]];
117     }
118 }
119
120 #pragma mark -
121 #pragma mark Collections API Management
122
123 - (void)observeGetLimits:(OpenStackRequest *)request {
124     self.rateLimits = [request rateLimits];
125     [[NSNotificationCenter defaultCenter] removeObserver:getLimitsObserver];
126 }
127
128 - (void)refreshCollections {
129     if (!self.manager) {
130         self.manager = [[AccountManager alloc] init];
131         self.manager.account = self;
132     }
133
134     [[self.manager authenticate] success:^(OpenStackRequest *request){
135         
136         if (self.serversURL != nil) {
137             [self.manager getImages];
138             [self.manager getFlavors];
139             [self.manager getLimits];        
140             [self.manager getServers];
141         
142         // handle success; don't worry about failure
143         getLimitsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getLimitsSucceeded" object:self
144                                                                                queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) 
145                              {
146                                  [self performSelectorOnMainThread:@selector(observeGetLimits:) withObject:[notification.userInfo objectForKey:@"request"] waitUntilDone:NO];
147                              }];
148         }
149     } failure:^(OpenStackRequest *request){
150     }];
151 }
152
153 #pragma mark -
154 #pragma mark Serialization
155
156 - (void)loadTimer {    
157     if (![timers objectForKey:uuid]) {
158 //        if (!hasBeenRefreshed) {
159 //            [self refreshCollections];
160 //        }
161         /*
162         [NSTimer scheduledTimerWithTimeInterval:4.0 target:self.manager selector:@selector(getServers) userInfo:nil repeats:NO];
163         NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:kOpenStackPollingFrequency * 20 target:self selector:@selector(refreshCollections) userInfo:nil repeats:YES];
164         [timers setObject:timer forKey:uuid];
165          */
166     }
167 }
168
169 - (id)copyWithZone:(NSZone *)zone {
170     OpenStackAccount *copy = [[OpenStackAccount allocWithZone:zone] init];
171     copy.uuid = self.uuid;
172     copy.provider = self.provider;
173     copy.username = self.username;
174     copy.apiKey = self.apiKey;
175     copy.authToken = self.authToken;
176     
177     copy.images = [[NSMutableDictionary alloc] initWithDictionary:self.images];
178     copy.flavors = [[NSDictionary alloc] initWithDictionary:self.flavors];
179     /*
180     copy.servers = [[NSMutableDictionary alloc] initWithDictionary:self.servers];
181     copy.containers = self.containers;
182     copy.loadBalancers = self.loadBalancers;
183     copy.serversByPublicIP = self.serversByPublicIP;
184     */
185     
186     copy.serversURL = self.serversURL;
187     copy.filesURL = self.filesURL;
188     copy.cdnURL = self.cdnURL;
189     copy.rateLimits = [[NSArray alloc] initWithArray:self.rateLimits];
190     copy.lastUsedFlavorId = self.lastUsedFlavorId;
191     copy.lastUsedImageId = self.lastUsedImageId;
192     copy.containerCount = self.containerCount;
193     copy.totalBytesUsed = self.totalBytesUsed;
194     copy.apiVersion = self.apiVersion;
195     manager = [[AccountManager alloc] init];
196     manager.account = copy;
197     return copy;
198 }
199
200 - (void)encodeWithCoder: (NSCoder *)coder {
201     [coder encodeObject:uuid forKey:@"uuid"];
202     [coder encodeObject:provider forKey:@"provider"];
203     [coder encodeObject:username forKey:@"username"];
204     
205     [coder encodeObject:serversURL forKey:@"serversURL"];
206     [coder encodeObject:filesURL forKey:@"filesURL"];
207     [coder encodeObject:cdnURL forKey:@"cdnURL"];
208     [coder encodeObject:rateLimits forKey:@"rateLimits"];
209     [coder encodeObject:lastUsedFlavorId forKey:@"lastUsedFlavorId"];
210     [coder encodeObject:lastUsedImageId forKey:@"lastUsedImageId"];
211     [coder encodeInt:containerCount forKey:@"containerCount"];
212     [coder encodeInt:totalBytesUsed forKey:@"totalBytesUsed"];
213     
214     [coder encodeObject:images forKey:@"images"];
215     [coder encodeObject:flavors forKey:@"flavors"];
216     /*
217     [coder encodeObject:servers forKey:@"servers"];
218     [coder encodeObject:serversByPublicIP forKey:@"serversByPublicIP"];
219     [coder encodeObject:containers forKey:@"containers"];
220     [coder encodeObject:loadBalancers forKey:@"loadBalancers"];
221     */
222     
223     [coder encodeObject:apiVersion forKey:@"apiVersion"];
224 }
225
226 - (id)decode:(NSCoder *)coder key:(NSString *)key {    
227     @try {
228         return [[coder decodeObjectForKey:key] retain];
229     }
230     @catch (NSException *exception) {
231         return nil;
232     }
233 }
234
235 - (id)initWithCoder:(NSCoder *)coder {
236     if ((self = [super init])) {
237         uuid = [self decode:coder key:@"uuid"];
238         provider = [self decode:coder key:@"provider"];
239         username = [self decode:coder key:@"username"];
240
241         images = [self decode:coder key:@"images"];
242         
243         // make sure images stored aren't corrupt
244         if ([images count] > 0) {
245             for (id obj in [images allValues]) {
246                 if (![obj isKindOfClass:[Image class]]) {
247                     images = nil;
248                     break;
249                 }
250             }
251         }        
252         
253         flavors = [self decode:coder key:@"flavors"];
254         servers = [self decode:coder key:@"servers"];
255         serversByPublicIP = [self decode:coder key:@"serversByPublicIP"];
256         
257         serversURL = [self decode:coder key:@"serversURL"];
258         filesURL = [self decode:coder key:@"filesURL"];
259         cdnURL = [self decode:coder key:@"cdnURL"];
260         rateLimits = [self decode:coder key:@"rateLimits"];
261
262         [self loadTimer];
263         
264         lastUsedFlavorId = [self decode:coder key:@"lastUserFlavorId"];
265         lastUsedImageId = [self decode:coder key:@"lastUsedImageId"];
266         
267         containerCount = [coder decodeIntForKey:@"containerCount"];
268         //totalBytesUsed = [coder decodeIntForKey:@"totalBytesUsed"];
269         
270         containers = [self decode:coder key:@"containers"];
271         loadBalancers = [self decode:coder key:@"loadBalancers"];
272
273         apiVersion = [self decode:coder key:@"apiVersion"];
274         if (!apiVersion) {
275             NSString *component = [[[provider.authEndpointURL description] componentsSeparatedByString:@"/"] lastObject];
276             if ([component isEqualToString:@"v1.1"]) {
277                 self.apiVersion = @"1.1";
278             } else {
279                 self.apiVersion = component;
280             }
281         }
282         
283         manager = [[AccountManager alloc] init];
284         manager.account = self;
285     }
286     return self;
287 }
288
289 - (id)init {
290     if ((self = [super init])) {
291         uuid = [[NSString alloc] initWithString:[OpenStackAccount stringWithUUID]];
292
293         [self loadTimer];
294         
295         manager = [[AccountManager alloc] init];
296         manager.account = self;
297     }
298     return self;
299 }
300
301 + (NSArray *)accounts {
302     if (accounts == nil) {
303         accounts = [[Archiver retrieve:@"accounts"] retain];
304     }
305     return accounts;
306 }
307
308 + (void)persist:(NSArray *)accountArray {
309     accounts = [[NSArray arrayWithArray:accountArray] retain];
310     [Archiver persist:accounts key:@"accounts"];
311     [accounts release];
312     accounts = nil;
313 }
314
315 - (void)persist {
316     //return NO;
317     //*
318     if (!flaggedForDelete) {        
319         NSMutableArray *accountArr = [NSMutableArray arrayWithArray:[OpenStackAccount accounts]];
320         
321         BOOL accountPresent = NO;
322         for (int i = 0; i < [accountArr count]; i++) {
323             OpenStackAccount *account = [accountArr objectAtIndex:i];
324             
325             if ([account.uuid isEqualToString:self.uuid]) {
326                 accountPresent = YES;
327                 [accountArr replaceObjectAtIndex:i withObject:self];
328                 break;
329             }
330         }
331             
332         if (!accountPresent) {
333             [accountArr insertObject:self atIndex:0];
334         }
335         
336         [Archiver persist:[NSArray arrayWithArray:accountArr] key:@"accounts"];    
337         [accounts release];
338         accounts = nil;
339         //return result;
340     }     //*/
341 }
342
343 // the API key and auth token are stored in the Keychain, so overriding the 
344 // getter and setter to abstract the encryption away and make it easy to use
345
346 - (NSString *)apiKeyKeychainKey {
347     return [NSString stringWithFormat:@"%@-apiKey", self.uuid];
348 }
349
350 - (NSString *)apiKey {    
351     return [Keychain getStringForKey:[self apiKeyKeychainKey]];
352 }
353
354 - (void)setApiKey:(NSString *)newAPIKey {
355     [Keychain setString:newAPIKey forKey:[self apiKeyKeychainKey]];
356 }
357
358 - (NSString *)authTokenKeychainKey {
359     return [NSString stringWithFormat:@"%@-authToken", self.uuid];
360 }
361
362 - (NSString *)authToken {
363     NSString *authToken = [Keychain getStringForKey:[self authTokenKeychainKey]];
364     if (!authToken) {
365         authToken = @"";
366     }
367     return authToken;
368 }
369
370 - (void)setAuthToken:(NSString *)newAuthToken {
371     [Keychain setString:newAuthToken forKey:[self authTokenKeychainKey]];
372 }
373
374 - (NSString *)accountNumber {
375     NSString *accountNumber = nil;
376     if (self.serversURL) {
377         NSString *surl = [self.serversURL description];
378         accountNumber = [[surl componentsSeparatedByString:@"/"] lastObject];
379     }
380     return accountNumber;
381 }
382
383 - (NSString *)loadBalancerEndpointForRegion:(NSString *)region {
384     NSString *accountNumber = [self accountNumber];
385     if ([region isEqualToString:@"DFW"]) {
386         return [NSString stringWithFormat:@"https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber];
387     } else if ([region isEqualToString:@"ORD"]) {
388         return [NSString stringWithFormat:@"https://ord.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber];
389     } else {
390         return @"";
391     }
392 }
393
394 - (NSString *)loadBalancerRegionForEndpoint:(NSString *)endpoint {
395     NSString *component = [[endpoint componentsSeparatedByString:@"."] objectAtIndex:0];
396     component = [[component componentsSeparatedByString:@"//"] objectAtIndex:1];
397     return [component uppercaseString];
398 }
399
400 - (NSArray *)loadBalancerURLs {
401     NSString *accountNumber = [self accountNumber];
402     
403     if (accountNumber && [self.provider isRackspace]) {        
404         if ([self.provider isRackspaceUS]) {
405             NSString *ord = [NSString stringWithFormat:@"https://ord.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber];
406             NSString *dfw = [NSString stringWithFormat:@"https://dfw.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber];
407             return [NSArray arrayWithObjects:ord, dfw, nil];
408         } else if ([self.provider isRackspaceUK]) {
409             NSString *lon = [NSString stringWithFormat:@"https://lon.loadbalancers.api.rackspacecloud.com/v1.0/%@", accountNumber];
410             return [NSArray arrayWithObjects:lon, nil];
411         } else {
412             return nil;
413         }
414     } else {
415         return nil;
416     }
417 }
418
419 - (NSArray *)loadBalancerRegions {
420     NSString *accountNumber = [self accountNumber];
421     
422     if (accountNumber && [self.provider isRackspace]) {        
423         if ([self.provider isRackspaceUS]) {
424             return [NSArray arrayWithObjects:@"ORD", @"DFW", nil];
425         } else if ([self.provider isRackspaceUK]) {
426             return [NSArray arrayWithObjects:@"LON", nil];
427         } else {
428             return [NSArray array];
429         }
430     } else {
431         return [NSArray array];
432     }
433 }
434
435 #pragma mark - Memory Management
436
437 - (void)dealloc {
438     NSTimer *timer = [timers objectForKey:uuid];
439     [timer invalidate];
440     [timers removeObjectForKey:uuid];
441     
442     [uuid release];
443     [manager release];
444     [provider release];
445     [username release];
446     [projectId release];
447     [flavors release];
448     [images release];
449     [servers release];
450     [serversURL release];
451     [filesURL release];
452     [cdnURL release];
453     [rateLimits release];
454     [containers release];
455     [loadBalancers release];
456     [lbProtocols release];
457     [serversByPublicIP release];
458     [lastUsedFlavorId release];
459     [lastUsedImageId release];
460     [apiVersion release];
461     
462     [super dealloc];
463 }
464
465 @end