/**
* Copyright (C) 2009-2015 Dell, Inc
* See annotations for authorship information
*
* ====================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
package org.dasein.cloud.joyent.compute;
import org.apache.log4j.Logger;
import org.dasein.cloud.*;
import org.dasein.cloud.compute.*;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.joyent.JoyentException;
import org.dasein.cloud.joyent.JoyentMethod;
import org.dasein.cloud.joyent.SmartDataCenter;
import org.dasein.cloud.util.APITrace;
import org.dasein.cloud.util.Cache;
import org.dasein.cloud.util.CacheLevel;
import org.dasein.util.CalendarWrapper;
import org.dasein.util.uom.storage.Megabyte;
import org.dasein.util.uom.storage.Storage;
import org.dasein.util.uom.time.Day;
import org.dasein.util.uom.time.TimePeriod;
import org.dasein.util.uom.time.TimePeriodUnit;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Machine extends AbstractVMSupport<SmartDataCenter> {
Logger logger = SmartDataCenter.getLogger(Machine.class, "std");
private SmartDataCenter provider;
private transient volatile MachineCapabilities capabilities;
Machine(SmartDataCenter sdc) {
super(sdc);
provider = sdc;
}
@Override
/**
* Only resizing of type=smartmachine is supported.
*/
public VirtualMachine alterVirtualMachineProduct( @Nonnull String virtualMachineId, @Nonnull String productId ) throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
method.doPostString(provider.getEndpoint(), "machines/" + virtualMachineId, "action=resize&package="+productId);
return getVirtualMachine(virtualMachineId);
}
@Override
public void start(@Nonnull String vmId) throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=start");
}
static private class MiData {
public Architecture architecture;
public Platform platform;
}
static private HashMap<String,MiData> miCache = new HashMap<String,MiData>();
private void discover(@Nonnull VirtualMachine vm) throws InternalException, CloudException {
String miId = vm.getProviderMachineImageId();
if( miCache.containsKey(miId) ) {
MiData d = miCache.get(miId);
vm.setArchitecture(d.architecture);
vm.setPlatform(d.platform);
}
else {
if( !provider.getComputeServices().hasImageSupport() ) {
vm.setArchitecture(Architecture.I64);
vm.setPlatform(Platform.UNKNOWN);
return;
}
MachineImage img = provider.getComputeServices().getImageSupport().getImage(miId);
if( img == null ) {
vm.setArchitecture(Architecture.I64);
vm.setPlatform(Platform.UNKNOWN);
}
else {
if( img.getName().contains("64") ) {
vm.setArchitecture(Architecture.I64);
}
else if( img.getName().contains("32") ) {
vm.setArchitecture(Architecture.I32);
}
else {
vm.setArchitecture(Architecture.I64);
}
vm.setPlatform(Platform.guess(img.getName() + " " + img.getDescription()));
MiData d = new MiData();
d.architecture = vm.getArchitecture();
d.platform = vm.getPlatform();
miCache.put(miId, d);
}
}
}
static private HashMap<String,VirtualMachineProduct> productCache = new HashMap<String,VirtualMachineProduct>();
@Override
public @Nonnull VirtualMachineCapabilities getCapabilities() throws InternalException, CloudException {
if( capabilities == null ) {
capabilities = new MachineCapabilities(provider);
}
return capabilities;
}
@Override
public @Nullable VirtualMachineProduct getProduct(@Nonnull String productId) throws InternalException, CloudException {
if( productCache.containsKey(productId) ) {
return productCache.get(productId);
}
for( VirtualMachineProduct prd : listProducts(Architecture.I64) ) {
if( prd.getProviderProductId().equals(productId) ) {
productCache.put(productId, prd);
return prd;
}
}
return null;
}
@Override
public @Nullable VirtualMachine getVirtualMachine(@Nonnull String vmId) throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
try {
String json = method.doGetJson(provider.getEndpoint(), "machines/" + vmId);
if( json == null ) {
return null;
}
return toVirtualMachine(new JSONObject(json));
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
@Override
public boolean isSubscribed() throws CloudException, InternalException {
org.dasein.cloud.util.Cache<Boolean> cache = org.dasein.cloud.util.Cache.getInstance(getProvider(),
"isSubscribedVirtualMachine", Boolean.class, CacheLevel.REGION_ACCOUNT);
final Iterable<Boolean> cachedIsSubscribed = cache.get(getContext());
if (cachedIsSubscribed != null && cachedIsSubscribed.iterator().hasNext()) {
final Boolean isSubscribed = cachedIsSubscribed.iterator().next();
if (isSubscribed != null) {
return isSubscribed;
}
}
JoyentMethod method = new JoyentMethod(provider);
try {
method.doGetJson(provider.getEndpoint(), "packages");
} catch (JoyentException e) {
if (e.getErrorType().equals(CloudErrorType.AUTHENTICATION)) {
cache.put(getContext(), Collections.singleton(false));
return false;
}
throw e;
}
cache.put(getContext(), Collections.singleton(true));
return true;
}
@Override
public @Nonnull VirtualMachine launch(@Nonnull VMLaunchOptions withLaunchOptions) throws CloudException, InternalException {
JoyentMethod method = new JoyentMethod(provider);
Map<String, Object> post = new HashMap<String,Object>();
if( withLaunchOptions.getUserData() != null ) {
post.put("metadata.user-script", withLaunchOptions.getUserData());
}
if( withLaunchOptions.getHostName() != null ) {
String name = validateName(withLaunchOptions.getHostName());
post.put("name", name);
}
// Joyent will use datacenter default package/dataset if missing
if( withLaunchOptions.getStandardProductId() != null ) {
post.put("package", withLaunchOptions.getStandardProductId());
}
if( withLaunchOptions.getMachineImageId() != null ) {
post.put("dataset", withLaunchOptions.getMachineImageId());
}
Map<String, Object> meta = withLaunchOptions.getMetaData();
if( meta.size() > 0 ) {
for( Map.Entry<String,Object> entry : meta.entrySet() ) {
post.put("metadata." + entry.getKey(), String.valueOf(entry.getValue()));
}
}
post.put("metadata.dsnTrueImage", withLaunchOptions.getMachineImageId());
post.put("metadata.dsnTrueProduct", withLaunchOptions.getStandardProductId());
post.put("metadata.dsnDescription", withLaunchOptions.getDescription());
String json = method.doPostString(provider.getEndpoint(), "machines", new JSONObject(post).toString());
if( json == null ) {
throw new CloudException("No machine was created");
}
try {
return toVirtualMachine(new JSONObject(json));
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
@SuppressWarnings("deprecation")
@Override
public @Nonnull VirtualMachine launch(@Nonnull String fromMachineImageId, @Nonnull VirtualMachineProduct product, @Nonnull String dataCenterId, @Nonnull String name, @Nonnull String description, @Nullable String withKeypairId, @Nullable String inVlanId, boolean withAnalytics, boolean asSandbox, @Nullable String[] firewallIds, @Nullable Tag... tags) throws InternalException, CloudException {
VMLaunchOptions options;
if( inVlanId == null ) {
options = VMLaunchOptions.getInstance(product.getProviderProductId(), fromMachineImageId, name, description).inDataCenter(dataCenterId);
}
else {
options = VMLaunchOptions.getInstance(product.getProviderProductId(), fromMachineImageId, name, description).inVlan(null, dataCenterId, inVlanId);
}
if( withKeypairId != null ) {
options = options.withBoostrapKey(withKeypairId);
}
if( tags != null ) {
for( Tag t : tags ) {
options = options.withMetaData(t.getKey(), t.getValue());
}
}
if( firewallIds != null ) {
options = options.behindFirewalls(firewallIds);
}
// this is backwards compat for some old enStratus stuff in Joyent
// you really should be using the launch(VMLaunchOptions) and you won't be burdened
// by this legacy stuff
options = options.withUserData("#!/usr/bin/env sh\n"
+"\n"
+"export PATH=/opt/local/bin:/opt/local/sbin:/usr/bin:/usr/sbin\n"
+"\n"
+"if [[ $EUID -ne 0 ]] ; then\n"
+" echo \"Must be root to install enstratus package\"\n"
+" exit 1\n"
+"fi\n"
+"\n"
+"echo \"==> Updating package lists...\"\n"
+"out=$(pkgin update)\n"
+"if [[ $? -ne 0 ]] ; then\n"
+" echo \"error updating package lists\"\n"
+" exit 1\n"
+"fi\n"
+"\n"
+"echo \"==> Installing Sun-jdk6...\"\n"
+"out=$(yes | pkgin in sun-jdk6 sun-jre6)\n"
+"if [[ $? -ne 0 ]] ; then\n"
+" echo \"error installing java\"\n"
+" exit 1\n"
+"fi\n"
+"\n"
+"echo \"==> Installing enstratus package...\"\n"
+"out=$(pkgin -y in enstratus-agent)\n"
+"if [[ $? -ne 0 ]] ; then\n"
+" echo \"error installing enstratus agent\"\n"
+" exit 1\n"
+"fi\n");
return launch(options);
}
@Override
public @Nonnull Iterable<String> listFirewalls(@Nonnull String vmId) throws InternalException, CloudException {
// TODO
return Collections.emptyList();
}
@Override
public @Nonnull Iterable<VirtualMachineProduct> listProducts(@Nonnull String machineImageId) throws InternalException, CloudException {
MachineImage image = getProvider().getComputeServices().getImageSupport().getImage(machineImageId);
Iterable<VirtualMachineProduct> allProducts = listProducts(image.getArchitecture());
List<VirtualMachineProduct> products = new ArrayList<VirtualMachineProduct>();
long minRamSize = 0L;
String value = image.getProviderMetadata().get("min_ram");
if( value != null ) {
minRamSize = Long.parseLong(value);
}
for( VirtualMachineProduct product : allProducts ) {
boolean includeProduct = true;
if( product.getRamSize().longValue() < minRamSize ) {
includeProduct = false;
}
else if( product.getRootVolumeSize() != null && product.getRootVolumeSize().longValue() < image.getMinimumDiskSizeGb() ) {
includeProduct = false;
}
if( includeProduct ) {
products.add(product);
}
}
return products;
}
@Override
public @Nonnull Iterable<VirtualMachineProduct> listProducts(VirtualMachineProductFilterOptions options, Architecture architecture) throws InternalException, CloudException {
Cache<VirtualMachineProduct> cache = Cache.getInstance(getProvider(), "VM.listProducts", VirtualMachineProduct.class, CacheLevel.REGION_ACCOUNT, TimePeriod.valueOf(1, "day"));
final Iterable<VirtualMachineProduct> cachedProducts = cache.get(getContext());
if( cachedProducts != null && cachedProducts.iterator().hasNext() ) {
return cachedProducts;
}
JoyentMethod method = new JoyentMethod(provider);
String json = method.doGetJson(provider.getEndpoint(), "packages");
if( json == null ) {
return Collections.emptyList();
}
try {
ArrayList<VirtualMachineProduct> products = new ArrayList<VirtualMachineProduct>();
JSONArray list = new JSONArray(json);
for( int i=0; i<list.length(); i++ ) {
JSONObject ob = list.getJSONObject(i);
VirtualMachineProduct prd = new VirtualMachineProduct();
if( ob.has("name") ) {
prd.setName(ob.getString("name"));
}
if( ob.has("memory") ) {
prd.setRamSize(new Storage<Megabyte>(ob.getInt("memory"), Storage.MEGABYTE));
}
if( ob.has("disk") ) {
prd.setRootVolumeSize(new Storage<Megabyte>(ob.getInt("disk"), Storage.MEGABYTE));
}
if( ob.has("vcpus") ) {
prd.setCpuCount(ob.getInt("vcpus"));
}
// SmartOS products are returned with 0 vCPUs as this metric doesn't apply
// to them according to Joyent. In SmartOS you get some burstable capacity.
// We will set them to 1 CPU anyway, as zero CPU is no CPU.
if( prd.getCpuCount() == 0 ) {
prd.setCpuCount(1);
}
if( ob.has("description") ) {
prd.setDescription(ob.getString("description"));
}
else {
prd.setDescription(prd.getName());
}
prd.setProviderProductId(ob.getString("id"));
if( options != null) {
if( options.matches(prd) )
products.add(prd);
}
else {
products.add(prd);
}
}
cache.put(getContext(), products);
return products;
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
@Override
public @Nonnull Iterable<ResourceStatus> listVirtualMachineStatus() throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
try {
JSONArray machines = new JSONArray(method.doGetJson(provider.getEndpoint(), "machines"));
ArrayList<ResourceStatus> vms = new ArrayList<ResourceStatus>();
for( int i=0; i<machines.length(); i++ ) {
ResourceStatus vm = toStatus(machines.getJSONObject(i));
if( vm != null ) {
vms.add(vm);
}
}
return vms;
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
@Override
public @Nonnull Iterable<VirtualMachine> listVirtualMachines() throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
try {
JSONArray machines = new JSONArray(method.doGetJson(provider.getEndpoint(), "machines"));
ArrayList<VirtualMachine> vms = new ArrayList<VirtualMachine>();
for( int i=0; i<machines.length(); i++ ) {
VirtualMachine vm = toVirtualMachine(machines.getJSONObject(i));
if( vm != null ) {
vms.add(vm);
}
}
return vms;
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
@Override
public @Nonnull String[] mapServiceAction(@Nonnull ServiceAction action) {
return new String[0];
}
@Override
public void stop(@Nonnull String vmId, boolean force) throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=stop");
}
@Override
public void reboot(@Nonnull String vmId) throws CloudException, InternalException {
JoyentMethod method = new JoyentMethod(provider);
method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=reboot");
}
@Override
public void terminate(@Nonnull String vmId, @Nullable String explanation) throws InternalException, CloudException {
long timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 20);
JoyentMethod method = new JoyentMethod(provider);
VirtualMachine vm = getVirtualMachine(vmId);
if( vm == null ) {
throw new CloudException("No such server: " + vmId);
}
VmState currentState = vm.getCurrentState();
while( VmState.PENDING.equals(currentState) && System.currentTimeMillis() < timeout ) {
try { Thread.sleep(10000); }
catch( InterruptedException e ) { /* ignore */ }
vm = getVirtualMachine(vmId);
if( vm == null ) {
return;
}
currentState = vm.getCurrentState();
}
if( VmState.RUNNING.equals(currentState) ) {
method.doPostString(provider.getEndpoint(), "machines/" + vmId, "action=stop");
}
while( !VmState.STOPPED.equals(currentState) && !VmState.TERMINATED.equals(currentState) && System.currentTimeMillis() < timeout ) {
try { Thread.sleep(10000); }
catch( InterruptedException e ) { /* ignore */ }
vm = getVirtualMachine(vmId);
if( vm == null ) {
return;
}
currentState = vm.getCurrentState();
}
method.doDelete(provider.getEndpoint(), "machines/" + vmId);
if( timeout < CalendarWrapper.MINUTE * 5 ) {
timeout = System.currentTimeMillis() + (CalendarWrapper.MINUTE * 5);
}
while( System.currentTimeMillis() < timeout ) {
if( vm == null || VmState.TERMINATED.equals(vm.getCurrentState()) ) {
return;
}
try { Thread.sleep(10000L); }
catch( InterruptedException ignore ) { }
vm = getVirtualMachine(vmId);
}
logger.warn("System timed out waiting for VM termination");
}
@Override
public @Nullable String getPassword( @Nonnull String vmId ) throws InternalException, CloudException {
final String[] adminUsernames = {"root", "administrator", "admin"};
JoyentMethod method = new JoyentMethod(provider);
try {
JSONObject ob = new JSONObject(method.doGetJson(provider.getEndpoint(), "machines/"+vmId+"/metadata?credentials=true"));
if( ob.has("credentials") ) {
JSONObject credentials = ob.getJSONObject("credentials");
for( String username : adminUsernames ) {
if( ob.has(username) ) {
return ob.getString(username);
}
}
}
} catch( JSONException e ) {
throw new InternalException("Unable to parse the response", e);
}
return null;
}
@Override
public @Nullable String getUserData( @Nonnull String vmId ) throws InternalException, CloudException {
JoyentMethod method = new JoyentMethod(provider);
try {
JSONObject ob = new JSONObject(method.doGetJson(provider.getEndpoint(), "machines/"+vmId+"/metadata"));
if( ob.has("user-script") ) {
return ob.getString("user-script");
}
} catch( JSONException e ) {
throw new InternalException("Unable to parse the response", e);
}
return null;
}
private VirtualMachine toVirtualMachine(JSONObject ob) throws CloudException, InternalException {
if( ob == null ) {
return null;
}
try {
VirtualMachine vm = new VirtualMachine();
vm.setClonable(false);
vm.setImagable(false);
vm.setLastPauseTimestamp(-1L);
vm.setPersistent(true);
vm.setProviderDataCenterId(provider.getContext().getRegionId() + "a");
vm.setProviderOwnerId(provider.getContext().getAccountNumber());
vm.setProviderRegionId(provider.getContext().getRegionId());
vm.setTerminationTimestamp(-1L);
if(ob.has("id") ) {
vm.setProviderVirtualMachineId(ob.getString("id"));
}
if( ob.has("name") ) {
vm.setName(ob.getString("name"));
}
if( ob.has("package") ) {
vm.setProductId(ob.getString("package"));
}
if( ob.has("ips") ) {
JSONArray ips = ob.getJSONArray("ips");
ArrayList<String> pubIp = new ArrayList<String>();
ArrayList<String> privIp = new ArrayList<String>();
for( int i=0; i<ips.length(); i++ ) {
String addr = ips.getString(i);
boolean pub = false;
if( !addr.startsWith("10.") && !addr.startsWith("192.168.") ) {
if( addr.startsWith("172.") ) {
String[] nums = addr.split("\\.");
if( nums.length != 4 ) {
pub = true;
}
else {
try {
int x = Integer.parseInt(nums[1]);
if( x < 16 || x > 31 ) {
pub = true;
}
}
catch( NumberFormatException ignore ) {
// ignore
}
}
}
else {
pub = true;
}
}
if( pub ) {
pubIp.add(addr);
}
else {
privIp.add(addr);
}
}
if( !pubIp.isEmpty() ) {
vm.setPublicIpAddresses(pubIp.toArray(new String[pubIp.size()]));
}
if( !privIp.isEmpty() ) {
vm.setPrivateIpAddresses(privIp.toArray(new String[privIp.size()]));
}
}
if( ob.has("metadata") ) {
JSONObject md = ob.getJSONObject("metadata");
JSONArray names = md.names();
if( names != null ) {
for( int i=0; i<names.length(); i++ ) {
String name = names.getString(i);
if( name.equals("dsnDescription") ) {
vm.setDescription(md.getString(name));
}
else if( name.equals("dsnTrueImage") ) {
vm.setProviderMachineImageId(md.getString(name));
}
// else if( name.equals("dsnTrueProduct") ) {
// vm.setProductId(md.getString(name));
// }
else {
vm.addTag(name, md.getString(name));
}
}
}
}
if( vm.getProviderMachineImageId() == null && ob.has("dataset") ) {
vm.setProviderMachineImageId(getImageIdFromUrn(ob.getString("dataset")));
}
if( ob.has("created") ) {
vm.setCreationTimestamp(provider.parseTimestamp(ob.getString("created")));
}
vm.setPausable(false); // can't ever pause/resume joyent vms
vm.setRebootable(false);
if( ob.has("state") ) {
vm.setCurrentState(toState(ob.getString("state")));
if( VmState.RUNNING.equals(vm.getCurrentState()) ) {
vm.setRebootable(true);
}
else if( VmState.STOPPED.equals(vm.getCurrentState())) {
vm.setImagable(true);
}
}
vm.setLastBootTimestamp(vm.getCreationTimestamp());
if( vm.getName() == null ) {
vm.setName(vm.getProviderVirtualMachineId());
}
if( vm.getDescription() == null ) {
vm.setDescription(vm.getName());
}
discover(vm);
boolean isVMSmartOs = (vm.getPlatform().equals(Platform.SMARTOS));
if( vm.getProductId() == null ) {
VirtualMachineProduct d = null;
int disk, ram;
disk = ob.getInt("disk");
ram = ob.getInt("memory");
for( VirtualMachineProduct prd : listProducts(vm.getArchitecture()) ) {
d = prd;
boolean isProductSmartOs = prd.getName().contains("smartos");
if( prd.getRootVolumeSize().convertTo(Storage.MEGABYTE).intValue() == disk && prd.getRamSize().intValue() == ram ) {
if (isVMSmartOs && !isProductSmartOs){
continue;
}
if (!isVMSmartOs && isProductSmartOs){
continue;
}
vm.setProductId(prd.getProviderProductId());
break;
}
}
// if( vm.getProductId() == null && d != null ) {
// vm.setProductId(d.getProviderProductId());
// }
}
return vm;
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
private @Nonnull VmState toState(@Nonnull String s) {
if( s.equalsIgnoreCase("running") ) {
return VmState.RUNNING;
}
else if( s.equalsIgnoreCase("provisioning") ) {
return VmState.PENDING;
}
else if( s.equalsIgnoreCase("stopping") ) {
return VmState.STOPPING;
}
else if( s.equalsIgnoreCase("stopped") ) {
return VmState.STOPPED;
}
else if( s.equalsIgnoreCase("deleted") ) {
return VmState.TERMINATED;
}
else {
logger.warn("DEBUG: Unknown Joyent VM state: " + s);
return VmState.PENDING;
}
}
private @Nullable ResourceStatus toStatus(@Nullable JSONObject ob) throws CloudException, InternalException {
if( ob == null ) {
return null;
}
try {
VmState state = VmState.PENDING;
String vmId = null;
if(ob.has("id") ) {
vmId = ob.getString("id");
}
if( ob.has("state") ) {
state = toState(ob.getString("state"));
}
if( vmId == null ) {
return null;
}
return new ResourceStatus(vmId, state);
}
catch( JSONException e ) {
throw new CloudException(e);
}
}
static private HashMap<String,String> urnMapping = new HashMap<String,String>();
private String getImageIdFromUrn(String urn) throws CloudException, InternalException {
if( logger.isTraceEnabled() ) {
logger.trace("ENTER: " + Machine.class.getName() + ".getImageIdFromUrn(" + urn + ")");
}
try {
if( urnMapping.containsKey(urn) ) {
return urnMapping.get(urn);
}
MachineImage img = provider.getComputeServices().getImageSupport().getImage(urn);
if( img != null ) {
String id = img.getProviderMachineImageId();
if( id != null ) {
urnMapping.put(urn, id);
return id;
}
}
return urn;
}
finally {
if( logger.isTraceEnabled() ) {
logger.trace("EXIT: " + Machine.class.getName() + ".getImageIdFromUrn()");
}
}
}
/*
TODO: fix
@see org.dasein.cloud.joyent.compute.MachineCapabilities#getVirtualMachineNamingConstraints
*/
private String validateName(String originalName) {
StringBuilder name = new StringBuilder();
for( int i=0; i<originalName.length(); i++ ) {
char c = originalName.charAt(i);
if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ) {
name.append(c);
}
else if( ((c >= '0' && c <= '9') || c == '-' || c == '_' || c == ' ') && name.length() > 0 ) {
if( c == ' ' ) {
c = '-';
}
name.append(c);
}
}
if( name.length() < 1 ) {
return "unnamed-" + System.currentTimeMillis();
}
if ( name.charAt(name.length()-1)=='-' || name.charAt(name.length()-1)=='_') { // check for trailing - or _
name.deleteCharAt(name.length()-1);
}
return name.toString();
}
@Override
public void setTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Server.createTags");
try {
try{
JoyentMethod method = new JoyentMethod(provider);
method.doDelete(provider.getEndpoint(), "machines/" + vmId + "/tags");
// Delete tags takes time to reflect
do {
try { Thread.sleep(4000); }
catch( InterruptedException e ) { /* ignore */ }
} while (!method.doGetJson(provider.getEndpoint(), "machines/" + vmId + "/tags").equals("{}"));
}
catch(CloudException e){
logger.error("Error while deleting all tags for - " + vmId + ".", e);
}
updateTags(vmId, tags);
}
finally {
APITrace.end();
}
}
@Override
public void setTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException {
for (String vmId : vmIds) {
setTags(vmId , tags);
}
}
@Override
public void updateTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Server.updateTags");
try {
try{
JoyentMethod method = new JoyentMethod(provider);
Map<String, Object> post = new HashMap<String,Object>();
for (int i = 0; i < tags.length; i++){
post.put(tags[i].getKey(), tags[i].getValue() == null ? "" : tags[i].getValue());
}
method.doPostString(provider.getEndpoint(), "machines/"+ vmId +"/tags", new JSONObject(post).toString());
}
catch(CloudException e){
logger.error("Error while creating the tags for - " + vmId + ".", e);
}
}
finally {
APITrace.end();
}
}
@Override
public void updateTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException {
for( String vmId : vmIds ) {
updateTags(vmId, tags);
}
}
@Override
public void removeTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException {
APITrace.begin(getProvider(), "Server.removeTags");
try {
JoyentMethod method = new JoyentMethod(provider);
for (int i = 0; i < tags.length; i++) {
try{
method.doDelete(provider.getEndpoint(), "machines/" + vmId + "/tags/" + tags[i].getKey());
// Delete tags takes time to reflect
while (method.doGetJson(provider.getEndpoint(), "machines/" + vmId + "/tags/" + tags[i].getKey()) != null) {
try { Thread.sleep(4000); }
catch( InterruptedException e ) { /* ignore */ }
}
}
catch(CloudException e){
logger.error("Error while deleting the tags for - " + vmId + ".", e);
}
}
}
finally {
APITrace.end();
}
}
@Override
public void removeTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException {
for (String vmId : vmIds) {
removeTags(vmId , tags);
}
}
}