5 // Created by Mike Mayo on 10/1/10.
6 // The OpenStack project is provided under the Apache 2.0 license.
9 #import "OpenStackAccount.h"
13 #import "OpenStackRequest.h"
14 #import "NSObject+Conveniences.h"
18 #import "AccountManager.h"
19 #import "LoadBalancer.h"
20 #import "APICallback.h"
23 static NSArray *accounts = nil;
24 static NSMutableDictionary *timers = nil;
26 @implementation OpenStackAccount
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;
34 accounts = [Archiver retrieve:@"accounts"];
35 if (accounts == nil) {
36 accounts = [[NSArray alloc] init];
37 [Archiver persist:accounts key:@"accounts"];
39 timers = [[NSMutableDictionary alloc] initWithCapacity:[accounts count]];
42 - (NSString *)serversKey {
43 return [NSString stringWithFormat:@"%@-servers", self.uuid];
46 - (NSMutableDictionary *)servers {
47 if (!serversUnarchived) {
48 servers = [Archiver retrieve:[self serversKey]];
49 serversUnarchived = YES;
54 // no sense wasting space by storing sorted arrays, so override the getters to be sure
55 // we at least return something
57 - (NSArray *)sortedImages {
58 return [[self.images allValues] sortedArrayUsingSelector:@selector(compare:)];
61 - (NSArray *)sortedFlavors {
62 return [[self.flavors allValues] sortedArrayUsingSelector:@selector(compare:)];
65 - (NSArray *)sortedServers {
66 return [[self.servers allValues] sortedArrayUsingSelector:@selector(compare:)];
69 - (NSArray *)sortedRateLimits {
70 return [self.rateLimits sortedArrayUsingSelector:@selector(compare:)];
73 - (NSArray *)sortedContainers {
74 return [[self.containers allValues] sortedArrayUsingSelector:@selector(compare:)];
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;
87 for (NSString *key in lbs) {
88 LoadBalancer *lb = [lbs objectForKey:key];
89 if (![lb.status isEqualToString:@"PENDING_DELETE"]) {
90 [allLoadBalancers addObject:lb];
96 NSArray *sortedArray = [NSArray arrayWithArray:[allLoadBalancers sortedArrayUsingSelector:@selector(compare:)]];
97 [allLoadBalancers release];
101 - (void)setServers:(NSMutableDictionary *)s {
102 if ([servers isEqual:s]) {
106 servers = [s retain];
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];
116 [Archiver persist:servers key:[self serversKey]];
121 #pragma mark Collections API Management
123 - (void)observeGetLimits:(OpenStackRequest *)request {
124 self.rateLimits = [request rateLimits];
125 [[NSNotificationCenter defaultCenter] removeObserver:getLimitsObserver];
128 - (void)refreshCollections {
130 self.manager = [[AccountManager alloc] init];
131 self.manager.account = self;
134 [[self.manager authenticate] success:^(OpenStackRequest *request){
136 if (self.serversURL != nil) {
137 [self.manager getImages];
138 [self.manager getFlavors];
139 [self.manager getLimits];
140 [self.manager getServers];
142 // handle success; don't worry about failure
143 getLimitsObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getLimitsSucceeded" object:self
144 queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification)
146 [self performSelectorOnMainThread:@selector(observeGetLimits:) withObject:[notification.userInfo objectForKey:@"request"] waitUntilDone:NO];
149 } failure:^(OpenStackRequest *request){
154 #pragma mark Serialization
157 if (![timers objectForKey:uuid]) {
158 // if (!hasBeenRefreshed) {
159 // [self refreshCollections];
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];
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;
177 copy.images = [[NSMutableDictionary alloc] initWithDictionary:self.images];
178 copy.flavors = [[NSDictionary alloc] initWithDictionary:self.flavors];
180 copy.servers = [[NSMutableDictionary alloc] initWithDictionary:self.servers];
181 copy.containers = self.containers;
182 copy.loadBalancers = self.loadBalancers;
183 copy.serversByPublicIP = self.serversByPublicIP;
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;
200 - (void)encodeWithCoder: (NSCoder *)coder {
201 [coder encodeObject:uuid forKey:@"uuid"];
202 [coder encodeObject:provider forKey:@"provider"];
203 [coder encodeObject:username forKey:@"username"];
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"];
214 [coder encodeObject:images forKey:@"images"];
215 [coder encodeObject:flavors forKey:@"flavors"];
217 [coder encodeObject:servers forKey:@"servers"];
218 [coder encodeObject:serversByPublicIP forKey:@"serversByPublicIP"];
219 [coder encodeObject:containers forKey:@"containers"];
220 [coder encodeObject:loadBalancers forKey:@"loadBalancers"];
223 [coder encodeObject:apiVersion forKey:@"apiVersion"];
226 - (id)decode:(NSCoder *)coder key:(NSString *)key {
228 return [[coder decodeObjectForKey:key] retain];
230 @catch (NSException *exception) {
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"];
241 images = [self decode:coder key:@"images"];
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]]) {
253 flavors = [self decode:coder key:@"flavors"];
254 servers = [self decode:coder key:@"servers"];
255 serversByPublicIP = [self decode:coder key:@"serversByPublicIP"];
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"];
264 lastUsedFlavorId = [self decode:coder key:@"lastUserFlavorId"];
265 lastUsedImageId = [self decode:coder key:@"lastUsedImageId"];
267 containerCount = [coder decodeIntForKey:@"containerCount"];
268 //totalBytesUsed = [coder decodeIntForKey:@"totalBytesUsed"];
270 containers = [self decode:coder key:@"containers"];
271 loadBalancers = [self decode:coder key:@"loadBalancers"];
273 apiVersion = [self decode:coder key:@"apiVersion"];
275 NSString *component = [[[provider.authEndpointURL description] componentsSeparatedByString:@"/"] lastObject];
276 if ([component isEqualToString:@"v1.1"]) {
277 self.apiVersion = @"1.1";
279 self.apiVersion = component;
283 manager = [[AccountManager alloc] init];
284 manager.account = self;
290 if ((self = [super init])) {
291 uuid = [[NSString alloc] initWithString:[OpenStackAccount stringWithUUID]];
295 manager = [[AccountManager alloc] init];
296 manager.account = self;
301 + (NSArray *)accounts {
302 if (accounts == nil) {
303 accounts = [[Archiver retrieve:@"accounts"] retain];
308 + (void)persist:(NSArray *)accountArray {
309 accounts = [[NSArray arrayWithArray:accountArray] retain];
310 [Archiver persist:accounts key:@"accounts"];
318 if (!flaggedForDelete) {
319 NSMutableArray *accountArr = [NSMutableArray arrayWithArray:[OpenStackAccount accounts]];
321 BOOL accountPresent = NO;
322 for (int i = 0; i < [accountArr count]; i++) {
323 OpenStackAccount *account = [accountArr objectAtIndex:i];
325 if ([account.uuid isEqualToString:self.uuid]) {
326 accountPresent = YES;
327 [accountArr replaceObjectAtIndex:i withObject:self];
332 if (!accountPresent) {
333 [accountArr insertObject:self atIndex:0];
336 [Archiver persist:[NSArray arrayWithArray:accountArr] key:@"accounts"];
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
346 - (NSString *)apiKeyKeychainKey {
347 return [NSString stringWithFormat:@"%@-apiKey", self.uuid];
350 - (NSString *)apiKey {
351 return [Keychain getStringForKey:[self apiKeyKeychainKey]];
354 - (void)setApiKey:(NSString *)newAPIKey {
355 [Keychain setString:newAPIKey forKey:[self apiKeyKeychainKey]];
358 - (NSString *)authTokenKeychainKey {
359 return [NSString stringWithFormat:@"%@-authToken", self.uuid];
362 - (NSString *)authToken {
363 NSString *authToken = [Keychain getStringForKey:[self authTokenKeychainKey]];
370 - (void)setAuthToken:(NSString *)newAuthToken {
371 [Keychain setString:newAuthToken forKey:[self authTokenKeychainKey]];
374 - (NSString *)accountNumber {
375 NSString *accountNumber = nil;
376 if (self.serversURL) {
377 NSString *surl = [self.serversURL description];
378 accountNumber = [[surl componentsSeparatedByString:@"/"] lastObject];
380 return accountNumber;
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];
394 - (NSString *)loadBalancerRegionForEndpoint:(NSString *)endpoint {
395 NSString *component = [[endpoint componentsSeparatedByString:@"."] objectAtIndex:0];
396 component = [[component componentsSeparatedByString:@"//"] objectAtIndex:1];
397 return [component uppercaseString];
400 - (NSArray *)loadBalancerURLs {
401 NSString *accountNumber = [self accountNumber];
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];
419 - (NSArray *)loadBalancerRegions {
420 NSString *accountNumber = [self accountNumber];
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];
428 return [NSArray array];
431 return [NSArray array];
435 #pragma mark - Memory Management
438 NSTimer *timer = [timers objectForKey:uuid];
440 [timers removeObjectForKey:uuid];
450 [serversURL 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];