package com.rackspacecloud.android;
import java.util.ArrayList;
import org.apache.http.HttpResponse;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.rackspace.cloud.android.R;
import com.rackspace.cloud.loadbalancer.api.client.LoadBalancer;
import com.rackspace.cloud.loadbalancer.api.client.Node;
import com.rackspace.cloud.loadbalancer.api.client.NodeManager;
import com.rackspace.cloud.servers.api.client.CloudServersException;
import com.rackspace.cloud.servers.api.client.Server;
import com.rackspace.cloud.servers.api.client.ServerManager;
import com.rackspace.cloud.servers.api.client.http.HttpBundle;
public class AddMoreNodesActivity extends CloudListActivity {
private static final int ADD_NODE_CODE = 178;
private static final int ADD_EXTERNAL_NODE_CODE = 188;
private ArrayList<Server> possibleNodes;
//the last position in the listview that was clicked
private int lastCheckedPos;
//the location in nodes of the last node that was clicked
private int positionOfNode;
private ArrayList<Node> nodes;
private ArrayList<Node> nodesToAdd;
private LoadBalancer loadBalancer;
@SuppressWarnings("unchecked")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
nodes = (ArrayList<Node>) this.getIntent().getExtras().get("nodes");
loadBalancer = (LoadBalancer) this.getIntent().getExtras().get("loadBalancer");
setContentView(R.layout.addnodes);
restoreState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("nodes", nodes);
outState.putSerializable("loadBalancer", loadBalancer);
outState.putSerializable("nodesToAdd", nodesToAdd);
outState.putSerializable("possibleNodes", possibleNodes);
outState.putInt("lastCheckedPos", lastCheckedPos);
outState.putInt("positionOfNode", positionOfNode);
}
@SuppressWarnings("unchecked")
protected void restoreState(Bundle state) {
super.restoreState(state);
if (state != null && state.containsKey("nodes")){
nodes = (ArrayList<Node>) state.getSerializable("nodes");
if(nodes == null){
nodes = new ArrayList<Node>();
}
}
if (state != null && state.containsKey("loadBalancer")){
loadBalancer = (LoadBalancer) state.getSerializable("loadBalancer");
if(loadBalancer == null){
loadBalancer = new LoadBalancer();
}
}
if (state != null && state.containsKey("lastCheckedPos")){
lastCheckedPos = (Integer) state.getSerializable("lastCheckedPos");
}
if (state != null && state.containsKey("positionOfNode")){
positionOfNode = (Integer) state.getSerializable("positionOfNode");
}
if (state != null && state.containsKey("nodesToAdd")){
nodesToAdd = (ArrayList<Node>) state.getSerializable("nodesToAdd");
}
else{
nodesToAdd = new ArrayList<Node>();
}
if (state != null && state.containsKey("possibleNodes") && state.getSerializable("possibleNodes") != null) {
possibleNodes = (ArrayList<Server>) state.getSerializable("possibleNodes");
if (possibleNodes.size() == 0) {
displayNoServersCell();
} else {
getListView().setDividerHeight(1); // restore divider lines
setListAdapter(new ServerAdapter());
}
} else {
loadServers();
}
Button submitNodes = (Button) findViewById(R.id.submit_nodes_button);
submitNodes.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(nodesToAdd.size() == 0){
finish();
} else {
trackEvent(GoogleAnalytics.CATEGORY_LOAD_BALANCER, GoogleAnalytics.EVENT_ADD_LB_NODES, "", -1);
new AddNodesTask().execute();
}
}
});
Button addExternalNode = (Button) findViewById(R.id.add_external_node);
addExternalNode.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
positionOfNode = -1;
lastCheckedPos = -1;
Intent viewIntent = new Intent(getContext(), AddExternalNodeActivity.class);
viewIntent.putExtra("loadBalancerPort", loadBalancer.getPort());
viewIntent.putExtra("weighted", loadBalancer.getAlgorithm().toLowerCase().contains("weighted"));
startActivityForResult(viewIntent, ADD_EXTERNAL_NODE_CODE);
}
});
}
@Override
public void onBackPressed(){
setResult(RESULT_CANCELED);
finish();
}
//When a list item is click just change the checkbox state
//and then the checkbox's onClick will be performed
protected void onListItemClick(ListView l, View v, int position, long id) {
if (possibleNodes != null && possibleNodes.size() > 0) {
if(!possibleNodes.get(position).getName().equals("External Node")){
LinearLayout linear = (LinearLayout) findViewById(R.id.nodes_linear_layout);
ListView serversList = (ListView) linear.findViewById(android.R.id.list);
View row = serversList.getChildAt(position);
CheckBox checkBox = (CheckBox)row.findViewById(R.id.add_node_checkbox);
if(!checkBox.isChecked()){
//if the checkbox was not previously checked, treat the listItemClick
//the same as checking the checkbox
checkBox.setChecked(!checkBox.isChecked());
} else {
//if the checkbox was already checked when the listItemClick occurred,
//then treat it like an edit
Server server = possibleNodes.get(position);
//Need to put all the ip's of the server into one
//list so they can all be displayed in one spinner
String[] ipAddresses = getAllIpsOfServer(server);
Node node = getNodeFromServer(server);
positionOfNode = findNodePosition(node);
lastCheckedPos = position;
Intent viewIntent = new Intent(getContext(), AddNodeActivity.class);
viewIntent.putExtra("ipAddresses", ipAddresses);
viewIntent.putExtra("name", server.getName());
if(node != null){
viewIntent.putExtra("node", node);
}
viewIntent.putExtra("weighted", loadBalancer.getAlgorithm().toLowerCase().contains("weighted"));
startActivityForResult(viewIntent, ADD_NODE_CODE);
}
} else {
//When clicked on an external node
Server server = possibleNodes.get(position);
Node node = getNodeFromServer(server);
positionOfNode = findNodePosition(node);
lastCheckedPos = position;
Intent viewIntent = new Intent(getContext(), AddExternalNodeActivity.class);
if(node != null){
viewIntent.putExtra("node", node);
}
//weighted is false, because on initial node add
//weight is not option
viewIntent.putExtra("weighted", false);
startActivityForResult(viewIntent, ADD_EXTERNAL_NODE_CODE);
}
}
}
//return the location of node in nodes
//if it is no in there then -1
private int findNodePosition(Node node){
for(int i = 0; i < nodesToAdd.size(); i++){
String address = node.getAddress();
if(address.equals(nodesToAdd.get(i).getAddress())){
return i;
}
}
return -1;
}
private void displayNoServersCell() {
String a[] = new String[1];
a[0] = "No Servers";
setListAdapter(new ArrayAdapter<String>(this, R.layout.noserverscell2, R.id.no_servers_label, a));
getListView().setTextFilterEnabled(true);
getListView().setDividerHeight(0); // hide the dividers so it won't look like a list row
getListView().setItemsCanFocus(false);
}
private void setServerList(ArrayList<Server> servers) {
if (servers == null) {
servers = new ArrayList<Server>();
}
for(int i = 0; i < servers.size(); i++) {
/*
* if all the IP's of a server are
* already used as nodes, we do not
* need to display that server
*/
if(!notAllIpsNodes(servers.get(i))){
servers.remove(i);
}
}
String[] serverNames = new String[servers.size()];
this.possibleNodes = new ArrayList<Server>();
for(int i = 0; i < servers.size(); i++){
serverNames[i] = servers.get(i).getName();
this.possibleNodes.add(i, servers.get(i));
}
if (serverNames.length == 0) {
displayNoServersCell();
} else {
getListView().setDividerHeight(1); // restore divider lines
setListAdapter(new ServerAdapter());
}
}
/*
* determine if all the IP's of a server are
* already used as nodes
*/
private boolean notAllIpsNodes(Server server){
for(String address : server.getPrivateIpAddresses()){
if(!nodeHasAddress(address)){
return true;
}
}
for(String address : server.getPublicIpAddresses()){
if(!nodeHasAddress(address)){
return true;
}
}
return false;
}
/*
* determine is an existing node has IP Address,
* address
*/
private boolean nodeHasAddress(String address){
for(Node n : nodes){
if(n.getAddress().equals(address)){
return true;
}
}
return false;
}
private void loadServers() {
new LoadServersTask().execute((Void[]) null);
}
// * Adapter/
class ServerAdapter extends ArrayAdapter<Server> {
ServerAdapter() {
super(AddMoreNodesActivity.this, R.layout.listservernodecell, possibleNodes);
}
public View getView(int position, View convertView, ViewGroup parent) {
final Server server = possibleNodes.get(position);
LayoutInflater inflater = getLayoutInflater();
View row = inflater.inflate(R.layout.listservernodecell, parent, false);
TextView label = (TextView) row.findViewById(R.id.label);
label.setText(server.getName());
TextView sublabel = (TextView) row.findViewById(R.id.sublabel);
if(server.getName().equals("External Node")){
sublabel.setText(server.getPrivateIpAddresses()[0]);
} else {
sublabel.setText(server.getFlavor().getName() + " - " + server.getImage().getName());
}
String[] publicIp = server.getPublicIpAddresses();
String[] privateIp = server.getPrivateIpAddresses();
if(publicIp == null){
publicIp = new String[0];
}
if(privateIp == null){
privateIp = new String[0];
}
ArrayList<String> ipAddressList = new ArrayList<String>();
for(int i = 0; i < privateIp.length; i++){
if(!nodeHasAddress(privateIp[i])){
ipAddressList.add(privateIp[i]);
}
}
for(int i = 0; i < publicIp.length; i++){
if(!nodeHasAddress(publicIp[i])){
ipAddressList.add(publicIp[i]);
}
}
final String[] ipAddresses = ipAddressList.toArray(new String[ipAddressList.size()]);
final int pos = position;
CheckBox add = (CheckBox) row.findViewById(R.id.add_node_checkbox);
if(inToAddList(server)){
add.setChecked(true);
}
add.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
//if node is being check it won't be in nodes positionOfNode is -1
positionOfNode = -1;
lastCheckedPos = pos;
Intent viewIntent = new Intent(getContext(), AddNodeActivity.class);
viewIntent.putExtra("ipAddresses", ipAddresses);
viewIntent.putExtra("name", server.getName());
viewIntent.putExtra("loadBalancerPort", loadBalancer.getPort());
viewIntent.putExtra("weighted", loadBalancer.getAlgorithm().contains("WEIGHTED"));
startActivityForResult(viewIntent, ADD_NODE_CODE);
}
else{
removeNodeFromList(server);
if(server.getName().equals("External Node")){
possibleNodes.remove(server);
setServerList(possibleNodes);
}
}
}
});
return(row);
}
/*
* need to remove by id because that is
* what is unique
*/
private void removeNodeFromList(Server server){
for(int i = 0; i < nodesToAdd.size(); i++){
Node node = nodesToAdd.get(i);
if(serverHasIp(server, node.getAddress())){
nodesToAdd.remove(i);
break;
}
}
}
}
private boolean serverHasIp(Server server, String address){
String[] addresses = server.getPrivateIpAddresses();
if(addresses != null){
for(int i = 0; i < addresses.length; i++){
if(addresses[i].equals(address)){
return true;
}
}
}
addresses = server.getPublicIpAddresses();
if(addresses != null){
for(int i = 0; i < addresses.length; i++){
if(addresses[i].equals(address)){
return true;
}
}
}
return false;
}
private boolean inToAddList(Server server){
for(Node node : nodesToAdd){
if(serverHasIp(server, node.getAddress())){
return true;
}
}
return false;
}
private class AddNodesTask extends AsyncTask<Void, Void, HttpBundle> {
private CloudServersException exception;
protected void onPreExecute(){
showDialog();
}
@Override
protected HttpBundle doInBackground(Void... arg0) {
HttpBundle bundle = null;
try {
bundle = (new NodeManager(getContext())).add(loadBalancer, nodesToAdd);
} catch (CloudServersException e) {
exception = e;
}
return bundle;
}
@Override
protected void onPostExecute(HttpBundle bundle) {
hideDialog();
HttpResponse response = bundle.getResponse();
if (response != null) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 202) {
setResult(Activity.RESULT_OK);
finish();
} else {
CloudServersException cse = parseCloudServersException(response);
if ("".equals(cse.getMessage())) {
showError("There was a problem creating your load balancer.", bundle);
} else {
//if container with same name already exists
showError("There was a problem creating your load balancer: " + cse.getMessage() + "\n See details for more information", bundle);
}
}
} else if (exception != null) {
showError("There was a problem creating your container: " + exception.getMessage()+"\n See details for more information.", bundle);
}
finish();
}
}
private class LoadServersTask extends AsyncTask<Void, Void, ArrayList<Server>> {
private CloudServersException exception;
@Override
protected void onPreExecute(){
showDialog();
}
@Override
protected ArrayList<Server> doInBackground(Void... arg0) {
ArrayList<Server> servers = null;
try {
servers = (new ServerManager()).createList(true, getContext());
} catch (CloudServersException e) {
exception = e;
}
return servers;
}
@Override
protected void onPostExecute(ArrayList<Server> result) {
hideDialog();
if (exception != null) {
showAlert("Error", exception.getMessage());
}
setServerList(result);
}
}
private String getNameFromIp(String address){
for(Server s: possibleNodes){
if(serverHasIp(s, address)){
return s.getName();
}
}
return "";
}
private boolean ipInList(String address){
for(Server s : possibleNodes){
if(serverHasIp(s, address) && !s.getName().equals("External Node")){
return true;
}
}
return false;
}
//returns the node that is using an ip from server
private Node getNodeFromServer(Server server){
for(Node node : nodesToAdd){
if(serverHasIp(server, node.getAddress())){
return node;
}
}
return null;
}
//returns an array of all the ip's of server
private String[] getAllIpsOfServer(Server server){
String[] publicIp = server.getPublicIpAddresses();
String[] privateIp = server.getPrivateIpAddresses();
if(publicIp == null){
publicIp = new String[0];
}
if(privateIp == null){
privateIp = new String[0];
}
String[] ipAddresses = new String[privateIp.length + publicIp.length];
for(int i = 0; i < privateIp.length; i++){
ipAddresses[i] = privateIp[i];
}
for(int i = 0; i < publicIp.length; i++){
ipAddresses[i+privateIp.length] = publicIp[i];
}
return ipAddresses;
}
protected void onActivityResult(int requestCode, int resultCode, Intent data){
int pos = lastCheckedPos;
if(requestCode == ADD_NODE_CODE && resultCode == RESULT_OK){
//data will be null if user backed out on edit
//we dont need to do anything then
//if new node added data won't be null
//so create the new node and add it to the list
if(data != null){
//will remove the node if it's already in the list
//so we can update it
if(positionOfNode >= 0){
nodesToAdd.remove(positionOfNode);
}
Node node = new Node();
node.setAddress(data.getStringExtra("nodeIp"));
node.setCondition(data.getStringExtra("nodeCondition"));
node.setName(possibleNodes.get(pos).getName());
node.setPort(data.getStringExtra("nodePort"));
node.setWeight(data.getStringExtra("nodeWeight"));
nodesToAdd.add(node);
}
}
else if(requestCode == ADD_NODE_CODE && resultCode == RESULT_CANCELED){
//uncheck the node at lastCheckedPos
LinearLayout linear = (LinearLayout) findViewById(R.id.nodes_linear_layout);
ListView serversList = (ListView) linear.findViewById(android.R.id.list);
View row = serversList.getChildAt(pos);
CheckBox checkBox = (CheckBox)row.findViewById(R.id.add_node_checkbox);
checkBox.setChecked(false);
}
else if(requestCode == ADD_EXTERNAL_NODE_CODE && resultCode == RESULT_OK){
Node node = new Node();
node.setAddress(data.getStringExtra("nodeIp"));
node.setCondition(data.getStringExtra("nodeCondition"));
node.setName("External Node");
node.setPort(data.getStringExtra("nodePort"));
node.setWeight(data.getStringExtra("nodeWeight"));
/*
* If the ip is from a cloud server or already added, alert to user
* so they can select it from there
*/
String nodeIP = node.getAddress();
ArrayList<Node> lbNodes = loadBalancer.getNodes();
boolean nodeIpExists = false;
for(int i = 0; i < lbNodes.size(); i++){
if(nodeIP.equals(lbNodes.get(i).getAddress())){
nodeIpExists = true;
}
}
if(nodeIpExists){
showAlert("Error", "This IP has already been added as an external node, please edit " +
"it from the list.");
} else if(!ipInList(node.getAddress())){
if(positionOfNode >= 0){
nodesToAdd.remove(positionOfNode);
}
nodesToAdd.add(node);
//Add it to server list so it display in the listview
Server server = new Server();
server.setName("External Node");
String[] ip = {data.getStringExtra("nodeIp")};
server.setPrivateIpAddresses(ip);
if(pos != -1){
possibleNodes.remove(pos);
}
possibleNodes.add(server);
setServerList(possibleNodes);
} else {
String name = getNameFromIp(node.getAddress());
if(name.equals("External Node")){
showAlert("Error", "This IP has already been added as an external node, please edit " +
"it from the list.");
} else {
showAlert("Error", "This IP belongs to a cloud server: \"" + getNameFromIp(node.getAddress())
+ "\", please edit it from the list.");
}
}
}
printTheNodes();
}
private void printTheNodes(){
for(Node n: nodesToAdd){
Log.d("info", "address: " + n.getAddress() + " Port: " + n.getPort() + " Cond: " + n.getCondition());
}
Log.d("info", " SPACE ");
}
}