root / Classes / OpenStackRequest.m @ 38d59b6c
History | View | Annotate | Download (29.9 kB)
1 |
// |
---|---|
2 |
// OpenStackRequest.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 10/8/10. |
6 |
// The OpenStack project is provided under the Apache 2.0 license. |
7 |
// |
8 |
|
9 |
#import "OpenStackRequest.h" |
10 |
#import "Provider.h" |
11 |
#import "OpenStackAccount.h" |
12 |
#import "Server.h" |
13 |
#import "Image.h" |
14 |
#import "Flavor.h" |
15 |
#import "APILogger.h" |
16 |
#import "JSON.h" |
17 |
#import "RateLimit.h" |
18 |
#import "Container.h" |
19 |
#import "StorageObject.h" |
20 |
#import "Folder.h" |
21 |
#import "BackupSchedule.h" |
22 |
#import "AccountManager.h" |
23 |
#import "GetFlavorsRequest.h" |
24 |
#import "APICallback.h" |
25 |
#import "APILogEntry.h" |
26 |
|
27 |
|
28 |
static NSRecursiveLock *accessDetailsLock = nil; |
29 |
|
30 |
@implementation OpenStackRequest |
31 |
|
32 |
@synthesize account, callback, retriedCount; |
33 |
|
34 |
- (BOOL)isSuccess { |
35 |
return (200 <= [self responseStatusCode]) && ([self responseStatusCode] <= 299); |
36 |
} |
37 |
|
38 |
- (void)notify:(NSString *)name { |
39 |
NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"]; |
40 |
NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:callbackUserInfo]; |
41 |
[[NSNotificationCenter defaultCenter] postNotification:notification]; |
42 |
} |
43 |
|
44 |
- (void)notify { |
45 |
NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description]]; |
46 |
NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description], self.callback.uuid]; |
47 |
|
48 |
NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"]; |
49 |
|
50 |
NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo]; |
51 |
[[NSNotificationCenter defaultCenter] postNotification:observeNotification]; |
52 |
|
53 |
NSNotification *callbackNotification = [NSNotification notificationWithName:callbackName object:nil userInfo:callbackUserInfo]; |
54 |
[[NSNotificationCenter defaultCenter] postNotification:callbackNotification]; |
55 |
|
56 |
} |
57 |
|
58 |
- (NSString *)responseString { |
59 |
if (retried) { |
60 |
return [retriedRequest responseString]; |
61 |
} else { |
62 |
return [super responseString]; |
63 |
} |
64 |
} |
65 |
|
66 |
- (NSData *)responseData { |
67 |
if (retried) { |
68 |
return [retriedRequest responseData]; |
69 |
} else { |
70 |
return [super responseData]; |
71 |
} |
72 |
} |
73 |
|
74 |
- (NSDictionary *)responseHeaders { |
75 |
if (retried) { |
76 |
return [retriedRequest responseHeaders]; |
77 |
} else { |
78 |
return [super responseHeaders]; |
79 |
} |
80 |
} |
81 |
|
82 |
- (int)responseStatusCode { |
83 |
if (retried) { |
84 |
return [retriedRequest responseStatusCode]; |
85 |
} else { |
86 |
return [super responseStatusCode]; |
87 |
} |
88 |
} |
89 |
|
90 |
- (NSString *)responseStatusMessage { |
91 |
if (retried) { |
92 |
return [retriedRequest responseStatusMessage]; |
93 |
} else { |
94 |
return [super responseStatusMessage]; |
95 |
} |
96 |
} |
97 |
|
98 |
- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock { |
99 |
[super setCompletionBlock:aCompletionBlock]; |
100 |
[backupCompletionBlock release]; |
101 |
backupCompletionBlock = [aCompletionBlock copy]; |
102 |
} |
103 |
|
104 |
- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock { |
105 |
[super setFailedBlock:aFailedBlock]; |
106 |
[backupFailureBlock release]; |
107 |
backupFailureBlock = [aFailedBlock copy]; |
108 |
} |
109 |
|
110 |
+ (RateLimit *)limitForPath:(NSString *)path verb:(NSString *)verb account:(OpenStackAccount *)anAccount { |
111 |
|
112 |
// find the lowest requests remaining of all the limits |
113 |
|
114 |
RateLimit *lowestLimit = nil; |
115 |
|
116 |
for (RateLimit *limit in anAccount.rateLimits) { |
117 |
if ([limit.verb isEqualToString:verb]) { |
118 |
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:limit.regex options:NSRegularExpressionCaseInsensitive error:nil]; |
119 |
NSInteger matches = [regex numberOfMatchesInString:path options:0 range:NSMakeRange(0, [path length])]; |
120 |
|
121 |
if (matches > 0) { |
122 |
if (lowestLimit) { |
123 |
if (limit.remaining < lowestLimit.remaining) { |
124 |
lowestLimit = limit; |
125 |
} |
126 |
} else { |
127 |
lowestLimit = limit; |
128 |
} |
129 |
} |
130 |
} |
131 |
} |
132 |
|
133 |
return lowestLimit; |
134 |
} |
135 |
|
136 |
#pragma mark - |
137 |
#pragma mark Generic Constructors |
138 |
|
139 |
+ (void)initialize { |
140 |
if (self == [OpenStackRequest class]) { |
141 |
accessDetailsLock = [[NSRecursiveLock alloc] init]; |
142 |
} |
143 |
} |
144 |
|
145 |
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url { |
146 |
OpenStackRequest *request = [[[OpenStackRequest alloc] initWithURL:url] autorelease]; |
147 |
request.account = account; |
148 |
[request setRequestMethod:method]; |
149 |
[request addRequestHeader:@"X-Auth-Token" value:[account authToken]]; |
150 |
[request addRequestHeader:@"Content-Type" value:@"application/json"]; |
151 |
[request setTimeOutSeconds:60]; |
152 |
request.retriedCount = 0; |
153 |
return request; |
154 |
} |
155 |
|
156 |
+ (id)serversRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path { |
157 |
NSString *now = [[[NSDate date] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
158 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?now=%@", account.serversURL, path, now]]; |
159 |
return [OpenStackRequest request:account method:method url:url]; |
160 |
} |
161 |
|
162 |
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path { |
163 |
NSString *now = [[[NSDate date] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
164 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json&now=%@", account.filesURL, path, now]]; |
165 |
return [OpenStackRequest request:account method:method url:url]; |
166 |
} |
167 |
|
168 |
+ (id)cdnRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path { |
169 |
NSString *now = [[[NSDate date] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
170 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json&now=%@", account.cdnURL, path, now]]; |
171 |
return [OpenStackRequest request:account method:method url:url]; |
172 |
} |
173 |
|
174 |
#pragma mark - |
175 |
#pragma mark Auth Retry |
176 |
|
177 |
- (void)authRetrySucceded:(OpenStackRequest *)retryRequest { |
178 |
|
179 |
if ([self isKindOfClass:[GetFlavorsRequest class]]) { |
180 |
NSLog(@"flavor request"); |
181 |
} |
182 |
|
183 |
self.account.authToken = [[retryRequest responseHeaders] objectForKey:@"X-Auth-Token"]; |
184 |
[self.account persist]; |
185 |
|
186 |
// TODO: make this work for GetServersRequest, etc |
187 |
|
188 |
// try the original request again! |
189 |
retried = YES; |
190 |
retriedRequest = [self copy]; |
191 |
[retriedRequest addRequestHeader:@"X-Auth-Token" value:self.account.authToken]; |
192 |
|
193 |
if (backupCompletionBlock) { |
194 |
[retriedRequest setCompletionBlock:^{ |
195 |
backupCompletionBlock(); |
196 |
}]; |
197 |
} |
198 |
if (backupFailureBlock) { |
199 |
[retriedRequest setFailedBlock:^{ |
200 |
backupFailureBlock(); |
201 |
}]; |
202 |
} |
203 |
[retriedRequest startSynchronous]; |
204 |
} |
205 |
|
206 |
- (void)authRetryFailed:(OpenStackRequest *)retryRequest { |
207 |
// if it fails due to bad connection, try again? |
208 |
NSLog(@"auth retry failed with status %i", retryRequest.responseStatusCode); |
209 |
NSNotification *notification = [NSNotification notificationWithName:[self.account.manager notificationName:@"authRetryFailed" identifier:0] object:nil userInfo:[NSDictionary dictionaryWithObject:retryRequest forKey:@"request"]]; |
210 |
[[NSNotificationCenter defaultCenter] postNotification:notification]; |
211 |
} |
212 |
|
213 |
#pragma mark - |
214 |
#pragma mark ASIHTTPRequest Overrides |
215 |
// overriding to log API calls |
216 |
|
217 |
- (void)requestFinished { |
218 |
//NSLog(@"request finished: %i %@", self.responseStatusCode, self.url); |
219 |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
220 |
NSString *loggingLevel = [defaults valueForKey:@"api_logging_level"]; |
221 |
if ([loggingLevel isEqualToString:@"all"] || ([loggingLevel isEqualToString:@"errors"] && ![self isSuccess])) { |
222 |
[APILogger log:self]; |
223 |
} |
224 |
|
225 |
[super requestFinished]; |
226 |
} |
227 |
|
228 |
- (void)failWithError:(NSError *)theError { |
229 |
|
230 |
if (responseStatusCode == 401 && ![url isEqual:account.provider.authEndpointURL]) { |
231 |
// auth is expired, so get a fresh token |
232 |
if (account) { |
233 |
OpenStackRequest *retryRequest = [OpenStackRequest authenticationRequest:account]; |
234 |
retryRequest.delegate = self; |
235 |
retryRequest.didFinishSelector = @selector(authRetrySucceded:); |
236 |
retryRequest.didFailSelector = @selector(authRetryFailed:); |
237 |
[retryRequest startSynchronous]; |
238 |
} |
239 |
} else if (responseStatusCode == 503) { |
240 |
NSNotification *notification = [NSNotification notificationWithName:@"serviceUnavailable" object:nil userInfo:nil]; |
241 |
[[NSNotificationCenter defaultCenter] postNotification:notification]; |
242 |
[super failWithError:theError]; |
243 |
} else if (responseStatusCode == 0) { |
244 |
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
245 |
if (![defaults boolForKey:@"already_failed_on_connection"]) { |
246 |
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Please check your connection or API URL and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; |
247 |
[alert show]; |
248 |
[alert release]; |
249 |
} |
250 |
[defaults setBool:YES forKey:@"already_failed_on_connection"]; |
251 |
[defaults synchronize]; |
252 |
} |
253 |
|
254 |
[super failWithError:theError]; |
255 |
} |
256 |
|
257 |
#pragma mark - Authentication |
258 |
|
259 |
+ (NSString *)apiVersionForURL:(NSURL *)url { |
260 |
NSArray *components = [[url description] componentsSeparatedByString:@"/"]; |
261 |
if ([[components lastObject] isEqualToString:@"2.0"]) { |
262 |
return @"2.0"; |
263 |
} else if ([[components lastObject] isEqualToString:@"v1.1"]) { |
264 |
return @"1.1"; |
265 |
} else { |
266 |
return @"1.0"; |
267 |
} |
268 |
} |
269 |
|
270 |
+ (void)setupV1AuthForRequest:(OpenStackRequest *)request account:(OpenStackAccount *)account apiVersion:(NSString *)apiVersion { |
271 |
[request addRequestHeader:@"X-Auth-User" value:account.username]; |
272 |
if (account.apiKey) { |
273 |
[request addRequestHeader:@"X-Auth-Key" value:account.apiKey]; |
274 |
} else { |
275 |
[request addRequestHeader:@"X-Auth-Key" value:@""]; |
276 |
} |
277 |
} |
278 |
|
279 |
+ (void)setupAuthForRequest:(OpenStackRequest *)request account:(OpenStackAccount *)account apiVersion:(NSString *)apiVersion { |
280 |
|
281 |
if ([apiVersion isEqualToString:@"1.0"]) { |
282 |
|
283 |
[OpenStackRequest setupV1AuthForRequest:request account:account apiVersion:apiVersion]; |
284 |
account.apiVersion = @"1.0"; |
285 |
|
286 |
} else if ([apiVersion isEqualToString:@"1.1"]) { |
287 |
|
288 |
[OpenStackRequest setupV1AuthForRequest:request account:account apiVersion:apiVersion]; |
289 |
if (account.projectId) { |
290 |
[request addRequestHeader:@"X-Auth-Project-Id" value:account.projectId]; |
291 |
} |
292 |
account.apiVersion = @"1.1"; |
293 |
|
294 |
} else if ([apiVersion isEqualToString:@"2.0"]) { |
295 |
|
296 |
// TODO: support 2.0 auth |
297 |
account.apiVersion = @"1.1"; |
298 |
} |
299 |
} |
300 |
|
301 |
+ (OpenStackRequest *)authenticationRequest:(OpenStackAccount *)account { |
302 |
|
303 |
OpenStackRequest *request = [[[OpenStackRequest alloc] initWithURL:account.provider.authEndpointURL] autorelease]; |
304 |
request.account = account; |
305 |
|
306 |
NSString *apiVersion = [OpenStackRequest apiVersionForURL:account.provider.authEndpointURL]; |
307 |
[OpenStackRequest setupAuthForRequest:request account:account apiVersion:apiVersion]; |
308 |
|
309 |
return request; |
310 |
} |
311 |
|
312 |
#pragma mark - Rate Limits |
313 |
|
314 |
+ (OpenStackRequest *)getLimitsRequest:(OpenStackAccount *)account { |
315 |
return [OpenStackRequest serversRequest:account method:@"GET" path:@"/limits"]; |
316 |
} |
317 |
|
318 |
- (NSDictionary *)limits { |
319 |
SBJSON *parser = [[SBJSON alloc] init]; |
320 |
NSDictionary *jsonObjects = [[parser objectWithString:[self responseString]] objectForKey:@"limits"]; |
321 |
[parser release]; |
322 |
return jsonObjects; |
323 |
} |
324 |
|
325 |
- (NSArray *)rateLimits { |
326 |
NSArray *jsonObjects = [[self limits] objectForKey:@"rate"]; |
327 |
NSMutableArray *rateLimits = [[NSMutableArray alloc] initWithCapacity:[jsonObjects count]]; |
328 |
|
329 |
for (NSDictionary *dict in jsonObjects) { |
330 |
[rateLimits addObject:[RateLimit fromJSON:dict]]; |
331 |
} |
332 |
|
333 |
NSArray *result = [NSArray arrayWithArray:rateLimits]; |
334 |
|
335 |
[rateLimits release]; |
336 |
return result; |
337 |
} |
338 |
|
339 |
#pragma mark Collections |
340 |
|
341 |
+ (OpenStackRequest *)getServersRequest:(OpenStackAccount *)account { |
342 |
return [OpenStackRequest serversRequest:account method:@"GET" path:@"/servers/detail"]; |
343 |
} |
344 |
|
345 |
- (NSDictionary *)servers { |
346 |
SBJSON *parser = [[SBJSON alloc] init]; |
347 |
NSArray *jsonObjects = [[parser objectWithString:[self responseString]] objectForKey:@"servers"]; |
348 |
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]]; |
349 |
|
350 |
for (int i = 0; i < [jsonObjects count]; i++) { |
351 |
NSDictionary *dict = [jsonObjects objectAtIndex:i]; |
352 |
Server *server = [[Server alloc] initWithJSONDict:dict]; |
353 |
[objects setObject:server forKey:server.identifier]; |
354 |
[server release]; |
355 |
} |
356 |
|
357 |
[parser release]; |
358 |
return objects; |
359 |
} |
360 |
|
361 |
+ (OpenStackRequest *)getServerRequest:(OpenStackAccount *)account serverId:(NSString *)serverId { |
362 |
return [OpenStackRequest serversRequest:account method:@"GET" path:[NSString stringWithFormat:@"/servers/%@", serverId]]; |
363 |
} |
364 |
|
365 |
+ (OpenStackRequest *)getImagesRequest:(OpenStackAccount *)account { |
366 |
return [OpenStackRequest serversRequest:account method:@"GET" path:@"/images/detail"]; |
367 |
} |
368 |
|
369 |
- (NSDictionary *)images { |
370 |
SBJSON *parser = [[SBJSON alloc] init]; |
371 |
NSArray *jsonObjects = [[parser objectWithString:[self responseString]] objectForKey:@"images"]; |
372 |
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]]; |
373 |
|
374 |
for (int i = 0; i < [jsonObjects count]; i++) { |
375 |
NSDictionary *dict = [jsonObjects objectAtIndex:i]; |
376 |
Image *image = [[Image alloc] initWithJSONDict:dict]; |
377 |
[objects setObject:image forKey:image.identifier]; |
378 |
[image release]; |
379 |
} |
380 |
|
381 |
[parser release]; |
382 |
return objects; |
383 |
} |
384 |
|
385 |
+ (OpenStackRequest *)getImageRequest:(OpenStackAccount *)account imageId:(NSString *)imageId { |
386 |
return [OpenStackRequest serversRequest:account method:@"GET" path:[NSString stringWithFormat:@"/images/%@", imageId]]; |
387 |
} |
388 |
|
389 |
- (Image *)image { |
390 |
SBJSON *parser = [[SBJSON alloc] init]; |
391 |
NSDictionary *dict = [[parser objectWithString:[self responseString]] objectForKey:@"image"]; |
392 |
Image *image = [Image fromJSON:dict]; |
393 |
[parser release]; |
394 |
return image; |
395 |
} |
396 |
|
397 |
|
398 |
+ (OpenStackRequest *)getFlavorsRequest:(OpenStackAccount *)account { |
399 |
return [OpenStackRequest serversRequest:account method:@"GET" path:@"/flavors/detail"]; |
400 |
} |
401 |
|
402 |
- (NSDictionary *)flavors { |
403 |
SBJSON *parser = [[SBJSON alloc] init]; |
404 |
NSArray *jsonObjects = [[parser objectWithString:[self responseString]] objectForKey:@"flavors"]; |
405 |
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]]; |
406 |
|
407 |
for (int i = 0; i < [jsonObjects count]; i++) { |
408 |
NSDictionary *dict = [jsonObjects objectAtIndex:i]; |
409 |
Flavor *flavor = [Flavor fromJSON:dict]; |
410 |
[objects setObject:flavor forKey:flavor.identifier]; |
411 |
} |
412 |
|
413 |
[parser release]; |
414 |
return objects; |
415 |
} |
416 |
|
417 |
#pragma mark Server Actions |
418 |
|
419 |
#define kSoft 0 |
420 |
#define kHard 1 |
421 |
|
422 |
+ (OpenStackRequest *)rebootServerRequest:(OpenStackAccount *)account server:(Server *)server type:(NSInteger)type { |
423 |
NSString *body = [NSString stringWithFormat:@"{ \"reboot\": { \"type\": \"%@\" } }", (type == kSoft) ? @"SOFT" : @"HARD"]; |
424 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/action", server.identifier]]; |
425 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
426 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
427 |
return request; |
428 |
} |
429 |
|
430 |
+ (OpenStackRequest *)softRebootServerRequest:(OpenStackAccount *)account server:(Server *)server { |
431 |
return [OpenStackRequest rebootServerRequest:account server:server type:kSoft]; |
432 |
} |
433 |
|
434 |
+ (RateLimit *)softRebootServerLimit:(OpenStackAccount *)account server:(Server *)server { |
435 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
436 |
} |
437 |
|
438 |
+ (OpenStackRequest *)hardRebootServerRequest:(OpenStackAccount *)account server:(Server *)server { |
439 |
return [OpenStackRequest rebootServerRequest:account server:server type:kHard]; |
440 |
} |
441 |
|
442 |
+ (RateLimit *)hardRebootServerLimit:(OpenStackAccount *)account server:(Server *)server { |
443 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
444 |
} |
445 |
|
446 |
+ (OpenStackRequest *)changeServerAdminPasswordRequest:(OpenStackAccount *)account server:(Server *)server password:(NSString *)password { |
447 |
NSString *body = [NSString stringWithFormat:@"{ \"server\": { \"adminPass\": \"%@\" } }", password]; |
448 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/servers/%@", server.identifier]]; |
449 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
450 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
451 |
return request; |
452 |
} |
453 |
|
454 |
+ (RateLimit *)changeServerAdminPasswordLimit:(OpenStackAccount *)account server:(Server *)server { |
455 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@", server.identifier] verb:@"PUT" account:account]; |
456 |
} |
457 |
|
458 |
+ (OpenStackRequest *)renameServerRequest:(OpenStackAccount *)account server:(Server *)server name:(NSString *)name { |
459 |
NSString *body = [NSString stringWithFormat:@"{ \"server\": { \"name\": \"%@\" } }", name]; |
460 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/servers/%@", server.identifier]]; |
461 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
462 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
463 |
return request; |
464 |
} |
465 |
|
466 |
+ (RateLimit *)renameServerLimit:(OpenStackAccount *)account server:(Server *)server { |
467 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@", server.identifier] verb:@"PUT" account:account]; |
468 |
} |
469 |
|
470 |
+ (OpenStackRequest *)deleteServerRequest:(OpenStackAccount *)account server:(Server *)server { |
471 |
return [OpenStackRequest serversRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/servers/%@", server.identifier]]; |
472 |
} |
473 |
|
474 |
+ (RateLimit *)deleteServerLimit:(OpenStackAccount *)account server:(Server *)server { |
475 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@", server.identifier] verb:@"DELETE" account:account]; |
476 |
} |
477 |
|
478 |
+ (OpenStackRequest *)createServerRequest:(OpenStackAccount *)account server:(Server *)server { |
479 |
NSString *body = [server toJSON:account.apiVersion]; |
480 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@/servers", account.serversURL]]; |
481 |
NSLog(@"create server: %@", body); |
482 |
OpenStackRequest *request = [OpenStackRequest request:account method:@"POST" url:url]; |
483 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
484 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
485 |
return request; |
486 |
} |
487 |
|
488 |
+ (RateLimit *)createServerLimit:(OpenStackAccount *)account { |
489 |
return [OpenStackRequest limitForPath:@"/servers" verb:@"POST" account:account]; |
490 |
} |
491 |
|
492 |
- (Server *)server { |
493 |
SBJSON *parser = [[SBJSON alloc] init]; |
494 |
NSDictionary *dict = [[parser objectWithString:[self responseString]] objectForKey:@"server"]; |
495 |
Server *server = [Server fromJSON:dict]; |
496 |
[parser release]; |
497 |
return server; |
498 |
} |
499 |
|
500 |
+ (OpenStackRequest *)resizeServerRequest:(OpenStackAccount *)account server:(Server *)server flavor:(Flavor *)flavor { |
501 |
NSString *body; |
502 |
if ([account.apiVersion isEqualToString:@"1.0"]) { |
503 |
body = [NSString stringWithFormat:@"{ \"resize\": { \"flavorId\": %@ } }", flavor.identifier]; |
504 |
} else { |
505 |
body = [NSString stringWithFormat:@"{ \"resize\": { \"flavorRef\": %@ } }", flavor.identifier]; |
506 |
} |
507 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/action", server.identifier]]; |
508 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
509 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
510 |
return request; |
511 |
} |
512 |
|
513 |
+ (RateLimit *)resizeServerLimit:(OpenStackAccount *)account server:(Server *)server { |
514 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
515 |
} |
516 |
|
517 |
+ (OpenStackRequest *)confirmResizeServerRequest:(OpenStackAccount *)account server:(Server *)server { |
518 |
NSString *body = @"{ \"confirmResize\": null }"; |
519 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/action", server.identifier]]; |
520 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
521 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
522 |
return request; |
523 |
} |
524 |
|
525 |
+ (RateLimit *)confirmResizeServerLimit:(OpenStackAccount *)account server:(Server *)server { |
526 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
527 |
} |
528 |
|
529 |
+ (OpenStackRequest *)revertResizeServerRequest:(OpenStackAccount *)account server:(Server *)server { |
530 |
NSString *body = @"{ \"revertResize\": null }"; |
531 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/action", server.identifier]]; |
532 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
533 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
534 |
return request; |
535 |
} |
536 |
|
537 |
+ (RateLimit *)revertResizeServerLimit:(OpenStackAccount *)account server:(Server *)server { |
538 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
539 |
} |
540 |
|
541 |
+ (OpenStackRequest *)rebuildServerRequest:(OpenStackAccount *)account server:(Server *)server image:(Image *)image { |
542 |
NSString *body = [NSString stringWithFormat:@"{ \"rebuild\": { \"imageId\": %@ } }", image.identifier]; |
543 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/action", server.identifier]]; |
544 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
545 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
546 |
return request; |
547 |
} |
548 |
|
549 |
+ (RateLimit *)rebuildServerLimit:(OpenStackAccount *)account server:(Server *)server { |
550 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/action", server.identifier] verb:@"POST" account:account]; |
551 |
} |
552 |
|
553 |
|
554 |
+ (OpenStackRequest *)getBackupScheduleRequest:(OpenStackAccount *)account server:(Server *)server { |
555 |
return [OpenStackRequest serversRequest:account method:@"GET" path:[NSString stringWithFormat:@"/servers/%@/backup_schedule", server.identifier]]; |
556 |
} |
557 |
|
558 |
+ (RateLimit *)getBackupScheduleLimit:(OpenStackAccount *)account server:(Server *)server { |
559 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/backup_schedule", server.identifier] verb:@"GET" account:account]; |
560 |
} |
561 |
|
562 |
+ (OpenStackRequest *)updateBackupScheduleRequest:(OpenStackAccount *)account server:(Server *)server { |
563 |
NSString *body = [NSString stringWithFormat:@"{ \"backupSchedule\": { \"enabled\": true, \"weekly\": \"%@\", \"daily\": \"%@\" } }", server.backupSchedule.weekly, server.backupSchedule.daily]; |
564 |
OpenStackRequest *request = [OpenStackRequest serversRequest:account method:@"POST" path:[NSString stringWithFormat:@"/servers/%@/backup_schedule", server.identifier]]; |
565 |
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding]; |
566 |
[request setPostBody:[NSMutableData dataWithData:data]]; |
567 |
return request; |
568 |
} |
569 |
|
570 |
+ (RateLimit *)updateBackupScheduleLimit:(OpenStackAccount *)account server:(Server *)server { |
571 |
return [OpenStackRequest limitForPath:[NSString stringWithFormat:@"/servers/%@/backup_schedule", server.identifier] verb:@"POST" account:account]; |
572 |
} |
573 |
|
574 |
- (BackupSchedule *)backupSchedule { |
575 |
SBJSON *parser = [[SBJSON alloc] init]; |
576 |
NSDictionary *dict = [[parser objectWithString:[self responseString]] objectForKey:@"backupSchedule"]; |
577 |
BackupSchedule *backupSchedule = [BackupSchedule fromJSON:dict]; |
578 |
[parser release]; |
579 |
return backupSchedule; |
580 |
} |
581 |
|
582 |
#pragma mark - |
583 |
#pragma mark Object Storage Requests |
584 |
|
585 |
+ (OpenStackRequest *)getStorageAccountInfoRequest:(OpenStackAccount *)account { |
586 |
return [OpenStackRequest filesRequest:account method:@"HEAD" path:@""]; |
587 |
} |
588 |
|
589 |
+ (OpenStackRequest *)getContainersRequest:(OpenStackAccount *)account { |
590 |
return [OpenStackRequest filesRequest:account method:@"GET" path:@""]; |
591 |
} |
592 |
|
593 |
- (NSMutableDictionary *)containers { |
594 |
SBJSON *parser = [[SBJSON alloc] init]; |
595 |
NSArray *jsonObjects = [parser objectWithString:[self responseString]]; |
596 |
NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]]; |
597 |
|
598 |
for (int i = 0; i < [jsonObjects count]; i++) { |
599 |
NSDictionary *dict = [jsonObjects objectAtIndex:i]; |
600 |
Container *container = [Container fromJSON:dict]; |
601 |
[objects setObject:container forKey:container.name]; |
602 |
} |
603 |
|
604 |
[parser release]; |
605 |
return objects; |
606 |
} |
607 |
|
608 |
+ (OpenStackRequest *)createContainerRequest:(OpenStackAccount *)account container:(Container *)container { |
609 |
return [OpenStackRequest filesRequest:account method:@"PUT" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
610 |
} |
611 |
|
612 |
+ (OpenStackRequest *)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container { |
613 |
return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
614 |
} |
615 |
|
616 |
+ (OpenStackRequest *)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container { |
617 |
return [OpenStackRequest filesRequest:account method:@"GET" path:[[NSString stringWithFormat:@"/%@", container.name] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
618 |
} |
619 |
|
620 |
- (NSMutableDictionary *)objects { |
621 |
SBJSON *parser = [[SBJSON alloc] init]; |
622 |
NSArray *jsonObjects = [parser objectWithString:[self responseString]]; |
623 |
NSMutableDictionary *objects = [[[NSMutableDictionary alloc] initWithCapacity:[jsonObjects count]] autorelease]; |
624 |
|
625 |
for (int i = 0; i < [jsonObjects count]; i++) { |
626 |
NSDictionary *dict = [jsonObjects objectAtIndex:i]; |
627 |
StorageObject *object = [StorageObject fromJSON:dict]; |
628 |
[objects setObject:object forKey:object.name]; |
629 |
} |
630 |
|
631 |
[parser release]; |
632 |
return objects; |
633 |
} |
634 |
|
635 |
+ (OpenStackRequest *)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
636 |
return [OpenStackRequest filesRequest:account method:@"HEAD" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
637 |
} |
638 |
|
639 |
+ (OpenStackRequest *)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
640 |
return [OpenStackRequest filesRequest:account method:@"GET" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
641 |
} |
642 |
|
643 |
+ (OpenStackRequest *)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
644 |
NSString *fullPath = object.fullPath; |
645 |
if ([fullPath characterAtIndex:0] == '/') { |
646 |
fullPath = [fullPath substringFromIndex:1]; |
647 |
} |
648 |
|
649 |
OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"PUT" path:[[NSString stringWithFormat:@"/%@/%@", container.name, fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
650 |
[request setPostBody:[NSMutableData dataWithData:object.data]]; |
651 |
[request.requestHeaders setObject:object.contentType forKey:@"Content-Type"]; |
652 |
return request; |
653 |
} |
654 |
|
655 |
+ (OpenStackRequest *)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
656 |
NSString *fullPath = [NSString stringWithString:object.fullPath]; |
657 |
if ([fullPath length] && ([fullPath characterAtIndex:0] == '/')) |
658 |
fullPath = [fullPath substringFromIndex:1]; |
659 |
|
660 |
NSString *metadataKeyHeaderPrefix; |
661 |
if (![fullPath length]) |
662 |
metadataKeyHeaderPrefix = @"X-Container-Meta-"; |
663 |
else |
664 |
metadataKeyHeaderPrefix = @"X-Object-Meta-"; |
665 |
|
666 |
OpenStackRequest *request = [OpenStackRequest filesRequest:account method:@"POST" path:[[NSString stringWithFormat:@"/%@/%@", container.name, fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
667 |
|
668 |
for (NSString *metadataKey in object.metadata) { |
669 |
NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey]; |
670 |
[request.requestHeaders setObject:[object.metadata objectForKey:metadataKey] forKey:metadataKeyHeader]; |
671 |
} |
672 |
return request; |
673 |
} |
674 |
|
675 |
+ (OpenStackRequest *)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object { |
676 |
if ([object.fullPath characterAtIndex:0] == '/') { |
677 |
return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
678 |
} else { |
679 |
return [OpenStackRequest filesRequest:account method:@"DELETE" path:[[NSString stringWithFormat:@"/%@/%@", container.name, object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
680 |
} |
681 |
} |
682 |
|
683 |
#pragma mark - |
684 |
#pragma mark Memory Management |
685 |
|
686 |
- (void)releaseBackupBlocksOnMainThread { |
687 |
NSMutableArray *blocks = [NSMutableArray array]; |
688 |
if (backupCompletionBlock) { |
689 |
[blocks addObject:backupCompletionBlock]; |
690 |
[backupCompletionBlock release]; |
691 |
backupCompletionBlock = nil; |
692 |
} |
693 |
if (backupFailureBlock) { |
694 |
[blocks addObject:backupFailureBlock]; |
695 |
[backupFailureBlock release]; |
696 |
backupFailureBlock = nil; |
697 |
} |
698 |
[[self class] performSelectorOnMainThread:@selector(releaseBackupBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]]; |
699 |
} |
700 |
|
701 |
// Always called on main thread |
702 |
+ (void)releaseBackupBlocks:(NSArray *)blocks { |
703 |
// Blocks will be released when this method exits |
704 |
} |
705 |
|
706 |
- (void)dealloc { |
707 |
[account release]; |
708 |
[self releaseBackupBlocksOnMainThread]; |
709 |
[super dealloc]; |
710 |
} |
711 |
|
712 |
@end |