2 // ConfigureLoadBalancerViewController.m
5 // Created by Mike Mayo on 6/22/11.
6 // Copyright 2011 __MyCompanyName__. All rights reserved.
9 #import "ConfigureLoadBalancerViewController.h"
10 #import "LoadBalancer.h"
11 #import "LBProtocolViewController.h"
12 #import "OpenStackAccount.h"
13 #import "AccountManager.h"
14 #import "RSTextFieldCell.h"
15 #import "LBNodesViewController.h"
16 #import "LBAlgorithmViewController.h"
17 #import "AddLoadBalancerAlgorithmViewController.h"
18 #import "LoadBalancerProtocol.h"
19 #import "UIViewController+Conveniences.h"
20 #import "OpenStackRequest.h"
21 #import "APICallback.h"
22 #import "UIColor+MoreColors.h"
25 #import "PingIPAddressViewController.h"
26 #import "LoadBalancersViewController.h"
27 #import "LoadBalancerViewController.h"
29 #define kDetailsSection 0
30 #define kRegionSection 1
31 #define kVirtualIPsSection 2
32 #define kNodesSection 3
33 #define kConnectionLoggingSection 4
34 #define kDeleteSection 5
40 #define kVirtualIPType 0
43 @implementation ConfigureLoadBalancerViewController
45 @synthesize account, loadBalancer, loadBalancerViewController;
47 - (id)initWithAccount:(OpenStackAccount *)a loadBalancer:(LoadBalancer *)lb {
48 self = [super initWithStyle:UITableViewStyleGrouped];
51 self.loadBalancer = lb;
58 [loadBalancer release];
59 [algorithmNames release];
60 [deleteActionSheet release];
61 [ipActionSheet release];
62 [loadBalancerViewController release];
66 #pragma mark - View lifecycle
70 self.navigationItem.title = @"Configure";
73 // if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
74 [self addCancelButton];
77 algorithmNames = [[NSDictionary alloc] initWithObjectsAndKeys:
79 @"Round Robin", @"ROUND_ROBIN",
80 @"Weighted Round Robin", @"WEIGHTED_ROUND_ROBIN",
81 @"Least Connections", @"LEAST_CONNECTIONS",
82 @"Weighted Least Connections", @"WEIGHTED_LEAST_CONNECTIONS",
85 ipActionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Ping IP Address", @"Copy to Pasteboard", @"Open in Safari", nil];
86 deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this load balancer? This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete Load Balancer" otherButtonTitles:nil];
89 - (void)viewWillAppear:(BOOL)animated {
90 [super viewWillAppear:animated];
93 - (void)viewDidAppear:(BOOL)animated {
94 [super viewDidAppear:animated];
95 [self.tableView reloadData];
98 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
99 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
102 #pragma mark - Table view data source
104 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
108 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
109 if (section == kDetailsSection) {
111 } else if (section == kRegionSection) {
113 } else if (section == kVirtualIPsSection) {
114 return [self.loadBalancer.virtualIPs count];
120 - (UITableViewCell *)nameCell:(UITableView *)tableView {
121 static NSString *CellIdentifier = @"NameCell";
123 RSTextFieldCell *cell = (RSTextFieldCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
125 cell = [[[RSTextFieldCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
126 cell.textLabel.text = @"Name";
127 cell.textField.delegate = self;
130 cell.textField.text = self.loadBalancer.name;
135 - (UITableViewCell *)connectionLoggingCell:(UITableView *)tableView {
136 static NSString *CellIdentifier = @"ConnectionLoggingCell";
138 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
140 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
141 cell.textLabel.text = @"Connection Logging";
142 cell.selectionStyle = UITableViewCellSelectionStyleNone;
144 UISwitch *clSwitch = [[UISwitch alloc] init];
145 clSwitch.on = self.loadBalancer.connectionLoggingEnabled;
146 [clSwitch addTarget:self action:@selector(connectionLoggingSwitchChanged:) forControlEvents:UIControlEventValueChanged];
147 cell.accessoryView = clSwitch;
151 NSLog(@"self.loadBalancer.connectionLoggingEnabled = %i", self.loadBalancer.connectionLoggingEnabled);
152 ((UISwitch *)cell.accessoryView).on = self.loadBalancer.connectionLoggingEnabled;
157 - (UITableViewCell *)deleteCell:(UITableView *)tableView {
158 static NSString *CellIdentifier = @"DeleteCell";
160 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
162 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
163 cell.textLabel.text = @"Delete Load Balancer";
164 cell.textLabel.textAlignment = UITextAlignmentCenter;
165 cell.textLabel.textColor = [UIColor value1DetailTextLabelColor];
170 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
171 if (indexPath.section == kDetailsSection && indexPath.row == kName) {
172 return [self nameCell:tableView];
173 } else if (indexPath.section == kConnectionLoggingSection) {
174 return [self connectionLoggingCell:tableView];
175 } else if (indexPath.section == kDeleteSection) {
176 return [self deleteCell:tableView];
177 } else if (indexPath.section == kDetailsSection) {
178 static NSString *CellIdentifier = @"Cell";
180 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
182 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
183 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
186 switch (indexPath.row) {
188 cell.textLabel.text = @"Protocol";
189 cell.detailTextLabel.text = [NSString stringWithFormat:@"%@:%i", self.loadBalancer.protocol.name, self.loadBalancer.protocol.port];
190 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
191 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
194 cell.textLabel.text = @"Algorithm";
195 cell.detailTextLabel.text = [algorithmNames objectForKey:self.loadBalancer.algorithm];
196 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
197 cell.selectionStyle = UITableViewCellSelectionStyleBlue;
205 } else if (indexPath.section == kRegionSection) {
206 static NSString *CellIdentifier = @"RegionCell";
208 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
210 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
211 cell.accessoryType = UITableViewCellAccessoryNone;
212 cell.selectionStyle = UITableViewCellSelectionStyleNone;
215 switch (indexPath.row) {
217 cell.textLabel.text = @"Virtual IP Type";
218 cell.detailTextLabel.text = self.loadBalancer.virtualIPType;
221 cell.textLabel.text = @"Region";
222 cell.detailTextLabel.text = self.loadBalancer.region;
230 } else if (indexPath.section == kVirtualIPsSection) {
231 static NSString *CellIdentifier = @"VIPCell";
233 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
235 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
236 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
239 VirtualIP *vip = [self.loadBalancer.virtualIPs objectAtIndex:indexPath.row];
240 cell.textLabel.text = [vip.type capitalizedString];
241 cell.detailTextLabel.text = vip.address;
245 static NSString *CellIdentifier = @"NodeCell";
247 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
249 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
250 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
253 cell.textLabel.text = @"Nodes";
255 if ([self.loadBalancer.nodes count] == 1) {
256 cell.detailTextLabel.text = @"1 Node";
258 cell.detailTextLabel.text = [NSString stringWithFormat:@"%i Nodes", [self.loadBalancer.nodes count]];
265 #pragma mark - Table view delegate
267 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
268 if (indexPath.section == kVirtualIPsSection) {
269 VirtualIP *vip = [self.loadBalancer.virtualIPs objectAtIndex:indexPath.row];
270 selectedVirtualIP = vip;
271 selectedVIPIndexPath = indexPath;
272 ipActionSheet.title = vip.address;
273 [ipActionSheet showInView:self.view];
274 } else if (indexPath.section == kNodesSection) {
275 LBNodesViewController *vc = [[LBNodesViewController alloc] initWithNibName:@"LBNodesViewController" bundle:nil];
276 vc.isNewLoadBalancer = NO;
277 vc.account = self.account;
278 vc.loadBalancer = self.loadBalancer;
279 [self.navigationController pushViewController:vc animated:YES];
281 } else if (indexPath.section == kDeleteSection) {
282 [deleteActionSheet showInView:self.view];
283 } else if (indexPath.section == kDetailsSection) {
284 if (indexPath.row == kProtocol) {
285 LBProtocolViewController *vc = [[LBProtocolViewController alloc] initWithAccount:self.account loadBalancer:self.loadBalancer];
286 [self.navigationController pushViewController:vc animated:YES];
288 } else if (indexPath.row == kAlgorithm) {
289 LBAlgorithmViewController *vc = [[LBAlgorithmViewController alloc] initWithLoadBalancer:self.loadBalancer];
290 [self.navigationController pushViewController:vc animated:YES];
296 #pragma mark - Action Sheet Delegate
298 - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
299 if (actionSheet == deleteActionSheet) {
300 if (buttonIndex == 0) {
301 [[self.account.manager deleteLoadBalancer:self.loadBalancer] success:^(OpenStackRequest *request) {
302 LoadBalancersViewController *vc = [[self.loadBalancerViewController.navigationController viewControllers] objectAtIndex:2];
303 [vc refreshButtonPressed:nil];
304 [self dismissModalViewControllerAnimated:YES];
305 [self.loadBalancerViewController.navigationController popToViewController:vc animated:YES];
306 } failure:^(OpenStackRequest *request) {
307 [self alert:@"There was a problem deleting the load balancer." request:request];
310 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:kDeleteSection];
311 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
312 } else if (actionSheet == ipActionSheet) {
313 if (buttonIndex == 0) { // ping
314 TrackEvent(CATEGORY_LOAD_BALANCER, EVENT_PINGED);
315 PingIPAddressViewController *vc = [[PingIPAddressViewController alloc] initWithNibName:@"PingIPAddressViewController" bundle:nil ipAddress:selectedVirtualIP.address];
316 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
317 vc.modalPresentationStyle = UIModalPresentationPageSheet;
319 [self.navigationController presentModalViewController:vc animated:YES];
321 } else if (buttonIndex == 1) { // copy to pasteboard
322 UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
323 [pasteboard setString:selectedVirtualIP.address];
324 [self.tableView deselectRowAtIndexPath:selectedVIPIndexPath animated:YES];
325 } else if (buttonIndex == 2) { // open in safari
326 UIApplication *application = [UIApplication sharedApplication];
327 NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", selectedVirtualIP.address]];
328 if ([application canOpenURL:url]) {
329 [application openURL:url];
332 [self.tableView deselectRowAtIndexPath:selectedVIPIndexPath animated:YES];
336 #pragma mark - Text field delegate
338 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
339 self.loadBalancer.name = [textField.text stringByReplacingCharactersInRange:range withString:string];
343 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
344 [textField resignFirstResponder];
348 #pragma mark - Button Handlers
350 - (void)saveButtonPressed:(id)sender {
351 [[self.account.manager updateLoadBalancer:self.loadBalancer] success:^(OpenStackRequest *request) {
352 [self dismissModalViewControllerAnimated:YES];
353 } failure:^(OpenStackRequest *request) {
354 [self alert:@"There was a problem updating this load balancer." request:request];
358 - (void)cancelButtonPressed:(id)sender {
359 [self dismissModalViewControllerAnimated:YES];
362 #pragma mark - Connection Logging Switch
364 - (void)connectionLoggingSwitchChanged:(UISwitch *)sender {
365 self.loadBalancer.connectionLoggingEnabled = sender.on;
366 [[self.account.manager updateLoadBalancerConnectionLogging:self.loadBalancer] success:^(OpenStackRequest *request) {
367 } failure:^(OpenStackRequest *request) {
368 [self alert:@"There was a problem updating the load balancer." request:request];