2 // LoadBalancerViewController.m
5 // Created by Michael Mayo on 3/25/11.
6 // Copyright 2011 __MyCompanyName__. All rights reserved.
9 #import "LoadBalancerViewController.h"
10 #import "LoadBalancer.h"
11 #import "LoadBalancerNode.h"
12 #import <QuartzCore/QuartzCore.h>
13 #import "LBTitleView.h"
14 #import "LoadBalancerProtocol.h"
16 #import "ConfigureLoadBalancerViewController.h"
17 #import "OpenStackAccount.h"
18 #import "AccountManager.h"
19 #import "APICallback.h"
20 #import "LoadBalancerUsage.h"
21 #import "NSObject+Conveniences.h"
23 #import "UIViewController+Conveniences.h"
24 #import "LBNodeViewController.h"
30 #define kEnabled @"ENABLED"
31 #define kDisabled @"DISABLED"
32 #define kDraining @"DRAINING"
34 @implementation LoadBalancerViewController
36 @synthesize account, loadBalancer, tableView, titleView;
38 #pragma mark - Constructors and Memory Management
40 -(id)initWithLoadBalancer:(LoadBalancer *)lb {
41 self = [self initWithNibName:@"LoadBalancerViewController" bundle:nil];
43 self.loadBalancer = lb;
45 nodes = [[NSMutableDictionary alloc] init];
52 [loadBalancer release];
59 #pragma mark - Utilities
61 - (LoadBalancerNode *)nodeForIndexPath:(NSIndexPath *)indexPath {
62 LoadBalancerNode *node = nil;
63 if (indexPath.section == enabledSection) {
64 node = [[nodes objectForKey:kEnabled] objectAtIndex:indexPath.row];
65 } else if (indexPath.section == drainingSection) {
66 node = [[nodes objectForKey:kDraining] objectAtIndex:indexPath.row];
67 } else if (indexPath.section == disabledSection) {
68 node = [[nodes objectForKey:kDisabled] objectAtIndex:indexPath.row];
73 - (void)pollLoadBalancer {
74 NSString *endpoint = [self.account loadBalancerEndpointForRegion:self.loadBalancer.region];
75 [[self.account.manager getLoadBalancerDetails:self.loadBalancer endpoint:endpoint] success:^(OpenStackRequest *request) {
76 self.titleView.statusDot.image = [self.loadBalancer imageForStatus];
77 if ([self.loadBalancer shouldBePolled]) {
78 [self pollLoadBalancer];
80 } failure:^(OpenStackRequest *request) {
84 #pragma mark - View lifecycle
88 self.navigationItem.title = @"Load Balancer";
89 previousScrollPoint = CGPointZero;
92 titleView = [[LBTitleView alloc] initWithLoadBalancer:self.loadBalancer];
93 [self.view addSubview:titleView];
94 [titleView setNeedsDisplay];
97 // Uncomment the following line to preserve selection between presentations.
98 // self.clearsSelectionOnViewWillAppear = NO;
100 // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
101 //self.navigationItem.rightBarButtonItem = self.editButtonItem;
102 UIBarButtonItem *configure = [[UIBarButtonItem alloc] initWithTitle:@"Configure" style:UIBarButtonItemStyleBordered target:self action:@selector(configButtonPressed:)];
103 self.navigationItem.rightBarButtonItem = configure;
107 - (void)viewDidUnload {
108 [super viewDidUnload];
109 self.tableView = nil;
112 - (void)viewWillAppear:(BOOL)animated {
113 [super viewWillAppear:animated];
115 NSString *endpoint = [self.account loadBalancerEndpointForRegion:self.loadBalancer.region];
117 [self showToolbarActivityMessage:@"Loading nodes..."];
119 [[self.account.manager getLoadBalancerDetails:self.loadBalancer endpoint:endpoint] success:^(OpenStackRequest *request) {
121 [self hideToolbarActivityMessage];
123 // break up nodes by condition into ENABLED, DISABLED, and DRAINING
124 [nodes setObject:[NSMutableArray array] forKey:kEnabled];
125 [nodes setObject:[NSMutableArray array] forKey:kDisabled];
126 [nodes setObject:[NSMutableArray array] forKey:kDraining];
128 for (LoadBalancerNode *node in loadBalancer.nodes) {
129 if ([node.condition isEqualToString:kEnabled]) {
130 [[nodes objectForKey:kEnabled] addObject:node];
131 } else if ([node.condition isEqualToString:kDisabled]) {
132 [[nodes objectForKey:kDisabled] addObject:node];
133 } else if ([node.condition isEqualToString:kDraining]) {
134 [[nodes objectForKey:kDraining] addObject:node];
138 // sort each node group alphabetically
139 NSArray *sortedEnabled = [[nodes objectForKey:kEnabled] sortedArrayUsingSelector:@selector(compare:)];
140 NSArray *sortedDisabled = [[nodes objectForKey:kDisabled] sortedArrayUsingSelector:@selector(compare:)];
141 NSArray *sortedDraining = [[nodes objectForKey:kDraining] sortedArrayUsingSelector:@selector(compare:)];
142 [nodes setObject:[NSMutableArray arrayWithArray:sortedEnabled] forKey:kEnabled];
143 [nodes setObject:[NSMutableArray arrayWithArray:sortedDisabled] forKey:kDisabled];
144 [nodes setObject:[NSMutableArray arrayWithArray:sortedDraining] forKey:kDraining];
147 if ([[nodes objectForKey:kEnabled] count] > 0) {
148 enabledSection = totalSections++;
150 if ([[nodes objectForKey:kDisabled] count] > 0) {
151 disabledSection = totalSections++;
153 if ([[nodes objectForKey:kDraining] count] > 0) {
154 drainingSection = totalSections++;
157 [self.tableView reloadData];
158 } failure:^(OpenStackRequest *request) {
159 [self hideToolbarActivityMessage];
160 [self alert:@"There was a problem loading information for this load balancer." request:request];
163 [[self.account.manager getLoadBalancerUsage:self.loadBalancer endpoint:endpoint] success:^(OpenStackRequest *request) {
164 self.titleView.connectedLabel.text = [NSString stringWithFormat:@"%.0f connected", self.loadBalancer.usage.averageNumConnections];
165 self.titleView.bwInLabel.text = [NSString stringWithFormat:@"%@ in", [LoadBalancerUsage humanizedBytes:self.loadBalancer.usage.incomingTransfer]];
166 self.titleView.bwOutLabel.text = [NSString stringWithFormat:@"%@ out", [LoadBalancerUsage humanizedBytes:self.loadBalancer.usage.outgoingTransfer]];
167 } failure:^(OpenStackRequest *request) {
168 self.titleView.connectedLabel.text = @"";
169 self.titleView.bwInLabel.text = @"";
170 self.titleView.bwOutLabel.text = @"";
173 if ([self.loadBalancer shouldBePolled]) {
174 [self pollLoadBalancer];
178 - (void)viewDidAppear:(BOOL)animated {
179 [super viewDidAppear:animated];
182 - (void)viewWillDisappear:(BOOL)animated {
183 [super viewWillDisappear:animated];
186 - (void)viewDidDisappear:(BOOL)animated {
187 [super viewDidDisappear:animated];
190 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
191 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (interfaceOrientation == UIInterfaceOrientationPortrait);
194 #pragma mark - Table view data source
196 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
197 return totalSections;
200 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
201 if (section == enabledSection) {
202 return [[nodes objectForKey:kEnabled] count];
203 } else if (section == drainingSection) {
204 return [[nodes objectForKey:kDraining] count];
205 } else if (section == disabledSection) {
206 return [[nodes objectForKey:kDisabled] count];
212 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
213 if (section == enabledSection) {
214 return @"Enabled Nodes";
215 } else if (section == disabledSection) {
216 return @"Disabled Nodes";
217 } else if (section == drainingSection) {
218 return @"Draining Nodes";
224 - (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
225 static NSString *CellIdentifier = @"Cell";
227 UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
229 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
230 cell.textLabel.backgroundColor = [UIColor clearColor];
231 cell.detailTextLabel.backgroundColor = [UIColor clearColor];
232 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
235 LoadBalancerNode *node = [self nodeForIndexPath:indexPath];
238 Server *server = node.server;
239 cell.textLabel.text = server.name;
240 cell.detailTextLabel.text = node.address;
241 if ([server.image respondsToSelector:@selector(logoPrefix)]) {
242 if ([[server.image logoPrefix] isEqualToString:kCustomImage]) {
243 cell.imageView.image = [UIImage imageNamed:kCloudServersIcon];
245 cell.imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%@-icon.png", [server.image logoPrefix]]];
249 cell.textLabel.text = [NSString stringWithFormat:@"%@:%@", node.address, node.port];
250 cell.detailTextLabel.text = @"";
251 cell.imageView.image = nil;
258 #pragma mark - Table view delegate
260 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
261 LoadBalancerNode *node = [self nodeForIndexPath:indexPath];
262 LBNodeViewController *vc = [[LBNodeViewController alloc] initWithNode:node loadBalancer:self.loadBalancer account:self.account];
263 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
264 vc.lbViewController = self;
265 vc.lbIndexPath = indexPath;
266 [self presentModalViewControllerWithNavigation:vc];
268 [self.navigationController pushViewController:vc animated:YES];
273 #pragma mark - Button Handlers
275 - (void)configButtonPressed:(id)sender {
277 if ([self.loadBalancer shouldBePolled]) {
278 [self alert:nil message:@"This load balancer can not be changed until it is in an active state."];
280 ConfigureLoadBalancerViewController *vc = [[ConfigureLoadBalancerViewController alloc] initWithAccount:self.account loadBalancer:self.loadBalancer];
281 vc.loadBalancerViewController = self;
282 [self presentModalViewControllerWithNavigation:vc];