/*******************************************************************************
 *
 *  Copyright FUJITSU LIMITED 2017
 *
 *  Creation Date: 2016-05-24
 *
 *******************************************************************************/

package org.oscm.app.vmware.business;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.xml.datatype.XMLGregorianCalendar;

import org.oscm.app.v2_0.data.PasswordAuthentication;
import org.oscm.app.v2_0.data.ProvisioningSettings;
import org.oscm.app.v2_0.data.ServiceUser;
import org.oscm.app.v2_0.data.Setting;
import org.oscm.app.v2_0.exceptions.APPlatformException;
import org.oscm.app.vmware.business.VMwareValue.Unit;
import org.oscm.app.vmware.business.model.Cluster;
import org.oscm.app.vmware.business.model.VCenter;
import org.oscm.app.vmware.business.model.VLAN;
import org.oscm.app.vmware.i18n.Messages;
import org.oscm.app.vmware.persistence.APPDataAccessService;
import org.oscm.app.vmware.persistence.DataAccessService;
import org.oscm.app.vmware.persistence.VMwareNetwork;
import org.oscm.app.vmware.remote.bes.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vmware.vim25.LocalizableMessage;
import com.vmware.vim25.TaskInfo;
import com.vmware.vim25.TaskInfoState;

/**
 * Class to read and return the VMware specific properties.
 * 
 */
public class VMPropertyHandler {

    private static final Logger logger = LoggerFactory
            .getLogger(VMPropertyHandler.class);

    private double templateDiskSpace;
    private final ProvisioningSettings settings;
    DataAccessService das_stub = null;

    public static final String TS_GUEST_READY_TIMEOUT = "READY_TIMEOUT";

    public static final String GUEST_READY_TIMEOUT_REF = "READY_TIMEOUT_REF";

    public static final String SNAPSHOT_ID = "SNAPSHOT_ID";

    public static final String TS_SERVICE_TYPE = "SERVICE_TYPE";

    /**
     * for target folder generation
     */
    private static final String PLACEHOLDER_ORGID = "${ORGID}";

    /**
     * for instance name generation, see column identifier in table site
     */
    private static final String PLACEHOLDER_VCENTER = "${VC}";
    private static final String PLACEHOLDER_DATACENTER = "${DC}";
    private static final String PLACEHOLDER_ID3 = "${ID3}";
    private static final String PLACEHOLDER_ID4 = "${ID4}";
    private static final String PLACEHOLDER_ID5 = "${ID5}";
    private static final String PLACEHOLDER_ID6 = "${ID6}";
    private static final String PLACEHOLDER_ID7 = "${ID7}";
    private static final String PLACEHOLDER_ID8 = "${ID8}";
    private static final String PLACEHOLDER_ID10 = "${ID10}";
    private static final String PLACEHOLDER_ID12 = "${ID12}";

    /**
     * The key for accessing the BES webservice.
     */
    public static final String BSS_USER_KEY = "BSS_USER_KEY";

    /**
     * The id for accessing the BES webservice.
     */
    public static final String BSS_USER_ID = "BSS_USER_ID";

    /**
     * The password for accessing the BES webservice key.
     */
    public static final String BSS_USER_PWD = "BSS_USER_PWD";

    /**
     * Boolean which indicates whether SSO should be used.
     */
    public static final String BSS_USER_SSO = "BSS_USER_SSO";

    /**
     * The timer for evaluating the subscription end date is initialized with
     * this value.
     */
    public static final String CTL_TIMER_SCHEDULE_DAY = "TIMER_SCHEDULE_DAY";

    /**
     * The timer for evaluating the subscription end date is initialized with
     * this value.
     */
    public static final String CTL_TIMER_SCHEDULE_HOUR = "TIMER_SCHEDULE_HOUR";

    /**
     * The timer for evaluating the subscription end date is initialized with
     * this value.
     */
    public static final String CTL_TIMER_SCHEDULE_MINUTE = "TIMER_SCHEDULE_MINUTE";

    /**
     * Folder with shell scripts for report command
     */
    public static final String CTL_REPORT_FOLDER = "REPORT_FOLDER";

    /**
     * This URL is used to create approval tasks when the service parameter
     * START_PROCESS_AFTER_CREATION is defined and set to true
     */
    public static final String CTL_APPROVAL_URL = "APPROVAL_URL";

    public static final String CTL_APPROVAL_USER_ID = "APPROVAL_USER_ID";

    public static final String CTL_APPROVAL_USER_PWD = "APPROVAL_USER_PWD";

    /**
     * NOW-IT CMDB integration. Folder for csv files
     */
    public static final String CTL_CMDB_FOLDER = "CMDB_FOLDER";

    /**
     * NOW-IT Nagios integration
     */
    public static final String CTL_NAGIOS_SERVER = "NAGIOS_SERVER";

    /**
     * NOW-IT Nagios integration
     */
    public static final String CTL_NAGIOS_SITE = "NAGIOS_SITE";

    /**
     * The key of the last invoked vSphere task. This vSphere identifier
     * references an asynchronous task in order to retrieve the status of that
     * task later on.
     */
    public static final String TASK_KEY = "TASK_KEY";

    /**
     * The time of starting the vSphere task.
     */
    public static final String TASK_STARTTIME = "TASK_STARTTIME";

    /**
     * When the subscription end date is set the subscription is going to
     * several stages until it is automatically deleted
     */
    public enum SubscriptionEndStatus {
        UNDEFINED,
        SCHEDULED_FOR_NOTIFICATION,
        SCHEDULED_FOR_DEACTIVATION,
        SCHEDULED_FOR_DELETION
    };

    public static final String SUBSCRIPTION_END_STATUS = "SUBSCRIPTION_END_STATUS";

    /**
     * Fixed target host name (if not calculated by the balancer)
     */
    public static final String TS_TARGET_HOST = "TARGET_HOST";

    /**
     * Fixed target storage name (if not calculated by the balancer)
     */
    public static final String TS_TARGET_STORAGE = "TARGET_STORAGE";

    /**
     * The cluster where the VM is instantiated. Do not use together with
     * TARGET_LOCATION as service parameter.
     */
    public static final String TS_TARGET_CLUSTER = "TARGET_CLUSTER";

    /**
     * The cluster where the VM is instantiated. Do not use together with
     * TARGET_LOCATION as service parameter.
     */
    public static final String TS_TARGET_VCENTER_SERVER = "TARGET_VCENTER_SERVER";

    /**
     * The cluster where the VM is instantiated. Do not use together with
     * TARGET_LOCATION as service parameter.
     */
    public static final String TS_TARGET_DATACENTER = "TARGET_DATACENTER";

    /**
     * The vSphere folder where the VM will be located
     */
    public static final String TS_TARGET_FOLDER = "TARGET_FOLDER";

