Statistics
| Branch: | Tag: | Revision:

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