6f3f2c7f9a97cc1d2df61257aae5c74bcd755758
[pithos-ios] / Classes / AccountManager.m
1 //
2 //  AccountManager.m
3 //  OpenStack
4 //
5 //  Created by Mike Mayo on 10/21/10.
6 //  The OpenStack project is provided under the Apache 2.0 license.
7 //
8
9 #import "AccountManager.h"
10 #import "OpenStackAccount.h"
11 #import "OpenStackRequest.h"
12 #import "Server.h"
13 #import "Provider.h"
14 #import "Image.h"
15 #import "Container.h"
16 #import "Folder.h"
17 #import "StorageObject.h"
18 #import "GetServersRequest.h"
19 #import "GetContainersRequest.h"
20 #import "GetObjectsRequest.h"
21 #import "GetImagesRequest.h"
22 #import "ASINetworkQueue.h"
23 #import "UpdateCDNContainerRequest.h"
24 #import "GetFlavorsRequest.h"
25 #import "LoadBalancer.h"
26 #import "LoadBalancerRequest.h"
27 #import "APICallback.h"
28 #import "Analytics.h"
29 #import "JSON.h"
30
31
32
33
34 @implementation AccountManager
35
36 @synthesize account, queue;
37
38 #pragma mark - Callbacks
39
40 - (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success failure:(APIResponseBlock)failure {
41     APICallback *callback = [[[APICallback alloc] initWithAccount:self.account request:request] autorelease];
42     ((OpenStackRequest *)request).delegate = self;
43     ((OpenStackRequest *)request).callback = callback; 
44
45     [request setCompletionBlock:^{
46         if ([request isSuccess]) {
47             success(request);
48             [request notify];
49         } else {
50             failure(request);
51             [request notify];
52         }
53     }];
54     [request setFailedBlock:^{
55         failure(request);
56         [request notify];
57     }];
58     [request startAsynchronous];    
59     return callback;
60 }
61
62 - (APICallback *)callbackWithRequest:(id)request success:(APIResponseBlock)success {
63     return [self callbackWithRequest:request success:success failure:^(OpenStackRequest *request){}];
64 }
65
66 - (APICallback *)callbackWithRequest:(id)request {
67     return [self callbackWithRequest:request success:^(OpenStackRequest *request){} failure:^(OpenStackRequest *request){}];
68 }
69
70 #pragma mark - Notification
71
72 - (NSString *)notificationName:(NSString *)key identifier:(NSString *)identifier {
73     return [NSString stringWithFormat:@"%@-%@-%i", key, self.account.uuid, identifier];
74 }
75
76 - (void)requestFinished:(OpenStackRequest *)request {
77     NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
78     id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
79     
80     if ([request isSuccess]) {
81         NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Succeeded", notificationName] object:notificationObject];
82         [[NSNotificationCenter defaultCenter] postNotification:notification];
83     } else {
84         NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
85         [[NSNotificationCenter defaultCenter] postNotification:notification];
86     }
87 }
88
89 - (void)requestFailed:(OpenStackRequest *)request {
90     NSString *notificationName = [request.userInfo objectForKey:@"notificationName"];
91     id notificationObject = [request.userInfo objectForKey:@"notificationObject"];
92     NSNotification *notification = [NSNotification notificationWithName:[NSString stringWithFormat:@"%@Failed", notificationName] object:notificationObject userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
93     [[NSNotificationCenter defaultCenter] postNotification:notification];
94 }
95
96 - (void)sendRequest:(OpenStackRequest *)request name:(NSString *)name object:(id)notificationObject {
97     request.delegate = self;
98     request.userInfo = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:name, notificationObject, nil] forKeys:[NSArray arrayWithObjects:@"notificationName", @"notificationObject", nil]];
99     [request startAsynchronous];
100 }
101
102 - (void)notify:(NSString *)name request:(OpenStackRequest *)request {
103     NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
104     [[NSNotificationCenter defaultCenter] postNotification:notification];
105 }
106
107 - (void)notify:(NSString *)name request:(OpenStackRequest *)request object:(id)object {
108     NSNotification *notification = [NSNotification notificationWithName:name object:object userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
109     [[NSNotificationCenter defaultCenter] postNotification:notification];
110 }
111
112 #pragma mark - API Calls
113
114 #pragma mark Get Limits
115
116 - (void)getLimits {
117     __block OpenStackRequest *request = [OpenStackRequest getLimitsRequest:self.account];
118     request.delegate = self;
119     [request setCompletionBlock:^{
120         if ([request isSuccess] && [request limits]) {
121             self.account.rateLimits = [request rateLimits];
122             [self.account persist];
123             [self notify:@"getLimitsSucceeded" request:request object:self.account];
124         } else {
125             [self notify:@"getLimitsFailed" request:request object:self.account];
126         }
127     }];
128     [request setFailedBlock:^{
129         [self notify:@"getLimitsFailed" request:request object:self.account];
130     }];    
131     [request startAsynchronous];
132 }
133
134 #pragma mark Reboot Server
135
136 - (void)softRebootServer:(Server *)server {
137     TrackEvent(CATEGORY_SERVER, EVENT_REBOOTED);
138     
139     __block OpenStackRequest *request = [OpenStackRequest softRebootServerRequest:self.account server:server];
140     request.delegate = self;
141     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
142     [request setCompletionBlock:^{
143         [self notify:([request isSuccess] ? @"rebootSucceeded" : @"rebootFailed") request:request object:[request.userInfo objectForKey:@"server"]];
144     }];
145     [request setFailedBlock:^{
146         [self notify:@"rebootFailed" request:request object:[request.userInfo objectForKey:@"server"]];
147     }];
148     [request startAsynchronous];    
149 }
150
151 - (void)hardRebootServer:(Server *)server {
152     TrackEvent(CATEGORY_SERVER, EVENT_REBOOTED);
153     
154     __block OpenStackRequest *request = [OpenStackRequest hardRebootServerRequest:self.account server:server];
155     request.delegate = self;
156     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
157     [request setCompletionBlock:^{
158         [self notify:([request isSuccess] ? @"rebootSucceeded" : @"rebootFailed") request:request object:[request.userInfo objectForKey:@"server"]];
159     }];
160     [request setFailedBlock:^{
161         [self notify:@"rebootFailed" request:request object:[request.userInfo objectForKey:@"server"]];
162     }];
163     [request startAsynchronous];
164 }
165
166 #pragma mark Change Admin Password
167
168 - (void)changeAdminPassword:(Server *)server password:(NSString *)password {
169     TrackEvent(CATEGORY_SERVER, EVENT_PASSWORD_CHANGED);
170     
171     __block OpenStackRequest *request = [OpenStackRequest changeServerAdminPasswordRequest:self.account server:server password:password];
172     request.delegate = self;
173     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
174     [request setCompletionBlock:^{
175         [self notify:([request isSuccess] ? @"changeAdminPasswordSucceeded" : @"changeAdminPasswordFailed") request:request object:[request.userInfo objectForKey:@"server"]];
176     }];
177     [request setFailedBlock:^{
178         [self notify:@"changeAdminPasswordFailed" request:request object:[request.userInfo objectForKey:@"server"]];
179     }];
180     [request startAsynchronous];
181 }
182
183 #pragma mark Rename Server
184
185 - (APICallback *)renameServer:(Server *)server name:(NSString *)name {
186     TrackEvent(CATEGORY_SERVER, EVENT_RENAMED);
187     
188     __block OpenStackRequest *request = [OpenStackRequest renameServerRequest:self.account server:server name:name];
189     return [self callbackWithRequest:request];
190 }
191
192 #pragma mark Delete Server
193
194 - (void)deleteServer:(Server *)server {
195     TrackEvent(CATEGORY_SERVER, EVENT_DELETED);
196     
197     __block OpenStackRequest *request = [OpenStackRequest deleteServerRequest:self.account server:server];
198     request.delegate = self;
199     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
200     [request setCompletionBlock:^{
201         [self notify:([request isSuccess] ? @"deleteServerSucceeded" : @"deleteServerFailed") request:request object:[request.userInfo objectForKey:@"server"]];        
202         [self notify:([request isSuccess] ? @"deleteServerSucceeded" : @"deleteServerFailed") request:request object:self.account];
203     }];
204     [request setFailedBlock:^{
205         [self notify:@"deleteServerFailed" request:request object:[request.userInfo objectForKey:@"server"]];        
206     }];
207     if (![self queue]) {
208         [self setQueue:(ASINetworkQueue *)[[[NSOperationQueue alloc] init] autorelease]];
209     }
210     [queue addOperation:request];    
211 }
212
213 #pragma mark Create Server
214
215 - (void)createServer:(Server *)server {
216     TrackEvent(CATEGORY_SERVER, EVENT_CREATED);
217     
218     __block OpenStackRequest *request = [OpenStackRequest createServerRequest:self.account server:server];
219     request.delegate = self;
220     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
221     
222     // TODO: make these success block and failure block with "response" arg
223     [request setCompletionBlock:^{
224         NSLog(@"create server response: %i - %@", request.responseStatusCode, request.responseStatusMessage);
225         NSLog(@"body: %@", [request responseString]);            
226         [self notify:([request isSuccess] ? @"createServerSucceeded" : @"createServerFailed") request:request object:[request.userInfo objectForKey:@"server"]];        
227         [self notify:([request isSuccess] ? @"createServerSucceeded" : @"createServerFailed") request:request object:self.account];
228     }];    
229     [request setFailedBlock:^{
230         NSLog(@"create server response: %i - %@", request.responseStatusCode, request.responseStatusMessage);
231         NSLog(@"body: %@", [request responseString]);            
232         [self notify:@"createServerFailed" request:request object:[request.userInfo objectForKey:@"server"]];        
233     }];
234     if (![self queue]) {
235         [self setQueue:(ASINetworkQueue *)[[[NSOperationQueue alloc] init] autorelease]];
236     }
237     [queue addOperation:request];
238 }
239
240 #pragma mark Resize Server
241
242 - (void)resizeServer:(Server *)server flavor:(Flavor *)flavor {
243     TrackEvent(CATEGORY_SERVER, EVENT_RESIZED);
244     
245     __block OpenStackRequest *request = [OpenStackRequest resizeServerRequest:self.account server:server flavor:flavor];
246     request.delegate = self;
247     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
248     [request setCompletionBlock:^{
249         NSString *name = [request isSuccess] ? @"resizeServerSucceeded" : @"resizeServerFailed";
250         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:name identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
251         [[NSNotificationCenter defaultCenter] postNotification:notification];
252     }];
253     [request setFailedBlock:^{
254         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:@"resizeServerFailed" identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
255         [[NSNotificationCenter defaultCenter] postNotification:notification];
256     }];
257     [request startAsynchronous];
258 }
259
260 - (void)confirmResizeServer:(Server *)server {
261     __block OpenStackRequest *request = [OpenStackRequest confirmResizeServerRequest:self.account server:server];
262     request.delegate = self;
263     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
264     [request setCompletionBlock:^{
265         NSString *name = [request isSuccess] ? @"confirmResizeServerSucceeded" : @"confirmResizeServerFailed";
266         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:name identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
267         [[NSNotificationCenter defaultCenter] postNotification:notification];
268     }];
269     [request setFailedBlock:^{
270         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:@"confirmResizeServerFailed" identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
271         [[NSNotificationCenter defaultCenter] postNotification:notification];
272         
273     }];
274     [request startAsynchronous];
275 }
276
277 - (void)revertResizeServer:(Server *)server {
278     __block OpenStackRequest *request = [OpenStackRequest revertResizeServerRequest:self.account server:server];
279     request.delegate = self;
280     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
281     [request setCompletionBlock:^{
282         NSString *name = [request isSuccess] ? @"revertResizeServerSucceeded" : @"revertResizeServerFailed";
283         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:name identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
284         [[NSNotificationCenter defaultCenter] postNotification:notification];
285     }];
286     [request setFailedBlock:^{
287         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:@"revertResizeServerFailed" identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
288         [[NSNotificationCenter defaultCenter] postNotification:notification];
289     }];
290     [request startAsynchronous];
291 }
292
293 - (void)rebuildServer:(Server *)server image:(Image *)image {
294     TrackEvent(CATEGORY_SERVER, EVENT_REBUILT);
295     
296     __block OpenStackRequest *request = [OpenStackRequest rebuildServerRequest:self.account server:server image:image];
297     request.delegate = self;
298     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
299     [request setCompletionBlock:^{
300         NSString *name = [request isSuccess] ? @"rebuildServerSucceeded" : @"rebuildServerFailed";
301         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:name identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
302         [[NSNotificationCenter defaultCenter] postNotification:notification];
303     }];
304     [request setFailedBlock:^{
305         NSNotification *notification = [NSNotification notificationWithName:[self notificationName:@"rebuildServerFailed" identifier:server.identifier] object:nil userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
306         [[NSNotificationCenter defaultCenter] postNotification:notification];
307     }];
308     [request startAsynchronous];
309 }
310
311 - (void)getBackupSchedule:(Server *)server {
312     __block OpenStackRequest *request = [OpenStackRequest getBackupScheduleRequest:self.account server:server];
313     request.delegate = self;
314     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
315     [request setCompletionBlock:^{
316         server.backupSchedule = [request backupSchedule];
317         [self notify:([request isSuccess] ? @"getBackupScheduleSucceeded" : @"getBackupScheduleFailed") request:request object:[request.userInfo objectForKey:@"server"]];
318     }];
319     [request setFailedBlock:^{
320         [self notify:@"getBackupScheduleFailed" request:request object:[request.userInfo objectForKey:@"server"]];        
321     }];
322     [request startAsynchronous];
323 }
324
325 - (void)updateBackupSchedule:(Server *)server {
326     TrackEvent(CATEGORY_SERVER, EVENT_BACKUP_SCHEDULE_CHANGED);
327     
328     __block OpenStackRequest *request = [OpenStackRequest updateBackupScheduleRequest:self.account server:server];
329     request.delegate = self;
330     request.userInfo = [NSDictionary dictionaryWithObject:server forKey:@"server"];
331     [request setCompletionBlock:^{
332         [self notify:([request isSuccess] ? @"updateBackupScheduleSucceeded" : @"updateBackupScheduleFailed") request:request object:[request.userInfo objectForKey:@"server"]];
333         [self notify:([request isSuccess] ? @"updateBackupScheduleSucceeded" : @"updateBackupScheduleFailed") request:request object:self.account];
334     }];
335     [request setFailedBlock:^{
336         [self notify:@"updateBackupScheduleFailed" request:request object:[request.userInfo objectForKey:@"server"]];        
337         [self notify:@"updateBackupScheduleFailed" request:request object:self.account];        
338     }];
339     [request startAsynchronous];
340 }
341
342 #pragma mark Get Image
343
344 - (void)getImage:(Server *)server {
345     __block OpenStackRequest *request = [OpenStackRequest getImageRequest:self.account imageId:server.imageId];
346     request.delegate = self;
347     request.userInfo = [NSDictionary dictionaryWithObject:server.imageId forKey:@"imageId"];
348     [request setCompletionBlock:^{
349         if ([request isSuccess]) {
350             Image *image = [request image];
351             if ([image isKindOfClass:[Image class]]) {
352                 image.canBeLaunched = NO;
353                 [self.account.images setObject:image forKey:image.identifier];        
354                 [self.account persist];        
355             }
356             [self notify:@"getImageSucceeded" request:request];
357         } else {
358             [self notify:@"getImageFailed" request:request object:[request.userInfo objectForKey:@"imageId"]];
359         }
360     }];
361     [request setFailedBlock:^{
362         [self notify:@"getImageFailed" request:request object:[request.userInfo objectForKey:@"imageId"]];
363     }];
364     [request startAsynchronous];
365 }
366
367 #pragma mark Get Servers
368
369 - (void)getServers {
370     if (![self queue]) {
371         [self setQueue:[[[ASINetworkQueue alloc] init] autorelease]];
372     }
373     GetServersRequest *request = [GetServersRequest request:self.account];
374     [queue addOperation:request];
375 }
376
377 - (APICallback *)getServersWithCallback {
378     __block OpenStackRequest *request = [OpenStackRequest serversRequest:self.account method:@"GET" path:@"/servers/detail"];
379     return [self callbackWithRequest:request];
380 }
381
382
383 #pragma mark Get Flavors
384
385 - (void)getFlavors {
386     if (![self queue]) {
387         [self setQueue:[[[ASINetworkQueue alloc] init] autorelease]];
388     }
389     GetFlavorsRequest *request = [GetFlavorsRequest request:self.account];
390     [queue addOperation:request];
391 }
392
393 #pragma mark Get Images
394
395 - (void)getImages {
396     if (![self queue]) {
397         [self setQueue:[[[ASINetworkQueue alloc] init] autorelease]];
398     }
399     GetImagesRequest *request = [GetImagesRequest request:self.account];
400     [queue addOperation:request];
401 }
402
403 #pragma mark -
404 #pragma mark Object Storage
405
406
407 - (APICallback *)getStorageAccountInfo {
408     __block OpenStackRequest *request = [OpenStackRequest getStorageAccountInfoRequest:self.account];
409     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {        
410         self.account.containerCount = [[[request responseHeaders] objectForKey:@"X-Account-Container-Count"] intValue];
411         NSString *numStr = [[request responseHeaders] objectForKey:@"X-Account-Bytes-Used"];
412         self.account.totalBytesUsed = strtoull([numStr UTF8String], NULL, 0);
413         [self.account persist];
414         self.account.containerCount = [self.account.containers count];        
415     }];
416 }
417
418 - (APICallback *)getSharingAccounts {
419     __block OpenStackRequest *request = [OpenStackRequest getSharingAccountsRequest:self.account];
420     return [self callbackWithRequest:request];        
421 }
422
423 - (APICallback *)getContainers {
424     __block OpenStackRequest *request = [OpenStackRequest filesRequest:self.account method:@"GET" path:@""];
425     return [self callbackWithRequest:request];    
426 }
427
428 - (APICallback *)createContainer:(Container *)container {
429     TrackEvent(CATEGORY_CONTAINERS, EVENT_CREATED);
430
431     __block OpenStackRequest *request = [OpenStackRequest createContainerRequest:self.account container:container];
432     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
433         
434         [self.account.containers setObject:container forKey:container.name];        
435         [self.account persist];
436         self.account.containerCount = [self.account.containers count];        
437     }];
438 }
439
440 - (APICallback *)deleteContainer:(Container *)container {
441     TrackEvent(CATEGORY_CONTAINERS, EVENT_DELETED);
442     __block OpenStackRequest *request = [OpenStackRequest deleteContainerRequest:self.account container:container];
443     return [self callbackWithRequest:request];
444     
445 }
446
447 - (void)getObjects:(Container *)container {
448     [self getObjects:container afterMarker:nil objectsBuffer:nil];
449 }
450
451 - (void)getObjects:(Container *)container
452        afterMarker:(NSString *)marker
453      objectsBuffer:(NSMutableDictionary *)objectsBuffer {
454     if (![self queue]) {
455         [self setQueue:(ASINetworkQueue *)[[[NSOperationQueue alloc] init] autorelease]];
456     }    
457     GetObjectsRequest *request = [GetObjectsRequest request:self.account container:container marker:marker objectsBuffer:objectsBuffer];
458     [queue addOperation:request];
459 }
460
461 - (void)updateCDNContainer:(Container *)container {
462     TrackEvent(CATEGORY_CONTAINERS, EVENT_UPDATED);
463     
464     if (![self queue]) {
465         [self setQueue:(ASINetworkQueue *)[[[NSOperationQueue alloc] init] autorelease]];
466     }
467     UpdateCDNContainerRequest *request = [UpdateCDNContainerRequest request:self.account container:container];
468     [queue addOperation:request];
469 }
470
471 - (void)getObjectsSucceeded:(OpenStackRequest *)request {
472     if ([request isSuccess]) {
473         Container *container = [request.userInfo objectForKey:@"container"];
474         NSMutableDictionary *objects = [request objects];
475         container.rootFolder = [Folder folder];
476         container.rootFolder.objects = objects;
477         [self.account persist];
478         
479         NSNotification *notification = [NSNotification notificationWithName:@"getObjectsSucceeded" object:self.account userInfo:[NSDictionary dictionaryWithObject:container forKey:@"container"]];
480         [[NSNotificationCenter defaultCenter] postNotification:notification];
481     } else {
482         NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
483         [[NSNotificationCenter defaultCenter] postNotification:notification];
484     }
485 }
486
487 - (void)getObjectsFailed:(OpenStackRequest *)request {
488     NSNotification *notification = [NSNotification notificationWithName:@"getObjectsFailed" object:self.account userInfo:[NSDictionary dictionaryWithObject:request forKey:@"request"]];
489     [[NSNotificationCenter defaultCenter] postNotification:notification];
490 }
491
492 - (APICallback *)getObjectInfo:(Container *)container object:(StorageObject *)object {    
493     __block OpenStackRequest *request = [OpenStackRequest getObjectInfoRequest:self.account container:container object:object];
494     return [self callbackWithRequest:request];
495 }
496
497 - (APICallback *)getObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
498     __block OpenStackRequest *request = [OpenStackRequest getObjectRequest:self.account container:container object:object];
499     request.delegate = self;
500     request.downloadProgressDelegate = downloadProgressDelegate;
501     request.showAccurateProgress = YES;    
502     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
503         
504         NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
505         NSString *documentsDirectory = [paths objectAtIndex:0];        
506         NSString *shortPath = [NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath];
507         NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
508         NSString *directoryPath = [filePath stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"/%@", object.name] withString:@""];
509         
510         NSFileManager *fileManager = [NSFileManager defaultManager];
511         if ([fileManager createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:nil]) {
512             
513             [[request responseData] writeToFile:filePath atomically:YES];
514             
515         }
516
517     }];
518 }
519
520 - (APICallback *)writeObject:(Container *)container object:(StorageObject *)object downloadProgressDelegate:(id)downloadProgressDelegate {
521     TrackEvent(CATEGORY_FILES, EVENT_CREATED);
522     
523     __block OpenStackRequest *request = [OpenStackRequest writeObjectRequest:self.account container:container object:object];
524     request.delegate = self;
525     request.uploadProgressDelegate = downloadProgressDelegate;
526     request.showAccurateProgress = YES;
527     
528     return [self callbackWithRequest:request];
529 }
530
531 - (APICallback *)deleteObject:(Container *)container object:(StorageObject *)object {
532     TrackEvent(CATEGORY_FILES, EVENT_DELETED);
533     
534     __block OpenStackRequest *request = [OpenStackRequest deleteObjectRequest:self.account container:container object:object];
535     return [self callbackWithRequest:request];
536 }
537
538 - (APICallback *)writeObjectMetadata:(Container *)container object:(StorageObject *)object {
539     __block OpenStackRequest *request = [OpenStackRequest writeObjectMetadataRequest:self.account container:container object:object];
540     return [self callbackWithRequest:request];
541 }
542
543 - (APICallback *)writeAccountMetadata:(NSDictionary *)accountInfo {
544     __block OpenStackRequest *request = [OpenStackRequest writeAccountMetadataRequest:self.account withAccountInfo:accountInfo];
545     return [self callbackWithRequest:request];
546 }
547
548 - (APICallback *)writeContainerPolicy:(Container *)container {
549     __block OpenStackRequest *request = [OpenStackRequest writeContainerPolicyRequest:self.account container:container];
550     return [self callbackWithRequest:request];
551 }
552
553 #pragma mark -
554 #pragma mark Load Balancing
555
556 - (APICallback *)getLoadBalancers:(NSString *)endpoint {
557     __block LoadBalancerRequest *request = [LoadBalancerRequest getLoadBalancersRequest:self.account endpoint:endpoint];
558     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
559         if (!self.account.loadBalancers) {
560             self.account.loadBalancers = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease];
561         }
562         
563         NSLog(@"%@", self.account.loadBalancers);
564         NSLog(@"%@", [(LoadBalancerRequest *)request loadBalancers:self.account]);
565         NSLog(@"%@", endpoint);
566         NSMutableDictionary *lbs = [(LoadBalancerRequest *)request loadBalancers:self.account];
567         
568         for (NSString *identifier in lbs) {
569             LoadBalancer *lb = [lbs objectForKey:identifier];
570             lb.region = [self.account loadBalancerRegionForEndpoint:endpoint];
571             NSLog(@"lb.region = %@", lb.region);
572         }
573         
574         [self.account.loadBalancers setObject:lbs forKey:endpoint];
575         [self.account persist];
576     }];
577 }
578
579 - (APICallback *)getLoadBalancerDetails:(LoadBalancer *)loadBalancer endpoint:(NSString *)endpoint {
580     __block LoadBalancerRequest *request = [LoadBalancerRequest getLoadBalancerDetailsRequest:self.account loadBalancer:loadBalancer endpoint:endpoint];
581     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
582
583         LoadBalancer *newLB = [(LoadBalancerRequest *)request loadBalancer:self.account];
584         loadBalancer.status = newLB.status;
585         loadBalancer.nodes = newLB.nodes;
586         loadBalancer.connectionLoggingEnabled = newLB.connectionLoggingEnabled;
587         
588 //        if (!self.account.loadBalancers) {
589 //            self.account.loadBalancers = [[NSMutableDictionary alloc] initWithCapacity:2];
590 //        }
591 //        
592 //        NSLog(@"%@", self.account.loadBalancers);
593 //        NSLog(@"%@", [(LoadBalancerRequest *)request loadBalancers]);
594 //        NSLog(@"%@", endpoint);
595 //        NSMutableDictionary *lbs = [(LoadBalancerRequest *)request loadBalancers];
596 //        
597 //        for (NSString *identifier in lbs) {
598 //            LoadBalancer *lb = [lbs objectForKey:identifier];
599 //            lb.region = [self.account loadBalancerRegionForEndpoint:endpoint];
600 //            NSLog(@"lb.region = %@", lb.region);
601 //        }
602 //        
603 //        [self.account.loadBalancers setObject:lbs forKey:endpoint];
604         [self.account persist];
605     }];
606 }
607
608 - (APICallback *)getLoadBalancerProtocols:(NSString *)endpoint {
609     __block LoadBalancerRequest *request = [LoadBalancerRequest getLoadBalancerProtocols:self.account endpoint:endpoint];
610     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
611         self.account.lbProtocols = [(LoadBalancerRequest *)request protocols];
612     }];
613 }
614
615 - (APICallback *)createLoadBalancer:(LoadBalancer *)loadBalancer {
616     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_CREATED);
617     
618     NSString *endpoint = @"";
619     
620     for (NSString *url in [self.account loadBalancerURLs]) {
621         if ([url hasPrefix:[NSString stringWithFormat:@"https://%@", [loadBalancer.region lowercaseString]]]) {
622             endpoint = url;
623             break;
624         }
625     }
626     
627     __block LoadBalancerRequest *request = [LoadBalancerRequest createLoadBalancerRequest:self.account loadBalancer:loadBalancer endpoint:endpoint];
628     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
629     }];
630 }
631
632 - (APICallback *)updateLoadBalancer:(LoadBalancer *)loadBalancer {
633     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_UPDATED);
634     NSString *endpoint = [self.account loadBalancerEndpointForRegion:loadBalancer.region];
635     __block LoadBalancerRequest *request = [LoadBalancerRequest updateLoadBalancerRequest:self.account loadBalancer:loadBalancer endpoint:endpoint];
636     return [self callbackWithRequest:request];
637 }
638
639 - (APICallback *)deleteLoadBalancer:(LoadBalancer *)loadBalancer {
640     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_DELETED);
641     NSString *endpoint = [self.account loadBalancerEndpointForRegion:loadBalancer.region];
642     NSLog(@"endpoint: %@", endpoint);
643     __block LoadBalancerRequest *request = [LoadBalancerRequest deleteLoadBalancerRequest:self.account loadBalancer:loadBalancer endpoint:endpoint];
644     return [self callbackWithRequest:request];
645 }
646
647 - (APICallback *)updateLoadBalancerConnectionLogging:(LoadBalancer *)loadBalancer {
648     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_UPDATED_LB_CONNECTION_LOGGING);
649     __block LoadBalancerRequest *request = [LoadBalancerRequest updateConnectionLoggingRequest:self.account loadBalancer:loadBalancer];
650     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
651     } failure:^(OpenStackRequest *request) {
652         loadBalancer.connectionLoggingEnabled = !loadBalancer.connectionLoggingEnabled;
653     }];
654 }
655
656 - (APICallback *)getLoadBalancerConnectionThrottling:(LoadBalancer *)loadBalancer {
657     __block LoadBalancerRequest *request = [LoadBalancerRequest getConnectionThrottlingRequest:self.account loadBalancer:loadBalancer];
658     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
659         loadBalancer.connectionThrottle = [(LoadBalancerRequest *)request connectionThrottle];
660     }];
661 }
662
663 - (APICallback *)updateLoadBalancerConnectionThrottling:(LoadBalancer *)loadBalancer {
664     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_UPDATED_LB_CONNECTION_THROTTLING);
665     __block LoadBalancerRequest *request = [LoadBalancerRequest updateConnectionThrottlingRequest:self.account loadBalancer:loadBalancer];
666     return [self callbackWithRequest:request];
667 }
668
669 - (APICallback *)deleteLoadBalancerConnectionThrottling:(LoadBalancer *)loadBalancer {
670     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_DISABLED_LB_CONNECTION_THROTTLING);
671     __block LoadBalancerRequest *request = [LoadBalancerRequest disableConnectionThrottlingRequest:self.account loadBalancer:loadBalancer];
672     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
673         loadBalancer.connectionThrottle = nil;
674     }];
675 }
676
677 - (APICallback *)getLoadBalancerUsage:(LoadBalancer *)loadBalancer endpoint:(NSString *)endpoint {
678     __block LoadBalancerRequest *request = [LoadBalancerRequest getLoadBalancerUsageRequest:self.account loadBalancer:loadBalancer endpoint:endpoint];
679     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
680         loadBalancer.usage = [(LoadBalancerRequest *)request usage];
681     }];
682 }
683
684 - (APICallback *)addLBNodes:(NSArray *)nodes loadBalancer:(LoadBalancer *)loadBalancer endpoint:(NSString *)endpoint {
685     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_ADDED_LB_NODES);
686     __block LoadBalancerRequest *request = [LoadBalancerRequest addLoadBalancerNodesRequest:self.account loadBalancer:loadBalancer nodes:nodes endpoint:endpoint];
687     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
688         for (LoadBalancerNode *node in nodes) {
689             [loadBalancer.nodes addObject:node];
690         }
691         [self.account persist];
692     }];
693 }
694
695 - (APICallback *)updateLBNode:(LoadBalancerNode *)node loadBalancer:(LoadBalancer *)loadBalancer endpoint:(NSString *)endpoint {
696     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_UPDATED_LB_NODE);
697     __block LoadBalancerRequest *request = [LoadBalancerRequest updateLoadBalancerNodeRequest:self.account loadBalancer:loadBalancer node:node endpoint:endpoint];
698     return [self callbackWithRequest:request];
699 }
700
701 - (APICallback *)deleteLBNode:(LoadBalancerNode *)node loadBalancer:(LoadBalancer *)loadBalancer endpoint:(NSString *)endpoint {
702     TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_DELETED_LB_NODE);
703     __block LoadBalancerRequest *request = [LoadBalancerRequest deleteLoadBalancerNodeRequest:self.account loadBalancer:loadBalancer node:node endpoint:endpoint];
704     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
705         [loadBalancer.nodes removeObject:node];
706     }];
707 }
708
709 - (APICallback *)authenticate {
710     __block OpenStackRequest *request = [OpenStackRequest authenticationRequest:self.account];
711     return [self callbackWithRequest:request success:^(OpenStackRequest *request) {
712         if ([request isSuccess]) {        
713             self.account.authToken = [[request responseHeaders] objectForKey:@"X-Auth-Token"];
714             self.account.serversURL = [NSURL URLWithString:[[request responseHeaders] objectForKey:@"X-Server-Management-Url"]];
715             
716             if (![[request responseHeaders] objectForKey:@"X-Storage-URL"]) {
717                 NSString *filesStorageURL = [NSString stringWithFormat:@"%@/v1/%@",
718                                              [self.account.hostURL absoluteString],
719                                              [self.account username]];
720
721                 self.account.filesURL = [NSURL URLWithString:filesStorageURL];
722             }
723             else {
724                 self.account.filesURL = [NSURL URLWithString:[[request responseHeaders] objectForKey:@"X-Storage-Url"]];
725             }
726             
727             self.account.pithosPublicLinkURLPrefix = self.account.hostURL;
728             self.account.pithosLoginURLPrefix = [NSURL URLWithString:
729                                             [[self.account.hostURL absoluteString]
730                                              stringByAppendingString:@"/login"]];
731             self.account.cdnURL = [NSURL URLWithString:[[request responseHeaders] objectForKey:@"X-Cdn-Management-Url"]];
732             
733             [self.account persist];
734         }
735     }];
736 }
737
738
739 #pragma mark -
740 #pragma mark Memory Management
741
742 - (void)dealloc {
743     [super dealloc];
744 }
745
746 @end