    /**
     * When the target folder can be chosen during the subscription process
     * TARGET_FOLDER_ROOT determines all the sub folders that the user can
     * choose from.
     */
    public static final String TS_TARGET_FOLDER_ROOT = "TARGET_FOLDER_ROOT";

    /**
     * The custom name of the new instance
     */
    public static final String TS_INSTANCENAME = "INSTANCENAME";

    /**
     * The predefined prefix of the new instance
     */
    public static final String TS_INSTANCENAME_PREFIX = "INSTANCENAME_PREFIX";

    /**
     * The regular expression for validating the new instance name
     */
    public static final String TS_INSTANCENAME_PATTERN = "INSTANCENAME_PATTERN";

    /**
     * Size of main memory (MB).
     */
    public static final String TS_AMOUNT_OF_RAM = "AMOUNT_OF_RAM";

    /**
     * Number of CPUs.
     */
    public static final String TS_NUMBER_OF_CPU = "NUMBER_OF_CPU";

    /**
     * Size of system disk (GB).
     */
    public static final String TS_DISK_SIZE = "DISK_SIZE";

    /**
     * Size of custom data disk (GB).
     */
    public static final String TS_DATA_DISK_SIZE = "DATA_DISK_SIZE_#";

    /**
     * Target location for the data disk, e.g. /home/user/data for Linux VM or
     * d: for Windows VM.
     */
    public static final String TS_DATA_DISK_TARGET = "DATA_DISK_TARGET_#";

    /**
     * Regular expression pattern used to validate the target for the data disk.
     */
    public static final String TS_DATA_DISK_TARGET_VALIDATION = "DATA_DISK_TARGET_VALIDATION_#";

    /**
     * Saves the data disk mapping. Internal mapping of index to VMware device
     * key.
     */
    public static final String DATA_DISK_KEY = "DATA_DISK_KEY_";

    /**
     * The template that will be used to clone a VM.
     */
    public static final String TS_TEMPLATENAME = "TEMPLATENAME";

    /**
     * This parameter contains a filename. The file content describes the user
     * interface for the external configuration tool.
     */
    public static final String TS_WEBUI_CONFIG = "WEBUI_CONFIG";

    /**
     * Timezone setting for Windows operating systems. See
     * http://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx
     */
    public static final String CTL_TIMEZONE_WINDOWS = "TIMEZONE_WINDOWS";

    /**
     * Timezone setting for Linux operating systems. See
     * http://pubs.vmware.com/vsphere
     * -55/index.jsp?topic=%2Fcom.vmware.wssdk.smssdk.doc%2Ftimezone.html
     */
    public static final String CTL_TIMEZONE_LINUX = "TIMEZONE_LINUX";

    /**
     * A URL that points to a shell script that will be retrieved and executed
     * after the VM has been created and reconfigured
     */
    public static final String TS_SCRIPT_URL = "SCRIPT_URL";

    public static final String TS_SCRIPT_USERID = "SCRIPT_USERID";

    public static final String TS_SCRIPT_PWD = "SCRIPT_PWD";

    /**
     * for Linux this is the FQDN without the hostname, for Windows this is the
     * Windows domain
     */
    public static final String TS_DOMAIN_NAME = "DOMAIN_NAME";

    public static final String TS_WINDOWS_DOMAIN_JOIN = "WINDOWS_DOMAIN_JOIN";
    public static final String TS_WINDOWS_DOMAIN_ADMIN = "WINDOWS_DOMAIN_ADMIN";
    public static final String TS_WINDOWS_DOMAIN_ADMIN_PWD = "WINDOWS_DOMAIN_ADMIN_PWD";
    public static final String TS_WINDOWS_WORKGROUP = "WINDOWS_WORKGROUP";
    public static final String TS_WINDOWS_LICENSE_KEY = "WINDOWS_LICENSE_KEY";
    public static final String TS_WINDOWS_LOCAL_ADMIN_PWD = "WINDOWS_LOCAL_ADMIN_PWD";
    public static final String TS_SYSPREP_RUNONCE_COMMAND = "SYSPREP_RUNONCE_COMMAND";

    public static final String TS_LINUX_ROOT_PWD = "LINUX_ROOT_PWD";

    /**
     * The number of NICs is determined from the given template (see
     * TS_TEMPLATENAME).
     */
    public static final String TS_NUMBER_OF_NICS = "NUMBER_OF_NICS";

    public static final String NETWORK_SETTING_DHCP = "DHCP";
    public static final String NETWORK_SETTING_MANUAL = "MANUAL";
    public static final String NETWORK_SETTING_DATABASE = "DATABASE";

    public static final String TS_NIC1_NETWORK_ADAPTER = "NIC1_NETWORK_ADAPTER";
    public static final String TS_NIC1_NETWORK_SETTINGS = "NIC1_NETWORK_SETTINGS";
    public static final String TS_NIC1_IP_ADDRESS = "NIC1_IP_ADDRESS";
    public static final String TS_NIC1_SUBNET_MASK = "NIC1_SUBNET_MASK";
    public static final String TS_NIC1_GATEWAY = "NIC1_GATEWAY";
    public static final String TS_NIC1_DNS_SERVER = "NIC1_DNS_SERVER";
    public static final String TS_NIC1_DNS_SUFFIX = "NIC1_DNS_SUFFIX";

    public static final String TS_NIC2_NETWORK_ADAPTER = "NIC2_NETWORK_ADAPTER";
    public static final String TS_NIC2_NETWORK_SETTINGS = "NIC2_NETWORK_SETTINGS";
    public static final String TS_NIC2_IP_ADDRESS = "NIC2_IP_ADDRESS";
    public static final String TS_NIC2_SUBNET_MASK = "NIC2_SUBNET_MASK";
    public static final String TS_NIC2_GATEWAY = "NIC2_GATEWAY";
    public static final String TS_NIC2_DNS_SERVER = "NIC2_DNS_SERVER";
    public static final String TS_NIC2_DNS_SUFFIX = "NIC2_DNS_SUFFIX";

    public static final String TS_NIC3_NETWORK_ADAPTER = "NIC3_NETWORK_ADAPTER";
    public static final String TS_NIC3_NETWORK_SETTINGS = "NIC3_NETWORK_SETTINGS";
    public static final String TS_NIC3_IP_ADDRESS = "NIC3_IP_ADDRESS";
    public static final String TS_NIC3_SUBNET_MASK = "NIC3_SUBNET_MASK";
    public static final String TS_NIC3_GATEWAY = "NIC3_GATEWAY";
    public static final String TS_NIC3_DNS_SERVER = "NIC3_DNS_SERVER";
    public static final String TS_NIC3_DNS_SUFFIX = "NIC3_DNS_SUFFIX";

