root / Classes / ServerViewController.m @ 4b4b833e
History | View | Annotate | Download (58.1 kB)
1 |
// |
---|---|
2 |
// ServerViewController.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 10/7/10. |
6 |
// The OpenStack project is provided under the Apache 2.0 license. |
7 |
// |
8 |
|
9 |
#import "ServerViewController.h" |
10 |
#import "Server.h" |
11 |
#import "OpenStackAccount.h" |
12 |
#import "Image.h" |
13 |
#import "Flavor.h" |
14 |
#import "UIViewController+Conveniences.h" |
15 |
#import "PingIPAddressViewController.h" |
16 |
#import "RenameServerViewController.h" |
17 |
#import "ResetServerAdminPasswordViewController.h" |
18 |
#import "AccountManager.h" |
19 |
#import "NSObject+Conveniences.h" |
20 |
#import "ServersViewController.h" |
21 |
#import "ResizeServerViewController.h" |
22 |
#import "RebuildServerViewController.h" |
23 |
#import "AnimatedProgressView.h" |
24 |
#import "OpenStackRequest.h" |
25 |
#import "RateLimit.h" |
26 |
#import "ManageBackupScheduleViewController.h" |
27 |
#import "SimpleImagePickerViewController.h" |
28 |
#import "UIColor+MoreColors.h" |
29 |
#import <QuartzCore/QuartzCore.h> |
30 |
#import "AccountHomeViewController.h" |
31 |
#import "ServersOnHostViewController.h" |
32 |
#import "NameAndStatusTitleView.h" |
33 |
#import "APICallback.h" |
34 |
#import "Provider.h" |
35 |
#import "Analytics.h" |
36 |
|
37 |
// TODO: bring back host id section as "n servers on this (physical) host" |
38 |
|
39 |
@implementation ServerViewController |
40 |
|
41 |
@synthesize server, account, tableView, selectedIPAddressIndexPath, serversViewController, selectedServerIndexPath, accountHomeViewController; |
42 |
|
43 |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { |
44 |
//CGPoint point = scrollView.contentOffset; |
45 |
//CGRect tr = titleView.frame; |
46 |
//CGRect ar = actionView.frame; |
47 |
/* |
48 |
if (previousScrollPoint.y - point.y < 0) { |
49 |
titleView.frame = CGRectMake(tr.origin.x, (previousScrollPoint.y - point.y) / 3.0, tr.size.width, tr.size.height); |
50 |
actionView.frame = CGRectMake(ar.origin.x, 64 + ((previousScrollPoint.y - point.y) / 2.0), ar.size.width, ar.size.height); |
51 |
} |
52 |
*/ |
53 |
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{ |
54 |
CGRect tr = titleView.frame; |
55 |
CGRect ar = actionView.frame; |
56 |
|
57 |
if (self.tableView.contentOffset.y >= 150) { |
58 |
tr.origin.y = -67; |
59 |
ar.origin.y = -136.0; |
60 |
} else { |
61 |
tr.origin.y = 0; |
62 |
ar.origin.y = 64; |
63 |
} |
64 |
titleView.frame = tr; |
65 |
actionView.frame = ar; |
66 |
} completion:^(BOOL finished) { |
67 |
}]; |
68 |
|
69 |
} |
70 |
|
71 |
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { |
72 |
return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait); |
73 |
} |
74 |
|
75 |
- (void)setParallaxViews { |
76 |
|
77 |
if (!titleView) { |
78 |
// make an offset for the table |
79 |
self.tableView.tableHeaderView = [[[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 134.0)] autorelease]; |
80 |
|
81 |
NSString *logoFilename = @""; |
82 |
if ([self.server.image respondsToSelector:@selector(logoPrefix)]) { |
83 |
logoFilename = [[self.server.image logoPrefix] stringByAppendingString:@"-icon.png"]; |
84 |
} |
85 |
|
86 |
titleView = [[NameAndStatusTitleView alloc] initWithEntity:self.server logoFilename:logoFilename]; |
87 |
[self.view addSubview:titleView]; |
88 |
[titleView setNeedsDisplay]; |
89 |
} |
90 |
|
91 |
actionView.backgroundColor = [UIColor colorWithRed:0.929 green:0.929 blue:0.929 alpha:1]; |
92 |
actionView.clipsToBounds = NO; |
93 |
[actionView.layer setShadowColor:[[UIColor blackColor] CGColor]]; |
94 |
[actionView.layer setShadowRadius:2.0f]; |
95 |
[actionView.layer setShadowOffset:CGSizeMake(1, 1)]; |
96 |
[actionView.layer setShadowOpacity:0.8f]; |
97 |
|
98 |
/* |
99 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
100 |
// line up the action buttons with the table view on iPad |
101 |
CGRect rebootFrame = rebootButton.frame; |
102 |
rebootFrame.origin.x += 14; |
103 |
rebootButton.frame = rebootFrame; |
104 |
} |
105 |
*/ |
106 |
|
107 |
|
108 |
[self scrollViewDidScroll:self.tableView]; |
109 |
} |
110 |
|
111 |
- (void)setBackgroundView { |
112 |
if (self.server) { |
113 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
114 |
UIView *backgroundContainer = [[UIView alloc] init]; |
115 |
backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; |
116 |
backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor]; |
117 |
|
118 |
NSString *logoFilename = @""; |
119 |
if ([self.server.image respondsToSelector:@selector(logoPrefix)]) { |
120 |
logoFilename = [[self.server.image logoPrefix] stringByAppendingString:@"-large.png"]; |
121 |
} |
122 |
|
123 |
UIImageView *osLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:logoFilename]]; |
124 |
osLogo.contentMode = UIViewContentModeScaleAspectFit; |
125 |
osLogo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0); |
126 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
127 |
osLogo.alpha = 0.3; |
128 |
} |
129 |
|
130 |
[backgroundContainer addSubview:osLogo]; |
131 |
[osLogo release]; |
132 |
|
133 |
tableView.backgroundView = backgroundContainer; |
134 |
[backgroundContainer release]; |
135 |
|
136 |
} else { |
137 |
self.tableView.backgroundView = nil; |
138 |
/* |
139 |
NSString *logoFilename = [[server.image logoPrefix] stringByAppendingString:@"-large.png"]; |
140 |
UIImageView *osLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:logoFilename]]; |
141 |
osLogo.contentMode = UIViewContentModeScaleAspectFit; |
142 |
osLogo.frame = CGRectMake(0.0, 0.0, 320.0, 480.0); |
143 |
tableView.backgroundView = osLogo; |
144 |
[osLogo release]; |
145 |
*/ |
146 |
} |
147 |
} else { |
148 |
UIView *container = [[UIView alloc] init]; |
149 |
container.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; |
150 |
container.backgroundColor = [UIColor iPadTableBackgroundColor]; |
151 |
|
152 |
UILabel *label = [[UILabel alloc] init]; |
153 |
label.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; |
154 |
label.backgroundColor = [UIColor clearColor]; |
155 |
label.textColor = [UIColor emptyCollectionGrayColor]; |
156 |
label.font = [UIFont boldSystemFontOfSize:18.0]; |
157 |
label.text = @"No server selected"; |
158 |
label.textAlignment = UITextAlignmentCenter; |
159 |
[container addSubview:label]; |
160 |
[label release]; |
161 |
|
162 |
tableView.backgroundView = container; |
163 |
[container release]; |
164 |
} |
165 |
} |
166 |
|
167 |
- (void)refreshCountdownLabels:(NSTimer *)timer { |
168 |
[self refreshLimitStrings]; |
169 |
|
170 |
if (actionsExpanded) { |
171 |
NSMutableArray *indexPaths = [[NSMutableArray alloc] init]; |
172 |
|
173 |
if (![rebootCountdown isEqualToString:@""]) { |
174 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kReboot inSection:kActions]]; |
175 |
} |
176 |
if (![renameCountdown isEqualToString:@""]) { |
177 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kRename inSection:kActions]]; |
178 |
} |
179 |
if (![resizeCountdown isEqualToString:@""]) { |
180 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kResize inSection:kActions]]; |
181 |
} |
182 |
if (![changePasswordCountdown isEqualToString:@""]) { |
183 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kChangePassword inSection:kActions]]; |
184 |
} |
185 |
if (![backupsCountdown isEqualToString:@""]) { |
186 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kBackups inSection:kActions]]; |
187 |
} |
188 |
if (![rebuildCountdown isEqualToString:@""]) { |
189 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kRebuild inSection:kActions]]; |
190 |
} |
191 |
if (![deleteCountdown isEqualToString:@""]) { |
192 |
[indexPaths addObject:[NSIndexPath indexPathForRow:kDelete inSection:kActions]]; |
193 |
} |
194 |
|
195 |
[self.tableView reloadData]; |
196 |
[indexPaths release]; |
197 |
} |
198 |
|
199 |
} |
200 |
|
201 |
- (void)refreshLimitStrings { |
202 |
|
203 |
RateLimit *rebootLimit = [OpenStackRequest softRebootServerLimit:self.account server:self.server]; |
204 |
if (rebootLimit && rebootLimit.remaining == 0) { |
205 |
rebootCountdown = [ServerViewController timeUntilDate:rebootLimit.resetTime]; |
206 |
} |
207 |
|
208 |
RateLimit *renameLimit = [OpenStackRequest renameServerLimit:self.account server:self.server]; |
209 |
if (renameLimit && renameLimit.remaining == 0) { |
210 |
renameCountdown = [ServerViewController timeUntilDate:renameLimit.resetTime]; |
211 |
} |
212 |
|
213 |
RateLimit *resizeLimit = [OpenStackRequest resizeServerLimit:self.account server:self.server]; |
214 |
if (resizeLimit && resizeLimit.remaining == 0) { |
215 |
resizeCountdown = [ServerViewController timeUntilDate:resizeLimit.resetTime]; |
216 |
} |
217 |
|
218 |
RateLimit *changePasswordLimit = [OpenStackRequest changeServerAdminPasswordLimit:self.account server:self.server]; |
219 |
if (changePasswordLimit && changePasswordLimit.remaining == 0) { |
220 |
changePasswordCountdown = [ServerViewController timeUntilDate:changePasswordLimit.resetTime]; |
221 |
} |
222 |
|
223 |
RateLimit *backupsLimit = [OpenStackRequest updateBackupScheduleLimit:self.account server:self.server]; |
224 |
if (backupsLimit && backupsLimit.remaining == 0) { |
225 |
backupsCountdown = [ServerViewController timeUntilDate:backupsLimit.resetTime]; |
226 |
} |
227 |
|
228 |
RateLimit *rebuildLimit = nil; // [OpenStackRequest rebuildServerLimit:self.account server:self.server]; |
229 |
if (rebuildLimit && rebuildLimit.remaining == 0) { |
230 |
rebuildCountdown = [ServerViewController timeUntilDate:rebuildLimit.resetTime]; |
231 |
} |
232 |
|
233 |
RateLimit *deleteLimit = [OpenStackRequest deleteServerLimit:self.account server:self.server]; |
234 |
if (deleteLimit && deleteLimit.remaining == 0) { |
235 |
deleteCountdown = [ServerViewController timeUntilDate:deleteLimit.resetTime]; |
236 |
} |
237 |
} |
238 |
|
239 |
- (void)pollServer { |
240 |
pollRequest = [OpenStackRequest getServerRequest:self.account serverId:self.server.identifier]; |
241 |
polling = YES; |
242 |
pollRequest.delegate = self; |
243 |
pollRequest.didFinishSelector = @selector(getServerSucceeded:); |
244 |
pollRequest.didFailSelector = @selector(getServerFailed:); |
245 |
[pollRequest startAsynchronous]; |
246 |
[titleView setNeedsDisplay]; |
247 |
} |
248 |
|
249 |
- (void)getServerSucceeded:(OpenStackRequest *)request { |
250 |
if ([request isSuccess]) { |
251 |
self.server = [request server]; |
252 |
|
253 |
NSLog(@"flavor id: %@", self.server.flavorId); |
254 |
|
255 |
self.server.flavor = [self.account.flavors objectForKey:self.server.flavorId]; |
256 |
|
257 |
NSLog(@"server flavor: %@", self.server.flavor); |
258 |
|
259 |
self.server.image = [self.account.images objectForKey:self.server.imageId]; |
260 |
|
261 |
[self.account.servers setObject:server forKey:self.server.identifier]; |
262 |
[self.account persist]; |
263 |
|
264 |
NSLog(@"polling server worked. %i, %@", self.server.progress, self.server.status); |
265 |
[progressView setProgress:self.server.progress animated:YES]; |
266 |
|
267 |
if ([self.server.status isEqualToString:@"VERIFY_RESIZE"]) { |
268 |
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Resize Complete" message:@"Confirming the resize will destroy the saved copy of your original server." delegate:self cancelButtonTitle:@"Decide Later" otherButtonTitles:@"Confirm Resize", @"Revert to Previous Size", nil]; |
269 |
[alert show]; |
270 |
[alert release]; |
271 |
} |
272 |
} |
273 |
if (kOverview >= 0) { |
274 |
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:kStatus inSection:kOverview]] withRowAnimation:UITableViewRowAnimationNone]; |
275 |
} |
276 |
if ([self.server shouldBePolled]) { |
277 |
[self pollServer]; |
278 |
} else { |
279 |
polling = NO; |
280 |
//[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:kStatus inSection:kOverview]] withRowAnimation:UITableViewRowAnimationNone]; |
281 |
[self.tableView reloadData]; |
282 |
[self setBackgroundView]; |
283 |
if ([self.server.image respondsToSelector:@selector(logoPrefix)]) { |
284 |
titleView.logoView.image = [UIImage imageNamed:[[self.server.image logoPrefix] stringByAppendingString:@"-icon.png"]]; |
285 |
} |
286 |
|
287 |
if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad) { |
288 |
[self.serversViewController.tableView reloadData]; |
289 |
} |
290 |
|
291 |
} |
292 |
titleView.entity = self.server; |
293 |
[titleView setNeedsDisplay]; |
294 |
|
295 |
} |
296 |
|
297 |
- (void)getServerFailed:(OpenStackRequest *)request { |
298 |
NSLog(@"polling server failed. trying again."); |
299 |
[self pollServer]; |
300 |
} |
301 |
|
302 |
#pragma mark - |
303 |
#pragma mark View lifecycle |
304 |
|
305 |
- (void)viewDidLoad { |
306 |
[super viewDidLoad]; |
307 |
|
308 |
actionsExpanded = YES; |
309 |
|
310 |
previousScrollPoint = CGPointZero; |
311 |
titleView = nil; |
312 |
|
313 |
// Uncomment the following line to display an Edit button in the navigation bar for this view controller. |
314 |
// self.navigationItem.rightBarButtonItem = self.editButtonItem; |
315 |
|
316 |
UIApplication *application = [UIApplication sharedApplication]; |
317 |
NSURL *url = [NSURL URLWithString:@"ssh://123.123.123.123"]; |
318 |
if ([application canOpenURL:url]) { |
319 |
ipAddressActionSheet = [[UIActionSheet alloc] initWithTitle:selectedIPAddress delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Ping IP Address", @"Copy to Pasteboard", @"Open in Safari", @"Open in SSH Client", nil]; |
320 |
} else { |
321 |
ipAddressActionSheet = [[UIActionSheet alloc] initWithTitle:selectedIPAddress delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Ping IP Address", @"Copy to Pasteboard", @"Open in Safari", nil]; |
322 |
} |
323 |
|
324 |
rebootActionSheet = [[UIActionSheet alloc] initWithTitle:@"A soft reboot performs a graceful shutdown of your system. A hard reboot is the equivalent of unplugging your server." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Perform Hard Reboot" otherButtonTitles:@"Perform Soft Reboot", nil]; |
325 |
deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this server? This operation cannot be undone and you will lose all backup images." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Server" otherButtonTitles:nil]; |
326 |
progressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; |
327 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
328 |
CGRect rect = progressView.frame; |
329 |
rect.size.width = 440.0; |
330 |
progressView.frame = rect; |
331 |
} |
332 |
|
333 |
actionsArrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"arrow-up.png"]]; |
334 |
actionsArrow.highlightedImage = [UIImage imageNamed:@"arrow-up-highlighted.png"]; |
335 |
actionsArrow.transform = CGAffineTransformMakeRotation(180.0 * M_PI / 180.0); |
336 |
} |
337 |
|
338 |
- (void)viewWillAppear:(BOOL)animated { |
339 |
[super viewWillAppear:animated]; |
340 |
self.navigationItem.title = [self.account.provider isRackspace] ? @"Cloud Server" : @"Server"; |
341 |
[self setBackgroundView]; |
342 |
|
343 |
if (self.server) { |
344 |
[self setParallaxViews]; |
345 |
} else { |
346 |
actionView.alpha = 0; |
347 |
rebootButton.enabled = NO; |
348 |
pingButton.enabled = NO; |
349 |
} |
350 |
} |
351 |
|
352 |
- (void)viewDidAppear:(BOOL)animated { |
353 |
[super viewDidAppear:animated]; |
354 |
rebootCountdown = @""; |
355 |
renameCountdown = @""; |
356 |
resizeCountdown = @""; |
357 |
changePasswordCountdown = @""; |
358 |
backupsCountdown = @""; |
359 |
rebuildCountdown = @""; |
360 |
deleteCountdown = @""; |
361 |
|
362 |
[self refreshLimitStrings]; |
363 |
|
364 |
// handle success |
365 |
if (!getLimitsSucceededObserver) { |
366 |
getLimitsSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getLimitsSucceeded" object:self.account |
367 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
368 |
{ |
369 |
OpenStackRequest *request = [notification.userInfo objectForKey:@"request"]; |
370 |
self.account.rateLimits = [request rateLimits]; |
371 |
[[NSNotificationCenter defaultCenter] removeObserver:getLimitsSucceededObserver]; |
372 |
}]; |
373 |
|
374 |
getImageSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getImageSucceeded" object:nil |
375 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
376 |
{ |
377 |
Image *image = [self.account.images objectForKey:self.server.imageId]; |
378 |
self.server.image = image; |
379 |
|
380 |
/* |
381 |
NSString *logoFilename = [[server.image logoPrefix] stringByAppendingString:@"-large.png"]; |
382 |
UIImageView *osLogo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:logoFilename]]; |
383 |
osLogo.contentMode = UIViewContentModeScaleAspectFit; |
384 |
osLogo.frame = CGRectMake(0.0, 0.0, 320.0, 480.0); |
385 |
tableView.backgroundView = osLogo; |
386 |
[osLogo release]; |
387 |
*/ |
388 |
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:kImage inSection:kDetails]] withRowAnimation:UITableViewRowAnimationNone]; |
389 |
}]; |
390 |
|
391 |
getImageFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"getImageFailed" object:self.server.imageId |
392 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
393 |
{ |
394 |
NSLog(@"loading image failed"); |
395 |
}]; |
396 |
|
397 |
updateBackupScheduleSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateBackupScheduleSucceeded" object:self.server |
398 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
399 |
{ |
400 |
[self hideToolbarActivityMessage]; |
401 |
}]; |
402 |
|
403 |
updateBackupScheduleFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"updateBackupScheduleFailed" object:self.account |
404 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
405 |
{ |
406 |
[self hideToolbarActivityMessage]; |
407 |
[self alert:@"There was a problem updating your backup schedule." request:[notification.userInfo objectForKey:@"request"]]; |
408 |
}]; |
409 |
|
410 |
resizeServerSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"resizeServerSucceeded" identifier:self.server.identifier] object:nil |
411 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
412 |
{ |
413 |
[self hideToolbarActivityMessage]; |
414 |
self.server.status = @"QUEUE_RESIZE"; |
415 |
if (kOverview >= 0) { |
416 |
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:kStatus inSection:kOverview]] withRowAnimation:UITableViewRowAnimationNone]; |
417 |
} |
418 |
[self pollServer]; |
419 |
}]; |
420 |
|
421 |
resizeServerFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"resizeServerFailed" identifier:self.server.identifier] object:nil |
422 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
423 |
{ |
424 |
[self hideToolbarActivityMessage]; |
425 |
[self alert:@"There was a problem resizing this server." request:[notification.userInfo objectForKey:@"request"]]; |
426 |
}]; |
427 |
|
428 |
confirmResizeSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"confirmResizeServerSucceeded" identifier:self.server.identifier] object:nil |
429 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
430 |
{ |
431 |
[self hideToolbarActivityMessage]; |
432 |
[self pollServer]; |
433 |
}]; |
434 |
|
435 |
confirmResizeFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"confirmResizeServerFailed" identifier:self.server.identifier] object:nil |
436 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
437 |
{ |
438 |
[self hideToolbarActivityMessage]; |
439 |
[self alert:@"There was a problem confirming the resize." request:[notification.userInfo objectForKey:@"request"]]; |
440 |
}]; |
441 |
|
442 |
revertResizeSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"revertResizeServerSucceeded" identifier:self.server.identifier] object:nil |
443 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
444 |
{ |
445 |
[self hideToolbarActivityMessage]; |
446 |
[self pollServer]; |
447 |
}]; |
448 |
|
449 |
revertResizeFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"revertResizeServerFailed" identifier:self.server.identifier] object:nil |
450 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
451 |
{ |
452 |
[self hideToolbarActivityMessage]; |
453 |
[self alert:@"There was a problem reverting the resize." request:[notification.userInfo objectForKey:@"request"]]; |
454 |
}]; |
455 |
|
456 |
rebuildSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"rebuildServerSucceeded" identifier:self.server.identifier] object:nil |
457 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
458 |
{ |
459 |
[self hideToolbarActivityMessage]; |
460 |
[self pollServer]; |
461 |
}]; |
462 |
|
463 |
rebuildFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:[self.account.manager notificationName:@"rebuildServerFailed" identifier:self.server.identifier] object:nil |
464 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
465 |
{ |
466 |
[self hideToolbarActivityMessage]; |
467 |
[self alert:@"There was a problem rebuilding this server." request:[notification.userInfo objectForKey:@"request"]]; |
468 |
}]; |
469 |
} |
470 |
|
471 |
// if the server has less than 100% progress, we need to poll it. |
472 |
// normally, we would do this through the account manager, but this is the only |
473 |
// place we'll need the information. the downside to this is that we may |
474 |
// temporarily show a server as building when it's already complete, but that's |
475 |
// better than the possibility of polling several servers at once when a user |
476 |
// decides to add multiple nodes |
477 |
if (self.server && [server shouldBePolled]) { |
478 |
[self pollServer]; |
479 |
} |
480 |
|
481 |
if (!countdownTimer) { |
482 |
countdownTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(refreshCountdownLabels:) userInfo:nil repeats:YES]; |
483 |
} |
484 |
|
485 |
if (self.server && [self.server.status isEqualToString:@"VERIFY_RESIZE"]) { |
486 |
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Resize Complete" message:@"Confirming the resize will destroy the saved copy of your original server." delegate:self cancelButtonTitle:@"Decide Later" otherButtonTitles:@"Confirm Resize", @"Revert to Previous Size", nil]; |
487 |
[alert show]; |
488 |
[alert release]; |
489 |
} |
490 |
} |
491 |
|
492 |
- (void)viewDidUnload { |
493 |
[super viewDidUnload]; |
494 |
titleView = nil; |
495 |
actionView = nil; |
496 |
rebootButton = nil; |
497 |
pingButton = nil; |
498 |
self.tableView = nil; |
499 |
|
500 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootSucceededObserver]; |
501 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootFailedObserver]; |
502 |
[[NSNotificationCenter defaultCenter] removeObserver:getLimitsSucceededObserver]; |
503 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordSucceededObserver]; |
504 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordFailedObserver]; |
505 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerSucceededObserver]; |
506 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerFailedObserver]; |
507 |
[[NSNotificationCenter defaultCenter] removeObserver:getImageSucceededObserver]; |
508 |
[[NSNotificationCenter defaultCenter] removeObserver:getImageFailedObserver]; |
509 |
[[NSNotificationCenter defaultCenter] removeObserver:updateBackupScheduleSucceededObserver]; |
510 |
[[NSNotificationCenter defaultCenter] removeObserver:updateBackupScheduleFailedObserver]; |
511 |
[[NSNotificationCenter defaultCenter] removeObserver:resizeServerSucceededObserver]; |
512 |
[[NSNotificationCenter defaultCenter] removeObserver:resizeServerFailedObserver]; |
513 |
[[NSNotificationCenter defaultCenter] removeObserver:confirmResizeSucceededObserver]; |
514 |
[[NSNotificationCenter defaultCenter] removeObserver:confirmResizeFailedObserver]; |
515 |
[[NSNotificationCenter defaultCenter] removeObserver:revertResizeSucceededObserver]; |
516 |
[[NSNotificationCenter defaultCenter] removeObserver:revertResizeFailedObserver]; |
517 |
[[NSNotificationCenter defaultCenter] removeObserver:rebuildSucceededObserver]; |
518 |
[[NSNotificationCenter defaultCenter] removeObserver:rebuildFailedObserver]; |
519 |
} |
520 |
|
521 |
- (void)viewWillDisappear:(BOOL)animated { |
522 |
[super viewWillDisappear:animated]; |
523 |
|
524 |
if (polling && pollRequest != nil) { |
525 |
pollRequest.delegate = nil; |
526 |
} |
527 |
|
528 |
// [self.serversViewController.tableView deselectRowAtIndexPath:self.selectedServerIndexPath animated:YES]; |
529 |
|
530 |
[countdownTimer invalidate]; |
531 |
countdownTimer = nil; |
532 |
} |
533 |
|
534 |
#pragma mark - |
535 |
#pragma mark Table view data source |
536 |
|
537 |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { |
538 |
//return self.server ? 5 : 0; |
539 |
return self.server ? 4 : 0; |
540 |
} |
541 |
|
542 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
543 |
if (section == kOverview) { |
544 |
return 2; |
545 |
} else if (section == kDetails) { |
546 |
return 2; |
547 |
} else if (section == kIPAddresses) { |
548 |
NSArray *publicIPs = [self.server.addresses objectForKey:@"public"]; |
549 |
NSArray *privateIPs = [self.server.addresses objectForKey:@"private"]; |
550 |
return [publicIPs count] + [privateIPs count]; |
551 |
} else if (section == kActions) { |
552 |
//return actionsExpanded ? 8 : 1; |
553 |
return 6; |
554 |
} else { |
555 |
return 0; |
556 |
} |
557 |
} |
558 |
|
559 |
- (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font { |
560 |
CGSize textLabelSize; |
561 |
|
562 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
563 |
textLabelSize = CGSizeMake(530.0, 9000.0f); |
564 |
} else { |
565 |
textLabelSize = CGSizeMake(230.0, 9000.0f); |
566 |
} |
567 |
|
568 |
// pad \n\n to fix layout bug |
569 |
CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap]; |
570 |
return stringSize.height; |
571 |
} |
572 |
|
573 |
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
574 |
|
575 |
static NSString *CellIdentifier = @"Cell"; |
576 |
|
577 |
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
578 |
if (cell == nil) { |
579 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease]; |
580 |
//cell.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.93]; |
581 |
cell.textLabel.backgroundColor = [UIColor clearColor]; |
582 |
cell.detailTextLabel.backgroundColor = [UIColor clearColor]; |
583 |
cell.detailTextLabel.adjustsFontSizeToFitWidth = YES; |
584 |
cell.detailTextLabel.lineBreakMode = UILineBreakModeCharacterWrap; |
585 |
cell.detailTextLabel.textAlignment = UITextAlignmentRight; |
586 |
|
587 |
} |
588 |
|
589 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
590 |
cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8]; |
591 |
} |
592 |
|
593 |
cell.textLabel.textColor = [UIColor blackColor]; |
594 |
cell.accessoryView = nil; |
595 |
|
596 |
if (indexPath.section == kOverview) { |
597 |
cell.accessoryType = UITableViewCellAccessoryNone; |
598 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
599 |
cell.accessoryView = nil; |
600 |
|
601 |
if (indexPath.row == kName) { |
602 |
cell.textLabel.text = @"Name"; |
603 |
cell.detailTextLabel.text = server.name; |
604 |
} else if (indexPath.row == kStatus) { |
605 |
if ([server.status isEqualToString:@"BUILD"]) { |
606 |
cell.textLabel.text = @"Building"; |
607 |
cell.detailTextLabel.text = @""; |
608 |
cell.accessoryView = progressView; |
609 |
} else if ([server.status isEqualToString:@"QUEUE_RESIZE"]) { |
610 |
cell.textLabel.text = @"Queueing"; |
611 |
cell.detailTextLabel.text = @""; |
612 |
cell.accessoryView = progressView; |
613 |
} else if ([server.status isEqualToString:@"PREP_RESIZE"]) { |
614 |
cell.textLabel.text = @"Preparing"; |
615 |
cell.detailTextLabel.text = @""; |
616 |
cell.accessoryView = progressView; |
617 |
} else if ([server.status isEqualToString:@"RESIZE"]) { |
618 |
cell.textLabel.text = @"Resizing"; |
619 |
cell.detailTextLabel.text = @""; |
620 |
cell.accessoryView = progressView; |
621 |
} else if ([server.status isEqualToString:@"VERIFY_RESIZE"]) { |
622 |
cell.textLabel.text = @"Status"; |
623 |
cell.detailTextLabel.text = @"Resize Complete"; |
624 |
} else if ([server.status isEqualToString:@"REBUILD"]) { |
625 |
cell.textLabel.text = @"Rebuilding"; |
626 |
cell.detailTextLabel.text = @""; |
627 |
cell.accessoryView = progressView; |
628 |
} else { |
629 |
cell.textLabel.text = @"Status"; |
630 |
cell.detailTextLabel.text = server.status; |
631 |
} |
632 |
|
633 |
} else if (indexPath.row == kHostId) { |
634 |
cell.textLabel.text = @"Host ID"; |
635 |
cell.detailTextLabel.text = server.hostId; |
636 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
637 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
638 |
} |
639 |
} else if (indexPath.section == kDetails) { |
640 |
cell.accessoryType = UITableViewCellAccessoryNone; |
641 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
642 |
cell.accessoryView = nil; |
643 |
|
644 |
if (indexPath.row == kImage) { |
645 |
cell.textLabel.text = @"Image"; |
646 |
cell.detailTextLabel.text = server.image.name; |
647 |
} else if (indexPath.row == kMemory) { |
648 |
cell.textLabel.text = @"Size"; |
649 |
cell.detailTextLabel.text = [NSString stringWithFormat:@"%i MB RAM, %i GB Disk", self.server.flavor.ram, self.server.flavor.disk]; |
650 |
} else if (indexPath.row == kDisk) { |
651 |
cell.textLabel.text = @"Disk"; |
652 |
cell.detailTextLabel.text = [NSString stringWithFormat:@"%i GB", server.flavor.disk]; |
653 |
} |
654 |
} else if (indexPath.section == kIPAddresses) { |
655 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
656 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
657 |
cell.accessoryView = nil; |
658 |
|
659 |
NSArray *publicIPs = [server.addresses objectForKey:@"public"]; |
660 |
NSArray *privateIPs = [server.addresses objectForKey:@"private"]; |
661 |
|
662 |
if (indexPath.row < [publicIPs count]) { |
663 |
cell.textLabel.text = @"Public"; |
664 |
cell.detailTextLabel.text = [publicIPs objectAtIndex:indexPath.row]; |
665 |
if ([[publicIPs objectAtIndex:indexPath.row] isKindOfClass:[NSString class]]) { |
666 |
// v1.0 API |
667 |
cell.detailTextLabel.text = [publicIPs objectAtIndex:indexPath.row]; |
668 |
} else { |
669 |
// v1.1 API |
670 |
cell.detailTextLabel.text = [[publicIPs objectAtIndex:indexPath.row] objectForKey:@"addr"]; |
671 |
} |
672 |
} else { |
673 |
cell.textLabel.text = @"Private"; |
674 |
cell.detailTextLabel.text = [privateIPs objectAtIndex:indexPath.row - [publicIPs count]]; |
675 |
} |
676 |
} else if (indexPath.section == kActions) { |
677 |
cell.textLabel.text = @"Actions"; |
678 |
cell.detailTextLabel.text = @""; |
679 |
cell.accessoryView = nil; |
680 |
if (indexPath.row == kActionsRow) { |
681 |
cell.accessoryType = UITableViewCellAccessoryNone; |
682 |
cell.accessoryView = actionsArrow; |
683 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
684 |
cell.textLabel.textColor = actionsExpanded ? cell.detailTextLabel.textColor : [UIColor blackColor]; |
685 |
} else { |
686 |
if (performingAction) { |
687 |
cell.textLabel.textColor = [UIColor grayColor]; |
688 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
689 |
cell.accessoryType = UITableViewCellAccessoryNone; |
690 |
} else { |
691 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
692 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
693 |
} |
694 |
|
695 |
if (indexPath.row == kReboot) { |
696 |
cell.textLabel.text = @"Reboot Server"; |
697 |
if (![rebootCountdown isEqualToString:@""]) { |
698 |
cell.detailTextLabel.text = rebootCountdown; |
699 |
cell.accessoryType = UITableViewCellAccessoryNone; |
700 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
701 |
} else { |
702 |
cell.detailTextLabel.text = @""; |
703 |
} |
704 |
} else if (indexPath.row == kRename) { |
705 |
cell.textLabel.text = @"Rename Server"; |
706 |
if (renameCountdown && ![renameCountdown isEqualToString:@""]) { |
707 |
cell.detailTextLabel.text = renameCountdown; |
708 |
cell.accessoryType = UITableViewCellAccessoryNone; |
709 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
710 |
} else { |
711 |
cell.detailTextLabel.text = @""; |
712 |
} |
713 |
} else if (indexPath.row == kResize) { |
714 |
cell.textLabel.text = @"Resize Server"; |
715 |
if (![resizeCountdown isEqualToString:@""]) { |
716 |
cell.detailTextLabel.text = resizeCountdown; |
717 |
cell.accessoryType = UITableViewCellAccessoryNone; |
718 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
719 |
} else { |
720 |
cell.detailTextLabel.text = @""; |
721 |
} |
722 |
} else if (indexPath.row == kChangePassword) { |
723 |
cell.textLabel.text = @"Change Root Password"; |
724 |
if (![changePasswordCountdown isEqualToString:@""]) { |
725 |
cell.detailTextLabel.text = changePasswordCountdown; |
726 |
cell.accessoryType = UITableViewCellAccessoryNone; |
727 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
728 |
} else { |
729 |
cell.detailTextLabel.text = @""; |
730 |
} |
731 |
} else if (indexPath.row == kBackups) { |
732 |
cell.textLabel.text = @"Manage Backup Schedules"; |
733 |
if (![backupsCountdown isEqualToString:@""]) { |
734 |
cell.detailTextLabel.text = backupsCountdown; |
735 |
cell.accessoryType = UITableViewCellAccessoryNone; |
736 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
737 |
} else { |
738 |
cell.detailTextLabel.text = @""; |
739 |
} |
740 |
} else if (indexPath.row == kRebuild) { |
741 |
cell.textLabel.text = @"Rebuild Server"; |
742 |
if (![rebuildCountdown isEqualToString:@""]) { |
743 |
cell.detailTextLabel.text = rebuildCountdown; |
744 |
cell.accessoryType = UITableViewCellAccessoryNone; |
745 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
746 |
} else { |
747 |
cell.detailTextLabel.text = @""; |
748 |
} |
749 |
} else if (indexPath.row == kDelete) { |
750 |
cell.textLabel.text = @"Delete Server"; |
751 |
if (![deleteCountdown isEqualToString:@""]) { |
752 |
cell.detailTextLabel.text = deleteCountdown; |
753 |
cell.accessoryType = UITableViewCellAccessoryNone; |
754 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
755 |
} else { |
756 |
cell.detailTextLabel.text = @""; |
757 |
} |
758 |
} |
759 |
} |
760 |
} |
761 |
|
762 |
return cell; |
763 |
} |
764 |
|
765 |
#pragma mark - |
766 |
#pragma mark Table view delegate |
767 |
|
768 |
- (void)reloadActionsTitleRow { |
769 |
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:kActions]] withRowAnimation:UITableViewRowAnimationNone]; |
770 |
} |
771 |
|
772 |
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
773 |
|
774 |
if (indexPath.section == kOverview) { |
775 |
if (indexPath.row == kHostId) { |
776 |
ServersOnHostViewController *vc = [[ServersOnHostViewController alloc] initWithNibName:@"ServersOnHostViewController" bundle:nil]; |
777 |
vc.account = self.account; |
778 |
vc.hostID = self.server.hostId; |
779 |
[self.navigationController pushViewController:vc animated:YES]; |
780 |
[vc release]; |
781 |
|
782 |
// deselect the row |
783 |
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self.tableView selector:@selector(reloadData) userInfo:nil repeats:NO]; |
784 |
} |
785 |
} else if (indexPath.section == kIPAddresses) { |
786 |
NSArray *publicIPs = [server.addresses objectForKey:@"public"]; |
787 |
NSArray *privateIPs = [server.addresses objectForKey:@"private"]; |
788 |
if (indexPath.row < [publicIPs count]) { |
789 |
if ([[publicIPs objectAtIndex:indexPath.row] isKindOfClass:[NSString class]]) { |
790 |
selectedIPAddress = [publicIPs objectAtIndex:indexPath.row]; |
791 |
} else { |
792 |
selectedIPAddress = [[publicIPs objectAtIndex:indexPath.row] objectForKey:@"addr"]; |
793 |
} |
794 |
} else { |
795 |
if ([[privateIPs objectAtIndex:[publicIPs count] - indexPath.row] isKindOfClass:[NSString class]]) { |
796 |
selectedIPAddress = [privateIPs objectAtIndex:[publicIPs count] - indexPath.row]; |
797 |
} else { |
798 |
selectedIPAddress = [[privateIPs objectAtIndex:[publicIPs count] - indexPath.row] objectForKey:@"addr"]; |
799 |
} |
800 |
} |
801 |
selectedIPAddressIndexPath = indexPath; |
802 |
|
803 |
ipAddressActionSheet.title = selectedIPAddress; |
804 |
[ipAddressActionSheet showInView:self.view]; |
805 |
} else if (indexPath.section == kActions) { |
806 |
if (indexPath.row == kActionsRow) { |
807 |
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:7]; |
808 |
for (int i = 1; i < 8; i++) { |
809 |
[indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:kActions]]; |
810 |
} |
811 |
if (actionsExpanded) { |
812 |
actionsExpanded = NO; |
813 |
[tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop]; |
814 |
} else { |
815 |
actionsExpanded = YES; |
816 |
[tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationBottom]; |
817 |
[tableView scrollToRowAtIndexPath:[indexPaths lastObject] atScrollPosition:UITableViewScrollPositionBottom animated:YES]; |
818 |
} |
819 |
[indexPaths release]; |
820 |
[tableView deselectRowAtIndexPath:indexPath animated:YES]; |
821 |
[NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(reloadActionsTitleRow) userInfo:nil repeats:NO]; |
822 |
|
823 |
// flip the arrow |
824 |
[UIView beginAnimations:nil context:NULL]; |
825 |
[UIView setAnimationDuration:0.4]; |
826 |
if (actionsExpanded) { |
827 |
actionsArrow.transform = CGAffineTransformMakeRotation(0.0); |
828 |
} else { |
829 |
actionsArrow.transform = CGAffineTransformMakeRotation(180.0 * M_PI / 180.0); |
830 |
} |
831 |
[UIView commitAnimations]; |
832 |
|
833 |
} else if (indexPath.row == kReboot) { |
834 |
if (!performingAction && [rebootCountdown isEqualToString:@""]) { |
835 |
[rebootActionSheet showInView:self.view]; |
836 |
} else if (![rebootCountdown isEqualToString:@""]) { |
837 |
[self alert:nil message:@"Reboots are not available because the API rate limit has been reached for this account. These actions will be available as soon as the countdown reaches zero."]; |
838 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
839 |
} else { |
840 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
841 |
} |
842 |
} else if (indexPath.row == kRename) { |
843 |
if (!performingAction) { |
844 |
RenameServerViewController *vc = [[RenameServerViewController alloc] initWithNibName:@"RenameServerViewController" bundle:nil]; |
845 |
vc.serverViewController = self; |
846 |
vc.actionIndexPath = indexPath; |
847 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
848 |
vc.modalPresentationStyle = UIModalPresentationFormSheet; |
849 |
} |
850 |
[self.navigationController presentModalViewController:vc animated:YES]; |
851 |
[vc release]; |
852 |
} else { |
853 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
854 |
} |
855 |
} else if (indexPath.row == kResize) { |
856 |
if (!performingAction) { |
857 |
ResizeServerViewController *vc = [[ResizeServerViewController alloc] initWithNibName:@"ResizeServerViewController" bundle:nil]; |
858 |
vc.serverViewController = self; |
859 |
vc.actionIndexPath = indexPath; |
860 |
vc.account = account; |
861 |
vc.server = server; |
862 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
863 |
vc.modalPresentationStyle = UIModalPresentationFormSheet; |
864 |
} |
865 |
[self.navigationController presentModalViewController:vc animated:YES]; |
866 |
[vc release]; |
867 |
} else { |
868 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
869 |
} |
870 |
} else if (indexPath.row == kRebuild) { |
871 |
SimpleImagePickerViewController *vc = [[SimpleImagePickerViewController alloc] initWithNibName:@"SimpleImagePickerViewController" bundle:nil]; |
872 |
vc.mode = kModeRebuildServer; |
873 |
vc.account = self.account; |
874 |
vc.selectedImageId = self.server.imageId; |
875 |
vc.serverViewController = self; |
876 |
vc.delegate = self; |
877 |
[self presentModalViewControllerWithNavigation:vc]; |
878 |
[vc release]; |
879 |
} else if (indexPath.row == kChangePassword) { |
880 |
if (!performingAction) { |
881 |
ResetServerAdminPasswordViewController *vc = [[ResetServerAdminPasswordViewController alloc] initWithNibName:@"ResetServerAdminPasswordViewController" bundle:nil]; |
882 |
vc.serverViewController = self; |
883 |
vc.actionIndexPath = indexPath; |
884 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
885 |
vc.modalPresentationStyle = UIModalPresentationFormSheet; |
886 |
} |
887 |
[self.navigationController presentModalViewController:vc animated:YES]; |
888 |
[vc release]; |
889 |
} else { |
890 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
891 |
} |
892 |
} else if (indexPath.row == kBackups) { |
893 |
if (!performingAction) { |
894 |
ManageBackupScheduleViewController *vc = [[ManageBackupScheduleViewController alloc] initWithNibName:@"ManageBackupScheduleViewController" bundle:nil]; |
895 |
vc.serverViewController = self; |
896 |
vc.actionIndexPath = indexPath; |
897 |
vc.account = self.account; |
898 |
vc.server = self.server; |
899 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
900 |
vc.modalPresentationStyle = UIModalPresentationFormSheet; |
901 |
} |
902 |
[self.navigationController presentModalViewController:vc animated:YES]; |
903 |
[vc release]; |
904 |
} else { |
905 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
906 |
} |
907 |
} else if (indexPath.row == kDelete) { |
908 |
if (!performingAction) { |
909 |
[deleteActionSheet showInView:self.view]; |
910 |
} else { |
911 |
[self.tableView deselectRowAtIndexPath:indexPath animated:NO]; |
912 |
} |
913 |
} |
914 |
} |
915 |
} |
916 |
|
917 |
#pragma mark - |
918 |
#pragma mark Action Sheet Delegate |
919 |
|
920 |
- (void)changeAdminPassword:(NSString *)password { |
921 |
[self showToolbarActivityMessage:@"Changing password..."]; |
922 |
performingAction = YES; |
923 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
924 |
|
925 |
// handle success |
926 |
changeAdminPasswordSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"changeAdminPasswordSucceeded" object:self.server |
927 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
928 |
{ |
929 |
[self hideToolbarActivityMessage]; |
930 |
performingAction = NO; |
931 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
932 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordSucceededObserver]; |
933 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordFailedObserver]; |
934 |
}]; |
935 |
|
936 |
// handle failure |
937 |
changeAdminPasswordFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"changeAdminPasswordFailed" object:self.server |
938 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
939 |
{ |
940 |
[self alert:@"There was a problem changing the root password." request:[notification.userInfo objectForKey:@"request"]]; |
941 |
[self hideToolbarActivityMessage]; |
942 |
performingAction = NO; |
943 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
944 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordSucceededObserver]; |
945 |
[[NSNotificationCenter defaultCenter] removeObserver:changeAdminPasswordFailedObserver]; |
946 |
}]; |
947 |
|
948 |
[self.account.manager changeAdminPassword:self.server password:password]; |
949 |
} |
950 |
|
951 |
- (void)removeServersListRow { |
952 |
// if ([self.account.servers count] == 0) { |
953 |
[self.serversViewController.tableView reloadData]; |
954 |
// } else { |
955 |
// [self.serversViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedServerIndexPath] withRowAnimation:UITableViewRowAnimationLeft]; |
956 |
// } |
957 |
} |
958 |
|
959 |
- (void)popViewController { |
960 |
[self.navigationController popViewControllerAnimated:YES]; |
961 |
} |
962 |
|
963 |
- (void)deleteServer { |
964 |
[self showToolbarActivityMessage:@"Deleting server..."]; |
965 |
performingAction = YES; |
966 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
967 |
|
968 |
// handle success |
969 |
deleteServerSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteServerSucceeded" object:self.server |
970 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
971 |
{ |
972 |
[self hideToolbarActivityMessage]; |
973 |
performingAction = NO; |
974 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
975 |
|
976 |
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:self.account.servers]; |
977 |
[dict removeObjectForKey:self.server.identifier]; |
978 |
self.account.servers = [[[NSMutableDictionary alloc] initWithDictionary:dict] autorelease]; // TODO: release |
979 |
[self.account persist]; |
980 |
[dict release]; |
981 |
|
982 |
self.serversViewController.account.servers = self.account.servers; |
983 |
|
984 |
if ([self.account.servers count] == 0 && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
985 |
// on ipad, delete needs to get rid of the server on the main view |
986 |
self.server = nil; |
987 |
[self setBackgroundView]; |
988 |
[self.tableView reloadData]; |
989 |
} else { |
990 |
[self.navigationController popViewControllerAnimated:YES]; |
991 |
} |
992 |
[self.serversViewController.tableView selectRowAtIndexPath:selectedServerIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; |
993 |
[NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(removeServersListRow) userInfo:nil repeats:NO]; |
994 |
|
995 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerSucceededObserver]; |
996 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerFailedObserver]; |
997 |
}]; |
998 |
|
999 |
// handle failure |
1000 |
deleteServerFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"deleteServerFailed" object:self.server |
1001 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
1002 |
{ |
1003 |
[self alert:@"There was a problem deleting this server." request:[notification.userInfo objectForKey:@"request"]]; |
1004 |
[self hideToolbarActivityMessage]; |
1005 |
performingAction = NO; |
1006 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1007 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerFailedObserver]; |
1008 |
[[NSNotificationCenter defaultCenter] removeObserver:deleteServerSucceededObserver]; |
1009 |
}]; |
1010 |
|
1011 |
[self.account.manager deleteServer:self.server]; |
1012 |
} |
1013 |
|
1014 |
- (void)showAction:(NSString *)name { |
1015 |
[self showToolbarActivityMessage:name]; |
1016 |
//performingAction = YES; |
1017 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1018 |
} |
1019 |
|
1020 |
- (void)hideAction { |
1021 |
[self hideToolbarActivityMessage]; |
1022 |
performingAction = NO; |
1023 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1024 |
} |
1025 |
|
1026 |
- (void)renameServer:(NSString *)name { |
1027 |
[self showAction:@"Renaming server..."]; |
1028 |
[[self.account.manager renameServer:self.server name:name] success:^(OpenStackRequest *request) { |
1029 |
[self hideAction]; |
1030 |
[self.account refreshCollections]; |
1031 |
self.server.name = name; |
1032 |
titleView.nameLabel.text = self.server.name; |
1033 |
[self.serversViewController.tableView reloadData]; |
1034 |
} failure:^(OpenStackRequest *request) { |
1035 |
[self alert:@"There was a problem renaming this server." request:request]; |
1036 |
[self hideAction]; |
1037 |
}]; |
1038 |
} |
1039 |
|
1040 |
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { |
1041 |
if ([actionSheet isEqual:ipAddressActionSheet]) { |
1042 |
|
1043 |
UIApplication *application = [UIApplication sharedApplication]; |
1044 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@", selectedIPAddress]]; |
1045 |
|
1046 |
if (buttonIndex == 0) { // Ping IP Address |
1047 |
|
1048 |
TrackEvent(CATEGORY_SERVER, EVENT_PINGED); |
1049 |
|
1050 |
PingIPAddressViewController *vc = [[PingIPAddressViewController alloc] initWithNibName:@"PingIPAddressViewController" bundle:nil ipAddress:selectedIPAddress]; |
1051 |
vc.serverViewController = self; |
1052 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
1053 |
vc.modalPresentationStyle = UIModalPresentationPageSheet; |
1054 |
} |
1055 |
[self.navigationController presentModalViewController:vc animated:YES]; |
1056 |
[vc release]; |
1057 |
[self.tableView deselectRowAtIndexPath:selectedIPAddressIndexPath animated:YES]; |
1058 |
} else if (buttonIndex == 1) { // Copy to Pasteboard |
1059 |
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; |
1060 |
[pasteboard setString:selectedIPAddress]; |
1061 |
[self.tableView deselectRowAtIndexPath:selectedIPAddressIndexPath animated:YES]; |
1062 |
} else if (buttonIndex == 2) { |
1063 |
UIApplication *application = [UIApplication sharedApplication]; |
1064 |
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", selectedIPAddress]]; |
1065 |
if ([application canOpenURL:url]) { |
1066 |
[application openURL:url]; |
1067 |
} |
1068 |
[self.tableView deselectRowAtIndexPath:selectedIPAddressIndexPath animated:YES]; |
1069 |
} else if (buttonIndex == 3 && [application canOpenURL:url]) { |
1070 |
[application openURL:url]; |
1071 |
} else { |
1072 |
[self.tableView deselectRowAtIndexPath:selectedIPAddressIndexPath animated:YES]; |
1073 |
} |
1074 |
} else if ([actionSheet isEqual:rebootActionSheet]) { |
1075 |
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:kReboot inSection:kActions] animated:YES]; |
1076 |
|
1077 |
if (buttonIndex != 2) { |
1078 |
[self showToolbarActivityMessage:@"Rebooting..."]; |
1079 |
performingAction = YES; |
1080 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1081 |
|
1082 |
// handle success |
1083 |
rebootSucceededObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"rebootSucceeded" object:self.server |
1084 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
1085 |
{ |
1086 |
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(pollServer) userInfo:nil repeats:NO]; |
1087 |
[self hideToolbarActivityMessage]; |
1088 |
performingAction = NO; |
1089 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1090 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootSucceededObserver]; |
1091 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootFailedObserver]; |
1092 |
}]; |
1093 |
|
1094 |
// handle failure |
1095 |
rebootFailedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:@"rebootFailed" object:self.server |
1096 |
queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification) |
1097 |
{ |
1098 |
[self alert:@"There was a problem rebooting this server." request:[notification.userInfo objectForKey:@"request"]]; |
1099 |
[self hideToolbarActivityMessage]; |
1100 |
performingAction = NO; |
1101 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationFade]; |
1102 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootSucceededObserver]; |
1103 |
[[NSNotificationCenter defaultCenter] removeObserver:rebootFailedObserver]; |
1104 |
}]; |
1105 |
|
1106 |
if (buttonIndex == 0) { |
1107 |
[self.account.manager hardRebootServer:self.server]; |
1108 |
} else if (buttonIndex == 1) { |
1109 |
[self.account.manager softRebootServer:self.server]; |
1110 |
} |
1111 |
} |
1112 |
|
1113 |
} else if ([actionSheet isEqual:deleteActionSheet]) { |
1114 |
if (buttonIndex == 0) { |
1115 |
[self deleteServer]; |
1116 |
} |
1117 |
[self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:kDelete inSection:kActions] animated:YES]; |
1118 |
} |
1119 |
} |
1120 |
|
1121 |
#pragma mark - |
1122 |
#pragma mark Resize Alert Delegate |
1123 |
|
1124 |
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { |
1125 |
if (buttonIndex == 1) { |
1126 |
[self.account.manager confirmResizeServer:self.server]; |
1127 |
[self showToolbarActivityMessage:@"Confirming resize..."]; |
1128 |
} else if (buttonIndex == 2) { |
1129 |
[self.account.manager revertResizeServer:self.server]; |
1130 |
[self showToolbarActivityMessage:@"Reverting resize..."]; |
1131 |
} |
1132 |
} |
1133 |
|
1134 |
#pragma - Button Handlers |
1135 |
|
1136 |
- (IBAction)rebootButtonPressed:(id)sender { |
1137 |
if (!performingAction && [rebootCountdown isEqualToString:@""]) { |
1138 |
[rebootActionSheet showInView:self.view]; |
1139 |
} else if (![rebootCountdown isEqualToString:@""]) { |
1140 |
[self alert:nil message:@"Reboots are not available because the API rate limit has been reached for this account. These actions will be available as soon as the countdown reaches zero."]; |
1141 |
} |
1142 |
} |
1143 |
|
1144 |
- (IBAction)snapshotButtonPressed:(id)sender { |
1145 |
|
1146 |
} |
1147 |
|
1148 |
- (IBAction)pingIPButtonPressed:(id)sender { |
1149 |
if ([[server.addresses objectForKey:@"public"] count] > 0) { |
1150 |
|
1151 |
TrackEvent(CATEGORY_SERVER, EVENT_PINGED); |
1152 |
|
1153 |
NSString *ipAddress = [[server.addresses objectForKey:@"public"] objectAtIndex:0]; |
1154 |
PingIPAddressViewController *vc = [[PingIPAddressViewController alloc] initWithNibName:@"PingIPAddressViewController" bundle:nil ipAddress:ipAddress]; |
1155 |
vc.serverViewController = self; |
1156 |
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { |
1157 |
vc.modalPresentationStyle = UIModalPresentationPageSheet; |
1158 |
} |
1159 |
[self.navigationController presentModalViewController:vc animated:YES]; |
1160 |
[vc release]; |
1161 |
} |
1162 |
} |
1163 |
|
1164 |
#pragma mark - |
1165 |
#pragma mark Memory management |
1166 |
|
1167 |
- (void)dealloc { |
1168 |
[server release]; |
1169 |
[account release]; |
1170 |
[tableView release]; |
1171 |
[selectedIPAddressIndexPath release]; |
1172 |
[ipAddressActionSheet release]; |
1173 |
[rebootActionSheet release]; |
1174 |
[deleteActionSheet release]; |
1175 |
[serversViewController release]; |
1176 |
[selectedServerIndexPath release]; |
1177 |
[progressView release]; |
1178 |
[actionsArrow release]; |
1179 |
[accountHomeViewController release]; |
1180 |
[super dealloc]; |
1181 |
} |
1182 |
|
1183 |
|
1184 |
@end |
1185 |
|