ZK goes EC2 (Part 2)

Part 1 of this tutorial we concluded with an web application that displays all our instances and their status. In this part we will add some more features to control our instances.

Prerequisites:

  • The project and environment from part 1

Tutorial (complete sourcecode at the end):

  • Display the region endpoints and allow to select different one
    We hardcoded our endpoint in the first version, if you run instances across the globe in the various AWS datacentres (US, Ireland, Singapore, Tokyo) we need to switch the endpoint easily.
    Lets add one more listbox, that we hide in a ZK popup (we could use a combo listbox, but for the sake of playing with all the available ZK components I use the popup). Same concept add a listbox in the zul and a EC2 region list and a list model with customer renderer in our controller.

    Retrieve regions

    Custom renderer

    Listbox in popup

  • Start and Stop function for the selected instance
    We use the startInstances and stopInstances provided by the Amazon SDK, these functions expect a list of ID’s.
    Add 2 additional buttons and related controller functions

    Start and Stop instances

    Note, these are async calls, we wont know how long it takes to perform the startup or shutdown, so we just throw the result message (JSON which needs to be beautified) as status at the user and ask him to refresh after a while.

  • Run the application

    Running Web Application

    Display Regions

Remarks:

  • The application is very basic, we still have the access keys hardcoded and no security like a login to protect this web application is in place. I recommend to implement this if you plan to use this on the public web
  • There is still some features to be added and improved, we will cover this in part 3.

Complete sourcecode:
instances.zul

<?xml version="1.0" encoding="UTF-8"?>

<zk xmlns="http://www.zkoss.org/2005/zul">

    <window id="list" apply="controller.instancesController" title="ZK EC2 CloudControl" width="100%">
        <listbox id="lstInstance" width="100%" >
            <listhead sizable="true">
                <listheader  label="Instance ID"/>
                <listheader label="Name"/>
                <listheader  label="Public IP" />
                <listheader  label="State" />
                <listheader  label="Launch Time" />
            </listhead>
        </listbox>
        <vbox>
            <hbox>
                <label value="Selected Endpoint:" />
                <label id="lblEndpoint"/>
            </hbox>
            <button id="btnRefresh" label="Refresh" />
            <hbox>
                <button id="btnStart" label="Start instance" />
                <button id="btnStop" label="Stop instance" />
                <label id="lblStatus"/>
            </hbox>
            <button id="btnEndPoints" label="Show endpoints" onClick='regions.open(list,"overlap")'/>
        </vbox>

        <popup id="regions" width="350px" >
            <listbox id="lstRegion" width="100%">
                <listhead sizable="true" >
                    <listheader id="a" label="Region Name"/>
                    <listheader id="b" label="Region Endpoint" />
                </listhead>
            </listbox>
            <button id="btnSelectEndpoint" label="Select Endpoint" onClick=""/>
            <button id="btnCloseEndPoints" label="Close" onClick='regions.close()'/>
        </popup>

    </window>
</zk>

 
instancesController.java

package controller;

import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.DescribeRegionsResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.amazonaws.services.ec2.model.Region;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StartInstancesResult;
import com.amazonaws.services.ec2.model.StopInstancesRequest;
import com.amazonaws.services.ec2.model.StopInstancesResult;
import com.amazonaws.services.ec2.model.Tag;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.util.ComposerExt;
import org.zkoss.zk.ui.util.GenericForwardComposer;
import org.zkoss.zul.Label;
import org.zkoss.zul.ListModelList;
import org.zkoss.zul.Listbox;
import org.zkoss.zul.Listcell;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.ListitemRenderer;
import org.zkoss.zul.Messagebox;
import org.zkoss.zul.Popup;

public class instancesController extends GenericForwardComposer implements ComposerExt {

    // ZK Variables
    private Listbox lstInstance;
    private Listbox lstRegion;
    private Label lblStatus;
    private Label lblEndpoint;
    private Popup regions;

    //Amazon Variables
    AmazonEC2 ec2;
    List<Reservation> listEC2Reservations = null;
    List<Instance> listEC2Instances = null;
    List<Region> listEC2Regions = null;

    String msgboxTitle = "ZKEC2CloudControl";

    String defEndpoint = "ec2.ap-southeast-1.amazonaws.com";