    public static final String TS_NIC4_NETWORK_ADAPTER = "NIC4_NETWORK_ADAPTER";
    public static final String TS_NIC4_NETWORK_SETTINGS = "NIC4_NETWORK_SETTINGS";
    public static final String TS_NIC4_IP_ADDRESS = "NIC4_IP_ADDRESS";
    public static final String TS_NIC4_SUBNET_MASK = "NIC4_SUBNET_MASK";
    public static final String TS_NIC4_GATEWAY = "NIC4_GATEWAY";
    public static final String TS_NIC4_DNS_SERVER = "NIC4_DNS_SERVER";
    public static final String TS_NIC4_DNS_SUFFIX = "NIC4_DNS_SUFFIX";

    /**
     * Signals whether an existing VM instance should be imported (mapped to the
     * subscription).
     */
    public static final String TS_IMPORT_EXISTING_VM = "IMPORT_EXISTING_VM";

    /**
     * If set to true then process triggers are approved automatically
     */
    public static final String TS_AUTO_APPROVE_TRIGGER = "AUTO_APPROVE_TRIGGER";

    /**
     * The id of requesting user. Can only be retrieved in
     * VMwareController.createInstance() and VMwareController.modifyInstance()
     * and is stored there as service instance parameter for later use.
     */
    public static final String REQUESTING_USER = "REQUESTING_USER";

    /**
     * The mail of requesting user. Can only be retrieved in
     * VMwareController.createInstance() and VMwareController.modifyInstance()
     * and is stored there as service instance parameter for later use.
     */
    public static final String REQUESTING_USER_EMAIL = "REQUESTING_USER_EMAIL";

    /**
     * Pauses provisioning. It works like a synchronous trigger. This can be
     * used for configuration steps that must be done by administrators after a
     * VM is created like installing antivirus software. This person gets
     * notified if the provisioning has paused. The person receives an email
     * with a link in it to continue provisioning.
     */
    public static final String TS_MAIL_FOR_COMPLETION = "MAIL_FOR_COMPLETION";

    /**
     * Identifies the person that is responsible for a VM. Can be used together
     * with the LDAP to retrieve other information about that person. Then this
     * identifier is used as commonName attribute.
     */
    public static final String TS_RESPONSIBLE_PERSON = "RESPONSIBLE_PERSON";

    /**
     * A pattern which describes the naming rules for the access info.
     */
    public static final String TS_ACCESS_INFO = "ACCESS_INFO";

    /**
     * internal settings for state machine execution
     */
    public static final String SM_STATE = "SM_STATE";
    public static final String SM_STATE_HISTORY = "SM_STATE_HISTORY";
    public static final String SM_STATE_MACHINE = "SM_STATE_MACHINE";
    public static final String SM_ERROR_MESSAGE = "SM_ERROR_MESSAGE";

    public VMPropertyHandler(ProvisioningSettings settings) {
        this.settings = settings;
    }

    public ProvisioningSettings getSettings() {
        return settings;
    }

    public void setSetting(String key, String value) {
        if (value != null) {
            settings.getParameters().put(key, new Setting(key, value));
        } else {
            logger.warn("Setting not set because null value. key:" + key);
        }
    }

    /**
     * @return parameters keys for the data disk mount point, the list is sorted
     *         ascending
     */
    public List<String> getDataDiskMountPointParameterKeys() {
        String regex = TS_DATA_DISK_TARGET.replace("#", "").concat("\\d+");
        List<String> result = new ArrayList<>();
        for (String key : settings.getParameters().keySet()) {
            if (key.matches(regex)) {
                result.add(key);
            }
        }
        Collections.sort(result);
        return result;
    }

    /**
     * @return parameters keys for the data disk size, the list is sorted
     *         ascending
     */
    public List<String> getDataDiskSizeParameterKeys() {
        String regex = TS_DATA_DISK_SIZE.replace("#", "").concat("\\d+");
        List<String> result = new ArrayList<>();
        for (String key : settings.getParameters().keySet()) {
            if (key.matches(regex)) {
                result.add(key);
            }
        }
        Collections.sort(result);
        return result;
    }

    public String getMountPointValidationPattern(String mointPointKey) {
        String patternKey = mointPointKey.replace("TARGET_",
                "TARGET_VALIDATION_");
        return getValue(patternKey, settings.getParameters());
    }

    public String getGuestReadyTimeout(String key) {
        if (settings.getParameters().containsKey(key)) {
            return getValue(key, settings.getParameters());
        }
        return getValue(key, settings.getConfigSettings());
    }

    public int getNumberOfNetworkAdapter() {
        return Integer.parseInt(
                getServiceSetting(VMPropertyHandler.TS_NUMBER_OF_NICS));
    }

    /**
     * Release IP address for later usage by another VM instance. Only valid for
     * manually assigned IP addresses. Does not make sense for DHCP.
     */
    public void releaseManuallyDefinedIPAddresses() throws Exception {
        logger.debug("");
        int numNIC = Integer.parseInt(
                getServiceSetting(VMPropertyHandler.TS_NUMBER_OF_NICS));
        for (int i = 1; i <= numNIC; i++) {
            if (isAdapterConfiguredByDatabase(i)) {
                String ipAddress = getIpAddress(i);

                if (ipAddress != null) {
                    String vcenter = getTargetVCenterServer();
                    String datacenter = getTargetDatacenter();
                    String cluster = getTargetCluster();
                    String vlan = getVLAN(i);
                    try {
                        DataAccessService das = getDataAccessService();
                        das.releaseIPAddress(vcenter, datacenter, cluster, vlan,
                                ipAddress);
                    } catch (Exception e) {
                        logger.error(
                                "Failed to release IP address " + ipAddress, e);
                    }
                }
            }
        }
    }

