Java tutorial
/** * 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.openstack.nova.os.compute; import org.apache.commons.codec.binary.Base64; import org.apache.commons.net.util.SubnetUtils; import org.apache.http.HttpStatus; import org.apache.log4j.Logger; import org.dasein.cloud.*; import org.dasein.cloud.compute.*; import org.dasein.cloud.network.Firewall; import org.dasein.cloud.network.FirewallSupport; import org.dasein.cloud.network.IPVersion; import org.dasein.cloud.network.IpAddress; import org.dasein.cloud.network.IpAddressSupport; import org.dasein.cloud.network.NetworkServices; import org.dasein.cloud.network.RawAddress; import org.dasein.cloud.network.Subnet; import org.dasein.cloud.network.VLAN; import org.dasein.cloud.network.VLANSupport; import org.dasein.cloud.openstack.nova.os.NovaException; import org.dasein.cloud.openstack.nova.os.NovaMethod; import org.dasein.cloud.openstack.nova.os.NovaOpenStack; import org.dasein.cloud.openstack.nova.os.OpenStackProvider; import org.dasein.cloud.openstack.nova.os.network.NovaNetworkServices; import org.dasein.cloud.openstack.nova.os.network.Quantum; 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.Gigabyte; 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.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.omg.PortableInterceptor.ACTIVE; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Implements services supporting interaction with cloud virtual machines. * @author George Reese (george.reese@imaginary.com) * @version 2012.09 addressed issue with alternate security group lookup in some OpenStack environments (issue #1) * @version 2013.02 implemented setting kernel/ramdisk image IDs (see issue #40 in dasein-cloud-core) * @version 2013.02 updated with support for Dasein Cloud 2013.02 model * @version 2013.02 added support for fetching shell keys (issue #4) * @since unknown */ public class NovaServer extends AbstractVMSupport<NovaOpenStack> { static private final Logger logger = NovaOpenStack.getLogger(NovaServer.class, "std"); static public final String SERVICE = "compute"; NovaServer(NovaOpenStack provider) { super(provider); } @Nonnull protected String getTenantId() throws CloudException, InternalException { return getProvider().getContext().getAccountNumber(); } private transient volatile NovaServerCapabilities capabilities; @Nonnull @Override public VirtualMachineCapabilities getCapabilities() throws InternalException, CloudException { if (capabilities == null) { capabilities = new NovaServerCapabilities(getProvider()); } return capabilities; } protected NovaMethod getMethod() { return new NovaMethod(getProvider()); } protected int getMinorVersion() throws CloudException, InternalException { return getProvider().getMinorVersion(); } protected int getMajorVersion() throws CloudException, InternalException { return getProvider().getMajorVersion(); } protected String getRegionId() throws InternalException { return getContext().getRegionId(); } protected Platform getPlatform(String vmName, String vmDescription, String imageId) throws CloudException, InternalException { Platform p = Platform.guess(vmName + " " + vmDescription); if (p.equals(Platform.UNKNOWN)) { if (imageId != null) { MachineImage img = getProvider().getComputeServices().getImageSupport().getImage(imageId); if (img != null) { p = img.getPlatform(); } } } return p; } @Override public @Nonnull String getConsoleOutput(@Nonnull String vmId) throws CloudException, InternalException { APITrace.begin(getProvider(), "VM.getConsoleOutput"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } Map<String, Object> json = new HashMap<String, Object>(); json.put("os-getConsoleOutput", new HashMap<String, Object>()); String console = getMethod().postServersForString("/servers", vmId, new JSONObject(json), true); return (console == null ? "" : console); } finally { APITrace.end(); } } @Override public @Nullable VirtualMachineProduct getProduct(@Nonnull String productId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.getProduct"); try { for (FlavorRef flavor : listFlavors()) { if (flavor.product.getProviderProductId().equals(productId)) { return flavor.product; } } return null; } finally { APITrace.end(); } } protected NetworkServices getNetworkServices() { return getProvider().getNetworkServices(); } protected OpenStackProvider getCloudProvider() { return getProvider().getCloudProvider(); } @Override public @Nullable VirtualMachine getVirtualMachine(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.getVirtualMachine"); try { JSONObject ob = getMethod().getServers("/servers", vmId, true); if (ob == null) { return null; } Iterable<IpAddress> ipv4, ipv6; Iterable<VLAN> networks; NetworkServices services = getNetworkServices(); if (services != null) { IpAddressSupport support = services.getIpAddressSupport(); if (support != null) { ipv4 = support.listIpPool(IPVersion.IPV4, false); ipv6 = support.listIpPool(IPVersion.IPV6, false); } else { ipv4 = ipv6 = Collections.emptyList(); } VLANSupport vs = services.getVlanSupport(); if (vs != null) { networks = vs.listVlans(); } else { networks = Collections.emptyList(); } } else { ipv4 = ipv6 = Collections.emptyList(); networks = Collections.emptyList(); } try { if (ob.has("server")) { JSONObject server = ob.getJSONObject("server"); VirtualMachine vm = toVirtualMachine(server, ipv4, ipv6, networks); if (vm != null) { return vm; } } } catch (JSONException e) { logger.error("getVirtualMachine(): Unable to identify expected values in JSON: " + e.getMessage()); throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", "Missing JSON element for servers"); } return null; } finally { APITrace.end(); } } protected @Nonnull String getServerStatus(@Nonnull String virtualMachineId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.getVmStatus"); try { final JSONObject ob = getMethod().getServers("/servers", virtualMachineId, true); return ob.getJSONObject("server").getString("status"); } catch (JSONException e) { throw new CloudException(e); } finally { APITrace.end(); } } @Override public @Nonnull VirtualMachine alterVirtualMachineProduct(@Nonnull String virtualMachineId, @Nonnull String productId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.resize"); try { Map<String, Object> json = new HashMap<String, Object>(); Map<String, Object> action = new HashMap<String, Object>(); action.put("flavorRef", productId); json.put("resize", action); getMethod().postServers("/servers", virtualMachineId, new JSONObject(json), true); String status; while ("resize".equalsIgnoreCase(status = getServerStatus(virtualMachineId))) { try { Thread.sleep(5000L); } catch (InterruptedException e) { } } if ("verify_resize".equalsIgnoreCase(status)) { json.clear(); json.put("confirmResize", null); getMethod().postServers("/servers", virtualMachineId, new JSONObject(json), true); } VirtualMachine vm = getVirtualMachine(virtualMachineId); if (status.equals("ACTIVE") && !(vm.getProductId().equals(productId))) { throw new CloudException("Failed to resize VM from " + getProduct(vm.getProductId()).getName() + " to " + getProduct(productId).getName()); } return vm; } finally { APITrace.end(); } } @Override public boolean isSubscribed() throws CloudException, InternalException { APITrace.begin(getProvider(), "VM.isSubscribed"); try { return (getProvider().testContext() != null); } finally { APITrace.end(); } } @Override public @Nonnull VirtualMachine launch(@Nonnull VMLaunchOptions options) throws CloudException, InternalException { APITrace.begin(getProvider(), "VM.launch"); VirtualMachine vm = null; String portId = null; try { MachineImage targetImage = getProvider().getComputeServices().getImageSupport() .getImage(options.getMachineImageId()); //Additional LPAR Call boolean isBareMetal = false; try { String lparMetadataKey = "hypervisor_type"; String lparMetadataValue = "Hitachi"; JSONObject ob = getMethod().getServers("/images/" + options.getMachineImageId() + "/metadata", lparMetadataKey, false); if (ob.has("metadata")) { JSONObject metadata = ob.getJSONObject("metadata"); if (metadata.has(lparMetadataKey) && metadata.getString(lparMetadataKey).equals(lparMetadataValue)) isBareMetal = true; } } catch (Exception ex) { //Something failed while checking Hitachi LPAR metadata logger.error("Failed to find Hitachi LPAR metadata"); } if (targetImage == null) { throw new CloudException("No such machine image: " + options.getMachineImageId()); } Map<String, Object> wrapper = new HashMap<String, Object>(); Map<String, Object> json = new HashMap<String, Object>(); json.put("name", options.getHostName()); if (options.getBootstrapPassword() != null) { json.put("adminPass", options.getBootstrapPassword()); } if (options.getUserData() != null) { try { json.put("user_data", Base64.encodeBase64String(options.getUserData().getBytes("utf-8"))); } catch (UnsupportedEncodingException e) { throw new InternalException(e); } } if (getMinorVersion() == 0 && getMajorVersion() == 1) { json.put("imageId", String.valueOf(options.getMachineImageId())); json.put("flavorId", options.getStandardProductId()); } else { if (getProvider().getProviderName().equals("HP")) { json.put("imageRef", options.getMachineImageId()); } else { json.put("imageRef", getProvider().getComputeServices().getImageSupport() .getImageRef(options.getMachineImageId())); } json.put("flavorRef", getFlavorRef(options.getStandardProductId())); } if (options.getVlanId() != null) { List<Map<String, Object>> vlans = new ArrayList<Map<String, Object>>(); Map<String, Object> vlan = new HashMap<String, Object>(); vlan.put("uuid", options.getVlanId()); vlans.add(vlan); json.put("networks", vlans); } else { if (options.getSubnetId() != null && !getProvider().isRackspace()) { NovaNetworkServices services = getProvider().getNetworkServices(); if (services != null) { Quantum support = services.getVlanSupport(); if (support != null) { List<Map<String, Object>> vlans = new ArrayList<Map<String, Object>>(); Map<String, Object> vlan = new HashMap<String, Object>(); try { portId = support.createPort(options.getSubnetId(), options.getHostName(), options.getFirewallIds()); vlan.put("port", portId); vlans.add(vlan); json.put("networks", vlans); options.withMetaData("org.dasein.portId", portId); } catch (CloudException e) { if (e.getHttpCode() != 403) { throw new CloudException(e.getMessage()); } logger.warn("Unable to create port - trying to launch into general network"); Subnet subnet = support.getSubnet(options.getSubnetId()); vlan.put("uuid", subnet.getProviderVlanId()); vlans.add(vlan); json.put("networks", vlans); } } } } } if (options.getBootstrapKey() != null) { json.put("key_name", options.getBootstrapKey()); } if (options.getFirewallIds().length > 0) { List<Map<String, Object>> firewalls = new ArrayList<Map<String, Object>>(); for (String id : options.getFirewallIds()) { NetworkServices services = getProvider().getNetworkServices(); Firewall firewall = null; if (services != null) { FirewallSupport support = services.getFirewallSupport(); if (support != null) { firewall = support.getFirewall(id); } } if (firewall != null) { Map<String, Object> fw = new HashMap<String, Object>(); fw.put("name", firewall.getName()); firewalls.add(fw); } } json.put("security_groups", firewalls); } if (isBareMetal) { Map<String, String> blockDeviceMapping = new HashMap<String, String>(); //blockDeviceMapping.put("device_name", "/dev/sdb1"); blockDeviceMapping.put("boot_index", "0"); blockDeviceMapping.put("uuid", getProvider().getComputeServices().getImageSupport() .getImageRef(options.getMachineImageId())); //blockDeviceMapping.put("guest_format", "ephemeral"); String volumeSize = ""; if (targetImage.getTag("minDisk") != null) { volumeSize = (String) targetImage.getTag("minDisk"); } else { String minSize = (String) targetImage.getTag("minSize"); volumeSize = roundUpToGB(Long.valueOf(minSize)) + ""; } blockDeviceMapping.put("volume_size", volumeSize); blockDeviceMapping.put("source_type", "image"); blockDeviceMapping.put("destination_type", "volume"); blockDeviceMapping.put("delete_on_termination", "True"); json.put("block_device_mapping_v2", blockDeviceMapping); } if (!targetImage.getPlatform().equals(Platform.UNKNOWN)) { options.withMetaData("org.dasein.platform", targetImage.getPlatform().name()); } options.withMetaData("org.dasein.description", options.getDescription()); Map<String, Object> tmpMeta = options.getMetaData(); Map<String, Object> newMeta = new HashMap<String, Object>(); for (Map.Entry entry : tmpMeta.entrySet()) { if (entry.getValue() != null) { //null values not supported by openstack newMeta.put(entry.getKey().toString(), entry.getValue()); } } json.put("metadata", newMeta); wrapper.put("server", json); JSONObject result = getMethod().postServers(isBareMetal ? "/os-volumes_boot" : "/servers", null, new JSONObject(wrapper), true); if (result.has("server")) { try { Collection<IpAddress> ips = Collections.emptyList(); Collection<VLAN> nets = Collections.emptyList(); JSONObject server = result.getJSONObject("server"); vm = toVirtualMachine(server, ips, ips, nets); if (vm != null) { String vmId = vm.getProviderVirtualMachineId(); long timeout = System.currentTimeMillis() + 5 * 60 * 1000; while ((vm == null || vm.getCurrentState() == null) && System.currentTimeMillis() < timeout) { try { Thread.sleep(5000); } catch (InterruptedException ignore) { } vm = getVirtualMachine(vmId); } if (vm == null || vm.getCurrentState() == null) { throw new CloudException("VM failed to launch with a meaningful status"); } return vm; } } catch (JSONException e) { logger.error("launch(): Unable to understand launch response: " + e.getMessage()); if (logger.isTraceEnabled()) { e.printStackTrace(); } throw new CloudException(e); } } logger.error("launch(): No server was created by the launch attempt, and no error was returned"); throw new CloudException("No virtual machine was launched"); } finally { if (portId != null && (vm == null || VmState.ERROR.equals(vm.getCurrentState()))) { //if launch fails or instance in error state - remove port Quantum quantum = getProvider().getNetworkServices().getVlanSupport(); if (quantum != null) { quantum.removePort(portId); } } APITrace.end(); } } public static int roundUpToGB(Long size) { Double round = Math.ceil(size / Math.pow(2, 30)); return round.intValue(); } private @Nonnull Iterable<String> listFirewalls(@Nonnull String vmId, @Nonnull JSONObject server) throws InternalException, CloudException { try { if (server.has("security_groups")) { NetworkServices services = getProvider().getNetworkServices(); Iterable<Firewall> firewalls = null; if (services != null) { FirewallSupport support = services.getFirewallSupport(); if (support != null) { firewalls = support.list(); } } if (firewalls == null) { firewalls = Collections.emptyList(); } JSONArray groups = server.getJSONArray("security_groups"); List<String> results = new ArrayList<String>(); for (int i = 0; i < groups.length(); i++) { JSONObject group = groups.getJSONObject(i); String id = group.has("id") ? group.getString("id") : null; String name = group.has("name") ? group.getString("name") : null; if (id != null || name != null) { for (Firewall fw : firewalls) { if (id != null) { if (id.equals(fw.getProviderFirewallId())) { results.add(id); } } else if (name.equals(fw.getName())) { results.add(fw.getProviderFirewallId()); } } } } return results; } else { List<String> results = new ArrayList<String>(); JSONObject ob = getMethod().getServers("/os-security-groups/servers", vmId + "/os-security-groups", true); if (ob != null) { if (ob.has("security_groups")) { JSONArray groups = ob.getJSONArray("security_groups"); for (int i = 0; i < groups.length(); i++) { JSONObject group = groups.getJSONObject(i); if (group.has("id")) { results.add(group.getString("id")); } } } } return results; } } catch (JSONException e) { throw new CloudException(e); } } @Override public @Nonnull Iterable<String> listFirewalls(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.listFirewalls"); try { JSONObject ob = getMethod().getServers("/servers", vmId, true); if (ob == null) { return Collections.emptyList(); } try { if (ob.has("server")) { JSONObject server = ob.getJSONObject("server"); return listFirewalls(vmId, server); } throw new CloudException("No such server: " + vmId); } catch (JSONException e) { logger.error("listFirewalls(): Unable to identify expected values in JSON: " + e.getMessage()); throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", "Missing JSON element for servers"); } } finally { APITrace.end(); } } static public class FlavorRef { public String id; public String[][] links; VirtualMachineProduct product; public String toString() { return (id + " -> " + product); } } @Nonnull protected Iterable<FlavorRef> listFlavors() throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.listFlavors"); try { Cache<FlavorRef> cache = Cache.getInstance(getProvider(), "flavorRefs", FlavorRef.class, CacheLevel.REGION_ACCOUNT, new TimePeriod<Day>(1, TimePeriod.DAY)); Iterable<FlavorRef> refs = cache.get(getContext()); if (refs != null) { return refs; } JSONObject ob = getMethod().getServers("/flavors", null, true); List<FlavorRef> flavors = new ArrayList<FlavorRef>(); try { if (ob != null && ob.has("flavors")) { JSONArray list = ob.getJSONArray("flavors"); for (int i = 0; i < list.length(); i++) { JSONObject p = list.getJSONObject(i); FlavorRef ref = new FlavorRef(); if (p.has("id")) { ref.id = p.getString("id"); } else { continue; } if (p.has("links")) { JSONArray links = p.getJSONArray("links"); ref.links = new String[links.length()][]; for (int j = 0; j < links.length(); j++) { JSONObject link = links.getJSONObject(j); ref.links[j] = new String[2]; if (link.has("rel")) { ref.links[j][0] = link.getString("rel"); } if (link.has("href")) { ref.links[j][1] = link.getString("href"); } } } else { ref.links = new String[0][]; } ref.product = toProduct(p); if (ref.product != null) { flavors.add(ref); } } } } catch (JSONException e) { logger.error("listProducts(): Unable to identify expected values in JSON: " + e.getMessage()); throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", "Missing JSON element for flavors: " + e.getMessage()); } cache.put(getContext(), flavors); return flavors; } finally { APITrace.end(); } } public @Nullable String getFlavorRef(@Nonnull String flavorId) throws InternalException, CloudException { for (FlavorRef ref : listFlavors()) { if (ref.id.equals(flavorId)) { String def = null; for (String[] link : ref.links) { if (link[0] != null && link[0].equals("self") && link[1] != null) { return link[1]; } else if (def == null && link[1] != null) { def = link[1]; } } return def; } } return null; } @Override public @Nonnull Iterable<VirtualMachineProduct> listAllProducts() throws CloudException, InternalException { return listProducts(null, VirtualMachineProductFilterOptions.getInstance()); } @Override public @Nonnull Iterable<VirtualMachineProduct> listProducts(@Nonnull String machineImageId, @Nullable VirtualMachineProductFilterOptions options) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.listProducts"); try { List<VirtualMachineProduct> products = new ArrayList<VirtualMachineProduct>(); for (FlavorRef flavor : listFlavors()) { if (options != null) { if (options.matches(flavor.product)) { products.add(flavor.product); } } else { products.add(flavor.product); } } return products; } finally { APITrace.end(); } } @Override public @Nonnull Iterable<ResourceStatus> listVirtualMachineStatus() throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.listVirtualMachineStatus"); try { JSONObject ob = getMethod().getServers("/servers", null, true); List<ResourceStatus> servers = new ArrayList<ResourceStatus>(); try { if (ob != null && ob.has("servers")) { JSONArray list = ob.getJSONArray("servers"); for (int i = 0; i < list.length(); i++) { JSONObject server = list.getJSONObject(i); ResourceStatus vm = toStatus(server); if (vm != null) { servers.add(vm); } } } } catch (JSONException e) { logger.error( "listVirtualMachines(): Unable to identify expected values in JSON: " + e.getMessage()); e.printStackTrace(); throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", "Missing JSON element for servers in " + ob.toString()); } return servers; } finally { APITrace.end(); } } @Override public @Nonnull Iterable<VirtualMachine> listVirtualMachines() throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.listVirtualMachines"); try { JSONObject ob = getMethod().getServers("/servers", null, true); List<VirtualMachine> servers = new ArrayList<VirtualMachine>(); Iterable<IpAddress> ipv4 = Collections.emptyList(), ipv6 = Collections.emptyList(); Iterable<VLAN> nets = Collections.emptyList(); NetworkServices services = getProvider().getNetworkServices(); if (services != null) { IpAddressSupport support = services.getIpAddressSupport(); if (support != null) { ipv4 = support.listIpPool(IPVersion.IPV4, false); ipv6 = support.listIpPool(IPVersion.IPV6, false); } VLANSupport vs = services.getVlanSupport(); if (vs != null) { nets = vs.listVlans(); } } try { if (ob != null && ob.has("servers")) { JSONArray list = ob.getJSONArray("servers"); for (int i = 0; i < list.length(); i++) { JSONObject server = list.getJSONObject(i); VirtualMachine vm = toVirtualMachine(server, ipv4, ipv6, nets); if (vm != null) { servers.add(vm); } } } } catch (JSONException e) { logger.error( "listVirtualMachines(): Unable to identify expected values in JSON: " + e.getMessage()); e.printStackTrace(); throw new CloudException(CloudErrorType.COMMUNICATION, 200, "invalidJson", "Missing JSON element for servers in " + ob.toString()); } return servers; } finally { APITrace.end(); } } @Override public void pause(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.pause"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsPause()) { throw new OperationNotSupportedException( "Pause/unpause is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("pause", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void resume(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.resume"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsResume()) { throw new OperationNotSupportedException( "Suspend/resume is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("resume", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void start(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.start"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsStart()) { throw new OperationNotSupportedException( "Start/stop is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("os-start", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void stop(@Nonnull String vmId, boolean force) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.stop"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsStop()) { throw new OperationNotSupportedException( "Start/stop is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("os-stop", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void suspend(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.suspend"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsSuspend()) { throw new OperationNotSupportedException( "Suspend/resume is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("suspend", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void unpause(@Nonnull String vmId) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.unpause"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { throw new CloudException("No such virtual machine: " + vmId); } if (!getCapabilities().supportsPause()) { throw new OperationNotSupportedException( "Pause/unpause is not supported in " + getProvider().getCloudName()); } Map<String, Object> json = new HashMap<String, Object>(); json.put("unpause", null); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void reboot(@Nonnull String vmId) throws CloudException, InternalException { APITrace.begin(getProvider(), "VM.reboot"); try { Map<String, Object> json = new HashMap<String, Object>(); Map<String, Object> action = new HashMap<String, Object>(); action.put("type", "HARD"); json.put("reboot", action); getMethod().postServers("/servers", vmId, new JSONObject(json), true); } finally { APITrace.end(); } } @Override public void terminate(@Nonnull String vmId, @Nullable String explanation) throws InternalException, CloudException { APITrace.begin(getProvider(), "VM.terminate"); try { VirtualMachine vm = getVirtualMachine(vmId); if (vm == null) { return; // do nothing, machine is already gone } long timeout = System.currentTimeMillis() + CalendarWrapper.HOUR; do { try { Quantum quantum = getProvider().getNetworkServices().getVlanSupport(); if (quantum != null) { String cachedPortId = (String) vm.getTag("org.dasein.portId"); Iterable<String> portIds = quantum.listPorts(vm); for (String portId : portIds) { quantum.removePort(portId); if (portId.equalsIgnoreCase(cachedPortId)) { cachedPortId = null; } } // if ports were detached, listPorts will not return any ports, diff method to be used if (cachedPortId != null) { quantum.removePort(cachedPortId); } } getMethod().deleteServers("/servers", vmId); return; } catch (NovaException e) { if (e.getHttpCode() != HttpStatus.SC_CONFLICT) { throw e; } } try { Thread.sleep(CalendarWrapper.MINUTE); } catch (InterruptedException e) { /* ignore */ } } while (System.currentTimeMillis() < timeout); } finally { APITrace.end(); } } private @Nullable VirtualMachineProduct toProduct(@Nullable JSONObject json) throws JSONException, InternalException, CloudException { if (json == null) { return null; } VirtualMachineProduct product = new VirtualMachineProduct(); if (json.has("id")) { product.setProviderProductId(json.getString("id")); } if (json.has("name")) { product.setName(json.getString("name")); } if (json.has("description")) { product.setDescription(json.getString("description")); } if (json.has("ram")) { product.setRamSize(new Storage<Megabyte>(json.getInt("ram"), Storage.MEGABYTE)); } if (json.has("disk")) { product.setRootVolumeSize(new Storage<Gigabyte>(json.getInt("disk"), Storage.GIGABYTE)); } product.setCpuCount(1); if (product.getProviderProductId() == null) { return null; } if (product.getName() == null) { product.setName(product.getProviderProductId()); } if (product.getDescription() == null) { product.setDescription(product.getName()); } product.setArchitectures(Architecture.I32, Architecture.I64); return product; } private @Nullable ResourceStatus toStatus(@Nullable JSONObject server) throws JSONException, InternalException, CloudException { if (server == null) { return null; } String serverId = null; if (server.has("id")) { serverId = server.getString("id"); } if (serverId == null) { return null; } VmState state = VmState.PENDING; if (server.has("status")) { String s = server.getString("status").toLowerCase(); if (s.equals("active")) { state = VmState.RUNNING; } else if (s.equals("build")) { state = VmState.PENDING; } else if (s.equals("deleted")) { state = VmState.TERMINATED; } else if (s.equals("suspended")) { state = VmState.SUSPENDED; } else if (s.equalsIgnoreCase("paused")) { state = VmState.PAUSED; } else if (s.equalsIgnoreCase("stopped") || s.equalsIgnoreCase("shutoff")) { state = VmState.STOPPED; } else if (s.equalsIgnoreCase("stopping")) { state = VmState.STOPPING; } else if (s.equalsIgnoreCase("pausing")) { state = VmState.PAUSING; } else if (s.equalsIgnoreCase("suspending")) { state = VmState.SUSPENDING; } else if (s.equals("error")) { state = VmState.ERROR; } else if (s.equals("reboot") || s.equals("hard_reboot")) { state = VmState.REBOOTING; } else { logger.warn("toVirtualMachine(): Unknown server state: " + s); state = VmState.PENDING; } } return new ResourceStatus(serverId, state); } protected @Nullable VirtualMachine toVirtualMachine(@Nullable JSONObject server, @Nonnull Iterable<IpAddress> ipv4, @Nonnull Iterable<IpAddress> ipv6, @Nonnull Iterable<VLAN> networks) throws JSONException, InternalException, CloudException { if (server == null) { return null; } VirtualMachine vm = new VirtualMachine(); String description = null; // vm.setCurrentState(VmState.RUNNING); vm.setArchitecture(Architecture.I64); vm.setClonable(false); vm.setCreationTimestamp(-1L); vm.setImagable(false); vm.setLastBootTimestamp(-1L); vm.setLastPauseTimestamp(-1L); vm.setPausable(false); vm.setPersistent(true); vm.setPlatform(Platform.UNKNOWN); vm.setRebootable(true); vm.setProviderOwnerId(getTenantId()); if (getCloudProvider().equals(OpenStackProvider.RACKSPACE)) { vm.setPersistent(false); } if (server.has("id")) { vm.setProviderVirtualMachineId(server.getString("id")); } else return null; if (server.has("name")) { vm.setName(server.getString("name")); } if (server.has("description") && !server.isNull("description")) { description = server.getString("description"); } if (server.has("kernel_id")) { vm.setProviderKernelImageId(server.getString("kernel_id")); } if (server.has("ramdisk_id")) { vm.setProviderRamdiskImageId(server.getString("ramdisk_id")); } JSONObject md = (server.has("metadata") && !server.isNull("metadata")) ? server.getJSONObject("metadata") : null; Map<String, String> map = new HashMap<String, String>(); boolean imaging = false; if (md != null) { if (md.has("org.dasein.description") && vm.getDescription() == null) { description = md.getString("org.dasein.description"); } else if (md.has("Server Label")) { description = md.getString("Server Label"); } if (md.has("org.dasein.platform")) { try { vm.setPlatform(Platform.valueOf(md.getString("org.dasein.platform"))); } catch (Throwable ignore) { // ignore } } String[] keys = JSONObject.getNames(md); if (keys != null) { for (String key : keys) { String value = md.getString(key); if (value != null) { map.put(key, value); } } } } if (server.has("OS-EXT-STS:task_state") && !server.isNull("OS-EXT-STS:task_state")) { String t = server.getString("OS-EXT-STS:task_state"); map.put("OS-EXT-STS:task_state", t); imaging = t.equalsIgnoreCase("image_snapshot"); } if (description == null) { if (vm.getName() == null) { vm.setName(vm.getProviderVirtualMachineId()); } vm.setDescription(vm.getName()); } else { vm.setDescription(description); } if (server.has("hostId")) { map.put("host", server.getString("hostId")); } vm.setTags(map); if (server.has("image") && !server.isNull("image")) { try { JSONObject img = server.getJSONObject("image"); if (img.has("id")) { vm.setProviderMachineImageId(img.getString("id")); } } catch (JSONException ex) { logger.error("Unable to parse the image object"); try { server.getString("image"); logger.error( "Image object has been returned as a string from cloud " + server.getString("image")); } catch (JSONException ignore) { } } } if (server.has("flavor")) { JSONObject f = server.getJSONObject("flavor"); if (f.has("id")) { vm.setProductId(f.getString("id")); } } else if (server.has("flavorId")) { vm.setProductId(server.getString("flavorId")); } if (server.has("adminPass")) { vm.setRootPassword(server.getString("adminPass")); } if (server.has("key_name")) { vm.setProviderShellKeyIds(server.getString("key_name")); } if (server.has("status")) { String s = server.getString("status").toLowerCase(); if (s.equals("active")) { vm.setCurrentState(VmState.RUNNING); } else if (s.startsWith("build")) { vm.setCurrentState(VmState.PENDING); } else if (s.equals("deleted")) { vm.setCurrentState(VmState.TERMINATED); } else if (s.equals("suspended")) { vm.setCurrentState(VmState.SUSPENDED); } else if (s.equalsIgnoreCase("paused")) { vm.setCurrentState(VmState.PAUSED); } else if (s.equalsIgnoreCase("stopped") || s.equalsIgnoreCase("shutoff")) { vm.setCurrentState(VmState.STOPPED); } else if (s.equalsIgnoreCase("stopping")) { vm.setCurrentState(VmState.STOPPING); } else if (s.equalsIgnoreCase("pausing")) { vm.setCurrentState(VmState.PAUSING); } else if (s.equalsIgnoreCase("suspending")) { vm.setCurrentState(VmState.SUSPENDING); } else if (s.equals("error")) { vm.setCurrentState(VmState.ERROR); } else if (s.equals("reboot") || s.equals("hard_reboot")) { vm.setCurrentState(VmState.REBOOTING); } else { logger.warn("toVirtualMachine(): Unknown server state: " + s); vm.setCurrentState(VmState.PENDING); } } if (vm.getCurrentState() == null && imaging) { vm.setCurrentState(VmState.PENDING); } if (server.has("created")) { vm.setCreationTimestamp(NovaOpenStack.parseTimestamp(server.getString("created"))); } if (server.has("addresses")) { JSONObject addrs = server.getJSONObject("addresses"); String[] names = JSONObject.getNames(addrs); if (names != null && names.length > 0) { List<RawAddress> pub = new ArrayList<RawAddress>(); List<RawAddress> priv = new ArrayList<RawAddress>(); for (String name : names) { JSONArray arr = addrs.getJSONArray(name); String subnet = null; for (int i = 0; i < arr.length(); i++) { RawAddress addr = null; String type = null; if (getMinorVersion() == 0 && getMajorVersion() == 1) { addr = new RawAddress(arr.getString(i).trim(), IPVersion.IPV4); } else { JSONObject a = arr.getJSONObject(i); type = a.optString("OS-EXT-IPS:type"); if (a.has("version") && a.getInt("version") == 4 && a.has("addr")) { subnet = a.getString("addr"); addr = new RawAddress(a.getString("addr"), IPVersion.IPV4); } else if (a.has("version") && a.getInt("version") == 6 && a.has("addr")) { subnet = a.getString("addr"); addr = new RawAddress(a.getString("addr"), IPVersion.IPV6); } } if (addr != null) { if ("public".equalsIgnoreCase(name) || "internet".equalsIgnoreCase(name)) { pub.add(addr); } else if ("floating".equalsIgnoreCase(type)) { pub.add(addr); } else if ("fixed".equalsIgnoreCase(type)) { priv.add(addr); } else if (addr.isPublicIpAddress()) { pub.add(addr); } else { priv.add(addr); } } } if (vm.getProviderVlanId() == null) { // && !name.equals("public") && !name.equals("private") && !name.equals("nova_fixed") ) { for (VLAN network : networks) { if (network.getName().equals(name)) { vm.setProviderVlanId(network.getProviderVlanId()); //get subnet NetworkServices services = getProvider().getNetworkServices(); VLANSupport support = services.getVlanSupport(); Iterable<Subnet> subnets = support.listSubnets(network.getProviderVlanId()); for (Subnet sub : subnets) { try { SubnetUtils utils = new SubnetUtils(sub.getCidr()); if (utils.getInfo().isInRange(subnet)) { vm.setProviderSubnetId(sub.getProviderSubnetId()); break; } } catch (IllegalArgumentException arg) { logger.warn("Couldn't match against an invalid CIDR: " + sub.getCidr()); continue; } } break; } } } } vm.setPublicAddresses(pub.toArray(new RawAddress[pub.size()])); vm.setPrivateAddresses(priv.toArray(new RawAddress[priv.size()])); } RawAddress[] raw = vm.getPublicAddresses(); if (raw != null) { for (RawAddress addr : vm.getPublicAddresses()) { if (addr.getVersion().equals(IPVersion.IPV4)) { for (IpAddress a : ipv4) { if (a.getRawAddress().getIpAddress().equals(addr.getIpAddress())) { vm.setProviderAssignedIpAddressId(a.getProviderIpAddressId()); break; } } } else if (addr.getVersion().equals(IPVersion.IPV6)) { for (IpAddress a : ipv6) { if (a.getRawAddress().getIpAddress().equals(addr.getIpAddress())) { vm.setProviderAssignedIpAddressId(a.getProviderIpAddressId()); break; } } } } } if (vm.getProviderAssignedIpAddressId() == null) { for (IpAddress addr : ipv4) { String serverId = addr.getServerId(); if (serverId != null && serverId.equals(vm.getProviderVirtualMachineId())) { vm.setProviderAssignedIpAddressId(addr.getProviderIpAddressId()); break; } } if (vm.getProviderAssignedIpAddressId() == null) { for (IpAddress addr : ipv6) { String serverId = addr.getServerId(); if (serverId != null && addr.getServerId().equals(vm.getProviderVirtualMachineId())) { vm.setProviderAssignedIpAddressId(addr.getProviderIpAddressId()); break; } } } } if (vm.getProviderAssignedIpAddressId() == null) { for (IpAddress addr : ipv6) { if (addr.getServerId().equals(vm.getProviderVirtualMachineId())) { vm.setProviderAssignedIpAddressId(addr.getProviderIpAddressId()); break; } } } } vm.setProviderRegionId(getRegionId()); vm.setProviderDataCenterId(vm.getProviderRegionId() + "-a"); vm.setTerminationTimestamp(-1L); if (vm.getName() == null) { vm.setName(vm.getProviderVirtualMachineId()); } if (vm.getDescription() == null) { vm.setDescription(vm.getName()); } if (Platform.UNKNOWN.equals(vm.getPlatform())) { vm.setPlatform(getPlatform(vm.getName(), vm.getDescription(), vm.getProviderMachineImageId())); } vm.setImagable(vm.getCurrentState() == null); vm.setRebootable(vm.getCurrentState() == null); if (getCloudProvider().equals(OpenStackProvider.RACKSPACE)) { //Rackspace does not support the concept for firewalls in servers vm.setProviderFirewallIds(null); } else { Iterable<String> fwIds = listFirewalls(vm.getProviderVirtualMachineId(), server); int count = 0; //noinspection UnusedDeclaration for (String id : fwIds) { count++; } String[] ids = new String[count]; int i = 0; for (String id : fwIds) { ids[i++] = id; } vm.setProviderFirewallIds(ids); } return vm; } @Override public void setTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.setTags"); try { getProvider().createTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void setTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { setTags(id, tags); } } @Override public void updateTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.updateTags"); try { getProvider().updateTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void updateTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { updateTags(id, tags); } } @Override public void removeTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.removeTags"); try { getProvider().removeTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void removeTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { removeTags(id, tags); } } @Override public void setTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.setTags"); try { getProvider().createTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void setTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { setTags(id, tags); } } @Override public void updateTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.updateTags"); try { getProvider().updateTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void updateTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { updateTags(id, tags); } } @Override public void removeTags(@Nonnull String vmId, @Nonnull Tag... tags) throws CloudException, InternalException { APITrace.begin(getProvider(), "Server.removeTags"); try { getProvider().removeTags(SERVICE, "/servers", vmId, tags); } finally { APITrace.end(); } } @Override public void removeTags(@Nonnull String[] vmIds, @Nonnull Tag... tags) throws CloudException, InternalException { for (String id : vmIds) { removeTags(id, tags); } } }