    private void initEC2() {
        BasicAWSCredentials ecProp = new BasicAWSCredentials("{YOURKEY}", "{YOURSCRETKEY});
        ec2 = new AmazonEC2Client(ecProp);
        ec2.setEndpoint(defEndpoint);
    }

    @Override
    public void doAfterCompose(Component comp) throws Exception {
        super.doAfterCompose(comp);

        initEC2();
        setupRenderer();

        lblEndpoint.setValue(defEndpoint);
        listReservationsInstances();

        // Retrieve the available EC2 regions
        DescribeRegionsResult regionsResult = ec2.describeRegions();
        listEC2Regions = regionsResult.getRegions();
        lstRegion.setModel(new ListModelList(listEC2Regions));

    }

    private void listReservationsInstances() {

        DescribeInstancesResult describeInstancesRequest = ec2.describeInstances();
        listEC2Reservations = describeInstancesRequest.getReservations();

        Set<Instance> instances = new HashSet<Instance>();
        for (Reservation reservation : listEC2Reservations) {
            instances.addAll(reservation.getInstances());
        }
        listEC2Instances = new ArrayList<Instance>(instances);

        lstInstance.setModel(new ListModelList(listEC2Instances));

    }

    public void onClick$btnRefresh(Event evt) throws InterruptedException {
        listReservationsInstances();
        lblStatus.setValue("");
    }

    public void onClick$btnSelectEndpoint(Event evt) throws InterruptedException {
         if (lstRegion.getSelectedIndex() > -1) {
             String newEndpoint = ((Region)lstRegion.getSelectedItem().getValue()).getEndpoint();
             ec2.setEndpoint(newEndpoint);
             lblEndpoint.setValue(newEndpoint);
             listReservationsInstances();
             regions.close();
         }
    }

    public void onClick$btnStart(Event evt) throws InterruptedException {
        if (lstInstance.getSelectedIndex() > -1) {

            if (Messagebox.show("Start instance <" + ((Instance) lstInstance.getSelectedItem().getValue()).getInstanceId() + "> ?", msgboxTitle, Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION, null) == Messagebox.OK) {
                List<String> startIDs = new ArrayList<String>();
                startIDs.add(((Instance) lstInstance.getSelectedItem().getValue()).getInstanceId());
                StartInstancesRequest start = new StartInstancesRequest(startIDs);
                StartInstancesResult result = ec2.startInstances(start);
                lblStatus.setValue(result.toString() + " Refresh after 10 or more seconds.");
            }
        }
    }

    public void onClick$btnStop(Event evt) throws InterruptedException {

        if (lstInstance.getSelectedIndex() > -1) {

            if (Messagebox.show("Stop instance <" + ((Instance) lstInstance.getSelectedItem().getValue()).getInstanceId() + "> ?", msgboxTitle, Messagebox.OK | Messagebox.CANCEL, Messagebox.QUESTION, null) == Messagebox.OK) {
                List<String> stopIDs = new ArrayList<String>();
                stopIDs.add(((Instance) lstInstance.getSelectedItem().getValue()).getInstanceId());
                StopInstancesRequest stop = new StopInstancesRequest(stopIDs);
                StopInstancesResult result = ec2.stopInstances(stop);
                lblStatus.setValue(result.toString() + " Refresh after 10 or more seconds.");
            }
        }
    }

    private void setupRenderer() {

        // EC2 Instances list renderer
        ListitemRenderer listRenderInstance = new ListitemRenderer() {

            @Override
            public void render(Listitem item, Object data) throws Exception {
                item.setValue(data);
                item.appendChild(new Listcell(((Instance) data).getInstanceId()));
                item.appendChild(new Listcell(findTagValuebyKey(((Instance) data).getTags(), "name")));
                item.appendChild(new Listcell(((Instance) data).getPublicIpAddress()));

                InstanceState state = ((Instance) data).getState();
                item.appendChild(new Listcell(state.getName()));

                item.appendChild(new Listcell(((Instance) data).getLaunchTime().toString()));

                Listcell listcell = new Listcell();
                item.appendChild(listcell);
            }
        };
        lstInstance.setItemRenderer(listRenderInstance);

        // EC2 Regions list renderer
        ListitemRenderer listitem = new ListitemRenderer() {

            @Override
            public void render(Listitem item, Object data) throws Exception {
                item.setValue(data);
                item.appendChild(new Listcell(((Region) data).getRegionName()));
                item.appendChild(new Listcell(((Region) data).getEndpoint()));
                Listcell listcell = new Listcell();
                item.appendChild(listcell);
            }
        };
        lstRegion.setItemRenderer(listitem);

    }

    private String findTagValuebyKey(List<Tag> tags, String key) {

        for (Tag tag : tags) {
            if (tag.getKey().toUpperCase().equals(key.toUpperCase())) {
                return tag.getValue();
            }
        }
        return "na";
    }
}

Advertisements

One thought on “ZK goes EC2 (Part 2)

  1. Pingback: ZK goes EC2 (Part 3) « The JavaDude Weblog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s