    /**
     * For all NICs that get there network configuration from the database:
     * <ul>
     * <li>Reserve an IP address
     * <li>Retrieve the network settings for the given cluster
     * <li>Set the NIC related technical service parameter
     * </ul>
     */
    public void getNetworkSettingsFromDatabase() throws APPlatformException {
        DataAccessService das = getDataAccessService();
        String vcenter = getTargetVCenterServer();
        String datacenter = getTargetDatacenter();
        String cluster = getTargetCluster();
        logger.debug("vcenter: " + vcenter + " datacenter: " + datacenter
                + " cluster: " + cluster);

        int numberOfNICs = Integer.parseInt(
                getServiceSetting(VMPropertyHandler.TS_NUMBER_OF_NICS));
        for (int i = 1; i <= numberOfNICs; i++) {

            if (isAdapterConfiguredByDatabase(i)) {
                String vlan = das.getVLANwithMostIPs(vcenter, datacenter,
                        cluster);
                if (vlan == null) {
                    throw new APPlatformException(Messages.getAll(
                            "error_read_vlans",
                            new Object[] { vcenter, datacenter, cluster })
                            .get(0).getText());
                }

                settings.getParameters().put("NIC" + i + "_NETWORK_ADAPTER",
                        new Setting("NIC" + i + "_NETWORK_ADAPTER", vlan));

                String ipAddress;
                VMwareNetwork nw;
                try {
                    ipAddress = das.reserveIPAddress(vcenter, datacenter,
                            cluster, vlan);
                    nw = das.getNetworkSettings(vcenter, datacenter, cluster,
                            vlan);
                } catch (Exception e) {
                    throw new APPlatformException(Messages.getAll(
                            "error_read_static_network_config",
                            new Object[] { Integer.valueOf(i), e.getMessage() })
                            .get(0).getText());
                }

                logger.debug("NIC" + i + " VLAN: " + vlan + " IP address: "
                        + ipAddress + " SubnetMask: " + nw.getSubnetMask()
                        + " Gateway: " + nw.getGateway() + " DNS Server: "
                        + nw.getDnsServer() + " DNS Suffix: "
                        + nw.getDnsSuffix());

                if (i == 1) {
                    settings.getParameters().put(TS_NIC1_IP_ADDRESS,
                            new Setting(TS_NIC1_IP_ADDRESS, ipAddress));
                    settings.getParameters().put(TS_NIC1_SUBNET_MASK,
                            new Setting(TS_NIC1_SUBNET_MASK,
                                    nw.getSubnetMask()));
                    settings.getParameters().put(TS_NIC1_GATEWAY,
                            new Setting(TS_NIC1_GATEWAY, nw.getGateway()));
                    settings.getParameters().put(TS_NIC1_DNS_SERVER,
                            new Setting(TS_NIC1_DNS_SERVER, nw.getDnsServer()));
                    settings.getParameters().put(TS_NIC1_DNS_SUFFIX,
                            new Setting(TS_NIC1_DNS_SUFFIX, nw.getDnsSuffix()));
                } else if (i == 2) {
                    settings.getParameters().put(TS_NIC2_IP_ADDRESS,
                            new Setting(TS_NIC1_IP_ADDRESS, ipAddress));
                    settings.getParameters().put(TS_NIC2_SUBNET_MASK,
                            new Setting(TS_NIC1_SUBNET_MASK,
                                    nw.getSubnetMask()));
                    settings.getParameters().put(TS_NIC2_GATEWAY,
                            new Setting(TS_NIC1_GATEWAY, nw.getGateway()));
                    settings.getParameters().put(TS_NIC2_DNS_SERVER,
                            new Setting(TS_NIC1_DNS_SERVER, nw.getDnsServer()));
                    settings.getParameters().put(TS_NIC2_DNS_SUFFIX,
                            new Setting(TS_NIC1_DNS_SUFFIX, nw.getDnsSuffix()));
                } else if (i == 3) {
                    settings.getParameters().put(TS_NIC3_IP_ADDRESS,
                            new Setting(TS_NIC1_IP_ADDRESS, ipAddress));
                    settings.getParameters().put(TS_NIC3_SUBNET_MASK,
                            new Setting(TS_NIC1_SUBNET_MASK,
                                    nw.getSubnetMask()));
                    settings.getParameters().put(TS_NIC3_GATEWAY,
                            new Setting(TS_NIC1_GATEWAY, nw.getGateway()));
                    settings.getParameters().put(TS_NIC3_DNS_SERVER,
                            new Setting(TS_NIC1_DNS_SERVER, nw.getDnsServer()));
                    settings.getParameters().put(TS_NIC3_DNS_SUFFIX,
                            new Setting(TS_NIC1_DNS_SUFFIX, nw.getDnsSuffix()));
                } else if (i == 4) {
                    settings.getParameters().put(TS_NIC4_IP_ADDRESS,
                            new Setting(TS_NIC1_IP_ADDRESS, ipAddress));
                    settings.getParameters().put(TS_NIC4_SUBNET_MASK,
                            new Setting(TS_NIC1_SUBNET_MASK,
                                    nw.getSubnetMask()));
                    settings.getParameters().put(TS_NIC4_GATEWAY,
                            new Setting(TS_NIC1_GATEWAY, nw.getGateway()));
                    settings.getParameters().put(TS_NIC4_DNS_SERVER,
                            new Setting(TS_NIC1_DNS_SERVER, nw.getDnsServer()));
                    settings.getParameters().put(TS_NIC4_DNS_SUFFIX,
                            new Setting(TS_NIC1_DNS_SUFFIX, nw.getDnsSuffix()));
                }

            }
        }
    }

    /**
     * Returns the defined amount of memory (MB).
     * 
     * @return the memory
     */
    public long getConfigMemoryMB() {
        return Long.parseLong(getServiceSettingValidated(TS_AMOUNT_OF_RAM));
    }

    /**
     * Returns the defined number of CPUs.
     * 
     * @return the number of CPUs
     */
    public int getConfigCPUs() {
        return Integer.parseInt(getServiceSettingValidated(TS_NUMBER_OF_CPU));
    }

    /**
     * Returns the defined disk size in GB.
     * 
     * @return the disk size in GBs or .0 if not defined
     */
    public double getConfigDiskSpaceMB() throws APPlatformException {
        String value = getServiceSetting(TS_DISK_SIZE);
        try {
            return (value != null) ? 1024.0 * (Long.parseLong(value)) : .0;
        } catch (NumberFormatException ne) {
            throw new APPlatformException(Messages.getAll(
                    "error_invalid_diskspacenum", new Object[] { value }), ne);
        }
    }

    /**
     * Returns the list of additionally defined data disks (sizes in MB).
     * 
     * @return a list of Long values for all defined data disks
     */
    public Double[] getDataDisksMB() {
        List<Double> ddlist = new ArrayList<>();
        for (int i = 1; i <= 999; i++) {
            String diskPrefix = TS_DATA_DISK_SIZE.replace("#",
                    Integer.toString(i));
            String value = getServiceSetting(diskPrefix);
            if (value != null && value.length() > 0) {
                double diskSize = 1024.0 * Long.parseLong(value);
                ddlist.add(Double.valueOf(diskSize));
            } else {
                break;
            }
        }

        return ddlist.toArray(new Double[ddlist.size()]);
    }

    /**
     * Returns the list of additionally defined data disks as comparable string.
     * 
     * @return a string with all defined data disks
     */
    public String getDataDisksMBAsString() {
        Double[] ddisks = getDataDisksMB();
        StringBuffer rc = new StringBuffer();
        for (Double ddisk : ddisks) {
            rc.append(ddisk.toString());
            rc.append("#");
        }
        return rc.toString();
    }

    /**
     * Returns the key of a custom virtual disk or "0" if not defined
     */
    public int getDataDiskKey(int index) {
        String val = getValue(DATA_DISK_KEY + Integer.toString(index),
                settings.getParameters());
        return (val != null && val.length() > 0) ? Integer.parseInt(val) : 0;
    }

