package com.urbancode.terraform.tasks.rackspace; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.DeleteMethod; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.log4j.Logger; import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import com.urbancode.x2o.tasks.SubTask; public class DatabaseInstanceTask extends SubTask { //********************************************************************************************** // CLASS //********************************************************************************************** static private final Logger log = Logger.getLogger(DatabaseInstanceTask.class); //********************************************************************************************** // INSTANCE //********************************************************************************************** private EnvironmentTaskRackspace env; private String name; private boolean appendSuffix = false; private String region; private String flavor; private int volumeSize; private String id; private String hostname; private List<DatabaseTask> databases = new ArrayList<DatabaseTask>(); private List<DatabaseUserTask> dbUsers = new ArrayList<DatabaseUserTask>(); //---------------------------------------------------------------------------------------------- public DatabaseInstanceTask(EnvironmentTaskRackspace env) { super(); this.env = env; } //---------------------------------------------------------------------------------------------- public String getName() { return name; } //---------------------------------------------------------------------------------------------- public String getRegion() { return region; } //---------------------------------------------------------------------------------------------- public String getFlavor() { return flavor; } //---------------------------------------------------------------------------------------------- public int getVolumeSize() { return volumeSize; } //---------------------------------------------------------------------------------------------- public String getId() { return id; } //---------------------------------------------------------------------------------------------- public String getHostname() { return hostname; } //---------------------------------------------------------------------------------------------- public List<DatabaseTask> getDatabases() { return databases; } //---------------------------------------------------------------------------------------------- public List<DatabaseUserTask> getUsers() { return dbUsers; } //---------------------------------------------------------------------------------------------- public void setName(String name) { this.name = name; } //---------------------------------------------------------------------------------------------- public void setAppendSuffix(boolean appendSuffix) { this.appendSuffix = appendSuffix; } //---------------------------------------------------------------------------------------------- public void setRegion(String region) { if(Region.containsIgnoreCase(region)) { this.region = region.toLowerCase(); } else { log.warn("You did not enter a valid region for a server."); } } //---------------------------------------------------------------------------------------------- public void setFlavor(String flavor) { if (DatabaseFlavor.contains(flavor)) { this.flavor = flavor; } else { log.warn("Flavor type is not valid."); } } //---------------------------------------------------------------------------------------------- public void setVolumeSize(int volumeSize) { this.volumeSize = volumeSize; } //---------------------------------------------------------------------------------------------- public void setId(String id) { this.id = id; } //---------------------------------------------------------------------------------------------- public void setHostname(String hostname) { this.hostname = hostname; } //---------------------------------------------------------------------------------------------- public DatabaseUserTask createUser() { DatabaseUserTask user = new DatabaseUserTask(); dbUsers.add(user); return user; } //---------------------------------------------------------------------------------------------- public DatabaseTask createDatabase() { DatabaseTask database = new DatabaseTask(); databases.add(database); return database; } //---------------------------------------------------------------------------------------------- private JSONObject pollForDatabaseStatus() throws IOException, JSONException { JSONObject result = null; RackspaceRestClient client = env.fetchContext().client; String uri = "https://" + client.encodePath(region) + ".databases.api.rackspacecloud.com/v1.0/" + client.encodePath(client.getTenantID()) + "/instances/" + client.encodePath(id); GetMethod method = new GetMethod(uri); method.setRequestHeader("X-Auth-Token", client.getAuthToken()); method.setRequestHeader("Content-Type", "application/json"); int status = client.invokeMethod(method); String body = client.getBody(method); method.releaseConnection(); if (200 <= status && status <= 204) { JSONObject serverJSON = new JSONObject(body).getJSONObject("instance"); result = serverJSON; } else { log.warn("Exception when polling database for status."); throw new IOException(String.format("%d %s %s", status, HttpStatus.getStatusText(status), body)); } return result; } //---------------------------------------------------------------------------------------------- private JSONArray generateUserJSON() throws JSONException { JSONArray result = new JSONArray(); for (DatabaseUserTask user : dbUsers) { JSONObject userJSON = new JSONObject(); JSONArray dbs = new JSONArray(); for (DatabaseTask database : databases) { dbs.put(new JSONObject().put("name", database.getName())); } userJSON.put("name", user.getUsername()); userJSON.put("password", user.fetchPassword()); userJSON.put("databases", dbs); result.put(userJSON); } return result; } //---------------------------------------------------------------------------------------------- private void createDbRestCall() throws JSONException, IOException { RackspaceRestClient client = env.fetchContext().client; String uri = "https://" + client.encodePath(region) + ".databases.api.rackspacecloud.com/v1.0/" + client.encodePath(client.getTenantID()) + "/instances"; JSONObject data = new JSONObject(); JSONObject instance = new JSONObject(); instance.put("name", name); instance.put("volume", new JSONObject().put("size", volumeSize)); String flavorRef = "https://" + region + ".databases.api.rackspacecloud.com/v1.0/"; flavorRef = flavorRef + client.getTenantID() + "/flavors/" + DatabaseFlavor.lookupFlavorID(flavor); instance.put("flavorRef", flavorRef); JSONArray dbs = new JSONArray(); for (DatabaseTask database : databases) { JSONObject dbJSON = new JSONObject(); dbJSON.put("name", database.getName()); dbJSON.put("character_set", database.getCharset()); dbJSON.put("collate", database.getCollate()); dbs.put(dbJSON); } instance.put("databases", dbs); instance.put("users", generateUserJSON()); data.put("instance", instance); PostMethod method = new PostMethod(uri); RequestEntity entity = new StringRequestEntity(data.toString(), "application/json", null); method.setRequestEntity(entity); method.setRequestHeader("X-Auth-Token", client.getAuthToken()); method.setRequestHeader("Content-Type", "application/json"); int status = client.invokeMethod(method); String body = client.getBody(method); method.releaseConnection(); if (200 <= status && status <= 202) { log.info("Database instance creation request succeeded."); JSONObject resultJSON = new JSONObject(body); id = resultJSON.getJSONObject("instance").getString("id"); hostname = resultJSON.getJSONObject("instance").getString("hostname"); } else { log.warn("Exception when creating database instance."); throw new IOException(String.format("%d %s %s", status, HttpStatus.getStatusText(status), body)); } } //---------------------------------------------------------------------------------------------- private void deleteDbRestCall() throws JSONException, IOException { RackspaceRestClient client = env.fetchContext().client; String uri = "https://" + client.encodePath(region) + ".databases.api.rackspacecloud.com/v1.0/" + client.encodePath(client.getTenantID()) + "/instances/" + client.encodePath(id); DeleteMethod method = new DeleteMethod(uri); method.setRequestHeader("X-Auth-Token", client.getAuthToken()); method.setRequestHeader("Content-Type", "application/json"); int status = client.invokeMethod(method); String body = client.getBody(method); method.releaseConnection(); if (200 <= status && status <= 204) { log.info("Database instance deletion request succeeded."); } else { log.warn("Exception when deleting database instance."); throw new IOException(String.format("%d %s %s", status, HttpStatus.getStatusText(status), body)); } } //---------------------------------------------------------------------------------------------- @Override public void create() throws Exception { if (appendSuffix) { name = name + "-" + env.fetchSuffix(); } try { createDbRestCall(); log.info("Database request succeeded. Polling for database to come online..."); boolean active = false; long pollInterval = 3000L; while (!active) { Thread.sleep(pollInterval); JSONObject dbJSON = pollForDatabaseStatus(); if (dbJSON.getString("status").equalsIgnoreCase("ACTIVE")) { active = true; } } log.info("The database is now active."); } catch(IOException e) { log.error("A REST call failed while creating database instance", e); } catch(JSONException e) { log.error("Malformed JSON while creating database instance", e); } } //---------------------------------------------------------------------------------------------- @Override public void destroy() throws Exception { try { deleteDbRestCall(); } catch(IOException e) { log.error("A REST call failed while creating database instance", e); } catch(JSONException e) { log.error("Malformed JSON while creating database instance", e); } } }