    /**
     * Saves the key of a custom virtual disk. Internal mapping of index to
     * VMware device key.
     */
    public void setDataDiskKey(int index, int key) {
        setValue(DATA_DISK_KEY + Integer.toString(index), Integer.toString(key),
                settings.getParameters());
    }

    public List<VLAN> getVLANs(Cluster cluster) {
        try {
            DataAccessService das = getDataAccessService();
            return das.getVLANs(cluster);
        } catch (Exception e) {
            logger.error("Failed to retrieve VLAN list.", e);
            return new ArrayList<>();
        }
    }

    public int addVLAN(VLAN vlan) {
        int newTKey = -1;
        DataAccessService das = getDataAccessService();
        try {
            newTKey = das.addVLAN(vlan);
        } catch (Exception e) {
            logger.error("Failed to add VLAN " + vlan.getName(), e);
        }
        return newTKey;
    }

    public void deleteVLAN(VLAN vlan) {
        DataAccessService das = getDataAccessService();
        try {
            das.deleteVLAN(vlan);
        } catch (Exception e) {
            logger.error("Failed to delete VLAN " + vlan.getName(), e);
        }
    }

    public void updateVLANs(List<VLAN> vlans) {
        DataAccessService das = getDataAccessService();
        try {
            das.updateVLANs(vlans);
        } catch (Exception e) {
            logger.error("Failed to update VLANs.", e);
        }
    }

    public String getTargetDatacenter() {
        return getServiceSetting(VMPropertyHandler.TS_TARGET_DATACENTER);
    }

    private String getDatacenterId() throws APPlatformException {

        String vcenter = getTargetVCenterServer();
        String datacenter = getTargetDatacenter();

        try {
            DataAccessService das = getDataAccessService();
            return das.getDatacenterId(vcenter, datacenter);
        } catch (Exception e) {
            throw new APPlatformException(e.getMessage());
        }
    }

    /**
     * Get all vCenter server from database (with datacenters and clusters)
     * 
     * @return list of vCenter server or empty list if not defined
     */
    public List<VCenter> getTargetVCenter() {
        List<VCenter> vcenter;
        DataAccessService das = getDataAccessService();
        try {
            vcenter = das.getVCenter();
        } catch (Exception e) {
            logger.error("Failed to retrieve vCenter server list.", e);
            vcenter = new ArrayList<>();
        }

        return vcenter;
    }

    public void saveTargetVCenter(VCenter vcenter) {
        DataAccessService das = getDataAccessService();
        try {
            das.setVCenter(vcenter);
        } catch (Exception e) {
            logger.error("Failed to save vCenter server configuration.", e);
        }
    }

    public String getTargetVCenterServer() {
        return getServiceSetting(VMPropertyHandler.TS_TARGET_VCENTER_SERVER);

    }

    public String getTargetCluster() {
        return getServiceSetting(VMPropertyHandler.TS_TARGET_CLUSTER);
    }

    /**
     * @return the full name of the new instance (including the prefix)
     */
    public String getInstanceName() throws APPlatformException {
        StringBuffer b = new StringBuffer();
        String prefix = getServiceSetting(
                VMPropertyHandler.TS_INSTANCENAME_PREFIX);
        String name = getServiceSettingValidated(TS_INSTANCENAME);
        if (prefix != null && !name.startsWith(prefix)
                && !isImportOfExistingVM()) {
            b.append(prefix);
        }
        b.append(getInstanceNameCustom(name));
        return b.toString();
    }

    public String getTargetFolder() {
        String targetFolder = getServiceSetting(TS_TARGET_FOLDER);
        if (targetFolder != null
                && targetFolder.indexOf(PLACEHOLDER_ORGID) >= 0) {
            String orgId = settings.getOrganizationId();
            targetFolder = targetFolder.replace(PLACEHOLDER_ORGID, orgId);
            setValue(TS_TARGET_FOLDER, targetFolder, settings.getParameters());
        }
        return targetFolder;
    }

    /**
     * Returns the custom name of the new instance.
     * 
     * @return the name of the custom defined instance name
     */
    private String getInstanceNameCustom(String name)
            throws APPlatformException {

        boolean contains_vcenter = (name.indexOf(PLACEHOLDER_VCENTER) >= 0);
        boolean contains_datacenter = (name
                .indexOf(PLACEHOLDER_DATACENTER) >= 0);
        boolean contains_id3 = (name.indexOf(PLACEHOLDER_ID3) >= 0);
        boolean contains_id4 = (name.indexOf(PLACEHOLDER_ID4) >= 0);
        boolean contains_id5 = (name.indexOf(PLACEHOLDER_ID5) >= 0);
        boolean contains_id6 = (name.indexOf(PLACEHOLDER_ID6) >= 0);
        boolean contains_id7 = (name.indexOf(PLACEHOLDER_ID7) >= 0);
        boolean contains_id8 = (name.indexOf(PLACEHOLDER_ID8) >= 0);
        boolean contains_id10 = (name.indexOf(PLACEHOLDER_ID10) >= 0);
        boolean contains_id12 = (name.indexOf(PLACEHOLDER_ID12) >= 0);

        try {
            DataAccessService das = getDataAccessService();
            String vcenter = getTargetVCenterServer();
            String vcenterId = das.getVCenterIdentifier(vcenter);

            if (contains_vcenter) {
                name = name.replace(PLACEHOLDER_VCENTER, vcenterId);
            }

            if (contains_datacenter) {
                String datacenterId = getDatacenterId();
                name = name.replace(PLACEHOLDER_DATACENTER, datacenterId);
            }

            if (contains_id3) {
                String seqNum = das.getNextSequenceNumber(3, vcenterId);
                name = name.replace(PLACEHOLDER_ID3, seqNum);
            }
            if (contains_id4) {
                String seqNum = das.getNextSequenceNumber(4, vcenterId);
                name = name.replace(PLACEHOLDER_ID4, seqNum);
            }
            if (contains_id5) {
                String seqNum = das.getNextSequenceNumber(5, vcenterId);
                name = name.replace(PLACEHOLDER_ID5, seqNum);
            }
            if (contains_id6) {
                String seqNum = das.getNextSequenceNumber(6, vcenterId);
                name = name.replace(PLACEHOLDER_ID6, seqNum);
            }
            if (contains_id7) {
                String seqNum = das.getNextSequenceNumber(7, vcenterId);
                name = name.replace(PLACEHOLDER_ID7, seqNum);
            }
            if (contains_id8) {
                String seqNum = das.getNextSequenceNumber(8, vcenterId);
                name = name.replace(PLACEHOLDER_ID8, seqNum);
            }
            if (contains_id10) {
                String seqNum = das.getNextSequenceNumber(10, vcenterId);
                name = name.replace(PLACEHOLDER_ID10, seqNum);
            }
            if (contains_id12) {
                String seqNum = das.getNextSequenceNumber(12, vcenterId);
                name = name.replace(PLACEHOLDER_ID12, seqNum);
            }

            setValue(TS_INSTANCENAME, name, settings.getParameters());

        } catch (Exception e) {
            logger.error("Failed to generate instance name", e);
            String message = Messages.get(getLocale(),
                    "error_generate_instancename");
            throw new APPlatformException(message, e);
        }

        return name;
    }

    /**
     * Returns the pattern for creating the access info of the instance.
     * <p>
     * After the instance has been created the real access info will be stored.
     * 
     * @return the pattern or value of the access info
     */
    public String getAccessInfo() {
        return getServiceSetting(TS_ACCESS_INFO);
    }

    public void setAccessInfo(String value) {
        setValue(TS_ACCESS_INFO, value, settings.getParameters());
    }

    public void setRequestingUser(ServiceUser userInfo) {
        if (userInfo != null && userInfo.getUserId() != null) {
            setValue(REQUESTING_USER, userInfo.getUserId(),
                    settings.getParameters());
        }

        if (userInfo != null && userInfo.getEmail() != null) {
            setValue(REQUESTING_USER_EMAIL, userInfo.getEmail(),
                    settings.getParameters());
        }
    }

    /**
     * Returns the name of the matching template.
     * 
     * @return the name of the matching template
     */
    public String getTemplateName() {
        return getServiceSettingValidated(TS_TEMPLATENAME);
    }

    public ProvisioningSettings getProvisioningSettings() {
        return settings;
    }

    public boolean isServiceSettingTrue(String serviceparameter) {
        String value = getServiceSetting(serviceparameter);
        boolean isTrue = (value != null ? value.equalsIgnoreCase("true")
                : false);
        logger.debug(serviceparameter + ": " + isTrue);
        return isTrue;
    }

    public boolean isControllerSettingTrue(String serviceparameter) {
        String value = getControllerSetting(serviceparameter);
        boolean isTrue = (value != null ? value.equalsIgnoreCase("true")
                : false);
        logger.debug(serviceparameter + ": " + isTrue);
        return isTrue;
    }

    /**
     * Updates the key of the last created task.
     */
    public void setTask(TaskInfo info) {
        if (info != null) {
            Setting s = new Setting(TASK_KEY, info.getKey());
            settings.getParameters().put(TASK_KEY, s);
        } else {
            Setting s = new Setting(TASK_KEY, "");
            settings.getParameters().put(TASK_KEY, s);
        }
        Setting s = new Setting(TASK_STARTTIME,
                Long.toString(System.currentTimeMillis()));
        settings.getParameters().put(TASK_STARTTIME, s);
        logTaskInfo(info);
    }

    private void logTaskInfo(TaskInfo info) {
        if (info == null) {
            logger.debug("Deleted task info key");
            return;
        }

        TaskInfoState state = info.getState();

        Integer progress = info.getProgress();
        if (state == TaskInfoState.SUCCESS) {
            progress = Integer.valueOf(100);
        } else if (progress == null) {
            progress = Integer.valueOf(0);
        }

        LocalizableMessage desc = info.getDescription();
        String description = desc != null ? desc.getMessage() : "";

        XMLGregorianCalendar queueT = info.getQueueTime();
        String queueTime = queueT != null
                ? queueT.toGregorianCalendar().getTime().toString() : "";

        XMLGregorianCalendar startT = info.getStartTime();
        String startTime = startT != null
                ? startT.toGregorianCalendar().getTime().toString() : "";

        XMLGregorianCalendar completeT = info.getCompleteTime();
        String completeTime = completeT != null
                ? completeT.toGregorianCalendar().getTime().toString() : "";

        logger.debug("Save task info key: " + info.getKey() + " name: "
                + info.getName() + " target: " + info.getEntityName()
                + " state: " + state.name() + " progress: " + progress
                + "% description: " + description + " queue-time: " + queueTime
                + " start-time: " + startTime + " complete-time: "
                + completeTime);
    }

    /**
     * Returns the start time of the last created task.
     */
    public Date getTaskStartTime() {
        String dateVal = getValue(TASK_STARTTIME, settings.getParameters());
        return (dateVal != null) ? new Date(Long.parseLong(dateVal)) : null;
    }

    /**
     * Returns the host configuration
     */
    public String getHostLoadBalancerConfig() {
        String xml = "";
        String vcenter = getTargetVCenterServer();
        String datacenter = getTargetDatacenter();
        String cluster = getTargetCluster();

        try {

            DataAccessService das = getDataAccessService();
            xml = das.getHostLoadBalancerConfig(vcenter, datacenter, cluster);
        } catch (Exception e) {
            logger.error("VMwarePropertyHandler.getHostLoadBalancerConfig() "
                    + e.getMessage(), e);
            throw new RuntimeException(
                    "Failed to retrieve host load balancing configuration for cluster "
                            + cluster);
        }

        if ("".equals(xml)) {
            logger.error(
                    "VMwarePropertyHandler.getHostLoadBalancerConfig() The retrieved host load balancing configuration for cluster "
                            + cluster + " is empty");
            throw new RuntimeException(
                    "The retrieved host load balancing configuration for cluster "
                            + cluster + " is empty");
        }

        return xml;
    }

    /**
     * Returns the preferred locale of the user interface.
     */
    public String getLocale() {
        return settings.getLocale();
    }

    /**
     * Returns the required disk space to create a virtual machine from the
     * requested template in MB.
     */
    public double getTemplateDiskSpaceMB() {
        return templateDiskSpace;
    }

    /**
     * Sets the disk space (MB) required to create a virtual machine from the
     * requested template.
     */
    public void setTemplateDiskSpaceMB(double templateDiskSpace) {
        this.templateDiskSpace = templateDiskSpace;
    }

    /**
     * Returns printable configuration
     */
    public String getConfigurationAsString(String locale)
            throws APPlatformException {
        String config = Messages.get(locale, "mail_VM_configuration.text",
                new Object[] { getInstanceName(), getTemplateName(),
                        settings.getSubscriptionId(),
                        Integer.toString(getConfigCPUs()),
                        formatMBasGB(getConfigMemoryMB()),
                        getDataDisksAsString() });
        return config;
    }

    /**
     * Returns printable configuration of data disks
     */
    public String getDataDisksAsString() throws APPlatformException {
        StringBuffer disksDisplay = new StringBuffer();

        disksDisplay.append(formatMBasGB(getConfigDiskSpaceMB()));

        Double[] ddisks = getDataDisksMB();
        for (Double ddisk : ddisks) {
            disksDisplay.append("/");
            disksDisplay.append(formatMBasGB(ddisk.doubleValue()));
        }

        return disksDisplay.toString();
    }

    /**
     * Returns printable responsible user
     */
    public String getResponsibleUserAsString(String locale) {
        logger.debug("locale: " + locale);
        String respUser = getServiceSetting(TS_RESPONSIBLE_PERSON);
        if (respUser == null) {
            return null;
        }

        return Messages.get(locale, "mail_VM_configuration_user.text",
                new Object[] { respUser });
    }

    public String formatMBasGB(double valueMB) {
        DecimalFormat format = new DecimalFormat("#0.# GB");
        return format
                .format(VMwareValue.fromMegaBytes(valueMB).getValue(Unit.GB));
    }

    /**
     * Returns the user key of the instance or controller specific technology
     * manager.
     */
    public long getTPUserKey() {
        String configUserKey = getControllerSetting(BSS_USER_KEY);
        return Long.parseLong(configUserKey);
    }

    /**
     * Returns the user Id of the controller specific technology manager.
     */
    public String getTPUserId() {
        return getControllerSetting(BSS_USER_ID);
    }

    /**
     * Returns the password of the instance or controller specific technology
     * manager.
     */
    public String getTPUserPassword() {
        String configUserPwd = getControllerSetting(BSS_USER_PWD);
        if (notNullNorEmpty(configUserPwd)) {
            return configUserPwd;
        }
        return getValue(BSS_USER_PWD, settings.getParameters());
    }

    /**
     * Returns the instance or controller specific technology manager.
     */
    public Credentials getTPUser() {
        Credentials user = new Credentials(isSSO(), getTPUserKey(),
                getTPUserPassword());
        user.setUserId(getTPUserId());
        return user;
    }

    public PasswordAuthentication getTechnologyProviderCredentials() {
        return getTPUser().toPasswordAuthentication();
    }

    /**
     * Returns whether SSO has been defined.
     */
    public boolean isSSO() {
        String sso = getControllerSetting(BSS_USER_SSO);
        return (sso != null) ? sso.equalsIgnoreCase("true") : false;
    }

    /**
     * Returns whether the given VM instance should be imported instead of being
     * created.
     * 
     * @return true if the defined instance should be imported
     */
    public boolean isImportOfExistingVM() {
        String rc = getValue(TS_IMPORT_EXISTING_VM, settings.getParameters());
        return rc != null && rc.toLowerCase().equals("true");
    }

    /**
     * Sets the marker which defines whether this is an imported VM.
     * <p>
     * Will be cleared after upgrade operations.
     */
    public void setImportOfExistingVM(boolean value) {
        setValue(TS_IMPORT_EXISTING_VM, value ? "true" : "false",
                settings.getParameters());
    }

    /**
     * Is DHCP defined for the given NIC.
     * 
     * @param adapter
     *            NIC identifier
     * @return true if DHCP is defined for the given NIC
     * @exception IllegalArgumentException
     *                if identifier is out of range
     */
    public boolean isAdapterConfiguredByDhcp(int adapter) {
        return NETWORK_SETTING_DHCP.equals(getNicSetting(adapter));
    }

    public boolean isAdapterConfiguredManually(int adapter) {
        return NETWORK_SETTING_MANUAL.equals(getNicSetting(adapter));
    }

    public boolean isAdapterConfiguredByDatabase(int i) {
        return NETWORK_SETTING_DATABASE.equals(getNicSetting(i));
    }

    public String getNicSetting(int adapter) {
        switch (adapter) {
        case 1:
            return getServiceSettingValidated(TS_NIC1_NETWORK_SETTINGS);
        case 2:
            return getServiceSettingValidated(TS_NIC2_NETWORK_SETTINGS);
        case 3:
            return getServiceSettingValidated(TS_NIC3_NETWORK_SETTINGS);
        case 4:
            return getServiceSettingValidated(TS_NIC4_NETWORK_SETTINGS);
        default:
            throw new IllegalArgumentException("NIC identifier " + adapter
                    + " is out of range. Valid range is [1-4].");
        }
    }

    /**
     * Determines the network adapter for the given NIC.
     * 
     * @param i
     *            NIC identifier
     */
    String getVLAN(int i) {
        String vlan = null;

        switch (i) {
        case 1:
            vlan = getServiceSetting(TS_NIC1_NETWORK_ADAPTER);
            break;
        case 2:
            vlan = getServiceSetting(TS_NIC2_NETWORK_ADAPTER);
            break;
        case 3:
            vlan = getServiceSetting(TS_NIC3_NETWORK_ADAPTER);
            break;
        case 4:
            vlan = getServiceSetting(TS_NIC4_NETWORK_ADAPTER);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        return vlan;

    }

    /**
     * Get the gateway IP addresses for the given NIC. The IP addresses are
     * comma separated.
     * 
     * @param i
     *            NIC identifier
     * @return a comma separated list of gateway IP addresses
     * @exception IllegalArgumentException
     *                if identifier is out of range
     * @exception RuntimeException
     *                if the technical service parameter is not defined
     */
    public String getGateway(int i) {
        String gateway = null;
        switch (i) {
        case 1:
            gateway = getServiceSettingValidated(TS_NIC1_GATEWAY);
            break;
        case 2:
            gateway = getServiceSettingValidated(TS_NIC2_GATEWAY);
            break;
        case 3:
            gateway = getServiceSettingValidated(TS_NIC3_GATEWAY);
            break;
        case 4:
            gateway = getServiceSettingValidated(TS_NIC4_GATEWAY);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        return gateway;

    }

    /**
     * Get the IP address of the given NIC.
     * 
     * @param adapter
     *            NIC identifier
     * @return the IP address
     * @exception IllegalArgumentException
     *                if identifier is out of range
     * @exception RuntimeException
     *                if the technical service parameter is not defined
     */
    public String getIpAddress(int adapter) {
        switch (adapter) {
        case 1:
            return getServiceSetting(TS_NIC1_IP_ADDRESS);
        case 2:
            return getServiceSetting(TS_NIC2_IP_ADDRESS);
        case 3:
            return getServiceSetting(TS_NIC3_IP_ADDRESS);
        case 4:
            return getServiceSetting(TS_NIC4_IP_ADDRESS);
        default:
            throw new IllegalArgumentException("NIC identifier " + adapter
                    + " is out of range. Valid range is [1-4].");
        }
    }

    /**
     * Get the network adapter (VLAN) for the given NIC.
     * 
     * @param i
     *            NIC identifier, i=[1,4]
     * @return the name of the network adapter
     * @exception IllegalArgumentException
     *                if identifier is out of range
     */
    public String getNetworkAdapter(int i) {
        String adapter = "";
        switch (i) {
        case 1:
            adapter = getServiceSetting(TS_NIC1_NETWORK_ADAPTER);
            break;
        case 2:
            adapter = getServiceSetting(TS_NIC2_NETWORK_ADAPTER);
            break;
        case 3:
            adapter = getServiceSetting(TS_NIC3_NETWORK_ADAPTER);
            break;
        case 4:
            adapter = getServiceSetting(TS_NIC4_NETWORK_ADAPTER);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        if (adapter == null) {
            adapter = "";
        }

        return adapter;
    }

    /**
     * Get a comma separated list of DNS servers for the given NIC.
     * 
     * @param i
     *            NIC identifier, range [1..4]
     * @return a comma separated list of DNS servers
     * @exception IllegalArgumentException
     *                if identifier is out of range
     * @exception RuntimeException
     *                if the technical service parameter is not defined
     */
    public String getDNSServer(int i) {
        String dnsserver = "";
        switch (i) {
        case 1:
            dnsserver = getServiceSetting(TS_NIC1_DNS_SERVER);
            break;
        case 2:
            dnsserver = getServiceSetting(TS_NIC2_DNS_SERVER);
            break;
        case 3:
            dnsserver = getServiceSetting(TS_NIC3_DNS_SERVER);
            break;
        case 4:
            dnsserver = getServiceSetting(TS_NIC4_DNS_SERVER);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        if (dnsserver == null) {
            dnsserver = "";
        }

        return dnsserver;
    }

    /**
     * Get a comma separated list of DNS suffixes for the given NIC.
     * 
     * @param i
     *            NIC identifier, range [1..4]
     * @return a comma separated list of DNS suffixes
     * @exception IllegalArgumentException
     *                if identifier is out of range
     * @exception RuntimeException
     *                if the technical service parameter is not defined
     */
    public String getDNSSuffix(int i) {
        String dnssuffix = "";
        switch (i) {
        case 1:
            dnssuffix = getServiceSetting(TS_NIC1_DNS_SUFFIX);
            break;
        case 2:
            dnssuffix = getServiceSetting(TS_NIC2_DNS_SUFFIX);
            break;
        case 3:
            dnssuffix = getServiceSetting(TS_NIC3_DNS_SUFFIX);
            break;
        case 4:
            dnssuffix = getServiceSetting(TS_NIC4_DNS_SUFFIX);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        if (dnssuffix == null) {
            dnssuffix = "";
        }
        return dnssuffix;
    }

    /**
     * Get the subnet mask for the given NIC.
     * 
     * @param i
     *            NIC identifier
     * @return the subnet mask
     * @exception IllegalArgumentException
     *                if identifier is out of range
     * @exception RuntimeException
     *                if the technical service parameter is not defined
     */
    public String getSubnetMask(int i) {
        String subnetmask = null;
        switch (i) {
        case 1:
            subnetmask = getServiceSettingValidated(TS_NIC1_SUBNET_MASK);
            break;
        case 2:
            subnetmask = getServiceSettingValidated(TS_NIC2_SUBNET_MASK);
            break;
        case 3:
            subnetmask = getServiceSettingValidated(TS_NIC3_SUBNET_MASK);
            break;
        case 4:
            subnetmask = getServiceSettingValidated(TS_NIC4_SUBNET_MASK);
            break;
        default:
            throw new IllegalArgumentException("NIC identifier " + i
                    + " is out of range. Valid range is [1-4].");
        }

        return subnetmask;
    }

    public SubscriptionEndStatus getSubscriptionEndStatus() {
        SubscriptionEndStatus state = SubscriptionEndStatus.UNDEFINED;
        String status = getServiceSetting(
                VMPropertyHandler.SUBSCRIPTION_END_STATUS);
        logger.debug("status: " + status);
        if (status != null && status.equals(
                SubscriptionEndStatus.SCHEDULED_FOR_DEACTIVATION.name())) {
            state = SubscriptionEndStatus.SCHEDULED_FOR_DEACTIVATION;
        } else if (status != null && status
                .equals(SubscriptionEndStatus.SCHEDULED_FOR_DELETION.name())) {
            state = SubscriptionEndStatus.SCHEDULED_FOR_DELETION;
        } else if (status != null && status.equals(
                SubscriptionEndStatus.SCHEDULED_FOR_NOTIFICATION.name())) {
            state = SubscriptionEndStatus.SCHEDULED_FOR_NOTIFICATION;
        }

        return state;
    }

    /**
     * Is called from executeOperation and is not storing settings. So therefore
     * the status is written directly to the database.
     */
    public void setSubscriptionEndStatus(String instanceId,
            SubscriptionEndStatus status) throws Exception {

        APPDataAccessService das = new APPDataAccessService();
        das.setSubscriptionEndStatus(instanceId, status);
    }

    public void setSubscriptionEndStatus(SubscriptionEndStatus status) {
        setValue(SUBSCRIPTION_END_STATUS, status.name(),
                settings.getParameters());
    }

    /**
     * Find the state that was executed before the given state. The given state
     * can occur several times in the state history.
     * 
     * @param state
     * @return the state that was executed before the given state
     * @exception if
     *                previous state does not exist
     */
    public String getPreviousStateFromHistory(VMPropertyHandler ph,
            String state) throws APPlatformException {
        logger.debug("state: " + state);
        String previousState = null;
        String stateHistory = getServiceSetting(SM_STATE_HISTORY);
        String[] states = stateHistory.split(",");
        for (int i = states.length - 1; i >= 0; i--) {
            if (!states[i].equals(state)) {
                previousState = states[i];
                break;
            }
        }

        if (previousState == null) {
            String message = Messages.get(ph.getLocale(),
                    "error_no_previous_state", new Object[] { state });
            throw new APPlatformException(message);
        }

        logger.debug("previousState: " + previousState);
        return previousState;
    }

    private boolean notNullNorEmpty(String value) {
        return value != null && value.trim().length() > 0;
    }

    /**
     * Returns a controller setting or NULL of not defined
     */
    public String getControllerSetting(String key) {
        return getValue(key, settings.getConfigSettings());
    }

    /**
     * Returns a service setting. If not set a RuntimeException is thrown.
     */
    public String getServiceSettingValidated(String key) {
        String value = getValue(key, settings.getParameters());
        if (value == null) {
            String message = String.format("No value set for property '%s'",
                    key);
            throw new RuntimeException(message);
        }
        return value;
    }

    /**
     * Returns a service setting or NULL if not set
     */
    public String getServiceSetting(String key) {
        return getValue(key, settings.getParameters());
    }

    public void useMock(DataAccessService das) {
        das_stub = das;
    }

    public DataAccessService getDataAccessService() {
        if (das_stub != null) {
            return das_stub;
        } else {
            return new DataAccessService(getLocale());
        }
    }

    private String getValue(String key, Map<String, Setting> source) {
        Setting setting = source.get(key);
        return setting != null ? setting.getValue() : null;
    }

    private void setValue(String key, String value,
            Map<String, Setting> target) {
        target.put(key, new Setting(key, value));
    }
}