diff --git a/agent/conf/agent.properties b/agent/conf/agent.properties index a7376b68947..b8b7a7cdad0 100644 --- a/agent/conf/agent.properties +++ b/agent/conf/agent.properties @@ -70,6 +70,17 @@ domr.scripts.dir=scripts/network/domr/kvm # In MegaBytes per second #vm.migrate.speed=0 +# set target downtime at end of livemigration, the 'hiccup' for final copy. Higher numbers +# make livemigration easier, lower numbers may cause migration to never complete. Less than 1 +# means hypervisor default (20ms). +#vm.migrate.downtime=0 + +# Busy VMs may never finish migrating, depending on environment. When its available, we will +# want to add support for autoconvergence migration flag which should fix this. Set an upper +# limit in milliseconds for how long live migration should wait, at which point VM is paused and +# migration will finish quickly. Less than 1 means disabled. +#vm.migrate.pauseafter=0 + # set the type of bridge used on the hypervisor, this defines what commands the resource # will use to setup networking. Currently supported NATIVE, OPENVSWITCH #network.bridge.type=native diff --git a/agent/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-agent.in b/agent/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-agent.in index 60e1e045b24..d1769ccdfb0 100755 --- a/agent/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-agent.in +++ b/agent/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-agent.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/agent/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-agent.in b/agent/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-agent.in index 60e1e045b24..d1769ccdfb0 100755 --- a/agent/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-agent.in +++ b/agent/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-agent.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/agent/distro/opensuse/sles/SYSCONFDIR/init.d/cloud-agent.in b/agent/distro/opensuse/sles/SYSCONFDIR/init.d/cloud-agent.in index 2422268f416..741317bde43 100644 --- a/agent/distro/opensuse/sles/SYSCONFDIR/init.d/cloud-agent.in +++ b/agent/distro/opensuse/sles/SYSCONFDIR/init.d/cloud-agent.in @@ -33,7 +33,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/agent/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-agent.in b/agent/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-agent.in index 60e1e045b24..d1769ccdfb0 100644 --- a/agent/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-agent.in +++ b/agent/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-agent.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/agent/distro/sles/SYSCONFDIR/init.d/cloud-agent.in b/agent/distro/sles/SYSCONFDIR/init.d/cloud-agent.in index 2422268f416..741317bde43 100644 --- a/agent/distro/sles/SYSCONFDIR/init.d/cloud-agent.in +++ b/agent/distro/sles/SYSCONFDIR/init.d/cloud-agent.in @@ -33,7 +33,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/api/src/com/cloud/agent/api/HostVmStateReportEntry.java b/api/src/com/cloud/agent/api/HostVmStateReportEntry.java index 4f0fb911225..08acfef6c86 100644 --- a/api/src/com/cloud/agent/api/HostVmStateReportEntry.java +++ b/api/src/com/cloud/agent/api/HostVmStateReportEntry.java @@ -32,16 +32,12 @@ public class HostVmStateReportEntry { // host name or host uuid String host; - // XS needs Xen Tools version info - String hostToolsVersion; - public HostVmStateReportEntry() { } - public HostVmStateReportEntry(PowerState state, String host, String hostToolsVersion) { + public HostVmStateReportEntry(PowerState state, String host) { this.state = state; this.host = host; - this.hostToolsVersion = hostToolsVersion; } public PowerState getState() { @@ -51,8 +47,4 @@ public class HostVmStateReportEntry { public String getHost() { return host; } - - public String getHostToolsVersion() { - return hostToolsVersion; - } } diff --git a/api/src/com/cloud/agent/api/to/GPUDeviceTO.java b/api/src/com/cloud/agent/api/to/GPUDeviceTO.java new file mode 100644 index 00000000000..8bc9e5229de --- /dev/null +++ b/api/src/com/cloud/agent/api/to/GPUDeviceTO.java @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.agent.api.to; + +import java.util.HashMap; + +public class GPUDeviceTO { + + private String gpuGroup; + private String vgpuType; + private HashMap> groupDetails = new HashMap>(); + + public GPUDeviceTO( String gpuGroup, String vgpuType, HashMap> groupDetails) { + this.gpuGroup = gpuGroup; + this.vgpuType = vgpuType; + this.groupDetails = groupDetails; + } + + public String getGpuGroup() { + return gpuGroup; + } + + public String getVgpuType() { + return vgpuType; + } + + public void setGpuGroup(String gpuGroup) { + this.gpuGroup = gpuGroup; + } + + public void setVgpuType(String vgpuType) { + this.vgpuType = vgpuType; + } + + public HashMap> getGroupDetails() { + return groupDetails; + } + + public void setGroupDetails(HashMap> groupDetails) { + this.groupDetails = groupDetails; + } +} diff --git a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java index bed3e1d6aaf..bbd83852abf 100644 --- a/api/src/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/com/cloud/agent/api/to/VirtualMachineTO.java @@ -60,6 +60,7 @@ public class VirtualMachineTO { DiskTO[] disks; NicTO[] nics; + GPUDeviceTO gpuDevice; public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, String os, boolean enableHA, boolean limitCpuUse, String vncPassword) { @@ -266,4 +267,12 @@ public class VirtualMachineTO { this.uuid = uuid; } + public GPUDeviceTO getGpuDevice() { + return gpuDevice; + } + + public void setGpuDevice(GPUDeviceTO gpuDevice) { + this.gpuDevice = gpuDevice; + } + } diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 37b2ebb169c..39ef710f8a9 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -50,6 +50,8 @@ import com.cloud.offering.DiskOffering; import com.cloud.offering.NetworkOffering; import com.cloud.offering.ServiceOffering; import com.cloud.projects.Project; +import com.cloud.storage.GuestOS; +import com.cloud.storage.GuestOSHypervisor; import com.cloud.storage.Snapshot; import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; @@ -440,8 +442,8 @@ public class EventTypes { public static final String EVENT_HOST_RESERVATION_RELEASE = "HOST.RESERVATION.RELEASE"; // Dedicated guest vlan range - public static final String EVENT_GUEST_VLAN_RANGE_DEDICATE = "GUESTVLANRANGE.DEDICATE"; - public static final String EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE = "GUESTVLANRANGE.RELEASE"; + public static final String EVENT_GUEST_VLAN_RANGE_DEDICATE = "GUESTVLANRANGE.DEDICATE"; + public static final String EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE = "GUESTVLANRANGE.RELEASE"; public static final String EVENT_PORTABLE_IP_RANGE_CREATE = "PORTABLE.IP.RANGE.CREATE"; public static final String EVENT_PORTABLE_IP_RANGE_DELETE = "PORTABLE.IP.RANGE.DELETE"; @@ -479,10 +481,19 @@ public class EventTypes { public static final String EVENT_EXTERNAL_OPENDAYLIGHT_DELETE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.DELETE"; public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE"; + //Guest OS related events + public static final String EVENT_GUEST_OS_ADD = "GUEST.OS.ADD"; + public static final String EVENT_GUEST_OS_REMOVE = "GUEST.OS.REMOVE"; + public static final String EVENT_GUEST_OS_UPDATE = "GUEST.OS.UPDATE"; + public static final String EVENT_GUEST_OS_MAPPING_ADD = "GUEST.OS.MAPPING.ADD"; + public static final String EVENT_GUEST_OS_MAPPING_REMOVE = "GUEST.OS.MAPPING.REMOVE"; + public static final String EVENT_GUEST_OS_MAPPING_UPDATE = "GUEST.OS.MAPPING.UPDATE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking // current ActionEvent annotation semantics + // TODO #2 - The map should be from event type to class. entityEventDetails = new HashMap(); @@ -493,10 +504,13 @@ public class EventTypes { entityEventDetails.put(EVENT_VM_REBOOT, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_UPDATE, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_UPGRADE, VirtualMachine.class.getName()); + entityEventDetails.put(EVENT_VM_DYNAMIC_SCALE, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_RESETPASSWORD, VirtualMachine.class.getName()); + entityEventDetails.put(EVENT_VM_RESETSSHKEY, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_MIGRATE, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_MOVE, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_VM_RESTORE, VirtualMachine.class.getName()); + entityEventDetails.put(EVENT_VM_EXPUNGE, VirtualMachine.class.getName()); entityEventDetails.put(EVENT_ROUTER_CREATE, VirtualRouter.class.getName()); entityEventDetails.put(EVENT_ROUTER_DESTROY, VirtualRouter.class.getName()); @@ -544,9 +558,11 @@ public class EventTypes { entityEventDetails.put(EVENT_LB_CERT_REMOVE, LoadBalancer.class.getName()); // Account events + entityEventDetails.put(EVENT_ACCOUNT_ENABLE, Account.class.getName()); entityEventDetails.put(EVENT_ACCOUNT_DISABLE, Account.class.getName()); entityEventDetails.put(EVENT_ACCOUNT_CREATE, Account.class.getName()); entityEventDetails.put(EVENT_ACCOUNT_DELETE, Account.class.getName()); + entityEventDetails.put(EVENT_ACCOUNT_UPDATE, Account.class.getName()); entityEventDetails.put(EVENT_ACCOUNT_MARK_DEFAULT_ZONE, Account.class.getName()); // UserVO Events @@ -661,13 +677,13 @@ public class EventTypes { entityEventDetails.put(EVENT_SECURITY_GROUP_REMOVE, SecurityGroup.class.getName()); // Host - entityEventDetails.put(EVENT_HOST_RECONNECT, Host.class.getName()); + entityEventDetails.put(EVENT_HOST_RECONNECT, Host.class.getName()); // Maintenance - entityEventDetails.put(EVENT_MAINTENANCE_CANCEL, Host.class.getName()); - entityEventDetails.put(EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE, Host.class.getName()); - entityEventDetails.put(EVENT_MAINTENANCE_PREPARE, Host.class.getName()); - entityEventDetails.put(EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE, Host.class.getName()); + entityEventDetails.put(EVENT_MAINTENANCE_CANCEL, Host.class.getName()); + entityEventDetails.put(EVENT_MAINTENANCE_CANCEL_PRIMARY_STORAGE, Host.class.getName()); + entityEventDetails.put(EVENT_MAINTENANCE_PREPARE, Host.class.getName()); + entityEventDetails.put(EVENT_MAINTENANCE_PREPARE_PRIMARY_STORAGE, Host.class.getName()); // VPN entityEventDetails.put(EVENT_REMOTE_ACCESS_VPN_CREATE, RemoteAccessVpn.class.getName()); @@ -762,8 +778,8 @@ public class EventTypes { entityEventDetails.put(EVENT_TAGS_DELETE, "tag"); // external network device events - entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_ADD, "NvpController"); - entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_DELETE, "NvpController"); + entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_ADD, "NvpController"); + entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_DELETE, "NvpController"); entityEventDetails.put(EVENT_EXTERNAL_NVP_CONTROLLER_CONFIGURE, "NvpController"); // AutoScale @@ -789,6 +805,14 @@ public class EventTypes { entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_ADD_CONTROLLER, "OpenDaylightController"); entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_DELETE_CONTROLLER, "OpenDaylightController"); entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER, "OpenDaylightController"); + + //Guest OS + entityEventDetails.put(EVENT_GUEST_OS_ADD, GuestOS.class.getName()); + entityEventDetails.put(EVENT_GUEST_OS_REMOVE, GuestOS.class.getName()); + entityEventDetails.put(EVENT_GUEST_OS_UPDATE, GuestOS.class.getName()); + entityEventDetails.put(EVENT_GUEST_OS_MAPPING_ADD, GuestOSHypervisor.class.getName()); + entityEventDetails.put(EVENT_GUEST_OS_MAPPING_REMOVE, GuestOSHypervisor.class.getName()); + entityEventDetails.put(EVENT_GUEST_OS_MAPPING_UPDATE, GuestOSHypervisor.class.getName()); } public static String getEntityForEvent(String eventName) { diff --git a/api/src/com/cloud/gpu/GPU.java b/api/src/com/cloud/gpu/GPU.java new file mode 100644 index 00000000000..0eb466758fc --- /dev/null +++ b/api/src/com/cloud/gpu/GPU.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.gpu; + + +public class GPU { + + public enum Keys { + pciDevice, + vgpuType + } + public enum Type { + GPU_Passthrough, + VGPU + } + + public enum vGPUType { + GRID_K100("GRID K100"), + GRID_K140Q("GRID K140Q"), + GRID_K200("GRID K200"), + GRID_K240Q("GRID K240Q"), + GRID_K260("GRID K260Q"), + passthrough("passthrough"); + + private String type; + + vGPUType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + } +} diff --git a/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java b/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java index eee62ceea76..382c460725d 100644 --- a/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java +++ b/api/src/com/cloud/network/VpcVirtualNetworkApplianceService.java @@ -16,10 +16,13 @@ // under the License. package com.cloud.network; +import java.util.Map; + import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.router.VirtualRouter; +import com.cloud.vm.VirtualMachineProfile; public interface VpcVirtualNetworkApplianceService extends VirtualNetworkApplianceService { @@ -27,13 +30,14 @@ public interface VpcVirtualNetworkApplianceService extends VirtualNetworkApplian * @param router * @param network * @param isRedundant + * @param params TODO * @return * @throws ConcurrentOperationException * @throws ResourceUnavailableException * @throws InsufficientCapacityException */ - boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException; + boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant, Map params) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; /** * @param router diff --git a/api/src/com/cloud/network/element/AggregatedCommandExecutor.java b/api/src/com/cloud/network/element/AggregatedCommandExecutor.java new file mode 100644 index 00000000000..012908fc268 --- /dev/null +++ b/api/src/com/cloud/network/element/AggregatedCommandExecutor.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.network.element; + +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; + +public interface AggregatedCommandExecutor { + public boolean prepareAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException; + public boolean completeAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException; + public boolean cleanupAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException; +} diff --git a/api/src/com/cloud/offering/DiskOfferingInfo.java b/api/src/com/cloud/offering/DiskOfferingInfo.java new file mode 100644 index 00000000000..75a520e2d5a --- /dev/null +++ b/api/src/com/cloud/offering/DiskOfferingInfo.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.cloud.offering; + +public class DiskOfferingInfo { + private DiskOffering _diskOffering; + private Long _size; + private Long _minIops; + private Long _maxIops; + + public DiskOfferingInfo() { + } + + public DiskOfferingInfo(DiskOffering diskOffering) { + _diskOffering = diskOffering; + } + + public void setDiskOffering(DiskOffering diskOffering) { + _diskOffering = diskOffering; + } + + public DiskOffering getDiskOffering() { + return _diskOffering; + } + + public void setSize(Long size) { + _size = size; + } + + public Long getSize() { + return _size; + } + + public void setMinIops(Long minIops) { + _minIops = minIops; + } + + public Long getMinIops() { + return _minIops; + } + + public void setMaxIops(Long maxIops) { + _maxIops = maxIops; + } + + public Long getMaxIops() { + return _maxIops; + } +} \ No newline at end of file diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index 9b138feb308..abe6e2cd36e 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -23,6 +23,13 @@ import java.util.Map; import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; @@ -67,6 +74,7 @@ import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.network.IpAddress; import com.cloud.org.Cluster; import com.cloud.storage.GuestOS; +import com.cloud.storage.GuestOSHypervisor; import com.cloud.storage.GuestOsCategory; import com.cloud.storage.StoragePool; import com.cloud.user.SSHKeyPair; @@ -146,6 +154,69 @@ public interface ManagementService { */ Pair, Integer> listGuestOSCategoriesByCriteria(ListGuestOsCategoriesCmd cmd); + /** + * Obtains a list of all guest OS mappings + * + * @return list of GuestOSHypervisor + */ + Pair, Integer> listGuestOSMappingByCriteria(ListGuestOsMappingCmd cmd); + + /** + * Adds a new guest OS mapping + * + * @return A VO containing the new mapping, with its hypervisor, hypervisor type, guest OS name, and the name of guest OS specific to hypervisor + */ + GuestOSHypervisor addGuestOsMapping(AddGuestOsMappingCmd addGuestOsMappingCmd); + + /** + * Find newly added guest OS mapping by ID + * + * @return A VO containing the guest OS mapping specified by ID, with its hypervisor, hypervisor type, guest OS name, and the name of guest OS specific to hypervisor + */ + GuestOSHypervisor getAddedGuestOsMapping(Long guestOsHypervisorId); + + /** + * Adds a new guest OS + * + * @return A VO containing the new guest OS, with its category ID, name and display name + */ + GuestOS addGuestOs(AddGuestOsCmd addGuestOsCmd); + + /** + * Find newly added guest OS by ID + * + * @return A VO containing the guest OS specified by ID, with its category ID, name and display name + */ + GuestOS getAddedGuestOs(Long guestOsId); + + /** + * Updates an existing guest OS + * + * @return A VO containing the updated display name + */ + GuestOS updateGuestOs(UpdateGuestOsCmd updateGuestOsCmd); + + /** + * Updates an existing guest OS mapping + * + * @return A VO containing the updated OS name for hypervisor + */ + GuestOSHypervisor updateGuestOsMapping(UpdateGuestOsMappingCmd updateGuestOsMappingCmd); + + /** + * Removes an existing guest OS + * + * @return True is successfully marked for delete, false otherwise + */ + boolean removeGuestOs(RemoveGuestOsCmd removeGuestOsCmd); + + /** + * Removes an existing guest OS mapping + * + * @return True is successfully marked for delete, false otherwise + */ + boolean removeGuestOsMapping(RemoveGuestOsMappingCmd removeGuestOsMappingCmd); + VirtualMachine stopSystemVM(StopSystemVmCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException; VirtualMachine startSystemVM(long vmId); @@ -269,10 +340,6 @@ public interface ManagementService { */ String generateRandomPassword(); - public Long saveStartedEvent(Long userId, Long accountId, String type, String description, boolean startEventId, Long displayResourceEnabled); - - public Long saveCompletedEvent(Long userId, Long accountId, String level, String type, String description, boolean displayResourceEnabled, long startEventId); - /** * Search registered key pairs for the logged in user. * @@ -365,4 +432,6 @@ public interface ManagementService { ConcurrentOperationException; void cleanupVMReservations(); + + } diff --git a/api/src/com/cloud/storage/GuestOS.java b/api/src/com/cloud/storage/GuestOS.java index 437cd37d1ab..767a60ecf9f 100644 --- a/api/src/com/cloud/storage/GuestOS.java +++ b/api/src/com/cloud/storage/GuestOS.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import java.util.Date; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -26,4 +28,8 @@ public interface GuestOS extends InternalIdentity, Identity { String getDisplayName(); long getCategoryId(); + + Date getCreated(); + + Date getRemoved(); } diff --git a/api/src/com/cloud/storage/GuestOSHypervisor.java b/api/src/com/cloud/storage/GuestOSHypervisor.java index f022722af04..1cfc3a17285 100644 --- a/api/src/com/cloud/storage/GuestOSHypervisor.java +++ b/api/src/com/cloud/storage/GuestOSHypervisor.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import java.util.Date; + import org.apache.cloudstack.api.InternalIdentity; public interface GuestOSHypervisor extends InternalIdentity { @@ -25,4 +27,12 @@ public interface GuestOSHypervisor extends InternalIdentity { String getGuestOsName(); long getGuestOsId(); + + String getHypervisorVersion(); + + String getUuid(); + + Date getRemoved(); + + Date getCreated(); } diff --git a/api/src/com/cloud/storage/VolumeApiService.java b/api/src/com/cloud/storage/VolumeApiService.java index 35bcb6f80f3..beaa29096e2 100644 --- a/api/src/com/cloud/storage/VolumeApiService.java +++ b/api/src/com/cloud/storage/VolumeApiService.java @@ -94,4 +94,6 @@ public interface VolumeApiService { * */ String extractVolume(ExtractVolumeCmd cmd); + + boolean isDisplayResourceEnabled(Long id); } diff --git a/api/src/com/cloud/user/AccountService.java b/api/src/com/cloud/user/AccountService.java index 37f61055f3d..85c71cace24 100755 --- a/api/src/com/cloud/user/AccountService.java +++ b/api/src/com/cloud/user/AccountService.java @@ -113,4 +113,6 @@ public interface AccountService { //TO be implemented, to check accessibility for an entity owned by domain void checkAccess(Account account, AccessType accessType, boolean sameOwner, PartOf... entities) throws PermissionDeniedException; + + Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly); } diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 51a8fdfbe38..af4e1d3699e 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -469,4 +469,10 @@ public interface UserVmService { */ String getVmUserData(long vmId); + /** + * determin whether the uservm should be visible to the end user + * @return value of the display flag + */ + public boolean isDisplayResourceEnabled(Long vmId); + } diff --git a/api/src/org/apache/cloudstack/api/ApiCommandJobType.java b/api/src/org/apache/cloudstack/api/ApiCommandJobType.java index f27f001f868..227fb30114f 100644 --- a/api/src/org/apache/cloudstack/api/ApiCommandJobType.java +++ b/api/src/org/apache/cloudstack/api/ApiCommandJobType.java @@ -51,5 +51,7 @@ public enum ApiCommandJobType { InternalLbVm, DedicatedGuestVlanRange, IAMPolicy, - IAMGroup -} \ No newline at end of file + IAMGroup, + GuestOs, + GuestOsMapping +} diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 62191fc602e..239b8cdeec8 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -23,7 +23,8 @@ public class ApiConstants { public static final String ACCOUNT_ID = "accountid"; public static final String ALGORITHM = "algorithm"; public static final String ALLOCATED_ONLY = "allocatedonly"; - public static final String API_KEY = "userapikey"; + public static final String API_KEY = "apikey"; + public static final String USER_API_KEY = "userapikey"; public static final String APPLIED = "applied"; public static final String AVAILABLE = "available"; public static final String BITS = "bits"; @@ -49,10 +50,16 @@ public class ApiConstants { public static final String CLUSTER_ID = "clusterid"; public static final String CLUSTER_NAME = "clustername"; public static final String CLUSTER_TYPE = "clustertype"; + public static final String COMMAND = "command"; + public static final String CMD_EVENT_TYPE = "cmdeventtype"; public static final String COMPONENT = "component"; public static final String CPU_NUMBER = "cpunumber"; public static final String CPU_SPEED = "cpuspeed"; public static final String CREATED = "created"; + public static final String CTX_ACCOUNT_ID = "ctxaccountid"; + public static final String CTX_USER_ID = "ctxuserid"; + public static final String CTXSTARTEVENTID = "ctxstarteventid"; + public static final String CTX_START_EVENT_ID = "ctxStartEventId"; public static final String CUSTOMIZED = "customized"; public static final String CUSTOMIZED_IOPS = "customizediops"; public static final String CUSTOM_ID = "customid"; @@ -79,6 +86,7 @@ public class ApiConstants { public static final String IP6_DNS2 = "ip6dns2"; public static final String DOMAIN = "domain"; public static final String DOMAIN_ID = "domainid"; + public static final String DOMAIN__ID = "domainId"; public static final String DURATION = "duration"; public static final String EMAIL = "email"; public static final String END_DATE = "enddate"; @@ -86,6 +94,7 @@ public class ApiConstants { public static final String END_IPV6 = "endipv6"; public static final String END_PORT = "endport"; public static final String ENTRY_TIME = "entrytime"; + public static final String EXPIRES = "expires"; public static final String FETCH_LATEST = "fetchlatest"; public static final String FIRSTNAME = "firstname"; public static final String FORCED = "forced"; @@ -162,6 +171,8 @@ public class ApiConstants { public static final String OP = "op"; public static final String OS_CATEGORY_ID = "oscategoryid"; public static final String OS_TYPE_ID = "ostypeid"; + public static final String OS_DISPLAY_NAME = "osdisplayname"; + public static final String OS_NAME_FOR_HYPERVISOR = "osnameforhypervisor"; public static final String PARAMS = "params"; public static final String PARENT_DOMAIN_ID = "parentdomainid"; public static final String PASSWORD = "password"; @@ -209,8 +220,11 @@ public class ApiConstants { public static final String SENT = "sent"; public static final String SENT_BYTES = "sentbytes"; public static final String SERVICE_OFFERING_ID = "serviceofferingid"; + public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; + public static final String SIGNATURE = "signature"; + public static final String SIGNATURE_VERSION = "signatureversion"; public static final String SIZE = "size"; public static final String SNAPSHOT_ID = "snapshotid"; public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; @@ -277,6 +291,7 @@ public class ApiConstants { public static final String NETWORKRATE = "networkrate"; public static final String HOST_TAGS = "hosttags"; public static final String SSH_KEYPAIR = "keypair"; + public static final String HTTPMETHOD = "httpmethod"; public static final String HOST_CPU_CAPACITY = "hostcpucapacity"; public static final String HOST_CPU_NUM = "hostcpunum"; public static final String HOST_MEM_CAPACITY = "hostmemcapacity"; @@ -568,6 +583,11 @@ public class ApiConstants { public static final String PASSIVE = "passive"; public static final String VERSION = "version"; public static final String START = "start"; + public static final String GPUGROUP = "gpugroup"; + public static final String GPUGROUPNAME = "gpugroupname"; + public static final String VGPU = "vgpu"; + public static final String VGPUTYPE = "vgputype"; + public static final String REMAININGCAPACITY = "remainingcapacity"; public enum HostDetails { diff --git a/api/src/org/apache/cloudstack/api/BaseAsyncCmd.java b/api/src/org/apache/cloudstack/api/BaseAsyncCmd.java index bbc898f2a51..0b1396fbf83 100644 --- a/api/src/org/apache/cloudstack/api/BaseAsyncCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseAsyncCmd.java @@ -16,9 +16,6 @@ // under the License. package org.apache.cloudstack.api; -import org.apache.cloudstack.context.CallContext; - -import com.cloud.user.User; /** * queryAsyncJobResult API command. @@ -92,37 +89,4 @@ public abstract class BaseAsyncCmd extends BaseCmd { public Object getJob() { return job; } - - protected long saveStartedEvent() { - return saveStartedEvent(getEventType(), "Executing job for " + getEventDescription(), getStartEventId()); - } - - protected long saveStartedEvent(String eventType, String description, Long startEventId) { - CallContext ctx = CallContext.current(); - Long userId = ctx.getCallingUserId(); - userId = (userId == null) ? User.UID_SYSTEM : userId; - Long startEvent = startEventId; - if (startEvent == null) { - startEvent = 0L; - } - return _mgr.saveStartedEvent((userId == null) ? User.UID_SYSTEM : userId, getEntityOwnerId(), eventType, description, - isDisplayResourceEnabled(), startEvent); - } - - protected long saveCompletedEvent(String level, String description) { - return saveCompletedEvent(level, getEventType(), description, getStartEventId()); - } - - protected long saveCompletedEvent(String level, String eventType, String description, Long startEventId) { - CallContext ctx = CallContext.current(); - Long userId = ctx.getCallingUserId(); - userId = (userId == null) ? User.UID_SYSTEM : userId; - Long startEvent = startEventId; - if (startEvent == null) { - startEvent = 0L; - } - return _mgr.saveCompletedEvent((userId == null) ? User.UID_SYSTEM : userId, getEntityOwnerId(), level, eventType, description, - isDisplayResourceEnabled(), startEvent); - } - } diff --git a/api/src/org/apache/cloudstack/api/BaseAsyncVMCmd.java b/api/src/org/apache/cloudstack/api/BaseAsyncVMCmd.java new file mode 100644 index 00000000000..abfc9530274 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/BaseAsyncVMCmd.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api; + +/** + * A generic class for implementing methods common to all async vm commands + */ +public abstract class BaseAsyncVMCmd extends BaseAsyncCmd { + + @Override + public boolean isDisplayResourceEnabled(){ + return _userVmService.isDisplayResourceEnabled(getId()); + } + + public abstract Long getId(); +} diff --git a/api/src/org/apache/cloudstack/api/BaseAsyncVolumeCmd.java b/api/src/org/apache/cloudstack/api/BaseAsyncVolumeCmd.java new file mode 100644 index 00000000000..1782e4a1348 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/BaseAsyncVolumeCmd.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api; + +/** + * A generic class for implementing methods common to all async volume commands + */ +public abstract class BaseAsyncVolumeCmd extends BaseAsyncCmd { + + @Override + public boolean isDisplayResourceEnabled(){ + return _volumeService.isDisplayResourceEnabled(getId()); + } + + public abstract Long getId(); +} diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index 7c720c9dfc0..f6f21ae7162 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -17,31 +17,34 @@ package org.apache.cloudstack.api; +import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Pattern; import javax.inject.Inject; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.alert.AlertService; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.query.QueryService; import org.apache.cloudstack.usage.UsageService; -import org.apache.log4j.Logger; import com.cloud.configuration.ConfigurationService; -import com.cloud.domain.Domain; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; -import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.NetworkModel; @@ -59,7 +62,6 @@ import com.cloud.network.vpc.VpcProvisioningService; import com.cloud.network.vpc.VpcService; import com.cloud.network.vpn.RemoteAccessVpnService; import com.cloud.network.vpn.Site2SiteVpnService; -import com.cloud.projects.Project; import com.cloud.projects.ProjectService; import com.cloud.resource.ResourceService; import com.cloud.server.ManagementService; @@ -74,6 +76,7 @@ import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.user.DomainService; import com.cloud.user.ResourceLimitService; +import com.cloud.utils.ReflectUtil; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.UUIDManager; import com.cloud.vm.UserVmService; @@ -81,34 +84,28 @@ import com.cloud.vm.snapshot.VMSnapshotService; public abstract class BaseCmd { private static final Logger s_logger = Logger.getLogger(BaseCmd.class.getName()); - - public static final String USER_ERROR_MESSAGE = "Internal error executing command, please contact your system administrator"; - public static final int PROGRESS_INSTANCE_CREATED = 1; - public static final String RESPONSE_TYPE_XML = "xml"; public static final String RESPONSE_TYPE_JSON = "json"; - - public enum CommandType { + public static final DateFormat INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + public static final DateFormat NEW_INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + public static final String USER_ERROR_MESSAGE = "Internal error executing command, please contact your system administrator"; + public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+"); + private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); + protected static final Map, List> fieldsForCmdClass = new HashMap, List>(); + public static enum HTTPMethod { + GET, POST, PUT, DELETE + } + public static enum CommandType { BOOLEAN, DATE, FLOAT, INTEGER, SHORT, LIST, LONG, OBJECT, MAP, STRING, TZDATE, UUID } - public static final DateFormat INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); - public static final DateFormat NEW_INPUT_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - public static Pattern newInputDateFormat = Pattern.compile("[\\d]+-[\\d]+-[\\d]+ [\\d]+:[\\d]+:[\\d]+"); - private static final DateFormat s_outputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); - - private Object _responseObject = null; + private Object _responseObject; private Map fullUrlParams; - - public enum HTTPMethod { - GET, POST, PUT, DELETE - } - private HTTPMethod httpMethod; - @Parameter(name = "response", type = CommandType.STRING) private String responseType; + @Inject public ConfigurationService _configService; @Inject @@ -165,7 +162,6 @@ public abstract class BaseCmd { public NetworkACLService _networkACLService; @Inject public Site2SiteVpnService _s2sVpnService; - @Inject public QueryService _queryService; @Inject @@ -205,7 +201,7 @@ public abstract class BaseCmd { return httpMethod; } - public void setHttpMethod(String method) { + public void setHttpMethod(final String method) { if (method != null) { if (method.equalsIgnoreCase("GET")) httpMethod = HTTPMethod.GET; @@ -227,12 +223,36 @@ public abstract class BaseCmd { return responseType; } - public void setResponseType(String responseType) { + public void setResponseType(final String responseType) { this.responseType = responseType; } + /** + * For some reason this method does not return the actual command name, but more a name that + * is used to create the response. So you can expect for a XCmd a value like xcmdresponse. Anyways + * this methods is used in too many places so for now instead of changing it we just create another + * method {@link BaseCmd#getActualCommandName()} that returns the value from {@link APICommand#name()} + * + * @return + */ public abstract String getCommandName(); + + /** + * Gets the CommandName based on the class annotations: the value from {@link APICommand#name()} + * + * @return the value from {@link APICommand#name()} + */ + public String getActualCommandName() { + String cmdName = null; + if (this.getClass().getAnnotation(APICommand.class) != null) { + cmdName = this.getClass().getAnnotation(APICommand.class).name(); + } else { + cmdName = this.getClass().getName(); + } + return cmdName; + } + /** * For commands the API framework needs to know the owner of the object being acted upon. This method is * used to determine that information. @@ -245,15 +265,11 @@ public abstract class BaseCmd { return _responseObject; } - public void setResponseObject(Object responseObject) { + public void setResponseObject(final Object responseObject) { _responseObject = responseObject; } - public ManagementService getMgmtServiceRef() { - return _mgr; - } - - public static String getDateString(Date date) { + public static String getDateString(final Date date) { if (date == null) { return ""; } @@ -264,92 +280,68 @@ public abstract class BaseCmd { return formattedString; } - // FIXME: move this to a utils method so that maps can be unpacked and integer/long values can be appropriately cast - @SuppressWarnings({"unchecked", "rawtypes"}) - public Map unpackParams(Map params) { - Map lowercaseParams = new HashMap(); - for (String key : params.keySet()) { - int arrayStartIndex = key.indexOf('['); - int arrayStartLastIndex = key.lastIndexOf('['); - if (arrayStartIndex != arrayStartLastIndex) { - throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + - "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); + protected List getAllFieldsForClass(final Class clazz) { + List filteredFields = fieldsForCmdClass.get(clazz); + + // If list of fields was not cached yet + if (filteredFields == null) { + final List allFields = ReflectUtil.getAllFieldsForClass(this.getClass(), BaseCmd.class); + filteredFields = new ArrayList(); + + for (final Field field : allFields) { + final Parameter parameterAnnotation = field.getAnnotation(Parameter.class); + if ((parameterAnnotation != null) && parameterAnnotation.expose()) { + filteredFields.add(field); + } + } + + // Cache the prepared list for future use + fieldsForCmdClass.put(clazz, filteredFields); + } + return filteredFields; + } + + /** + * This method doesn't return all the @{link Parameter}, but only the ones exposed + * and allowed for current @{link RoleType}. This method will get the fields for a given + * Cmd class only once and never again, so in case of a dynamic update the result would + * be obsolete (this might be a plugin update. It is agreed upon that we will not do + * upgrades dynamically but in case we come back on that decision we need to revisit this) + * + * @return + */ + public List getParamFields() { + final List allFields = getAllFieldsForClass(this.getClass()); + final List validFields = new ArrayList(); + final Account caller = CallContext.current().getCallingAccount(); + + for (final Field field : allFields) { + final Parameter parameterAnnotation = field.getAnnotation(Parameter.class); + + //TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate + final RoleType[] allowedRoles = parameterAnnotation.authorized(); + boolean roleIsAllowed = true; + if (allowedRoles.length > 0) { + roleIsAllowed = false; + for (final RoleType allowedRole : allowedRoles) { + if (allowedRole.getValue() == caller.getType()) { + roleIsAllowed = true; + break; + } + } } - if (arrayStartIndex > 0) { - int arrayEndIndex = key.indexOf(']'); - int arrayEndLastIndex = key.lastIndexOf(']'); - if ((arrayEndIndex < arrayStartIndex) || (arrayEndIndex != arrayEndLastIndex)) { - // malformed parameter - throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + - "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); - } - - // Now that we have an array object, check for a field name in the case of a complex object - int fieldIndex = key.indexOf('.'); - String fieldName = null; - if (fieldIndex < arrayEndIndex) { - throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + - "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); - } else { - fieldName = key.substring(fieldIndex + 1); - } - - // parse the parameter name as the text before the first '[' character - String paramName = key.substring(0, arrayStartIndex); - paramName = paramName.toLowerCase(); - - Map mapArray = null; - Map mapValue = null; - String indexStr = key.substring(arrayStartIndex + 1, arrayEndIndex); - int index = 0; - boolean parsedIndex = false; - try { - if (indexStr != null) { - index = Integer.parseInt(indexStr); - parsedIndex = true; - } - } catch (NumberFormatException nfe) { - s_logger.warn("Invalid parameter " + key + " received, unable to parse object array, returning an error."); - } - - if (!parsedIndex) { - throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + - "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); - } - - Object value = lowercaseParams.get(paramName); - if (value == null) { - // for now, assume object array with sub fields - mapArray = new HashMap(); - mapValue = new HashMap(); - mapArray.put(Integer.valueOf(index), mapValue); - } else if (value instanceof Map) { - mapArray = (HashMap)value; - mapValue = mapArray.get(Integer.valueOf(index)); - if (mapValue == null) { - mapValue = new HashMap(); - mapArray.put(Integer.valueOf(index), mapValue); - } - } - - // we are ready to store the value for a particular field into the map for this object - mapValue.put(fieldName, params.get(key)); - - lowercaseParams.put(paramName, mapArray); + if (roleIsAllowed) { + validFields.add(field); } else { - lowercaseParams.put(key.toLowerCase(), params.get(key)); + s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in"); } } - return lowercaseParams; + + return validFields; } - protected long getInstanceIdFromJobSuccessResult(String result) { - s_logger.debug("getInstanceIdFromJobSuccessResult not overridden in subclass " + this.getClass().getName()); - return 0; - } - - public void setFullUrlParams(Map map) { + public void setFullUrlParams(final Map map) { fullUrlParams = map; } @@ -357,50 +349,11 @@ public abstract class BaseCmd { return fullUrlParams; } - public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) { - if (accountName != null) { - if (domainId == null) { - throw new InvalidParameterValueException("Account must be specified with domainId parameter"); - } - - Domain domain = _domainService.getDomain(domainId); - if (domain == null) { - throw new InvalidParameterValueException("Unable to find domain by id"); - } - - Account account = _accountService.getActiveAccountByName(accountName, domainId); - if (account != null && account.getType() != Account.ACCOUNT_TYPE_PROJECT) { - if (!enabledOnly || account.getState() == Account.State.enabled) { - return account.getId(); - } else { - throw new PermissionDeniedException("Can't add resources to the account id=" + account.getId() + " in state=" + account.getState() + - " as it's no longer active"); - } - } else { - // idList is not used anywhere, so removed it now - //List idList = new ArrayList(); - //idList.add(new IdentityProxy("domain", domainId, "domainId")); - throw new InvalidParameterValueException("Unable to find account by name " + accountName + " in domain with specified id"); - } - } - - if (projectId != null) { - Project project = _projectService.getProject(projectId); - if (project != null) { - if (!enabledOnly || project.getState() == Project.State.Active) { - return project.getProjectAccountId(); - } else { - PermissionDeniedException ex = - new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() + - " as it's no longer active"); - ex.addProxyObject(project.getUuid(), "projectId"); - throw ex; - } - } else { - throw new InvalidParameterValueException("Unable to find project by id"); - } - } - return null; + /** + * To be overwritten by any class who needs specific validation + */ + public void validateSpecificParameters(final Map params){ + // To be overwritten by any class who needs specific validation } /** diff --git a/api/src/org/apache/cloudstack/api/BaseListCmd.java b/api/src/org/apache/cloudstack/api/BaseListCmd.java index c1a4b4c440f..f2800035ad5 100644 --- a/api/src/org/apache/cloudstack/api/BaseListCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseListCmd.java @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api; +import java.util.Map; + import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.exception.CSExceptionErrorCode; public abstract class BaseListCmd extends BaseCmd { @@ -83,7 +86,7 @@ public abstract class BaseListCmd extends BaseCmd { public Long getPageSizeVal() { Long defaultPageSize = s_maxPageSize; - Integer pageSizeInt = getPageSize(); + final Integer pageSizeInt = getPageSize(); if (pageSizeInt != null) { defaultPageSize = pageSizeInt.longValue(); } @@ -96,12 +99,12 @@ public abstract class BaseListCmd extends BaseCmd { public Long getStartIndex() { Long startIndex = Long.valueOf(0); - Long pageSizeVal = getPageSizeVal(); + final Long pageSizeVal = getPageSizeVal(); if (pageSizeVal == null) { startIndex = null; } else if (page != null) { - int pageNum = page.intValue(); + final int pageNum = page.intValue(); if (pageNum > 0) { startIndex = Long.valueOf(pageSizeVal * (pageNum - 1)); } @@ -112,4 +115,25 @@ public abstract class BaseListCmd extends BaseCmd { public ApiCommandJobType getInstanceType() { return ApiCommandJobType.None; } + + @Override + public void validateSpecificParameters(final Map params){ + super.validateSpecificParameters(params); + + final Object pageSizeObj = params.get(ApiConstants.PAGE_SIZE); + Long pageSize = null; + if (pageSizeObj != null) { + pageSize = Long.valueOf((String)pageSizeObj); + } + + if (params.get(ApiConstants.PAGE) == null && + pageSize != null && + !pageSize.equals(BaseListCmd.s_pageSizeUnlimited)) { + final ServerApiException ex = new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified"); + ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName())); + throw ex; + } else if (pageSize == null && (params.get(ApiConstants.PAGE) != null)) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified"); + } + } } diff --git a/api/src/org/apache/cloudstack/api/Parameter.java b/api/src/org/apache/cloudstack/api/Parameter.java index f1c6e1a4555..7ee6897af29 100644 --- a/api/src/org/apache/cloudstack/api/Parameter.java +++ b/api/src/org/apache/cloudstack/api/Parameter.java @@ -48,7 +48,5 @@ public @interface Parameter { String since() default ""; - String retrieveMethod() default "getById"; - RoleType[] authorized() default {}; } diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index 034e76fcc1e..10fb6df3a18 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -48,6 +48,7 @@ import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; @@ -176,6 +177,7 @@ import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; import com.cloud.server.ResourceTag; import com.cloud.storage.GuestOS; +import com.cloud.storage.GuestOSHypervisor; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.StoragePool; @@ -414,6 +416,8 @@ public interface ResponseGenerator { GuestOSResponse createGuestOSResponse(GuestOS os); + GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor osHypervisor); + SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule sched); UsageRecordResponse createUsageResponse(Usage usageRecord); diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java new file mode 100644 index 00000000000..3ba9669b305 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsCmd.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSCategoryResponse; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.storage.GuestOS; +import com.cloud.user.Account; + +@APICommand(name = "addGuestOs", description = "Add a new guest OS type", responseObject = GuestOSResponse.class, + since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class AddGuestOsCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(AddGuestOsCmd.class.getName()); + + private static final String s_name = "addguestosresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.OS_CATEGORY_ID, type = CommandType.UUID, entityType = GuestOSCategoryResponse.class, required = true, description = "ID of Guest OS category") + private Long osCategoryId; + + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS") + private String osDisplayName; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = false, description = "Optional name for Guest OS") + private String osName; + + +///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getOsCategoryId() { + return osCategoryId; + } + + public String getOsDisplayName() { + return osDisplayName; + } + + public String getOsName() { + return osName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void create() { + GuestOS guestOs = _mgr.addGuestOs(this); + if (guestOs != null) { + setEntityId(guestOs.getId()); + setEntityUuid(guestOs.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new guest OS type entity"); + } + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Guest OS Id: " + getEntityId()); + GuestOS guestOs = _mgr.getAddedGuestOs(getEntityId()); + if (guestOs != null) { + GuestOSResponse response = _responseGenerator.createGuestOSResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add new guest OS type"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_ADD; + } + + @Override + public String getEventDescription() { + return "adding a new guest OS type Id: " + getEntityId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOs; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_GUEST_OS_ADD; + } + + @Override + public String getCreateEventDescription() { + return "adding new guest OS type"; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java new file mode 100644 index 00000000000..8af50a15bfa --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/AddGuestOsMappingCmd.java @@ -0,0 +1,146 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; + +import com.cloud.event.EventTypes; +import com.cloud.storage.GuestOSHypervisor; +import com.cloud.user.Account; + +@APICommand(name = "addGuestOsMapping", description = "Adds a guest OS name to hypervisor OS name mapping", responseObject = GuestOsMappingResponse.class, + since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class AddGuestOsMappingCmd extends BaseAsyncCreateCmd { + public static final Logger s_logger = Logger.getLogger(AddGuestOsMappingCmd.class.getName()); + + private static final String s_name = "addguestosmappingresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, required = false, entityType = GuestOSResponse.class, description = "UUID of Guest OS type. Either the UUID or Display Name must be passed") + private Long osTypeId; + + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = false, description = "Display Name of Guest OS standard type. Either Display Name or UUID must be passed") + private String osStdName; + + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor type. One of : XenServer, KVM, VMWare") + private String hypervisor; + + @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = true, description = "Hypervisor version to create the mapping for. Use 'default' for default versions") + private String hypervisorVersion; + + @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = true, description = "OS name specific to the hypervisor") + private String osNameForHypervisor; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getOsTypeId() { + return osTypeId; + } + + public String getOsStdName() { + return osStdName; + } + + public String getHypervisor() { + return hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + + public String getOsNameForHypervisor() { + return osNameForHypervisor; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void create() { + GuestOSHypervisor guestOsMapping = _mgr.addGuestOsMapping(this); + if (guestOsMapping != null) { + setEntityId(guestOsMapping.getId()); + setEntityUuid(guestOsMapping.getUuid()); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add guest OS mapping entity"); + } + } + + @Override + public void execute() { + GuestOSHypervisor guestOsMapping = _mgr.getAddedGuestOsMapping(getEntityId()); + if (guestOsMapping != null) { + GuestOsMappingResponse response = _responseGenerator.createGuestOSMappingResponse(guestOsMapping); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add guest OS mapping"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_MAPPING_ADD; + } + + @Override + public String getEventDescription() { + return "adding a new guest OS mapping Id: " + getEntityId(); + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOsMapping; + } + + @Override + public String getCreateEventType() { + return EventTypes.EVENT_GUEST_OS_MAPPING_ADD; + } + + @Override + public String getCreateEventDescription() { + return "adding new guest OS mapping"; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java new file mode 100644 index 00000000000..a7fc9e0decd --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/ListGuestOsMappingCmd.java @@ -0,0 +1,101 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; +import org.apache.cloudstack.api.response.ListResponse; + +import com.cloud.storage.GuestOSHypervisor; +import com.cloud.utils.Pair; + +@APICommand(name = "listGuestOsMapping", description = "Lists all available OS mappings for given hypervisor", responseObject = GuestOsMappingResponse.class, + since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class ListGuestOsMappingCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListGuestOsMappingCmd.class.getName()); + + private static final String s_name = "listguestosmappingresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOsMappingResponse.class, required = false, description = "list mapping by its UUID") + private Long id; + + @Parameter(name = ApiConstants.OS_TYPE_ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = false, description = "list mapping by Guest OS Type UUID") + private Long osTypeId; + + @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, required = false, description = "list Guest OS mapping by hypervisor") + private String hypervisor; + + @Parameter(name = ApiConstants.HYPERVISOR_VERSION, type = CommandType.STRING, required = false, description = "list Guest OS mapping by hypervisor version. Must be used with hypervisor parameter") + private String hypervisorVersion; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getOsTypeId() { + return osTypeId; + } + + public String getHypervisor() { + return hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public void execute() { + Pair, Integer> result = _mgr.listGuestOSMappingByCriteria(this); + ListResponse response = new ListResponse(); + List osMappingResponses = new ArrayList(); + for (GuestOSHypervisor guestOSHypervisor : result.first()) { + GuestOsMappingResponse guestOsMappingResponse = _responseGenerator.createGuestOSMappingResponse(guestOSHypervisor); + osMappingResponses.add(guestOsMappingResponse); + } + + response.setResponses(osMappingResponses, result.second()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java new file mode 100644 index 00000000000..3de7f7a68da --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsCmd.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "removeGuestOs", description = "Removes a Guest OS from listing.", responseObject = SuccessResponse.class, since = "4.4.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class RemoveGuestOsCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(RemoveGuestOsCmd.class.getName()); + private static final String s_name = "removeguestosresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "ID of the guest OS") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Guest OS Id: " + id); + boolean result = _mgr.removeGuestOs(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove guest OS"); + } + } + + @Override + public String getEventDescription() { + return "Removing Guest OS: " + getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_REMOVE; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOs; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java new file mode 100644 index 00000000000..ddd45399de2 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/RemoveGuestOsMappingCmd.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; + +@APICommand(name = "removeGuestOsMapping", description = "Removes a Guest OS Mapping.", responseObject = SuccessResponse.class, since = "4.4.0", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class RemoveGuestOsMappingCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(RemoveGuestOsMappingCmd.class.getName()); + private static final String s_name = "removeguestosmappingresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOsMappingResponse.class, required = true, description = "ID of the guest OS mapping") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + CallContext.current().setEventDetails("Guest OS Mapping Id: " + id); + boolean result = _mgr.removeGuestOsMapping(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove guest OS mapping"); + } + } + + @Override + public String getEventDescription() { + return "Removing Guest OS Mapping: " + getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_MAPPING_REMOVE; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOsMapping; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java new file mode 100644 index 00000000000..e4b1ecd5719 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsCmd.java @@ -0,0 +1,104 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOSResponse; + +import com.cloud.event.EventTypes; +import com.cloud.storage.GuestOS; +import com.cloud.user.Account; + +@APICommand(name = "updateGuestOs", description = "Updates the information about Guest OS", responseObject = GuestOSResponse.class, + since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UpdateGuestOsCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(UpdateGuestOsCmd.class.getName()); + + private static final String s_name = "updateguestosresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOSResponse.class, required = true, description = "UUID of the Guest OS") + private Long id; + + @Parameter(name = ApiConstants.OS_DISPLAY_NAME, type = CommandType.STRING, required = true, description = "Unique display name for Guest OS") + private String osDisplayName; + +///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getOsDisplayName() { + return osDisplayName; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute() { + GuestOS guestOs = _mgr.updateGuestOs(this); + if (guestOs != null) { + GuestOSResponse response = _responseGenerator.createGuestOSResponse(guestOs); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update guest OS type"); + } + } + + @Override + public String getEventDescription() { + return "Updating guest OS: " + getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_UPDATE; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOs; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java new file mode 100644 index 00000000000..d760d534cfb --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/guest/UpdateGuestOsMappingCmd.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.command.admin.guest; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiCommandJobType; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; + +import com.cloud.event.EventTypes; +import com.cloud.storage.GuestOSHypervisor; +import com.cloud.user.Account; + +@APICommand(name = "updateGuestOsMapping", description = "Updates the information about Guest OS to Hypervisor specific name mapping", responseObject = GuestOsMappingResponse.class, + since = "4.4.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class UpdateGuestOsMappingCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(UpdateGuestOsMappingCmd.class.getName()); + + private static final String s_name = "updateguestosmappingresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = GuestOsMappingResponse.class, required = true, description = "UUID of the Guest OS to hypervisor name Mapping") + private Long id; + + @Parameter(name = ApiConstants.OS_NAME_FOR_HYPERVISOR, type = CommandType.STRING, required = true, description = "Hypervisor specific name for this Guest OS") + private String osNameForHypervisor; + +///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public String getOsNameForHypervisor() { + return osNameForHypervisor; + } + + @Override + public void execute() { + GuestOSHypervisor guestOsMapping = _mgr.updateGuestOsMapping(this); + if (guestOsMapping != null) { + GuestOsMappingResponse response = _responseGenerator.createGuestOSMappingResponse(guestOsMapping); + response.setResponseName(getCommandName()); + setResponseObject(response); + } + else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update guest OS mapping"); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventDescription() { + return "Updating Guest OS Mapping: " + getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_GUEST_OS_MAPPING_UPDATE; + } + + @Override + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.GuestOsMapping; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index 84c2c25d9fa..67c97923c06 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -93,9 +93,9 @@ public class CreateDiskOfferingCmd extends BaseCmd { private Long maxIops; @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, - type = CommandType.INTEGER, - required = false, - description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + type = CommandType.INTEGER, + required = false, + description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") private Integer hypervisorSnapshotReserve; ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 1d8cbff9307..78101fb11ad 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -17,10 +17,10 @@ package org.apache.cloudstack.api.command.admin.offering; import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -29,6 +29,7 @@ import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.log4j.Logger; import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; @@ -103,7 +104,7 @@ public class CreateServiceOfferingCmd extends BaseCmd { private String deploymentPlanner; @Parameter(name = ApiConstants.SERVICE_OFFERING_DETAILS, type = CommandType.MAP, description = "details for planner, used to store specific parameters") - private Map details; + private Map details; @Parameter(name = ApiConstants.BYTES_READ_RATE, type = CommandType.LONG, required = false, description = "bytes read rate of the disk offering") private Long bytesReadRate; @@ -117,6 +118,21 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.IOPS_WRITE_RATE, type = CommandType.LONG, required = false, description = "io requests write rate of the disk offering") private Long iopsWriteRate; + @Parameter(name = ApiConstants.CUSTOMIZED_IOPS, type = CommandType.BOOLEAN, required = false, description = "whether compute offering iops is custom or not") + private Boolean customizedIops; + + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "min iops of the compute offering") + private Long minIops; + + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "max iops of the compute offering") + private Long maxIops; + + @Parameter(name = ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, + type = CommandType.INTEGER, + required = false, + description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + private Integer hypervisorSnapshotReserve; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -190,13 +206,17 @@ public class CreateServiceOfferingCmd extends BaseCmd { } public Map getDetails() { - if (details == null || details.isEmpty()) { - return null; + Map detailsMap = null; + if (details != null && !details.isEmpty()) { + detailsMap = new HashMap(); + Collection props = details.values(); + Iterator iter = props.iterator(); + while (iter.hasNext()) { + HashMap detail = (HashMap) iter.next(); + detailsMap.put(detail.get("key"), detail.get("value")); + } } - - Collection paramsCollection = details.values(); - Map params = (Map)(paramsCollection.toArray())[0]; - return params; + return detailsMap; } public Long getBytesReadRate() { @@ -215,6 +235,22 @@ public class CreateServiceOfferingCmd extends BaseCmd { return iopsWriteRate; } + public Boolean isCustomizedIops() { + return customizedIops; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java b/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java index 24c0fce0ca0..51e218d7de8 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/user/CreateUserCmd.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.user; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -27,6 +25,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.user.Account; import com.cloud.user.User; diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java index eb9145ccc4c..378a826ef85 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/DeleteAffinityGroupCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.affinitygroup; + import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; diff --git a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java index a3358b29435..b603a152d58 100644 --- a/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/autoscale/CreateConditionCmd.java @@ -139,7 +139,7 @@ public class CreateConditionCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java b/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 5f5c61f69a6..3aabf926fc6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -121,8 +121,7 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd { public boolean listInReadyState() { Account account = CallContext.current().getCallingAccount(); // It is account specific if account is admin type and domainId and accountName are not null - boolean isAccountSpecific = (account == null || _accountService.isAdmin(account.getType())) - && (getAccountName() != null) && (getDomainId() != null); + boolean isAccountSpecific = (account == null || _accountService.isAdmin(account.getType())) && (getAccountName() != null) && (getDomainId() != null); // Show only those that are downloaded. TemplateFilter templateFilter = TemplateFilter.valueOf(getIsoFilter()); boolean onlyReady = diff --git a/api/src/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java b/api/src/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java index fa1a29ec0ad..37f294c31dc 100644 --- a/api/src/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/iso/RegisterIsoCmd.java @@ -18,8 +18,6 @@ package org.apache.cloudstack.api.command.user.iso; import java.util.List; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -34,6 +32,7 @@ import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.exception.ResourceAllocationException; import com.cloud.template.VirtualMachineTemplate; @@ -180,7 +179,7 @@ public class RegisterIsoCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java index 172b4c60bef..3ecec7bde80 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java @@ -285,7 +285,7 @@ public class CreateNetworkCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java index 701570327cc..67825761b8a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/region/ha/gslb/CreateGlobalLoadBalancerRuleCmd.java @@ -19,8 +19,6 @@ package org.apache.cloudstack.api.command.user.region.ha.gslb; import javax.inject.Inject; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -32,6 +30,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; import org.apache.cloudstack.api.response.RegionResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.exception.ResourceAllocationException; @@ -192,7 +191,7 @@ public class CreateGlobalLoadBalancerRuleCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java index bd8942a1cb8..1576d93784b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/ListResourceLimitsCmd.java @@ -19,14 +19,13 @@ package org.apache.cloudstack.api.command.user.resource; import java.util.ArrayList; import java.util.List; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; +import org.apache.log4j.Logger; import com.cloud.configuration.ResourceLimit; @@ -78,7 +77,7 @@ public class ListResourceLimitsCmd extends BaseListProjectAndAccountResourcesCmd @Override public void execute() { List result = - _resourceLimitService.searchForLimits(id, finalyzeAccountId(this.getAccountName(), this.getDomainId(), this.getProjectId(), false), this.getDomainId(), + _resourceLimitService.searchForLimits(id, _accountService.finalyzeAccountId(this.getAccountName(), this.getDomainId(), this.getProjectId(), false), this.getDomainId(), resourceType, this.getStartIndex(), this.getPageSizeVal()); ListResponse response = new ListResponse(); List limitResponses = new ArrayList(); diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java index 48ac9c6de5d..5a417496eb1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java @@ -19,8 +19,6 @@ package org.apache.cloudstack.api.command.user.resource; import java.util.ArrayList; import java.util.List; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -32,6 +30,7 @@ import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ResourceCountResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.configuration.ResourceCount; import com.cloud.user.Account; @@ -126,7 +125,7 @@ public class UpdateResourceCountCmd extends BaseCmd { @Override public void execute() { List result = - _resourceLimitService.recalculateResourceCount(finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType()); + _resourceLimitService.recalculateResourceCount(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), getResourceType()); if ((result != null) && (result.size() > 0)) { ListResponse response = new ListResponse(); diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java index 015cafb4960..22d20d3e8e1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.resource; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -28,6 +26,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.configuration.ResourceLimit; @@ -96,7 +95,7 @@ public class UpdateResourceLimitCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -106,7 +105,7 @@ public class UpdateResourceLimitCmd extends BaseCmd { @Override public void execute() { - ResourceLimit result = _resourceLimitService.updateResourceLimit(finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), resourceType, max); + ResourceLimit result = _resourceLimitService.updateResourceLimit(_accountService.finalyzeAccountId(accountName, domainId, projectId, true), getDomainId(), resourceType, max); if (result != null || (result == null && max != null && max.longValue() == -1L)) { ResourceLimitResponse response = _responseGenerator.createResourceLimitResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java index 4eca10fca0e..96509f10817 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupEgressCmd.java @@ -173,7 +173,7 @@ public class AuthorizeSecurityGroupEgressCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java index 41cea4124c0..1e324e2a1e4 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/AuthorizeSecurityGroupIngressCmd.java @@ -173,7 +173,7 @@ public class AuthorizeSecurityGroupIngressCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java index 8e08254d367..827c7d73899 100644 --- a/api/src/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/securitygroup/DeleteSecurityGroupCmd.java @@ -111,7 +111,7 @@ public class DeleteSecurityGroupCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java b/api/src/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java index 05eddbeb87e..a0f05d18d3f 100644 --- a/api/src/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/ssh/CreateSSHKeyPairCmd.java @@ -81,7 +81,7 @@ public class CreateSSHKeyPairCmd extends BaseCmd { ///////////////////////////////////////////////////// @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java b/api/src/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java index 19b29bd2714..d0a5234e05d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/ssh/DeleteSSHKeyPairCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.ssh; + import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; diff --git a/api/src/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java b/api/src/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java index 5fd04925b92..e095227d98e 100644 --- a/api/src/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/ssh/RegisterSSHKeyPairCmd.java @@ -89,7 +89,7 @@ public class RegisterSSHKeyPairCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index c7a228550b7..0be9ab80074 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -103,8 +103,7 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd { Account account = CallContext.current().getCallingAccount(); // It is account specific if account is admin type and domainId and accountName are not null - boolean isAccountSpecific = (account == null || _accountService.isAdmin(account.getType())) - && (getAccountName() != null) && (getDomainId() != null); + boolean isAccountSpecific = (account == null || _accountService.isAdmin(account.getType())) && (getAccountName() != null) && (getDomainId() != null); // Show only those that are downloaded. TemplateFilter templateFilter = TemplateFilter.valueOf(getTemplateFilter()); boolean onlyReady = diff --git a/api/src/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java index 63eb12bc307..f478c9bc8ee 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/RegisterTemplateCmd.java @@ -21,8 +21,6 @@ import java.util.Collection; import java.util.List; import java.util.Map; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -38,6 +36,7 @@ import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.exception.ResourceAllocationException; import com.cloud.template.VirtualMachineTemplate; @@ -250,7 +249,7 @@ public class RegisterTemplateCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 7d55974fa0d..bb92d657749 100755 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -384,7 +384,7 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -392,6 +392,16 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { return accountId; } + @Override + public boolean isDisplayResourceEnabled(){ + Boolean display = getDisplayVm(); + if(display == null){ + return true; + } else { + return display; + } + } + @Override public String getEventType() { return EventTypes.EVENT_VM_CREATE; @@ -455,12 +465,62 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { } } + // this is an opportunity to verify that parameters that came in via the Details Map are OK + // for example, minIops and maxIops should either both be specified or neither be specified and, + // if specified, minIops should be <= maxIops + private void verifyDetails() { + Map map = getDetails(); + + if (map != null) { + String minIops = (String)map.get("minIops"); + String maxIops = (String)map.get("maxIops"); + + if ((minIops != null && maxIops == null) || (minIops == null && maxIops != null)) { + throw new InvalidParameterValueException("Either 'Min IOPS' and 'Max IOPS' must both be specified or neither be specified."); + } + + long lMinIops; + + try { + if (minIops != null) { + lMinIops = Long.valueOf(minIops); + } + else { + lMinIops = 0; + } + } + catch (NumberFormatException ex) { + throw new InvalidParameterValueException("'Min IOPS' must be a whole number."); + } + + long lMaxIops; + + try { + if (maxIops != null) { + lMaxIops = Long.valueOf(maxIops); + } + else { + lMaxIops = 0; + } + } + catch (NumberFormatException ex) { + throw new InvalidParameterValueException("'Max IOPS' must be a whole number."); + } + + if (lMinIops > lMaxIops) { + throw new InvalidParameterValueException("'Min IOPS' must be less than or equal to 'Max IOPS'."); + } + } + } + @Override public void create() throws ResourceAllocationException { try { //Verify that all objects exist before passing them to the service Account owner = _accountService.getActiveAccountById(getEntityOwnerId()); + verifyDetails(); + DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); if (zone == null) { throw new InvalidParameterValueException("Unable to find zone by id=" + zoneId); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java index b8ac5612634..4c3a41589c4 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DestroyVMCmd.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.user.vm; import java.util.List; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -27,7 +28,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -43,7 +43,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "destroyVirtualMachine", description = "Destroys a virtual machine. Once destroyed, only the administrator can recover it.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class DestroyVMCmd extends BaseAsyncCmd { +public class DestroyVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(DestroyVMCmd.class.getName()); private static final String s_name = "destroyvirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index d1e5c098672..e5576e74cab 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -68,6 +68,10 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, description = "the ID of the virtual machine") private Long id; + @Parameter(name=ApiConstants.IDS, type=CommandType.LIST, collectionType=CommandType.UUID, entityType=UserVmResponse.class, + description="the IDs of the virtual machines, mutually exclusive with id") + private List ids; + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "name of the virtual machine") private String name; @@ -135,6 +139,10 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd { return id; } + public List getIds() { + return ids; + } + public String getName() { return name; } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java index 8f04430ea7d..0f041148cd6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RebootVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -25,7 +26,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -40,7 +40,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "rebootVirtualMachine", description = "Reboots a virtual machine.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class RebootVMCmd extends BaseAsyncCmd { +public class RebootVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(RebootVMCmd.class.getName()); private static final String s_name = "rebootvirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java index 2fd508b5935..615ef69369e 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -25,7 +26,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -42,7 +42,7 @@ import com.cloud.uservm.UserVm; "The virtual machine must be in a \"Stopped\" state and the template must already " + "support this feature for this command to take effect. [async]", responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class ResetVMPasswordCmd extends BaseAsyncCmd { +public class ResetVMPasswordCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(ResetVMPasswordCmd.class.getName()); private static final String s_name = "resetpasswordforvirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java index 288eed708db..7d438531498 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ResetVMSSHKeyCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -26,7 +27,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -44,7 +44,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "resetSSHKeyForVirtualMachine", responseObject = UserVmResponse.class, description = "Resets the SSH Key for virtual machine. " + "The virtual machine must be in a \"Stopped\" state. [async]", responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class ResetVMSSHKeyCmd extends BaseAsyncCmd { +public class ResetVMSSHKeyCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(ResetVMSSHKeyCmd.class.getName()); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java index 9befc670ff1..0d5ef70bee7 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RestoreVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -24,7 +25,6 @@ import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -43,7 +43,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "restoreVirtualMachine", description = "Restore a VM to original template/ISO or new template/ISO", responseObject = UserVmResponse.class, since = "3.0.0", responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class RestoreVMCmd extends BaseAsyncCmd { +public class RestoreVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(RestoreVMCmd.class); private static final String s_name = "restorevmresponse"; @@ -104,4 +104,10 @@ public class RestoreVMCmd extends BaseAsyncCmd { public Long getTemplateId() { return templateId; } + + // TODO - Remove vmid param and make it "id" in 5.0 so that we dont have two getters + @Override + public Long getId() { + return getVmId(); + } } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java index cd658cb9a43..d668ad4fed6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java @@ -30,7 +30,7 @@ import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; @@ -50,7 +50,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "scaleVirtualMachine", description = "Scales the virtual machine to a new service offering.", responseObject = SuccessResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ScaleVMCmd extends BaseAsyncCmd { +public class ScaleVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(ScaleVMCmd.class.getName()); private static final String s_name = "scalevirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java index 2e4187d9261..4e91c7bbdf4 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/StartVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -25,7 +26,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -46,7 +46,7 @@ import com.cloud.utils.exception.ExecutionException; @APICommand(name = "startVirtualMachine", responseObject = UserVmResponse.class, description = "Starts a virtual machine.", responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class StartVMCmd extends BaseAsyncCmd { +public class StartVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(StartVMCmd.class.getName()); private static final String s_name = "startvirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java index fe94964e81a..6db5d18d301 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/StopVMCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import org.apache.cloudstack.api.BaseAsyncVMCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -25,7 +26,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -39,7 +39,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "stopVirtualMachine", responseObject = UserVmResponse.class, description = "Stops a virtual machine.", responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = true) -public class StopVMCmd extends BaseAsyncCmd { +public class StopVMCmd extends BaseAsyncVMCmd { public static final Logger s_logger = Logger.getLogger(StopVMCmd.class.getName()); private static final String s_name = "stopvirtualmachineresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java index 7282ebacff0..3560b040985 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java @@ -117,6 +117,11 @@ public class UpgradeVMCmd extends BaseCmd { return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked } + @Override + public boolean isDisplayResourceEnabled(){ + return _userVmService.isDisplayResourceEnabled(getId()); + } + @Override public void execute() throws ResourceAllocationException { CallContext.current().setEventDetails("Vm Id: " + getId()); diff --git a/api/src/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java index 30b3ac89818..104cfa36728 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmgroup/CreateVMGroupCmd.java @@ -91,7 +91,7 @@ public class CreateVMGroupCmd extends BaseCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java index 553e2e9339d..f9d9081f364 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/AttachVolumeCmd.java @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. package org.apache.cloudstack.api.command.user.volume; + import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -24,7 +25,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncVolumeCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -38,7 +39,7 @@ import com.cloud.user.Account; @APICommand(name = "attachVolume", description = "Attaches a disk volume to a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.VirtualMachine}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class AttachVolumeCmd extends BaseAsyncCmd { +public class AttachVolumeCmd extends BaseAsyncVolumeCmd { public static final Logger s_logger = Logger.getLogger(AttachVolumeCmd.class.getName()); private static final String s_name = "attachvolumeresponse"; @@ -67,6 +68,7 @@ public class AttachVolumeCmd extends BaseAsyncCmd { return deviceId; } + @Override public Long getId() { return id; } @@ -108,15 +110,6 @@ public class AttachVolumeCmd extends BaseAsyncCmd { return EventTypes.EVENT_VOLUME_ATTACH; } - @Override - public boolean isDisplayResourceEnabled(){ - Volume volume = _responseGenerator.findVolumeById(getId()); - if (volume == null) { - return true; // bad id given, parent this command to true so ERROR events are tracked - } - return volume.isDisplayVolume(); - } - @Override public String getEventDescription() { return "attaching volume: " + getId() + " to vm: " + getVirtualMachineId(); diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java index ad06bc2fc81..9b83f61b0b9 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/CreateVolumeCmd.java @@ -153,10 +153,6 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd { } public Boolean getDisplayVolume() { - if(displayVolume == null){ - return true; - } - return displayVolume; } @@ -183,7 +179,7 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } @@ -198,7 +194,12 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd { @Override public boolean isDisplayResourceEnabled(){ - return getDisplayVolume(); + Boolean display = getDisplayVolume(); + if(display == null){ + return true; + } else { + return display; + } } @Override @@ -221,7 +222,6 @@ public class CreateVolumeCmd extends BaseAsyncCreateCustomIdCmd { @Override public void execute() { CallContext.current().setEventDetails("Volume Id: " + getEntityId() + ((getSnapshotId() == null) ? "" : " from snapshot: " + getSnapshotId())); - CallContext.current().setEventDisplayEnabled(getDisplayVolume()); Volume volume = _volumeService.createVolume(this); if (volume != null) { VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseView.Restricted, volume); diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java index 546a9d549cf..6ca96b08b07 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/DeleteVolumeCmd.java @@ -80,6 +80,11 @@ public class DeleteVolumeCmd extends BaseCmd { return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked } + @Override + public boolean isDisplayResourceEnabled(){ + return _volumeService.isDisplayResourceEnabled(getId()); + } + @Override public void execute() throws ConcurrentOperationException { CallContext.current().setEventDetails("Volume Id: " + getId()); diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java index 1a874e1e8e3..bfd032203c2 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/DetachVolumeCmd.java @@ -15,6 +15,7 @@ // specific language governing permissions and limitations // under the License. package org.apache.cloudstack.api.command.user.volume; + import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -24,7 +25,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncVolumeCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -39,7 +40,7 @@ import com.cloud.uservm.UserVm; @APICommand(name = "detachVolume", description = "Detaches a disk volume from a virtual machine.", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.Volume}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class DetachVolumeCmd extends BaseAsyncCmd { +public class DetachVolumeCmd extends BaseAsyncVolumeCmd { public static final Logger s_logger = Logger.getLogger(DetachVolumeCmd.class.getName()); private static final String s_name = "detachvolumeresponse"; @@ -65,6 +66,7 @@ public class DetachVolumeCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// + @Override public Long getId() { return id; } diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java index b9883cc5aca..cc7f56cc7bf 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/ExtractVolumeCmd.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.api.command.user.volume; +import org.apache.cloudstack.api.BaseAsyncVolumeCmd; import org.apache.log4j.Logger; import org.apache.cloudstack.acl.IAMEntityType; @@ -25,7 +26,6 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ExtractResponse; @@ -41,7 +41,7 @@ import com.cloud.user.Account; @APICommand(name = "extractVolume", description = "Extracts volume", responseObject = ExtractResponse.class, entityType = {IAMEntityType.Volume}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ExtractVolumeCmd extends BaseAsyncCmd { +public class ExtractVolumeCmd extends BaseAsyncVolumeCmd { public static final Logger s_logger = Logger.getLogger(ExtractVolumeCmd.class.getName()); private static final String s_name = "extractvolumeresponse"; diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java index 820d73d058b..d80595a78f3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java @@ -20,7 +20,7 @@ import org.apache.cloudstack.acl.IAMEntityType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncVolumeCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -33,7 +33,7 @@ import com.cloud.user.Account; @APICommand(name = "migrateVolume", description = "Migrate volume", responseObject = VolumeResponse.class, since = "3.0.0", responseView = ResponseView.Restricted, entityType = {IAMEntityType.Volume}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class MigrateVolumeCmd extends BaseAsyncCmd { +public class MigrateVolumeCmd extends BaseAsyncVolumeCmd { private static final String s_name = "migratevolumeresponse"; ///////////////////////////////////////////////////// @@ -60,10 +60,16 @@ public class MigrateVolumeCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// + // TODO remove this in 5.0 and use id as param instead. public Long getVolumeId() { return volumeId; } + @Override + public Long getId() { + return getVolumeId(); + } + public Long getStoragePoolId() { return storageId; } diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java index 2f34c758962..d5cd62bbc3d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java @@ -24,7 +24,7 @@ import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseAsyncVolumeCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; @@ -43,7 +43,7 @@ import com.cloud.user.Account; @APICommand(name = "resizeVolume", description = "Resizes a volume", responseObject = VolumeResponse.class, responseView = ResponseView.Restricted, entityType = {IAMEntityType.Volume}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class ResizeVolumeCmd extends BaseAsyncCmd { +public class ResizeVolumeCmd extends BaseAsyncVolumeCmd { public static final Logger s_logger = Logger.getLogger(ResizeVolumeCmd.class.getName()); private static final String s_name = "resizevolumeresponse"; @@ -73,10 +73,16 @@ public class ResizeVolumeCmd extends BaseAsyncCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// + //TODO use the method getId() instead of this one. public Long getEntityId() { return id; } + @Override + public Long getId() { + return getEntityId(); + } + public Long getSize() { return size; } diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java index d9fa82e2463..05e461e5ec1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/UpdateVolumeCmd.java @@ -128,6 +128,11 @@ public class UpdateVolumeCmd extends BaseAsyncCustomIdCmd { return volume.getAccountId(); } + @Override + public boolean isDisplayResourceEnabled(){ + return _volumeService.isDisplayResourceEnabled(getId()); + } + @Override public String getEventType() { return EventTypes.EVENT_VOLUME_UPDATE; diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java index 949e6df147f..e992759224b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/UploadVolumeCmd.java @@ -151,7 +151,7 @@ public class UploadVolumeCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java index 5cd1f6e163e..c179ec857f0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java @@ -202,7 +202,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java index d057629bb45..e839f9feff0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/AddVpnUserCmd.java @@ -98,7 +98,7 @@ public class AddVpnUserCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java index b8de6e01408..5da46f0ada3 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java @@ -144,7 +144,7 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); if (accountId == null) { accountId = CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java index ed39c9075b5..068746945c2 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/RemoveVpnUserCmd.java @@ -89,7 +89,7 @@ public class RemoveVpnUserCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java index dcd60a40ca9..21eb182d0bd 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/ResetVpnConnectionCmd.java @@ -83,7 +83,7 @@ public class ResetVpnConnectionCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java index 84259641873..9e178efc0e0 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java @@ -147,7 +147,7 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, null, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, null, true); if (accountId == null) { accountId = CallContext.current().getCallingAccount().getId(); } diff --git a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java index ebc22428326..0894eeced3a 100644 --- a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java @@ -73,6 +73,10 @@ public class DiskOfferingResponse extends BaseResponse { @Param(description = "the max iops of the disk offering") private Long maxIops; + @SerializedName(ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE) + @Param(description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + private Integer hypervisorSnapshotReserve; + @SerializedName(ApiConstants.TAGS) @Param(description = "the tags for the disk offering") private String tags; @@ -210,6 +214,14 @@ public class DiskOfferingResponse extends BaseResponse { this.maxIops = maxIops; } + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + + public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { + this.hypervisorSnapshotReserve = hypervisorSnapshotReserve; + } + public String getCacheMode() { return cacheMode; } diff --git a/api/src/org/apache/cloudstack/api/response/GpuResponse.java b/api/src/org/apache/cloudstack/api/response/GpuResponse.java new file mode 100644 index 00000000000..b655749126e --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/GpuResponse.java @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.response; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class GpuResponse extends BaseResponse { + + @SerializedName(ApiConstants.GPUGROUPNAME) + @Param(description = "GPU cards present in the host") + private String gpuGroupName; + + @SerializedName(ApiConstants.VGPU) + @Param(description = "the list of enabled vGPUs", responseObject = VgpuResponse.class) + private List vgpu; + + public void setGpuGroupName(String gpuGroupName) { + this.gpuGroupName = gpuGroupName; + } + + public void setVgpu(List vgpu) { + this.vgpu = vgpu; + } + +} diff --git a/api/src/org/apache/cloudstack/api/response/GuestOsMappingResponse.java b/api/src/org/apache/cloudstack/api/response/GuestOsMappingResponse.java new file mode 100644 index 00000000000..a0f7d1d0edd --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/GuestOsMappingResponse.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +import com.cloud.serializer.Param; +import com.cloud.storage.GuestOSHypervisor; + +@EntityReference(value = GuestOSHypervisor.class) +public class GuestOsMappingResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "the ID of the Guest OS mapping") + private String id; + + @SerializedName(ApiConstants.HYPERVISOR) + @Param(description = "the hypervisor") + private String hypervisor; + + @SerializedName(ApiConstants.HYPERVISOR_VERSION) + @Param(description = "version of the hypervisor for mapping") + private String hypervisorVersion; + + @SerializedName(ApiConstants.OS_TYPE_ID) + @Param(description = "the ID of the Guest OS type") + private String osTypeId; + + @SerializedName(ApiConstants.OS_DISPLAY_NAME) + @Param(description = "standard display name for the Guest OS") + private String osStdName; + + @SerializedName(ApiConstants.OS_NAME_FOR_HYPERVISOR) + @Param(description = "hypervisor specific name for the Guest OS") + private String osNameForHypervisor; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getHypervisor() { + return hypervisor; + } + + public void setHypervisor(String hypervisor) { + this.hypervisor = hypervisor; + } + + public String getHypervisorVersion() { + return hypervisorVersion; + } + + public void setHypervisorVersion(String hypervisorVersion) { + this.hypervisorVersion = hypervisorVersion; + } + + public String getOsTypeId() { + return osTypeId; + } + + public void setOsTypeId(String osTypeId) { + this.osTypeId = osTypeId; + } + + public String getOsStdName() { + return osStdName; + } + + public void setOsStdName(String osStdName) { + this.osStdName = osStdName; + } + + public String getOsNameForHypervisor() { + return osNameForHypervisor; + } + + public void setOsNameForHypervisor(String osNameForHypervisor) { + this.osNameForHypervisor = osNameForHypervisor; + } +} diff --git a/api/src/org/apache/cloudstack/api/response/HostResponse.java b/api/src/org/apache/cloudstack/api/response/HostResponse.java index e2d8eb5c473..1fbc6684e13 100644 --- a/api/src/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/org/apache/cloudstack/api/response/HostResponse.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.response; import java.util.Date; +import java.util.List; import com.google.gson.annotations.SerializedName; @@ -135,6 +136,10 @@ public class HostResponse extends BaseResponse { @Param(description = "the amount of the host's memory currently used") private Long memoryUsed; + @SerializedName(ApiConstants.GPUGROUP) + @Param(description = "GPU cards present in the host", responseObject = GpuResponse.class) + private List gpuGroup; + @SerializedName("disksizetotal") @Param(description = "the total disk size of the host") private Long diskSizeTotal; @@ -320,6 +325,9 @@ public class HostResponse extends BaseResponse { this.memoryUsed = memoryUsed; } + public void setGpuGroups(List gpuGroup) { + this.gpuGroup = gpuGroup; + } public void setDiskSizeTotal(Long diskSizeTotal) { this.diskSizeTotal = diskSizeTotal; } diff --git a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index 526e52b71a5..d371f9a9253 100644 --- a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -106,6 +106,22 @@ public class ServiceOfferingResponse extends BaseResponse { @Param(description = "data transfer rate in megabits per second allowed.") private Integer networkRate; + @SerializedName("iscustomizediops") + @Param(description = "true if disk offering uses custom iops, false otherwise") + private Boolean customizedIops; + + @SerializedName(ApiConstants.MIN_IOPS) + @Param(description = "the min iops of the disk offering") + private Long minIops; + + @SerializedName(ApiConstants.MAX_IOPS) + @Param(description = "the max iops of the disk offering") + private Long maxIops; + + @SerializedName(ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE) + @Param(description = "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + private Integer hypervisorSnapshotReserve; + @SerializedName("diskBytesReadRate") @Param(description = "bytes read rate of the service offering") private Long bytesReadRate; @@ -293,6 +309,38 @@ public class ServiceOfferingResponse extends BaseResponse { this.isVolatile = isVolatile; } + public Boolean isCustomizedIops() { + return customizedIops; + } + + public void setCustomizedIops(Boolean customizedIops) { + this.customizedIops = customizedIops; + } + + public Long getMinIops() { + return minIops; + } + + public void setMinIops(Long minIops) { + this.minIops = minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public void setMaxIops(Long maxIops) { + this.maxIops = maxIops; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + + public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { + this.hypervisorSnapshotReserve = hypervisorSnapshotReserve; + } + public void setBytesReadRate(Long bytesReadRate) { this.bytesReadRate = bytesReadRate; } diff --git a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java index 84d532bbc17..d6ce84f4951 100644 --- a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java @@ -164,6 +164,10 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp @Param(description = "the memory allocated for the virtual machine") private Integer memory; + @SerializedName(ApiConstants.VGPU) + @Param(description = "the vgpu type used by the virtual machine") + private String vgpu; + @SerializedName("cpuused") @Param(description = "the amount of the vm's CPU currently used") private String cpuUsed; @@ -420,6 +424,9 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp return memory; } + public String getVgpu() { + return vgpu; + } public String getCpuUsed() { return cpuUsed; } @@ -643,6 +650,9 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp this.memory = memory; } + public void setVgpu(String vgpu) { + this.vgpu = vgpu; + } public void setCpuUsed(String cpuUsed) { this.cpuUsed = cpuUsed; } diff --git a/api/src/org/apache/cloudstack/api/response/VgpuResponse.java b/api/src/org/apache/cloudstack/api/response/VgpuResponse.java new file mode 100644 index 00000000000..17e194be5b1 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/VgpuResponse.java @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api.response; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.serializer.Param; + +public class VgpuResponse extends BaseResponse { + + @SerializedName(ApiConstants.VGPUTYPE) + @Param(description = "Model Name of vGPU") + private String name; + + @SerializedName(ApiConstants.REMAININGCAPACITY) + @Param(description = "No. of more VMs can be deployped with this vGPU type") + private Long capacity; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getCapacity() { + return capacity; + } + + public void setCapacity(Long capacity) { + this.capacity = capacity; + } + +} diff --git a/api/test/org/apache/cloudstack/api/BaseCmdTest.java b/api/test/org/apache/cloudstack/api/BaseCmdTest.java new file mode 100644 index 00000000000..edf5776d2b3 --- /dev/null +++ b/api/test/org/apache/cloudstack/api/BaseCmdTest.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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.apache.cloudstack.api; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +public class BaseCmdTest { + + private static final String NON_EXPECTED_COMMAND_NAME = "Non expected command name"; + protected static final String CMD1_NAME = "Cmd1Name"; + protected static final String CMD2_NAME = "Cmd2Name"; + protected static final String CMD1_RESPONSE = "cmd1response"; + protected static final String CMD2_RESPONSE = "cmd2response"; + + @Test + public void testGetActualCommandName(){ + BaseCmd cmd1 = new Cmd1(); + BaseCmd cmd2 = new Cmd2(); + + assertEquals(NON_EXPECTED_COMMAND_NAME, CMD1_NAME, cmd1.getActualCommandName()); + assertEquals(NON_EXPECTED_COMMAND_NAME, CMD2_NAME, cmd2.getActualCommandName()); + } +} + +@APICommand(name=BaseCmdTest.CMD1_NAME, responseObject=BaseResponse.class) +class Cmd1 extends BaseCmd { + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + } + @Override + public String getCommandName() { + return BaseCmdTest.CMD1_RESPONSE; + } + @Override + public long getEntityOwnerId() { + return 0; + } +} + +@APICommand(name=BaseCmdTest.CMD2_NAME, responseObject=BaseResponse.class) +class Cmd2 extends Cmd1 { + @Override + public String getCommandName() { + return BaseCmdTest.CMD2_RESPONSE; + } +} diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 82ac499a85e..c46361a12e7 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -1291,6 +1291,7 @@ label.add.gslb=Add GSLB label.gslb.servicetype=Service Type label.gslb.details=GSLB details label.gslb.delete=delete GSLB +label.opendaylight.controller=OpenDaylight Controller label.opendaylight.controllers=OpenDaylight Controllers label.portable.ip.ranges=Portable IP Ranges label.add.portable.ip.range=Add Portable IP Range @@ -1319,6 +1320,167 @@ label.add.ucs.manager=Add UCS Manager label.ovm.traffic.label=OVM traffic label label.lxc.traffic.label=LXC Traffic Label label.hyperv.traffic.label=HyperV Traffic Label +label.resource.name=Resource Name +label.reource.id=Resource ID +label.vnmc.devices=VNMC Devices +label.add.vnmc.provider=Add VNMC provider +label.enable.vnmc.provider=Enable VNMC provider +label.add.vnmc.device=Add VNMC device +label.ciscovnmc.resource.details=CiscoVNMC resource details +label.delete.ciscovnmc.resource=delete CiscoVNMC resource +label.enable.vnmc.device=Enable VNMC device +label.disbale.vnmc.device=Disable VNMC device +label.disable.vnmc.provider=Disable VNMC provider +label.services=Services +label.secondary.staging.store=Secondary Staging Store +label.release.account=Release from Account +label.release.account.lowercase=release from account +label.vlan.vni.ranges=VLAN/VNI Range(s) +label.dedicated.vlan.vni.ranges=Dedicated VLAN/VNI Ranges +label.dedicate.vlan.vni.range=Dedicate VLAN/VNI Range +label.vlan.vni.range=VLAN/VNI Range +label.vlan.range.details=VLAN Range details +label.release.dedicated.vlan.range=Release dedicated VLAN range +label.broadcat.uri=broadcast URI +label.ipv4.cidr=IPv4 CIDR +label.guest.network.details=Guest network details +label.ipv4.gateway=IPv4 Gateway +label.release.dedicated.vlan.range=Release dedicated VLAN range +label.vlan.ranges=VLAN Range(s) +label.virtual.appliance.details=Virtual applicance details +label.start.lb.vm=Start LB VM +label.stop.lb.vm=Stop LB VM +label.migrate.lb.vm=Migrate LB VM +label.vpc.virtual.router=VPC Virtual Router +label.ovs=OVS +label.gslb.service=GSLB service +label.gslb.service.public.ip=GSLB service Public IP +label.gslb.service.private.ip=GSLB service Private IP +label.baremetal.dhcp.provider=Baremetal DHCP Provider +label.add.baremetal.dhcp.device=Add Baremetal DHCP Device +label.baremetal.pxe.provider=Baremetal PXE Provider +label.baremetal.pxe.device=Add Baremetal PXE Device +label.tftp.root.directory=Tftp root directory +label.add.vmware.datacenter=Add VMware datacenter +label.remove.vmware.datacenter=Remove VMware datacenter +label.dc.name=DC Name +label.vcenter=vcenter +label.dedicate.zone=Dedicate Zone +label.zone.dedicated=Zone Dedicated +label.release.dedicated.zone=Release Dedicated Zone +label.ipv6.dns1=IPv6 DNS1 +label.ipv6.dns2=IPv6 DNS2 +label.vmware.datacenter.name=VMware datacenter Name +label.vmware.datacenter.vcenter=VMware datacenter vcenter +label.vmware.datacenter.id=VMware datacenter ID +label.system.vm.details=System VM details +label.system.vm.scaled.up=System VM Scaled Up +label.console.proxy.vm=Console Proxy VM +label.settings=Settings +label.requires.upgrade=Requires Upgrade +label.upgrade.router.newer.template=Upgrade Router to Use Newer Template +label.router.vm.scaled.up=Router VM Scaled Up +label.total.virtual.routers=Total of Virtual Routers +label.upgrade.required=Upgrade is required +label.virtual.routers.group.zone=Virtual Routers group by zone +label.total.virtual.routers.upgrade=Total of Virtual Routers that require upgrade +label.virtual.routers.group.pod=Virtual Routers group by pod +label.virtual.routers.group.cluster=Virtual Routers group by cluster +label.zone.lower=zone +label.virtual.routers.group.account=Virtual Routers group by account +label.netscaler.details=NetScaler details +label.baremetal.dhcp.devices=Baremetal DHCP Devices +label.baremetal.pxe.devices=Baremetal PXE Devices +label.addes.new.f5=Added new F5 +label.f5.details=F5 details +label.srx.details=SRX details +label.palo.alto.details=Palo Alto details +label.added.nicira.nvp.controller=Added new Nicira NVP Controller\ +label.nicira.nvp.details=Nicira NVP details +label.added.new.bigswitch.vns.controller=Added new BigSwitch VNS Controller +label.bigswitch.vns.details=BigSwitch VNS details +label.dedicate=Dedicate +label.dedicate.pod=Dedicate Pod +label.pod.dedicated=Pod Dedicated +label.release.dedicated.pod=Release Dedicated Pod +label.override.public.traffic=Override Public-Traffic +label.public.traffic.vswitch.type=Public Traffic vSwitch Type +label.public.traffic.vswitch.name=Public Traffic vSwitch Name +label.override.guest.traffic=Override Guest-Traffic +label.guest.traffic.vswitch.type=Guest Traffic vSwitch Type +label.guest.traffic.vswitch.name=Guest Traffic vSwitch Name +label.cisco.nexus1000v.ip.address=Nexus 1000v IP Address +label.cisco.nexus1000v.username=Nexus 1000v Username +label.cisco.nexus1000v.password=Nexus 1000v Password +label.dedicate.cluster=Dedicate Cluster +label.release.dedicated.cluster=Release Dedicated Cluster +label.dedicate.host=Dedicate Host +label.release.dedicated.host=Release Dedicated Host +label.number.of.cpu.sockets=The Number of CPU Sockets +label.delete.ucs.manager=Delete UCS Manager +label.blades=Blades +label.chassis=Chassis +label.blade.id=Blade ID +label.associated.profile=Associated Profile +label.refresh.blades=Refresh Blades +label.instanciate.template.associate.profile.blade=Instanciate Template and Associate Profile to Blade +label.select.template=Select Template +label.profile=Profile +label.delete.profile=Delete Profile +label.disassociate.profile.blade=Disassociate Profile from Blade +label.secondary.storage.details=Secondary storage details +label.secondary.staging.store.details=Secondary Staging Store details +label.add.nfs.secondary.staging.store=Add NFS Secondary Staging Store +label.delete.secondary.staging.store=Delete Secondary Staging Store +label.ipv4.start.ip=IPv4 Start IP +label.ipv4.end.ip=IPv4 End IP +label.ipv6.start.ip=IPv6 Start IP +label.ipv6.end.ip=IPv6 End IP +label.vm.password=Password of the VM is +label.group.by.zone=group by zone +label.group.by.pod=group by pod +label.group.by.cluster=group by cluster +label.group.by.account=group by account +label.no.grouping=(no grouping) +label.create.nfs.secondary.staging.storage=Create NFS Secondary Staging Store +label.username.lower=username +label.password.lower=password +label.email.lower=email +label.firstname.lower=firstname +label.lastname.lower=lastname +label.domain.lower=domain +label.account.lower=account +label.type.lower=type +label.rule.number=Rule Number +label.action=Action +label.name.lower=name +label.ucs=UCS +label.change.affinity=Change Affinity +label.persistent=Persistent +label.broadcasturi=broadcasturi +label.network.cidr=Network CIDR +label.reserved.ip.range=Reserved IP Range +label.autoscale=AutoScale +label.health.check=Health Check +label.public.load.balancer.provider=Public Load Balancer Provider +label.add.isolated.network=Add Isolated Network +label.vlan=VLAN +label.secondary.isolated.vlan.id=Secondary Isolated VLAN ID +label.ipv4.netmask=IPv4 Netmask +label.custom=Custom +label.disable.network.offering=Disable network offering +label.enable.network.offering=Enable network offering +label.remove.network.offering=Remove network offering +label.system.offering.for.router=System Offering for Router +label.mode=Mode +label.associate.public.ip=Associate Public IP +label.acl=ACL +label.user.data=User Data +label.virtual.networking=Virtual Networking +label.allow=Allow +label.deny=Deny +label.default.egress.policy=Default egress policy +label.xenserver.tools.version.61.plus=XenServer Tools Version 6.1\+ managed.state=Managed State message.acquire.new.ip.vpc=Please confirm that you would like to acquire a new IP for this VPC. message.acquire.new.ip=Please confirm that you would like to acquire a new IP for this network. @@ -1669,6 +1831,53 @@ message.tier.required=Tier is required message.remove.ldap=Are you sure you want to delete the LDAP configuration? message.action.downloading.template=Downloading template. message.configure.ldap=Please confirm you would like to configure LDAP. +message.confirm.delete.ciscovnmc.resource=Please confirm you want to delete CiscoVNMC resource +message.confirm.add.vnmc.provider=Please confirm you would like to add the VNMC provider. +message.confirm.enable.vnmc.provider=Please confirm you would like to enable the VNMC provider. +message.confirm.disable.vnmc.provider=Please confirm you would like to disable the VNMC provider. +message.vnmc.available.list=VNMC is not available from provider list. +message.vnmc.not.available.list=VNMC is not available from provider list. +message.confirm.release.dedicate.vlan.range=Please confirm you want to release dedicated VLAN range +message.confirm.start.lb.vm=Please confirm you want to start LB VM +message.confirm.stop.lb.vm=Please confirm you want to stop LB VM +message.confirm.remove.vmware.datacenter=Please confirm you want to remove VMware datacenter +message.confirm.dedicate.zone=Do you really want to dedicate this zone to a domain/account? +message.confirm.release.dedicated.zone=Do you want to release this dedicated zone ? +message.dedicated.zone.released=Zone dedication released +message.read.admin.guide.scaling.up=Please read the dynamic scaling section in the admin guide before scaling up. +message.confirm.scale.up.system.vm=Do you really want to scale up the system VM ? +message.confirm.upgrade.router.newer.template=Please confirm that you want to upgrade router to use newer template +message.confirm.scale.up.router.vm=Do you really want to scale up the Router VM ? +message.confirm.upgrade.routers.newtemplate=Please confirm that you want to upgrade all routers in this zone to use newer template +message.confirm.upgrade.routers.pod.newtemplate=Please confirm that you want to upgrade all routers in this pod to use newer template +message.confirm.upgrade.routers.cluster.newtemplate=Please confirm that you want to upgrade all routers in this cluster to use newer template +message.confirm.upgrade.routers.account.newtemplate=Please confirm that you want to upgrade all routers in this account to use newer template +message.confirm.dedicate.pod.domain.account=Do you really want to dedicate this pod to a domain/account? +message.confirm.release.dedicated.pod=Do you want to release this dedicated pod ? +message.pod.dedication.released=Pod dedication released +message.confirm.dedicate.cluster.domain.account=Do you really want to dedicate this cluster to a domain/account? +message.cluster.dedicated=Cluster Dedicated +message.confirm.release.dedicated.cluster=Do you want to release this dedicated cluster ? +message.cluster.dedication.released=Cluster dedication released +message.confirm.dedicate.host.domain.account=Do you really want to dedicate this host to a domain/account? +message.host.dedicated=Host Dedicated +message.confirm.release.dedicated.host=Do you want to release this dedicated host ? +message.host.dedication.released=Host dedication released +message.confirm.delete.ucs.manager=Please confirm that you want to delete UCS Manager +message.confirm.refresh.blades=Please confirm that you want to refresh blades. +message.confirm.delete.secondary.staging.store=Please confirm you want to delete Secondary Staging Store. +message.select.tier=Please select a tier +message.disallowed.characters=Disallowed characters: \<\,\> +message.waiting.for.builtin.templates.to.load=Waiting for builtin templates to load... +message.systems.vms.ready=System VMs ready. +message.your.cloudstack.is.ready=Your CloudStack is ready\! +message.specifiy.tag.key.value=Please specify a tag key and value +message.enter.seperated.list.multiple.cidrs=Please enter a comma separated list of CIDRs if more than one +message.disabling.network.offering=Disabling network offering +message.confirm.enable.network.offering=Are you sure you want to enable this network offering? +message.enabling.network.offering=Enabling network offering +message.confirm.remove.network.offering=Are you sure you want to remove this network offering? +message.confirm.disable.network.offering=Are you sure you want to disable this network offering? mode=Mode network.rate=Network Rate notification.reboot.instance=Reboot instance diff --git a/client/WEB-INF/classes/resources/messages_ja_JP.properties b/client/WEB-INF/classes/resources/messages_ja_JP.properties index 828cf545c20..d3d50f0e889 100644 --- a/client/WEB-INF/classes/resources/messages_ja_JP.properties +++ b/client/WEB-INF/classes/resources/messages_ja_JP.properties @@ -29,7 +29,7 @@ label.smb.password=SMB \u30d1\u30b9\u30ef\u30fc\u30c9 label.smb.domain=SMB \u30c9\u30e1\u30a4\u30f3 label.hypervisors=\u30cf\u30a4\u30d1\u30fc\u30d0\u30a4\u30b6\u30fc label.home=\u30db\u30fc\u30e0 -label.sockets=CPU Sockets +label.sockets=CPU \u30bd\u30b1\u30c3\u30c8 label.root.disk.size=\u30eb\u30fc\u30c8 \u30c7\u30a3\u30b9\u30af \u30b5\u30a4\u30ba label.s3.nfs.server=S3 NFS \u30b5\u30fc\u30d0\u30fc label.s3.nfs.path=S3 NFS \u30d1\u30b9 @@ -63,7 +63,7 @@ message.action.delete.nic=\u3053\u306e NIC \u3092\u524a\u9664\u3057\u3066\u3082\ changed.item.properties=\u9805\u76ee\u306e\u30d7\u30ed\u30d1\u30c6\u30a3\u306e\u5909\u66f4 confirm.enable.s3=S3 \u30d9\u30fc\u30b9\u306e\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u306e\u30b5\u30dd\u30fc\u30c8\u3092\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u3001\u6b21\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 confirm.enable.swift=Swift \u306e\u30b5\u30dd\u30fc\u30c8\u3092\u6709\u52b9\u306b\u3059\u308b\u306b\u306f\u3001\u6b21\u306e\u60c5\u5831\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -error.could.not.change.your.password.because.ldap.is.enabled=Error could not change your password because LDAP is enabled. +error.could.not.change.your.password.because.ldap.is.enabled=\u30a8\u30e9\u30fc\u3002LDAP \u304c\u6709\u52b9\u306a\u305f\u3081\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u5909\u66f4\u3067\u304d\u307e\u305b\u3093\u3002 error.could.not.enable.zone=\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f error.installWizard.message=\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u623b\u3063\u3066\u30a8\u30e9\u30fc\u3092\u4fee\u6b63\u3067\u304d\u307e\u3059\u3002 error.invalid.username.password=\u7121\u52b9\u306a\u30e6\u30fc\u30b6\u30fc\u540d\u307e\u305f\u306f\u30d1\u30b9\u30ef\u30fc\u30c9\u3067\u3059\u3002 @@ -384,8 +384,8 @@ label.api.key=API \u30ad\u30fc label.apply=\u9069\u7528 label.assign.to.load.balancer=\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u306b\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u3092\u5272\u308a\u5f53\u3066\u3066\u3044\u307e\u3059 label.assign=\u5272\u308a\u5f53\u3066 -label.associated.network.id=\u95a2\u9023\u3065\u3051\u3089\u308c\u305f\u30cd\u30c3\u30c8\u30ef\u30fc\u30af ID -label.associated.network=\u95a2\u9023\u3065\u3051\u3089\u308c\u305f\u30cd\u30c3\u30c8\u30ef\u30fc\u30af +label.associated.network.id=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f\u30cd\u30c3\u30c8\u30ef\u30fc\u30af ID +label.associated.network=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f\u30cd\u30c3\u30c8\u30ef\u30fc\u30af label.attached.iso=\u30a2\u30bf\u30c3\u30c1\u3055\u308c\u305f ISO label.author.email=\u4f5c\u6210\u8005\u306e\u96fb\u5b50\u30e1\u30fc\u30eb label.author.name=\u4f5c\u6210\u8005\u306e\u540d\u524d @@ -601,6 +601,7 @@ label.full=\u5b8c\u5168 label.gateway=\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4 label.general.alerts=\u4e00\u822c\u30a2\u30e9\u30fc\u30c8 label.generating.url=URL \u3092\u751f\u6210\u3057\u3066\u3044\u307e\u3059 +label.gluster.volume=\u30dc\u30ea\u30e5\u30fc\u30e0 label.go.step.2=\u624b\u9806 2 \u306b\u9032\u3080 label.go.step.3=\u624b\u9806 3 \u306b\u9032\u3080 label.go.step.4=\u624b\u9806 4 \u306b\u9032\u3080 @@ -704,7 +705,7 @@ label.lang.arabic=\u30a2\u30e9\u30d3\u30a2\u8a9e label.lang.brportugese=\u30dd\u30eb\u30c8\u30ac\u30eb\u8a9e (\u30d6\u30e9\u30b8\u30eb) label.lang.catalan=\u30ab\u30bf\u30eb\u30cb\u30a2\u8a9e label.lang.chinese=\u7c21\u4f53\u5b57\u4e2d\u56fd\u8a9e -label.lang.dutch=Dutch (Netherlands) +label.lang.dutch=\u30aa\u30e9\u30f3\u30c0\u8a9e (\u30aa\u30e9\u30f3\u30c0) label.lang.english=\u82f1\u8a9e label.lang.french=\u30d5\u30e9\u30f3\u30b9\u8a9e label.lang.german=\u30c9\u30a4\u30c4\u8a9e @@ -712,7 +713,7 @@ label.lang.italian=\u30a4\u30bf\u30ea\u30a2\u8a9e label.lang.japanese=\u65e5\u672c\u8a9e label.lang.korean=\u97d3\u56fd\u8a9e label.lang.norwegian=\u30ce\u30eb\u30a6\u30a7\u30fc\u8a9e -label.lang.polish=Polish +label.lang.polish=\u30dd\u30fc\u30e9\u30f3\u30c9\u8a9e label.lang.russian=\u30ed\u30b7\u30a2\u8a9e label.lang.spanish=\u30b9\u30da\u30a4\u30f3\u8a9e label.last.disconnected=\u6700\u7d42\u5207\u65ad\u65e5\u6642 @@ -1067,7 +1068,7 @@ label.source=\u9001\u4fe1\u5143 label.specify.IP.ranges=IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u306e\u6307\u5b9a label.specify.vlan=VLAN \u3092\u6307\u5b9a\u3059\u308b label.specify.vxlan=VXLAN \u3092\u6307\u5b9a\u3059\u308b -label.SR.name = SR Name-Label +label.SR.name=SR \u540d\u30e9\u30d9\u30eb label.srx=SRX label.PA=Palo Alto label.start.IP=\u958b\u59cb IP \u30a2\u30c9\u30ec\u30b9 @@ -1111,7 +1112,7 @@ label.storage.tags=\u30b9\u30c8\u30ec\u30fc\u30b8 \u30bf\u30b0 label.storage.traffic=\u30b9\u30c8\u30ec\u30fc\u30b8 \u30c8\u30e9\u30d5\u30a3\u30c3\u30af label.storage.type=\u30b9\u30c8\u30ec\u30fc\u30b8\u306e\u7a2e\u985e label.qos.type=QoS \u306e\u7a2e\u985e -label.cache.mode=Write-cache Type +label.cache.mode=\u66f8\u304d\u8fbc\u307f\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u7a2e\u985e label.storage=\u30b9\u30c8\u30ec\u30fc\u30b8 label.subdomain.access=\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3 \u30a2\u30af\u30bb\u30b9 label.submit=\u9001\u4fe1 @@ -1144,7 +1145,7 @@ label.tier.details=\u968e\u5c64\u306e\u8a73\u7d30 label.tier=\u968e\u5c64 label.time.zone=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 label.time=\u6642\u523b -label.timeout.in.second = Timeout(seconds) +label.timeout.in.second=\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8 (\u79d2) label.timeout=\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8 label.timezone=\u30bf\u30a4\u30e0\u30be\u30fc\u30f3 label.token=\u30c8\u30fc\u30af\u30f3 @@ -1166,8 +1167,8 @@ label.unavailable=\u4f7f\u7528\u4e0d\u80fd label.unlimited=\u7121\u5236\u9650 label.untagged=\u30bf\u30b0\u306a\u3057 label.update.project.resources=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8 \u30ea\u30bd\u30fc\u30b9\u306e\u66f4\u65b0 -label.update.ssl.cert= SSL Certificate -label.update.ssl= SSL Certificate +label.update.ssl.cert=SSL \u8a3c\u660e\u66f8 +label.update.ssl=SSL \u8a3c\u660e\u66f8 label.updating=\u66f4\u65b0\u3057\u3066\u3044\u307e\u3059 label.upload.volume=\u30dc\u30ea\u30e5\u30fc\u30e0\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9 label.upload=\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9 @@ -1289,11 +1290,12 @@ label.add.gslb=GSLB \u306e\u8ffd\u52a0 label.gslb.servicetype=\u30b5\u30fc\u30d3\u30b9\u306e\u7a2e\u985e label.gslb.details=GSLB \u306e\u8a73\u7d30 label.gslb.delete=GSLB \u306e\u524a\u9664 -label.opendaylight.controllers=OpenDaylight Controllers +label.opendaylight.controller=OpenDaylight \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc +label.opendaylight.controllers=OpenDaylight \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc label.portable.ip.ranges=\u30dd\u30fc\u30bf\u30d6\u30eb IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2 label.add.portable.ip.range=\u30dd\u30fc\u30bf\u30d6\u30eb IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u306e\u8ffd\u52a0 label.delete.portable.ip.range=\u30dd\u30fc\u30bf\u30d6\u30eb IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u306e\u524a\u9664 -label.opendaylight.controllerdetail=OpenDaylight Controller Details +label.opendaylight.controllerdetail=OpenDaylight \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u8a73\u7d30 label.portable.ip.range.details=\u30dd\u30fc\u30bf\u30d6\u30eb IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2\u306e\u8a73\u7d30 label.portable.ips=\u30dd\u30fc\u30bf\u30d6\u30eb IP \u30a2\u30c9\u30ec\u30b9 label.gslb.assigned.lb=\u5272\u308a\u5f53\u3066\u6e08\u307f\u8ca0\u8377\u5206\u6563 @@ -1305,9 +1307,9 @@ label.enable.autoscale=\u81ea\u52d5\u30b5\u30a4\u30ba\u8a2d\u5b9a\u306e\u6709\u5 label.disable.autoscale=\u81ea\u52d5\u30b5\u30a4\u30ba\u8a2d\u5b9a\u306e\u7121\u52b9\u5316 label.min.instances=\u6700\u5c0f\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u6570 label.max.instances=\u6700\u5927\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u6570 -label.add.OpenDaylight.device=Add OpenDaylight Controller +label.add.OpenDaylight.device=OpenDaylight \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u8ffd\u52a0 label.show.advanced.settings=\u8a73\u7d30\u8a2d\u5b9a\u306e\u8868\u793a -label.delete.OpenDaylight.device=Delete OpenDaylight Controller +label.delete.OpenDaylight.device=OpenDaylight \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u306e\u524a\u9664 label.polling.interval.sec=\u30dd\u30fc\u30ea\u30f3\u30b0\u9593\u9694 (\u79d2) label.quiet.time.sec=\u5f85\u3061\u6642\u9593 (\u79d2) label.destroy.vm.graceperiod=VM \u7834\u68c4\u306e\u7336\u4e88\u671f\u9593 @@ -1317,6 +1319,167 @@ label.add.ucs.manager=UCS Manager \u306e\u8ffd\u52a0 label.ovm.traffic.label=OVM \u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af \u30e9\u30d9\u30eb label.lxc.traffic.label=LXC \u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af \u30e9\u30d9\u30eb label.hyperv.traffic.label=Hyper-V \u306e\u30c8\u30e9\u30d5\u30a3\u30c3\u30af \u30e9\u30d9\u30eb +label.resource.name=\u30ea\u30bd\u30fc\u30b9\u540d +label.reource.id=\u30ea\u30bd\u30fc\u30b9 ID +label.vnmc.devices=VNMC \u30c7\u30d0\u30a4\u30b9 +label.add.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u8ffd\u52a0 +label.enable.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u6709\u52b9\u5316 +label.add.vnmc.device=VNMC \u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0 +label.ciscovnmc.resource.details=Cisco VNMC \u30ea\u30bd\u30fc\u30b9\u306e\u8a73\u7d30 +label.delete.ciscovnmc.resource=Cisco VNMC \u30ea\u30bd\u30fc\u30b9\u306e\u524a\u9664 +label.enable.vnmc.device=VNMC \u30c7\u30d0\u30a4\u30b9\u306e\u6709\u52b9\u5316 +label.disbale.vnmc.device=VNMC \u30c7\u30d0\u30a4\u30b9\u306e\u7121\u52b9\u5316 +label.disable.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u306e\u7121\u52b9\u5316 +label.services=\u30b5\u30fc\u30d3\u30b9 +label.secondary.staging.store=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2 +label.release.account=\u30a2\u30ab\u30a6\u30f3\u30c8\u304b\u3089\u89e3\u653e +label.release.account.lowercase=\u30a2\u30ab\u30a6\u30f3\u30c8\u304b\u3089\u89e3\u653e +label.vlan.vni.ranges=VLAN/VNI \u306e\u7bc4\u56f2 +label.dedicated.vlan.vni.ranges=\u5c02\u7528 VLAN/VNI \u306e\u7bc4\u56f2 +label.dedicate.vlan.vni.range=VLAN/VNI \u306e\u7bc4\u56f2\u3092\u5c02\u7528\u306b\u8a2d\u5b9a +label.vlan.vni.range=VLAN/VNI \u306e\u7bc4\u56f2 +label.vlan.range.details=VLAN \u306e\u7bc4\u56f2\u306e\u8a73\u7d30 +label.release.dedicated.vlan.range=\u5c02\u7528 VLAN \u306e\u7bc4\u56f2\u306e\u89e3\u653e +label.broadcat.uri=\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8 URI +label.ipv4.cidr=IPv4 CIDR +label.guest.network.details=\u30b2\u30b9\u30c8 \u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u8a73\u7d30 +label.ipv4.gateway=IPv4 \u30b2\u30fc\u30c8\u30a6\u30a7\u30a4 +label.release.dedicated.vlan.range=\u5c02\u7528 VLAN \u306e\u7bc4\u56f2\u3092\u89e3\u653e +label.vlan.ranges=VLAN \u306e\u7bc4\u56f2 +label.virtual.appliance.details=\u4eee\u60f3\u30a2\u30d7\u30e9\u30a4\u30a2\u30f3\u30b9\u306e\u8a73\u7d30 +label.start.lb.vm=LB VM \u306e\u8d77\u52d5 +label.stop.lb.vm=LB VM \u306e\u505c\u6b62 +label.migrate.lb.vm=LB VM \u306e\u79fb\u884c +label.vpc.virtual.router=VPC \u4eee\u60f3\u30eb\u30fc\u30bf\u30fc +label.ovs=OVS +label.gslb.service=GSLB \u30b5\u30fc\u30d3\u30b9 +label.gslb.service.public.ip=GSLB \u30b5\u30fc\u30d3\u30b9\u306e\u30d1\u30d6\u30ea\u30c3\u30af IP \u30a2\u30c9\u30ec\u30b9 +label.gslb.service.private.ip=GSLB \u30b5\u30fc\u30d3\u30b9\u306e\u30d7\u30e9\u30a4\u30d9\u30fc\u30c8 IP \u30a2\u30c9\u30ec\u30b9 +label.baremetal.dhcp.provider=\u30d9\u30a2\u30e1\u30bf\u30eb DHCP \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc +label.add.baremetal.dhcp.device=\u30d9\u30a2\u30e1\u30bf\u30eb DHCP \u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0 +label.baremetal.pxe.provider=\u30d9\u30a2\u30e1\u30bf\u30eb PXE \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc +label.baremetal.pxe.device=\u30d9\u30a2\u30e1\u30bf\u30eb PXE \u30c7\u30d0\u30a4\u30b9\u306e\u8ffd\u52a0 +label.tftp.root.directory=TFTP \u30eb\u30fc\u30c8 \u30c7\u30a3\u30ec\u30af\u30c8\u30ea +label.add.vmware.datacenter=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306e\u8ffd\u52a0 +label.remove.vmware.datacenter=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306e\u524a\u9664 +label.dc.name=DC \u540d +label.vcenter=vCenter +label.dedicate.zone=\u30be\u30fc\u30f3\u3092\u5c02\u7528\u306b\u8a2d\u5b9a +label.zone.dedicated=\u5c02\u7528\u30be\u30fc\u30f3 +label.release.dedicated.zone=\u5c02\u7528\u30be\u30fc\u30f3\u306e\u89e3\u653e +label.ipv6.dns1=IPv6 DNS 1 +label.ipv6.dns2=IPv6 DNS 2 +label.vmware.datacenter.name=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u540d +label.vmware.datacenter.vcenter=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u306e vCenter +label.vmware.datacenter.id=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc ID +label.system.vm.details=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u8a73\u7d30 +label.system.vm.scaled.up=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u30b5\u30a4\u30ba\u304c\u62e1\u5927\u3055\u308c\u307e\u3057\u305f +label.console.proxy.vm=\u30b3\u30f3\u30bd\u30fc\u30eb \u30d7\u30ed\u30ad\u30b7 VM +label.settings=\u8a2d\u5b9a +label.requires.upgrade=\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u304c\u5fc5\u8981 +label.upgrade.router.newer.template=\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3059\u308b +label.router.vm.scaled.up=\u30eb\u30fc\u30bf\u30fc VM \u306e\u30b5\u30a4\u30ba\u304c\u62e1\u5927\u3055\u308c\u307e\u3057\u305f +label.total.virtual.routers=\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc\u5408\u8a08 +label.upgrade.required=\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u304c\u5fc5\u8981\u3067\u3059 +label.virtual.routers.group.zone=\u30be\u30fc\u30f3\u5225\u306e\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc \u30b0\u30eb\u30fc\u30d7 +label.total.virtual.routers.upgrade=\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u304c\u5fc5\u8981\u306a\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc\u5408\u8a08 +label.virtual.routers.group.pod=\u30dd\u30c3\u30c9\u5225\u306e\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc \u30b0\u30eb\u30fc\u30d7 +label.virtual.routers.group.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u5225\u306e\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc \u30b0\u30eb\u30fc\u30d7 +label.zone.lower=\u30be\u30fc\u30f3 +label.virtual.routers.group.account=\u30a2\u30ab\u30a6\u30f3\u30c8\u5225\u306e\u4eee\u60f3\u30eb\u30fc\u30bf\u30fc \u30b0\u30eb\u30fc\u30d7 +label.netscaler.details=NetScaler \u306e\u8a73\u7d30 +label.baremetal.dhcp.devices=\u30d9\u30a2\u30e1\u30bf\u30eb DHCP \u30c7\u30d0\u30a4\u30b9 +label.baremetal.pxe.devices=\u30d9\u30a2\u30e1\u30bf\u30eb PXE \u30c7\u30d0\u30a4\u30b9 +label.addes.new.f5=\u65b0\u3057\u3044 F5 \u3092\u8ffd\u52a0\u3057\u307e\u3057\u305f +label.f5.details=F5 \u306e\u8a73\u7d30 +label.srx.details=SRX \u306e\u8a73\u7d30 +label.palo.alto.details=Palo Alto \u306e\u8a73\u7d30 +label.added.nicira.nvp.controller=\u65b0\u3057\u3044 Nicira NVP Controller \u3092\u8ffd\u52a0\u3057\u307e\u3057\u305f\ +label.nicira.nvp.details=Nicira NVP \u306e\u8a73\u7d30 +label.added.new.bigswitch.vns.controller=\u65b0\u3057\u3044 Big Switch VNS \u30b3\u30f3\u30c8\u30ed\u30fc\u30e9\u30fc\u3092\u8ffd\u52a0\u3057\u307e\u3057\u305f +label.bigswitch.vns.details=Big Switch VNS \u306e\u8a73\u7d30 +label.dedicate=\u5c02\u7528\u306b\u8a2d\u5b9a +label.dedicate.pod=\u30dd\u30c3\u30c9\u3092\u5c02\u7528\u306b\u8a2d\u5b9a +label.pod.dedicated=\u30dd\u30c3\u30c9\u3092\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u307e\u3057\u305f +label.release.dedicated.pod=\u5c02\u7528\u30dd\u30c3\u30c9\u306e\u89e3\u653e +label.override.public.traffic=\u30d1\u30d6\u30ea\u30c3\u30af \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u3092\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u3059\u308b +label.public.traffic.vswitch.type=\u30d1\u30d6\u30ea\u30c3\u30af \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e vSwitch \u306e\u7a2e\u985e +label.public.traffic.vswitch.name=\u30d1\u30d6\u30ea\u30c3\u30af \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e vSwitch \u540d +label.override.guest.traffic=\u30b2\u30b9\u30c8 \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u3092\u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u3059\u308b +label.guest.traffic.vswitch.type=\u30b2\u30b9\u30c8 \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e vSwitch \u306e\u7a2e\u985e +label.guest.traffic.vswitch.name=\u30b2\u30b9\u30c8 \u30c8\u30e9\u30d5\u30a3\u30c3\u30af\u306e vSwitch \u540d +label.cisco.nexus1000v.ip.address=Nexus 1000V \u306e IP \u30a2\u30c9\u30ec\u30b9 +label.cisco.nexus1000v.username=Nexus 1000V \u306e\u30e6\u30fc\u30b6\u30fc\u540d +label.cisco.nexus1000v.password=Nexus 1000V \u306e\u30d1\u30b9\u30ef\u30fc\u30c9 +label.dedicate.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u5c02\u7528\u306b\u8a2d\u5b9a +label.release.dedicated.cluster=\u5c02\u7528\u30af\u30e9\u30b9\u30bf\u30fc\u306e\u89e3\u653e +label.dedicate.host=\u30db\u30b9\u30c8\u3092\u5c02\u7528\u306b\u8a2d\u5b9a +label.release.dedicated.host=\u5c02\u7528\u30db\u30b9\u30c8\u306e\u89e3\u653e +label.number.of.cpu.sockets=CPU \u30bd\u30b1\u30c3\u30c8\u6570 +label.delete.ucs.manager=UCS Manager \u306e\u524a\u9664 +label.blades=\u30d6\u30ec\u30fc\u30c9 +label.chassis=\u30b7\u30e3\u30fc\u30b7 +label.blade.id=\u30d6\u30ec\u30fc\u30c9 ID +label.associated.profile=\u95a2\u9023\u4ed8\u3051\u3089\u308c\u305f\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb +label.refresh.blades=\u30d6\u30ec\u30fc\u30c9\u306e\u66f4\u65b0 +label.instanciate.template.associate.profile.blade=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u4f5c\u6210\u304a\u3088\u3073\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3068\u30d6\u30ec\u30fc\u30c9\u306e\u95a2\u9023\u4ed8\u3051 +label.select.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u9078\u629e +label.profile=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb +label.delete.profile=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u306e\u524a\u9664 +label.disassociate.profile.blade=\u30d7\u30ed\u30d5\u30a1\u30a4\u30eb\u3068\u30d6\u30ec\u30fc\u30c9\u306e\u95a2\u9023\u4ed8\u3051\u306e\u89e3\u9664 +label.secondary.storage.details=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u306e\u8a73\u7d30 +label.secondary.staging.store.details=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2\u306e\u8a73\u7d30 +label.add.nfs.secondary.staging.store=NFS \u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2\u306e\u8ffd\u52a0 +label.delete.secondary.staging.store=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2\u306e\u524a\u9664 +label.ipv4.start.ip=IPv4 \u958b\u59cb IP \u30a2\u30c9\u30ec\u30b9 +label.ipv4.end.ip=IPv4 \u7d42\u4e86 IP \u30a2\u30c9\u30ec\u30b9 +label.ipv6.start.ip=IPv6 \u958b\u59cb IP \u30a2\u30c9\u30ec\u30b9 +label.ipv6.end.ip=IPv6 \u7d42\u4e86 IP \u30a2\u30c9\u30ec\u30b9 +label.vm.password=VM \u306e\u30d1\u30b9\u30ef\u30fc\u30c9: +label.group.by.zone=\u30be\u30fc\u30f3\u5225\u30b0\u30eb\u30fc\u30d7 +label.group.by.pod=\u30dd\u30c3\u30c9\u5225\u30b0\u30eb\u30fc\u30d7 +label.group.by.cluster=\u30af\u30e9\u30b9\u30bf\u30fc\u5225\u30b0\u30eb\u30fc\u30d7 +label.group.by.account=\u30a2\u30ab\u30a6\u30f3\u30c8\u5225\u30b0\u30eb\u30fc\u30d7 +label.no.grouping=(\u30b0\u30eb\u30fc\u30d7\u306a\u3057) +label.create.nfs.secondary.staging.storage=NFS \u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2\u3092\u4f5c\u6210\u3059\u308b +label.username.lower=\u30e6\u30fc\u30b6\u30fc\u540d +label.password.lower=\u30d1\u30b9\u30ef\u30fc\u30c9 +label.email.lower=\u96fb\u5b50\u30e1\u30fc\u30eb +label.firstname.lower=\u540d +label.lastname.lower=\u59d3 +label.domain.lower=\u30c9\u30e1\u30a4\u30f3 +label.account.lower=\u30a2\u30ab\u30a6\u30f3\u30c8 +label.type.lower=\u7a2e\u985e +label.rule.number=\u898f\u5247\u756a\u53f7 +label.action=\u64cd\u4f5c +label.name.lower=\u540d\u524d +label.ucs=UCS +label.change.affinity=\u30a2\u30d5\u30a3\u30cb\u30c6\u30a3\u306e\u5909\u66f4 +label.persistent=\u6c38\u7d9a +label.broadcasturi=\u30d6\u30ed\u30fc\u30c9\u30ad\u30e3\u30b9\u30c8 URI +label.network.cidr=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af CIDR +label.reserved.ip.range=\u4e88\u7d04\u6e08\u307f IP \u30a2\u30c9\u30ec\u30b9\u306e\u7bc4\u56f2 +label.autoscale=\u81ea\u52d5\u30b5\u30a4\u30ba\u8a2d\u5b9a +label.health.check=\u30d8\u30eb\u30b9 \u30c1\u30a7\u30c3\u30af +label.public.load.balancer.provider=\u30d1\u30d6\u30ea\u30c3\u30af\u8ca0\u8377\u5206\u6563\u88c5\u7f6e\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc +label.add.isolated.network=\u5206\u96e2\u3055\u308c\u305f\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u8ffd\u52a0 +label.vlan=VLAN +label.secondary.isolated.vlan.id=\u5206\u96e2\u3055\u308c\u305f\u30bb\u30ab\u30f3\u30c0\u30ea VLAN ID +label.ipv4.netmask=IPv4 \u30cd\u30c3\u30c8\u30de\u30b9\u30af +label.custom=\u30ab\u30b9\u30bf\u30e0 +label.disable.network.offering=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306e\u7121\u52b9\u5316 +label.enable.network.offering=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306e\u6709\u52b9\u5316 +label.remove.network.offering=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u306e\u524a\u9664 +label.system.offering.for.router=\u30eb\u30fc\u30bf\u30fc\u7528\u30b7\u30b9\u30c6\u30e0 \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0 +label.mode=\u30e2\u30fc\u30c9 +label.associate.public.ip=\u30d1\u30d6\u30ea\u30c3\u30af IP \u30a2\u30c9\u30ec\u30b9\u306e\u95a2\u9023\u4ed8\u3051 +label.acl=ACL +label.user.data=\u30e6\u30fc\u30b6\u30fc \u30c7\u30fc\u30bf +label.virtual.networking=\u4eee\u60f3\u30cd\u30c3\u30c8\u30ef\u30fc\u30af +label.allow=\u8a31\u53ef +label.deny=\u62d2\u5426 +label.default.egress.policy=\u30c7\u30d5\u30a9\u30eb\u30c8\u306e\u9001\u4fe1\u30dd\u30ea\u30b7\u30fc +label.xenserver.tools.version.61.plus=XenServer Tools Version 6.1 \u4ee5\u964d managed.state=\u7ba1\u7406\u5bfe\u8c61\u72b6\u614b message.acquire.new.ip.vpc=\u3053\u306e VPC \u306e\u65b0\u3057\u3044 IP \u30a2\u30c9\u30ec\u30b9\u3092\u53d6\u5f97\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.acquire.new.ip=\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u306e\u65b0\u3057\u3044 IP \u30a2\u30c9\u30ec\u30b9\u3092\u53d6\u5f97\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1460,7 +1623,7 @@ message.creating.primary.storage=\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30 message.creating.secondary.storage=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 message.creating.zone=\u30be\u30fc\u30f3\u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 message.decline.invitation=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u62db\u5f85\u3092\u8f9e\u9000\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? -message.dedicate.zone=\u30be\u30fc\u30f3\u3092\u5c02\u7528\u5316\u3057\u3066\u3044\u307e\u3059 +message.dedicate.zone=\u30be\u30fc\u30f3\u3092\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u3066\u3044\u307e\u3059 message.delete.account=\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.delete.affinity.group=\u3053\u306e\u30a2\u30d5\u30a3\u30cb\u30c6\u30a3 \u30b0\u30eb\u30fc\u30d7\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.delete.gateway=\u3053\u306e\u30b2\u30fc\u30c8\u30a6\u30a7\u30a4\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1632,21 +1795,21 @@ message.zone.step.3.desc=\u65b0\u3057\u3044\u30dd\u30c3\u30c9\u3092\u8ffd\u52a0\ message.zoneWizard.enable.local.storage=\u8b66\u544a\: \u3053\u306e\u30be\u30fc\u30f3\u306e\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u6709\u52b9\u306b\u3059\u308b\u5834\u5408\u306f\u3001\u30b7\u30b9\u30c6\u30e0 VM \u306e\u8d77\u52d5\u5834\u6240\u306b\u5fdc\u3058\u3066\u6b21\u306e\u64cd\u4f5c\u304c\u5fc5\u8981\u3067\u3059\u3002

1. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u30d7\u30e9\u30a4\u30de\u30ea \u30b9\u30c8\u30ec\u30fc\u30b8\u3092\u4f5c\u6210\u3057\u305f\u5f8c\u3067\u30be\u30fc\u30f3\u306b\u8ffd\u52a0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002\u30be\u30fc\u30f3\u3092\u7121\u52b9\u72b6\u614b\u304b\u3089\u958b\u59cb\u3059\u308b\u5fc5\u8981\u3082\u3042\u308a\u307e\u3059\u3002

2. \u30b7\u30b9\u30c6\u30e0 VM \u3092\u30ed\u30fc\u30ab\u30eb \u30b9\u30c8\u30ec\u30fc\u30b8\u3067\u8d77\u52d5\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u5834\u5408\u306f\u3001\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3059\u308b\u524d\u306b system.vm.use.local.storage \u3092 true \u306b\u8a2d\u5b9a\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002


\u7d9a\u884c\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.validate.fieldrequired=\u3053\u308c\u306f\u5fc5\u9808\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3067\u3059\u3002 message.validate.fixfield=\u3053\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u4fee\u6b63\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -message.validate.email.address=Please enter a valid email address. -message.validate.URL=Please enter a valid URL. -message.validate.date=Please enter a valid date. -message.validate.date.ISO=Please enter a valid date (ISO). -message.validate.number=Please enter a valid number. -message.validate.digits=Please enter only digits. -message.validate.creditcard=Please enter a valid credit card number. -message.validate.equalto=Please enter the same value again. -message.validate.accept=Please enter a value with a valid extension. -message.validate.maxlength=Please enter no more than {0} characters. -message.validate.minlength=Please enter at least {0} characters. -message.validate.range.length=Please enter a value between {0} and {1} characters long. -message.validate.range=Please enter a value between {0} and {1}. -message.validate.max=Please enter a value less than or equal to {0}. -messgae.validate.min=Please enter a value greater than or equal to {0}. +message.validate.email.address=\u30e1\u30fc\u30eb \u30a2\u30c9\u30ec\u30b9\u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.URL=URL \u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.date=\u65e5\u4ed8\u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.date.ISO=\u65e5\u4ed8\u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 (ISO)\u3002 +message.validate.number=\u6570\u5024\u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.digits=\u6570\u5b57\u306e\u307f\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.creditcard=\u30af\u30ec\u30b8\u30c3\u30c8 \u30ab\u30fc\u30c9\u756a\u53f7\u3092\u6b63\u3057\u304f\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.equalto=\u540c\u3058\u5024\u3092\u518d\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.accept=\u6709\u52b9\u306a\u62e1\u5f35\u5b50\u3092\u6301\u3064\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.maxlength={0} \u6587\u5b57\u4ee5\u4e0b\u3067\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.minlength={0} \u6587\u5b57\u4ee5\u4e0a\u3067\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.range.length={0} \uff5e {1} \u6587\u5b57\u3067\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.range={0} \uff5e {1} \u306e\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.validate.max={0} \u4ee5\u4e0b\u306e\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +messgae.validate.min={0} \u4ee5\u4e0a\u306e\u5024\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 message.creating.systemVM=\u30b7\u30b9\u30c6\u30e0 VM \u3092\u4f5c\u6210\u3057\u3066\u3044\u307e\u3059 (\u3057\u3070\u3089\u304f\u304a\u5f85\u3061\u304f\u3060\u3055\u3044) message.enabling.zone.dots=\u30be\u30fc\u30f3\u3092\u6709\u52b9\u306b\u3057\u3066\u3044\u307e\u3059... message.restoreVM=VM \u3092\u5fa9\u5143\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? @@ -1667,6 +1830,53 @@ message.tier.required=\u968e\u5c64\u306f\u5fc5\u9808\u3067\u3059 message.remove.ldap=LDAP \u69cb\u6210\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? message.action.downloading.template=\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u3044\u307e\u3059\u3002 message.configure.ldap=LDAP \u3092\u69cb\u6210\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.delete.ciscovnmc.resource=Cisco VNMC \u30ea\u30bd\u30fc\u30b9\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.add.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u8ffd\u52a0\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.enable.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.disable.vnmc.provider=VNMC \u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.vnmc.available.list=\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u4e00\u89a7\u3067 VNMC \u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +message.vnmc.not.available.list=\u30d7\u30ed\u30d0\u30a4\u30c0\u30fc\u4e00\u89a7\u3067 VNMC \u3092\u5229\u7528\u3067\u304d\u307e\u305b\u3093\u3002 +message.confirm.release.dedicate.vlan.range=\u5c02\u7528 VLAN \u306e\u7bc4\u56f2\u3092\u89e3\u653e\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.start.lb.vm=LB VM \u3092\u8d77\u52d5\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.stop.lb.vm=LB VM \u3092\u505c\u6b62\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.remove.vmware.datacenter=VMware \u30c7\u30fc\u30bf\u30bb\u30f3\u30bf\u30fc\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.dedicate.zone=\u3053\u306e\u30be\u30fc\u30f3\u3092\u30c9\u30e1\u30a4\u30f3/\u30a2\u30ab\u30a6\u30f3\u30c8\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.release.dedicated.zone=\u3053\u306e\u5c02\u7528\u30be\u30fc\u30f3\u3092\u89e3\u653e\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.dedicated.zone.released=\u5c02\u7528\u30be\u30fc\u30f3\u304c\u89e3\u653e\u3055\u308c\u307e\u3057\u305f +message.read.admin.guide.scaling.up=\u30b5\u30a4\u30ba\u3092\u62e1\u5927\u3059\u308b\u524d\u306b\u7ba1\u7406\u8005\u30ac\u30a4\u30c9\u306e\u52d5\u7684\u306a\u30b5\u30a4\u30ba\u5909\u66f4\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3092\u304a\u8aad\u307f\u304f\u3060\u3055\u3044\u3002 +message.confirm.scale.up.system.vm=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u30b5\u30a4\u30ba\u3092\u62e1\u5927\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.upgrade.router.newer.template=\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.scale.up.router.vm=\u30eb\u30fc\u30bf\u30fc VM \u306e\u30b5\u30a4\u30ba\u3092\u62e1\u5927\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.upgrade.routers.newtemplate=\u3053\u306e\u30be\u30fc\u30f3\u306e\u3059\u3079\u3066\u306e\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.upgrade.routers.pod.newtemplate=\u3053\u306e\u30dd\u30c3\u30c9\u306e\u3059\u3079\u3066\u306e\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.upgrade.routers.cluster.newtemplate=\u3053\u306e\u30af\u30e9\u30b9\u30bf\u30fc\u306e\u3059\u3079\u3066\u306e\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.upgrade.routers.account.newtemplate=\u3053\u306e\u30a2\u30ab\u30a6\u30f3\u30c8\u306e\u3059\u3079\u3066\u306e\u30eb\u30fc\u30bf\u30fc\u3092\u30a2\u30c3\u30d7\u30b0\u30ec\u30fc\u30c9\u3057\u3066\u65b0\u3057\u3044\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u3092\u4f7f\u7528\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.dedicate.pod.domain.account=\u3053\u306e\u30dd\u30c3\u30c9\u3092\u30c9\u30e1\u30a4\u30f3/\u30a2\u30ab\u30a6\u30f3\u30c8\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.release.dedicated.pod=\u3053\u306e\u5c02\u7528\u30dd\u30c3\u30c9\u3092\u89e3\u653e\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.pod.dedication.released=\u5c02\u7528\u30dd\u30c3\u30c9\u304c\u89e3\u653e\u3055\u308c\u307e\u3057\u305f +message.confirm.dedicate.cluster.domain.account=\u3053\u306e\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u30c9\u30e1\u30a4\u30f3/\u30a2\u30ab\u30a6\u30f3\u30c8\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.cluster.dedicated=\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u307e\u3057\u305f +message.confirm.release.dedicated.cluster=\u3053\u306e\u5c02\u7528\u30af\u30e9\u30b9\u30bf\u30fc\u3092\u89e3\u653e\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.cluster.dedication.released=\u5c02\u7528\u30af\u30e9\u30b9\u30bf\u30fc\u304c\u89e3\u653e\u3055\u308c\u307e\u3057\u305f +message.confirm.dedicate.host.domain.account=\u3053\u306e\u30db\u30b9\u30c8\u3092\u30c9\u30e1\u30a4\u30f3/\u30a2\u30ab\u30a6\u30f3\u30c8\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.host.dedicated=\u30db\u30b9\u30c8\u3092\u5c02\u7528\u306b\u8a2d\u5b9a\u3057\u307e\u3057\u305f +message.confirm.release.dedicated.host=\u3053\u306e\u5c02\u7528\u30db\u30b9\u30c8\u3092\u89e3\u653e\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.host.dedication.released=\u5c02\u7528\u30db\u30b9\u30c8\u304c\u89e3\u653e\u3055\u308c\u307e\u3057\u305f +message.confirm.delete.ucs.manager=UCS Manager \u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.refresh.blades=\u30d6\u30ec\u30fc\u30c9\u3092\u66f4\u65b0\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.delete.secondary.staging.store=\u30bb\u30ab\u30f3\u30c0\u30ea \u30b9\u30c6\u30fc\u30b8\u30f3\u30b0 \u30b9\u30c8\u30a2\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.select.tier=\u968e\u5c64\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +message.disallowed.characters=\u8a31\u53ef\u3055\u308c\u306a\u3044\u6587\u5b57: \<\,\> +message.waiting.for.builtin.templates.to.load=\u7d44\u307f\u8fbc\u307f\u30c6\u30f3\u30d7\u30ec\u30fc\u30c8\u306e\u30ed\u30fc\u30c9\u3092\u5f85\u6a5f\u3057\u3066\u3044\u307e\u3059... +message.systems.vms.ready=\u30b7\u30b9\u30c6\u30e0 VM \u306e\u6e96\u5099\u304c\u3067\u304d\u307e\u3057\u305f\u3002 +message.your.cloudstack.is.ready=CloudStack \u306e\u6e96\u5099\u304c\u3067\u304d\u307e\u3057\u305f\! +message.specifiy.tag.key.value=\u30bf\u30b0 \u30ad\u30fc\u304a\u3088\u3073\u5024\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044 +message.enter.seperated.list.multiple.cidrs=CIDR \u304c\u8907\u6570\u3042\u308b\u5834\u5408\u306f\u3001\u30b3\u30f3\u30de\u533a\u5207\u308a\u306e\u4e00\u89a7\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044 +message.disabling.network.offering=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u7121\u52b9\u306b\u3057\u3066\u3044\u307e\u3059 +message.confirm.enable.network.offering=\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u6709\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.enabling.network.offering=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u6709\u52b9\u306b\u3057\u3066\u3044\u307e\u3059 +message.confirm.remove.network.offering=\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u524a\u9664\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? +message.confirm.disable.network.offering=\u3053\u306e\u30cd\u30c3\u30c8\u30ef\u30fc\u30af \u30aa\u30d5\u30a1\u30ea\u30f3\u30b0\u3092\u7121\u52b9\u306b\u3057\u3066\u3082\u3088\u308d\u3057\u3044\u3067\u3059\u304b? mode=\u30e2\u30fc\u30c9 network.rate=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u901f\u5ea6 notification.reboot.instance=\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u518d\u8d77\u52d5 diff --git a/client/WEB-INF/classes/resources/messages_zh_CN.properties b/client/WEB-INF/classes/resources/messages_zh_CN.properties index f4c206b04f6..ea7bfd4c6ef 100644 --- a/client/WEB-INF/classes/resources/messages_zh_CN.properties +++ b/client/WEB-INF/classes/resources/messages_zh_CN.properties @@ -19,7 +19,7 @@ label.remove.ldap=\u5220\u9664 LDAP label.configure.ldap=\u914d\u7f6e LDAP label.ldap.configuration=LDAP \u914d\u7f6e label.ldap.port=LDAP \u7aef\u53e3 -label.create.nfs.secondary.staging.store=\u521b\u5efa NFS \u8f85\u52a9\u6682\u5b58\u5b58\u50a8 +label.create.nfs.secondary.staging.store=\u521b\u5efa NFS \u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8 label.volatile=\u53ef\u53d8 label.planner.mode=\u89c4\u5212\u5668\u6a21\u5f0f label.deployment.planner=\u90e8\u7f72\u89c4\u5212\u5668 @@ -29,7 +29,7 @@ label.smb.password=SMB \u5bc6\u7801 label.smb.domain=SMB \u57df label.hypervisors=\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f label.home=\u9996\u9875 -label.sockets=CPU Sockets +label.sockets=CPU \u63d2\u69fd label.root.disk.size=\u6839\u78c1\u76d8\u5927\u5c0f label.s3.nfs.server=S3 NFS \u670d\u52a1\u5668 label.s3.nfs.path=S3 NFS \u8def\u5f84 @@ -53,18 +53,18 @@ label.disk.iops.min=\u6700\u5c0f IOPS label.disk.iops.max=\u6700\u5927 IOPS label.disk.iops.total=\u603b IOPS label.hypervisor.snapshot.reserve=\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u5feb\u7167\u9884\u7559 -label.view.secondary.ips=\u67e5\u770b\u8f85\u52a9 IP +label.view.secondary.ips=\u67e5\u770b\u4e8c\u7ea7 IP message.validate.invalid.characters=\u67e5\u627e\u5230\u65e0\u6548\u5b57\u7b26\uff0c\u8bf7\u66f4\u6b63\u3002 -message.acquire.ip.nic=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u83b7\u53d6\u6b64 NIC \u7684\u65b0\u8f85\u52a9 IP\u3002
\u6ce8\u610f: \u60a8\u9700\u8981\u5728\u865a\u62df\u673a\u5185\u90e8\u624b\u52a8\u914d\u7f6e\u65b0\u83b7\u53d6\u7684\u8f85\u52a9 IP\u3002 +message.acquire.ip.nic=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u83b7\u53d6\u6b64 NIC \u7684\u65b0\u4e8c\u7ea7 IP\u3002
\u6ce8\u610f: \u60a8\u9700\u8981\u5728\u865a\u62df\u673a\u5185\u90e8\u624b\u52a8\u914d\u7f6e\u65b0\u83b7\u53d6\u7684\u4e8c\u7ea7 IP\u3002 message.select.affinity.groups=\u8bf7\u9009\u62e9\u60a8\u5e0c\u671b\u6b64 VM \u6240\u5c5e\u7684\u4efb\u4f55\u5173\u8054\u6027\u7ec4: message.no.affinity.groups=\u60a8\u6ca1\u6709\u4efb\u4f55\u5173\u8054\u6027\u7ec4\u3002\u8bf7\u7ee7\u7eed\u6267\u884c\u4e0b\u4e00\u6b65\u64cd\u4f5c\u3002 label.action.delete.nic=\u79fb\u9664 NIC message.action.delete.nic=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u79fb\u9664\u6b64 NIC\uff0c\u6b64\u64cd\u4f5c\u8fd8\u5c06\u4ece VM \u4e2d\u79fb\u9664\u5173\u8054\u7684\u7f51\u7edc\u3002 changed.item.properties=\u66f4\u6539\u9879\u76ee\u5c5e\u6027 -confirm.enable.s3=\u8bf7\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u542f\u7528\u5bf9 S3 \u652f\u6301\u7684\u8f85\u52a9\u5b58\u50a8\u7684\u652f\u6301 +confirm.enable.s3=\u8bf7\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u542f\u7528\u5bf9 S3 \u652f\u6301\u7684\u4e8c\u7ea7\u5b58\u50a8\u7684\u652f\u6301 confirm.enable.swift=\u8bf7\u586b\u5199\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u542f\u7528\u5bf9 SWIFT \u7684\u652f\u6301 -error.could.not.change.your.password.because.ldap.is.enabled=Error could not change your password because LDAP is enabled. -error.could.not.enable.zone=\u65e0\u6cd5\u542f\u7528\u533a\u57df +error.could.not.change.your.password.because.ldap.is.enabled=\u9519\u8bef\u3002LDAP \u5904\u4e8e\u542f\u7528\u72b6\u6001\uff0c\u65e0\u6cd5\u66f4\u6539\u60a8\u7684\u5bc6\u7801\u3002 +error.could.not.enable.zone=\u65e0\u6cd5\u542f\u7528\u8d44\u6e90\u57df error.installWizard.message=\u51fa\u73b0\u95ee\u9898\uff1b\u8bf7\u8fd4\u56de\u5e76\u66f4\u6b63\u4efb\u4f55\u9519\u8bef error.invalid.username.password=\u7528\u6237\u540d\u6216\u5bc6\u7801\u65e0\u6548 error.login=\u60a8\u7684\u7528\u6237\u540d/\u5bc6\u7801\u4e0e\u6211\u4eec\u7684\u8bb0\u5f55\u4e0d\u4e00\u81f4\u3002 @@ -96,7 +96,7 @@ label.account.specific=\u5e10\u6237\u4e13\u7528 label.account=\u5e10\u6237 label.accounts=\u5e10\u6237 label.acquire.new.ip=\u83b7\u53d6\u65b0 IP -label.acquire.new.secondary.ip=\u83b7\u53d6\u65b0\u8f85\u52a9 IP +label.acquire.new.secondary.ip=\u83b7\u53d6\u65b0\u4e8c\u7ea7 IP label.action.attach.disk.processing=\u6b63\u5728\u9644\u52a0\u78c1\u76d8... label.action.attach.disk=\u9644\u52a0\u78c1\u76d8 label.action.attach.iso.processing=\u6b63\u5728\u9644\u52a0 ISO... @@ -144,8 +144,8 @@ label.action.delete.pod.processing=\u6b63\u5728\u5220\u9664\u63d0\u4f9b\u70b9... label.action.delete.pod=\u5220\u9664\u63d0\u4f9b\u70b9 label.action.delete.primary.storage.processing=\u6b63\u5728\u5220\u9664\u4e3b\u5b58\u50a8... label.action.delete.primary.storage=\u5220\u9664\u4e3b\u5b58\u50a8 -label.action.delete.secondary.storage.processing=\u6b63\u5728\u5220\u9664\u8f85\u52a9\u5b58\u50a8... -label.action.delete.secondary.storage=\u5220\u9664\u8f85\u52a9\u5b58\u50a8 +label.action.delete.secondary.storage.processing=\u6b63\u5728\u5220\u9664\u4e8c\u7ea7\u5b58\u50a8... +label.action.delete.secondary.storage=\u5220\u9664\u4e8c\u7ea7\u5b58\u50a8 label.action.delete.security.group.processing=\u6b63\u5728\u5220\u9664\u5b89\u5168\u7ec4... label.action.delete.security.group=\u5220\u9664\u5b89\u5168\u7ec4 label.action.delete.service.offering.processing=\u6b63\u5728\u5220\u9664\u670d\u52a1\u65b9\u6848... @@ -159,8 +159,8 @@ label.action.delete.user.processing=\u6b63\u5728\u5220\u9664\u7528\u6237... label.action.delete.user=\u5220\u9664\u7528\u6237 label.action.delete.volume.processing=\u6b63\u5728\u5220\u9664\u5377... label.action.delete.volume=\u5220\u9664\u5377 -label.action.delete.zone.processing=\u6b63\u5728\u5220\u9664\u533a\u57df... -label.action.delete.zone=\u5220\u9664\u533a\u57df +label.action.delete.zone.processing=\u6b63\u5728\u5220\u9664\u8d44\u6e90\u57df... +label.action.delete.zone=\u5220\u9664\u8d44\u6e90\u57df label.action.destroy.instance.processing=\u6b63\u5728\u9500\u6bc1\u5b9e\u4f8b... label.action.destroy.instance=\u9500\u6bc1\u5b9e\u4f8b label.action.destroy.systemvm.processing=\u6b63\u5728\u9500\u6bc1\u7cfb\u7edf VM... @@ -181,8 +181,8 @@ label.action.disable.static.NAT.processing=\u6b63\u5728\u7981\u7528\u9759\u6001 label.action.disable.static.NAT=\u7981\u7528\u9759\u6001 NAT label.action.disable.user.processing=\u6b63\u5728\u7981\u7528\u7528\u6237... label.action.disable.user=\u7981\u7528\u7528\u6237 -label.action.disable.zone.processing=\u6b63\u5728\u7981\u7528\u533a\u57df... -label.action.disable.zone=\u7981\u7528\u533a\u57df +label.action.disable.zone.processing=\u6b63\u5728\u7981\u7528\u8d44\u6e90\u57df... +label.action.disable.zone=\u7981\u7528\u8d44\u6e90\u57df label.action.download.ISO=\u4e0b\u8f7d ISO label.action.download.template=\u4e0b\u8f7d\u6a21\u677f label.action.download.volume.processing=\u6b63\u5728\u4e0b\u8f7d\u5377... @@ -203,7 +203,7 @@ label.action.edit.resource.limits=\u7f16\u8f91\u8d44\u6e90\u9650\u5236 label.action.edit.service.offering=\u7f16\u8f91\u670d\u52a1\u65b9\u6848 label.action.edit.template=\u7f16\u8f91\u6a21\u677f label.action.edit.user=\u7f16\u8f91\u7528\u6237 -label.action.edit.zone=\u7f16\u8f91\u533a\u57df +label.action.edit.zone=\u7f16\u8f91\u8d44\u6e90\u57df label.action.enable.account.processing=\u6b63\u5728\u542f\u7528\u5e10\u6237... label.action.enable.account=\u542f\u7528\u5e10\u6237 label.action.enable.cluster.processing=\u6b63\u5728\u542f\u7528\u7fa4\u96c6... @@ -218,8 +218,8 @@ label.action.enable.static.NAT.processing=\u6b63\u5728\u542f\u7528\u9759\u6001 N label.action.enable.static.NAT=\u542f\u7528\u9759\u6001 NAT label.action.enable.user.processing=\u6b63\u5728\u542f\u7528\u7528\u6237... label.action.enable.user=\u542f\u7528\u7528\u6237 -label.action.enable.zone.processing=\u6b63\u5728\u542f\u7528\u533a\u57df... -label.action.enable.zone=\u542f\u7528\u533a\u57df +label.action.enable.zone.processing=\u6b63\u5728\u542f\u7528\u8d44\u6e90\u57df... +label.action.enable.zone=\u542f\u7528\u8d44\u6e90\u57df label.action.expunge.instance=\u5220\u9664\u5b9e\u4f8b label.action.expunge.instance.processing=\u6b63\u5728\u5220\u9664\u5b9e\u4f8b... label.action.force.reconnect.processing=\u6b63\u5728\u91cd\u65b0\u8fde\u63a5... @@ -325,11 +325,11 @@ label.add.physical.network=\u6dfb\u52a0\u7269\u7406\u7f51\u7edc label.add.pod=\u6dfb\u52a0\u63d0\u4f9b\u70b9 label.add.port.forwarding.rule=\u6dfb\u52a0\u7aef\u53e3\u8f6c\u53d1\u89c4\u5219 label.add.primary.storage=\u6dfb\u52a0\u4e3b\u5b58\u50a8 -label.add.region=\u6dfb\u52a0\u533a\u57df +label.add.region=\u6dfb\u52a0\u5730\u7406\u533a\u57df label.add.resources=\u6dfb\u52a0\u8d44\u6e90 label.add.route=\u6dfb\u52a0\u8def\u7531 label.add.rule=\u6dfb\u52a0\u89c4\u5219 -label.add.secondary.storage=\u6dfb\u52a0\u8f85\u52a9\u5b58\u50a8 +label.add.secondary.storage=\u6dfb\u52a0\u4e8c\u7ea7\u5b58\u50a8 label.add.security.group=\u6dfb\u52a0\u5b89\u5168\u7ec4 label.add.service.offering=\u6dfb\u52a0\u670d\u52a1\u65b9\u6848 label.add.SRX.device=\u6dfb\u52a0 SRX \u8bbe\u5907 @@ -351,7 +351,7 @@ label.add.vpc=\u6dfb\u52a0 VPC label.add.vpn.customer.gateway=\u6dfb\u52a0 VPN \u5ba2\u6237\u7f51\u5173 label.add.VPN.gateway=\u6dfb\u52a0 VPN \u7f51\u5173 label.add.vpn.user=\u6dfb\u52a0 VPN \u7528\u6237 -label.add.zone=\u6dfb\u52a0\u533a\u57df +label.add.zone=\u6dfb\u52a0\u8d44\u6e90\u57df label.add=\u6dfb\u52a0 label.adding.cluster=\u6b63\u5728\u6dfb\u52a0\u7fa4\u96c6 label.adding.failed=\u6dfb\u52a0\u5931\u8d25 @@ -359,7 +359,7 @@ label.adding.pod=\u6b63\u5728\u6dfb\u52a0\u63d0\u4f9b\u70b9 label.adding.processing=\u6b63\u5728\u6dfb\u52a0... label.adding.succeeded=\u5df2\u6210\u529f\u6dfb\u52a0 label.adding.user=\u6b63\u5728\u6dfb\u52a0\u7528\u6237 -label.adding.zone=\u6b63\u5728\u6dfb\u52a0\u533a\u57df +label.adding.zone=\u6b63\u5728\u6dfb\u52a0\u8d44\u6e90\u57df label.adding=\u6b63\u5728\u6dfb\u52a0 label.additional.networks=\u5176\u4ed6\u7f51\u7edc label.admin.accounts=\u7ba1\u7406\u5458\u5e10\u6237 @@ -389,7 +389,7 @@ label.associated.network=\u5173\u8054\u7f51\u7edc label.attached.iso=\u5df2\u9644\u52a0 ISO label.author.email=\u4f5c\u8005\u7535\u5b50\u90ae\u4ef6 label.author.name=\u4f5c\u8005\u59d3\u540d -label.availability.zone=\u53ef\u7528\u533a\u57df +label.availability.zone=\u53ef\u7528\u8d44\u6e90\u57df label.availability=\u53ef\u7528\u6027 label.available.public.ips=\u53ef\u7528\u516c\u7528 IP \u5730\u5740 label.available=\u53ef\u7528 @@ -411,10 +411,10 @@ label.by.pod=\u6309\u63d0\u4f9b\u70b9 label.by.role=\u6309\u89d2\u8272 label.by.start.date=\u6309\u5f00\u59cb\u65e5\u671f label.by.state=\u6309\u72b6\u6001 -label.by.traffic.type=\u6309\u901a\u4fe1\u7c7b\u578b +label.by.traffic.type=\u6309\u6d41\u91cf\u7c7b\u578b label.by.type.id=\u6309\u7c7b\u578b ID label.by.type=\u6309\u7c7b\u578b -label.by.zone=\u6309\u533a\u57df +label.by.zone=\u6309\u8d44\u6e90\u57df label.bytes.received=\u63a5\u6536\u7684\u5b57\u8282\u6570 label.bytes.sent=\u53d1\u9001\u7684\u5b57\u8282\u6570 label.cancel=\u53d6\u6d88 @@ -474,7 +474,7 @@ label.create.template=\u521b\u5efa\u6a21\u677f label.create.VPN.connection=\u521b\u5efa VPN \u8fde\u63a5 label.created.by.system=\u7531\u7cfb\u7edf\u521b\u5efa label.created=\u521b\u5efa\u65e5\u671f -label.cross.zones=\u8de8\u533a\u57df +label.cross.zones=\u8de8\u8d44\u6e90\u57df label.custom.disk.size=\u81ea\u5b9a\u4e49\u78c1\u76d8\u5927\u5c0f label.daily=\u6bcf\u5929 label.data.disk.offering=\u6570\u636e\u78c1\u76d8\u65b9\u6848 @@ -505,7 +505,7 @@ label.deleting.failed=\u5220\u9664\u5931\u8d25 label.deleting.processing=\u6b63\u5728\u5220\u9664... label.description=\u8bf4\u660e label.destination.physical.network.id=\u76ee\u6807\u7269\u7406\u7f51\u7edc ID -label.destination.zone=\u76ee\u6807\u533a\u57df +label.destination.zone=\u76ee\u6807\u8d44\u6e90\u57df label.destroy.router=\u9500\u6bc1\u8def\u7531\u5668 label.destroy=\u9500\u6bc1 label.detaching.disk=\u6b63\u5728\u53d6\u6d88\u9644\u52a0\u78c1\u76d8 @@ -554,7 +554,7 @@ label.edit.lb.rule=\u7f16\u8f91\u8d1f\u8f7d\u5e73\u8861\u5668\u89c4\u5219 label.edit.network.details=\u7f16\u8f91\u7f51\u7edc\u8be6\u60c5 label.edit.project.details=\u7f16\u8f91\u9879\u76ee\u8be6\u60c5 label.edit.tags=\u7f16\u8f91\u6807\u7b7e -label.edit.traffic.type=\u7f16\u8f91\u901a\u4fe1\u7c7b\u578b +label.edit.traffic.type=\u7f16\u8f91\u6d41\u91cf\u7c7b\u578b label.edit.vpc=\u7f16\u8f91 VPC label.edit=\u7f16\u8f91 label.egress.rule=\u51fa\u53e3\u89c4\u5219 @@ -564,7 +564,7 @@ label.elastic.LB=\u5f39\u6027\u8d1f\u8f7d\u5e73\u8861\u5668 label.elastic=\u5f39\u6027 label.email=\u7535\u5b50\u90ae\u4ef6 label.enable.provider=\u542f\u7528\u63d0\u4f9b\u7a0b\u5e8f -label.enable.s3=\u542f\u7528 S3 \u652f\u6301\u7684\u8f85\u52a9\u5b58\u50a8 +label.enable.s3=\u542f\u7528 S3 \u652f\u6301\u7684\u4e8c\u7ea7\u5b58\u50a8 label.enable.swift=\u542f\u7528 SWIFT label.enable.vpn=\u542f\u7528 VPN label.enabling.vpn.access=\u6b63\u5728\u542f\u7528 VPN \u8bbf\u95ee @@ -601,6 +601,7 @@ label.full=\u6ee1\u8f7d label.gateway=\u7f51\u5173 label.general.alerts=\u5e38\u89c4\u8b66\u62a5 label.generating.url=\u6b63\u5728\u751f\u6210 URL +label.gluster.volume=\u5377 label.go.step.2=\u8f6c\u81f3\u6b65\u9aa4 2 label.go.step.3=\u8f6c\u81f3\u6b65\u9aa4 3 label.go.step.4=\u8f6c\u81f3\u6b65\u9aa4 4 @@ -615,7 +616,7 @@ label.guest.ip=\u6765\u5bbe IP \u5730\u5740 label.guest.netmask=\u6765\u5bbe\u7f51\u7edc\u63a9\u7801 label.guest.networks=\u6765\u5bbe\u7f51\u7edc label.guest.start.ip=\u6765\u5bbe\u8d77\u59cb IP -label.guest.traffic=\u6765\u5bbe\u901a\u4fe1 +label.guest.traffic=\u6765\u5bbe\u6d41\u91cf label.guest.type=\u6765\u5bbe\u7c7b\u578b label.guest=\u6765\u5bbe label.ha.enabled=\u5df2\u542f\u7528\u9ad8\u53ef\u7528\u6027 @@ -650,11 +651,11 @@ label.installWizard.addPodIntro.subtitle=\u4ec0\u4e48\u662f\u63d0\u4f9b\u70b9? label.installWizard.addPodIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u63d0\u4f9b\u70b9 label.installWizard.addPrimaryStorageIntro.subtitle=\u4ec0\u4e48\u662f\u4e3b\u5b58\u50a8? label.installWizard.addPrimaryStorageIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u4e3b\u5b58\u50a8 -label.installWizard.addSecondaryStorageIntro.subtitle=\u4ec0\u4e48\u662f\u8f85\u52a9\u5b58\u50a8? -label.installWizard.addSecondaryStorageIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u8f85\u52a9\u5b58\u50a8 -label.installWizard.addZone.title=\u6dfb\u52a0\u533a\u57df -label.installWizard.addZoneIntro.subtitle=\u4ec0\u4e48\u662f\u533a\u57df? -label.installWizard.addZoneIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u533a\u57df +label.installWizard.addSecondaryStorageIntro.subtitle=\u4ec0\u4e48\u662f\u4e8c\u7ea7\u5b58\u50a8? +label.installWizard.addSecondaryStorageIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u4e8c\u7ea7\u5b58\u50a8 +label.installWizard.addZone.title=\u6dfb\u52a0\u8d44\u6e90\u57df +label.installWizard.addZoneIntro.subtitle=\u4ec0\u4e48\u662f\u8d44\u6e90\u57df? +label.installWizard.addZoneIntro.title=\u6dfb\u52a0\u4e00\u4e2a\u8d44\u6e90\u57df label.installWizard.click.launch=\u8bf7\u5355\u51fb\u201c\u542f\u52a8\u201d\u6309\u94ae\u3002 label.installWizard.subtitle=\u6b64\u6559\u7a0b\u5c06\u5e2e\u52a9\u60a8\u8bbe\u7f6e CloudStack&\#8482 \u5b89\u88c5 label.installWizard.title=\u60a8\u597d\uff0c\u6b22\u8fce\u4f7f\u7528 CloudStack&\#8482 @@ -698,13 +699,13 @@ label.item.listing=\u9879\u76ee\u5217\u8868 label.keep=\u4fdd\u7559 label.key=\u5bc6\u94a5 label.keyboard.type=\u952e\u76d8\u7c7b\u578b -label.kvm.traffic.label=KVM \u901a\u4fe1\u6807\u7b7e +label.kvm.traffic.label=KVM \u6d41\u91cf\u6807\u7b7e label.label=\u6807\u7b7e label.lang.arabic=\u963f\u62c9\u4f2f\u8bed label.lang.brportugese=\u8461\u8404\u7259\u8bed(\u5df4\u897f) label.lang.catalan=\u52a0\u6cf0\u7f57\u5c3c\u4e9a\u8bed label.lang.chinese=\u7b80\u4f53\u4e2d\u6587 -label.lang.dutch=Dutch (Netherlands) +label.lang.dutch=\u8377\u5170\u8bed(\u8377\u5170) label.lang.english=\u82f1\u8bed label.lang.french=\u6cd5\u8bed label.lang.german=\u5fb7\u8bed @@ -712,14 +713,14 @@ label.lang.italian=\u610f\u5927\u5229\u8bed label.lang.japanese=\u65e5\u8bed label.lang.korean=\u97e9\u8bed label.lang.norwegian=\u632a\u5a01\u8bed -label.lang.polish=Polish +label.lang.polish=\u6ce2\u5170\u8bed label.lang.russian=\u4fc4\u8bed label.lang.spanish=\u897f\u73ed\u7259\u8bed label.last.disconnected=\u4e0a\u6b21\u65ad\u5f00\u8fde\u63a5\u65f6\u95f4 label.last.name=\u59d3\u6c0f label.latest.events=\u6700\u65b0\u4e8b\u4ef6 label.launch.vm=\u542f\u52a8 VM -label.launch.zone=\u542f\u52a8\u533a\u57df +label.launch.zone=\u542f\u52a8\u8d44\u6e90\u57df label.launch=\u542f\u52a8 label.LB.isolation=\u8d1f\u8f7d\u5e73\u8861\u5668\u9694\u79bb label.least.connections=\u6700\u5c11\u8fde\u63a5\u7b97\u6cd5 @@ -747,7 +748,7 @@ label.max.memory=\u6700\u5927\u5185\u5b58(MiB) label.max.networks=\u6700\u5927\u7f51\u7edc\u6570 label.max.primary.storage=\u6700\u5927\u4e3b\u5b58\u50a8(GiB) label.max.public.ips=\u6700\u5927\u516c\u7528 IP \u6570 -label.max.secondary.storage=\u6700\u5927\u8f85\u52a9\u5b58\u50a8(GiB) +label.max.secondary.storage=\u6700\u5927\u4e8c\u7ea7\u5b58\u50a8(GiB) label.max.snapshots=\u6700\u5927\u5feb\u7167\u6570 label.max.templates=\u6700\u5927\u6a21\u677f\u6570 label.max.vms=\u6700\u5927\u7528\u6237 VM \u6570 @@ -787,7 +788,7 @@ label.menu.my.templates=\u6211\u7684\u6a21\u677f label.menu.network.offerings=\u7f51\u7edc\u65b9\u6848 label.menu.network=\u7f51\u7edc label.menu.physical.resources=\u7269\u7406\u8d44\u6e90 -label.menu.regions=\u533a\u57df +label.menu.regions=\u5730\u7406\u533a\u57df label.menu.running.instances=\u6b63\u5728\u8fd0\u884c\u7684\u5b9e\u4f8b label.menu.security.groups=\u5b89\u5168\u7ec4 label.menu.service.offerings=\u670d\u52a1\u65b9\u6848 @@ -863,7 +864,7 @@ label.nfs=NFS label.nic.adapter.type=NIC \u9002\u914d\u5668\u7c7b\u578b label.nicira.controller.address=\u63a7\u5236\u5668\u5730\u5740 label.nicira.l3gatewayserviceuuid=L3 Gateway Service UUID -label.nicira.transportzoneuuid=\u4f20\u8f93\u533a\u57df UUID +label.nicira.transportzoneuuid=\u4f20\u8f93\u8d44\u6e90\u57df UUID label.nics=NIC label.no.actions=\u65e0\u53ef\u7528\u64cd\u4f5c label.no.alerts=\u65e0\u6700\u8fd1\u53d1\u51fa\u7684\u8b66\u62a5 @@ -883,7 +884,7 @@ label.number.of.hosts=\u4e3b\u673a\u6570\u91cf label.number.of.pods=\u63d0\u4f9b\u70b9\u6570\u91cf label.number.of.system.vms=\u7cfb\u7edf VM \u6570 label.number.of.virtual.routers=\u865a\u62df\u8def\u7531\u5668\u6570 -label.number.of.zones=\u533a\u57df\u6570\u91cf +label.number.of.zones=\u8d44\u6e90\u57df\u6570\u91cf label.numretries=\u91cd\u8bd5\u6b21\u6570 label.ocfs2=OCFS2 label.offer.ha=\u63d0\u4f9b\u9ad8\u53ef\u7528\u6027 @@ -934,7 +935,7 @@ label.private.ip=\u4e13\u7528 IP \u5730\u5740 label.private.ips=\u4e13\u7528 IP \u5730\u5740 label.private.network=\u4e13\u7528\u7f51\u7edc label.private.port=\u4e13\u7528\u7aef\u53e3 -label.private.zone=\u4e13\u7528\u533a\u57df +label.private.zone=\u4e13\u7528\u8d44\u6e90\u57df label.privatekey=PKCS\#8 \u79c1\u94a5 label.project.dashboard=\u9879\u76ee\u63a7\u5236\u677f label.project.id=\u9879\u76ee ID @@ -950,8 +951,8 @@ label.public.ip=\u516c\u7528 IP \u5730\u5740 label.public.ips=\u516c\u7528 IP \u5730\u5740 label.public.network=\u516c\u7528\u7f51\u7edc label.public.port=\u516c\u7528\u7aef\u53e3 -label.public.traffic=\u516c\u5171\u901a\u4fe1 -label.public.zone=\u516c\u7528\u533a\u57df +label.public.traffic=\u516c\u5171\u6d41\u91cf +label.public.zone=\u516c\u7528\u8d44\u6e90\u57df label.public=\u516c\u7528 label.purpose=\u76ee\u7684 label.Pxe.server.type=Pxe \u670d\u52a1\u5668\u7c7b\u578b @@ -962,7 +963,7 @@ label.redundant.router.capability=\u5197\u4f59\u8def\u7531\u5668\u529f\u80fd label.redundant.router=\u5197\u4f59\u8def\u7531\u5668 label.redundant.state=\u5197\u4f59\u72b6\u6001 label.refresh=\u5237\u65b0 -label.region=\u533a\u57df +label.region=\u5730\u7406\u533a\u57df label.related=\u76f8\u5173\u8054 label.remind.later=\u4ee5\u540e\u63d0\u9192\u6211 label.remove.ACL=\u5220\u9664 ACL @@ -972,7 +973,7 @@ label.remove.ingress.rule=\u5220\u9664\u5165\u53e3\u89c4\u5219 label.remove.ip.range=\u5220\u9664 IP \u8303\u56f4 label.remove.pf=\u5220\u9664\u7aef\u53e3\u8f6c\u53d1\u89c4\u5219 label.remove.project.account=\u4ece\u9879\u76ee\u4e2d\u5220\u9664\u5e10\u6237 -label.remove.region=\u79fb\u9664\u533a\u57df +label.remove.region=\u5220\u9664\u5730\u7406\u533a\u57df label.remove.rule=\u5220\u9664\u89c4\u5219 label.remove.static.nat.rule=\u5220\u9664\u9759\u6001 NAT \u89c4\u5219 label.remove.static.route=\u5220\u9664\u9759\u6001\u8def\u7531 @@ -1019,11 +1020,11 @@ label.save=\u4fdd\u5b58 label.saving.processing=\u6b63\u5728\u4fdd\u5b58... label.scope=\u8303\u56f4 label.search=\u641c\u7d22 -label.secondary.storage.count=\u8f85\u52a9\u5b58\u50a8\u6c60 -label.secondary.storage.limits=\u8f85\u52a9\u5b58\u50a8\u9650\u5236(GiB) -label.secondary.storage.vm=\u8f85\u52a9\u5b58\u50a8 VM -label.secondary.storage=\u8f85\u52a9\u5b58\u50a8 -label.secondary.used=\u5df2\u4f7f\u7528\u7684\u8f85\u52a9\u5b58\u50a8 +label.secondary.storage.count=\u4e8c\u7ea7\u5b58\u50a8\u6c60 +label.secondary.storage.limits=\u4e8c\u7ea7\u5b58\u50a8\u9650\u5236(GiB) +label.secondary.storage.vm=\u4e8c\u7ea7\u5b58\u50a8 VM +label.secondary.storage=\u4e8c\u7ea7\u5b58\u50a8 +label.secondary.used=\u5df2\u4f7f\u7528\u7684\u4e8c\u7ea7\u5b58\u50a8 label.secret.key=\u5bc6\u94a5 label.security.group.name=\u5b89\u5168\u7ec4\u540d\u79f0 label.security.group=\u5b89\u5168\u7ec4 @@ -1031,7 +1032,7 @@ label.security.groups.enabled=\u5df2\u542f\u7528\u5b89\u5168\u7ec4 label.security.groups=\u5b89\u5168\u7ec4 label.select-view=\u9009\u62e9\u89c6\u56fe label.select.a.template=\u9009\u62e9\u4e00\u4e2a\u6a21\u677f -label.select.a.zone=\u9009\u62e9\u4e00\u4e2a\u533a\u57df +label.select.a.zone=\u9009\u62e9\u4e00\u4e2a\u8d44\u6e90\u57df label.select.instance.to.attach.volume.to=\u9009\u62e9\u8981\u5c06\u5377\u9644\u52a0\u5230\u7684\u5b9e\u4f8b label.select.instance=\u9009\u62e9\u5b9e\u4f8b label.select.iso.or.template=\u9009\u62e9 ISO \u6216\u6a21\u677f @@ -1045,9 +1046,9 @@ label.server=\u670d\u52a1\u5668 label.service.capabilities=\u670d\u52a1\u529f\u80fd label.service.offering=\u670d\u52a1\u65b9\u6848 label.session.expired=\u4f1a\u8bdd\u5df2\u8fc7\u671f -label.set.up.zone.type=\u8bbe\u7f6e\u533a\u57df\u7c7b\u578b +label.set.up.zone.type=\u8bbe\u7f6e\u8d44\u6e90\u57df\u7c7b\u578b label.setup.network=\u8bbe\u7f6e\u7f51\u7edc -label.setup.zone=\u8bbe\u7f6e\u533a\u57df +label.setup.zone=\u8bbe\u7f6e\u8d44\u6e90\u57df label.setup=\u8bbe\u7f6e label.shared=\u5df2\u5171\u4eab label.SharedMountPoint=SharedMountPoint @@ -1067,7 +1068,7 @@ label.source=\u6e90\u7b97\u6cd5 label.specify.IP.ranges=\u6307\u5b9a IP \u8303\u56f4 label.specify.vlan=\u6307\u5b9a VLAN label.specify.vxlan=\u6307\u5b9a VXLAN -label.SR.name = SR Name-Label +label.SR.name=SR \u540d\u79f0\u6807\u7b7e label.srx=SRX label.PA=Palo Alto label.start.IP=\u8d77\u59cb IP @@ -1108,10 +1109,10 @@ label.sticky.tablesize=\u8868\u5927\u5c0f label.stop=\u505c\u6b62 label.stopped.vms=\u5df2\u505c\u6b62\u7684 VM label.storage.tags=\u5b58\u50a8\u6807\u7b7e -label.storage.traffic=\u5b58\u50a8\u901a\u4fe1 +label.storage.traffic=\u5b58\u50a8\u6d41\u91cf label.storage.type=\u5b58\u50a8\u7c7b\u578b label.qos.type=QoS \u7c7b\u578b -label.cache.mode=Write-cache Type +label.cache.mode=\u5199\u5165\u7f13\u5b58\u7c7b\u578b label.storage=\u5b58\u50a8 label.subdomain.access=\u5b50\u57df\u8bbf\u95ee label.submit=\u63d0\u4ea4 @@ -1144,7 +1145,7 @@ label.tier.details=\u5c42\u8be6\u7ec6\u4fe1\u606f label.tier=\u5c42 label.time.zone=\u65f6\u533a label.time=\u65f6\u95f4 -label.timeout.in.second = Timeout(seconds) +label.timeout.in.second=\u8d85\u65f6(\u79d2) label.timeout=\u8d85\u65f6 label.timezone=\u65f6\u533a label.token=\u4ee4\u724c @@ -1156,9 +1157,9 @@ label.total.of.ip=\u603b IP \u5730\u5740\u6570 label.total.of.vm=\u603b VM \u6570 label.total.storage=\u5b58\u50a8\u603b\u91cf label.total.vms=\u603b VM \u6570 -label.traffic.label=\u901a\u4fe1\u6807\u7b7e -label.traffic.type=\u901a\u4fe1\u7c7b\u578b -label.traffic.types=\u901a\u4fe1\u7c7b\u578b +label.traffic.label=\u6d41\u91cf\u6807\u7b7e +label.traffic.type=\u6d41\u91cf\u7c7b\u578b +label.traffic.types=\u6d41\u91cf\u7c7b\u578b label.tuesday=\u661f\u671f\u4e8c label.type.id=\u7c7b\u578b ID label.type=\u7c7b\u578b @@ -1166,8 +1167,8 @@ label.unavailable=\u4e0d\u53ef\u7528 label.unlimited=\u65e0\u9650\u5236 label.untagged=\u5df2\u53d6\u6d88\u6807\u8bb0 label.update.project.resources=\u66f4\u65b0\u9879\u76ee\u8d44\u6e90 -label.update.ssl.cert= SSL Certificate -label.update.ssl= SSL Certificate +label.update.ssl.cert=SSL \u8bc1\u4e66 +label.update.ssl=SSL \u8bc1\u4e66 label.updating=\u6b63\u5728\u66f4\u65b0 label.upload.volume=\u4e0a\u8f7d\u5377 label.upload=\u4e0a\u8f7d @@ -1224,7 +1225,7 @@ label.vmsnapshot.memory=\u5feb\u7167\u5185\u5b58 label.vmsnapshot.parentname=\u7236\u540d\u79f0 label.vmsnapshot.type=\u7c7b\u578b label.vmsnapshot=VM \u5feb\u7167 -label.vmware.traffic.label=VMware \u901a\u4fe1\u6807\u7b7e +label.vmware.traffic.label=VMware \u6d41\u91cf\u6807\u7b7e label.volgroup=\u5377\u7ec4 label.volume.limits=\u5377\u9650\u5236 label.volume.name=\u5377\u540d\u79f0 @@ -1249,23 +1250,23 @@ label.weekly=\u6bcf\u5468 label.welcome.cloud.console=\u6b22\u8fce\u4f7f\u7528\u7ba1\u7406\u63a7\u5236\u53f0 label.welcome=\u6b22\u8fce label.what.is.cloudstack=\u4ec0\u4e48\u662f CloudStack&\#8482? -label.xen.traffic.label=XenServer \u901a\u4fe1\u6807\u7b7e +label.xen.traffic.label=XenServer \u6d41\u91cf\u6807\u7b7e label.yes=\u662f -label.zone.details=\u533a\u57df\u8be6\u60c5 -label.zone.id=\u533a\u57df ID -label.zone.name=\u533a\u57df\u540d\u79f0 +label.zone.details=\u8d44\u6e90\u57df\u8be6\u7ec6\u4fe1\u606f +label.zone.id=\u8d44\u6e90\u57df ID +label.zone.name=\u8d44\u6e90\u57df\u540d\u79f0 label.zone.step.1.title=\u6b65\u9aa4 1\: \u9009\u62e9\u4e00\u4e2a\u7f51\u7edc -label.zone.step.2.title=\u6b65\u9aa4 2\: \u6dfb\u52a0\u4e00\u4e2a\u533a\u57df +label.zone.step.2.title=\u6b65\u9aa4 2\: \u6dfb\u52a0\u4e00\u4e2a\u8d44\u6e90\u57df label.zone.step.3.title=\u6b65\u9aa4 3\: \u6dfb\u52a0\u4e00\u4e2a\u63d0\u4f9b\u70b9 label.zone.step.4.title=\u6b65\u9aa4 4\: \u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4 -label.zone.type=\u533a\u57df\u7c7b\u578b -label.zone.wide=\u6574\u4e2a\u533a\u57df -label.zone=\u533a\u57df -label.zones=\u533a\u57df -label.zoneWizard.trafficType.guest=\u6765\u5bbe\: \u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1 -label.zoneWizard.trafficType.management=\u7ba1\u7406\: CloudStack \u7684\u5185\u90e8\u8d44\u6e90(\u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u901a\u4fe1\u7684\u4efb\u4f55\u7ec4\u4ef6\uff0c\u4f8b\u5982\u4e3b\u673a\u548c CloudStack \u7cfb\u7edf VM)\u4e4b\u95f4\u7684\u901a\u4fe1 -label.zoneWizard.trafficType.public=\u516c\u7528\: \u4e91\u4e2d Internet \u4e0e\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1\u3002 -label.zoneWizard.trafficType.storage=\u5b58\u50a8\: \u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u4e0e\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668(\u4f8b\u5982 VM \u6a21\u677f\u4e0e\u5feb\u7167)\u4e4b\u95f4\u7684\u901a\u4fe1 +label.zone.type=\u8d44\u6e90\u57df\u7c7b\u578b +label.zone.wide=\u6574\u4e2a\u8d44\u6e90\u57df +label.zone=\u8d44\u6e90\u57df +label.zones=\u8d44\u6e90\u57df +label.zoneWizard.trafficType.guest=\u6765\u5bbe\: \u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u6d41\u91cf +label.zoneWizard.trafficType.management=\u7ba1\u7406\: CloudStack \u7684\u5185\u90e8\u8d44\u6e90(\u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u901a\u4fe1\u7684\u4efb\u4f55\u7ec4\u4ef6\uff0c\u4f8b\u5982\u4e3b\u673a\u548c CloudStack \u7cfb\u7edf VM)\u4e4b\u95f4\u7684\u6d41\u91cf +label.zoneWizard.trafficType.public=\u516c\u5171\: \u4e91\u4e2d Internet \u4e0e\u865a\u62df\u673a\u4e4b\u95f4\u7684\u6d41\u91cf\u3002 +label.zoneWizard.trafficType.storage=\u5b58\u50a8\: \u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u4e0e\u4e8c\u7ea7\u5b58\u50a8\u670d\u52a1\u5668(\u4f8b\u5982 VM \u6a21\u677f\u4e0e\u5feb\u7167)\u4e4b\u95f4\u7684\u6d41\u91cf label.ldap.group.name=LDAP \u7ec4 label.password.reset.confirm=\u5bc6\u7801\u5df2\u91cd\u7f6e\u4e3a label.provider=\u63d0\u4f9b\u7a0b\u5e8f @@ -1283,17 +1284,18 @@ label.ipv6.address=IPv6 IP \u5730\u5740 label.ipv6.gateway=IPv6 \u7f51\u5173 label.ipv6.CIDR=IPv6 CIDR label.VPC.limits=VPC \u9650\u5236 -label.edit.region=\u7f16\u8f91\u533a\u57df +label.edit.region=\u7f16\u8f91\u5730\u7406\u533a\u57df label.gslb.domain.name=GSLB \u57df\u540d label.add.gslb=\u6dfb\u52a0 GSLB label.gslb.servicetype=\u670d\u52a1\u7c7b\u578b label.gslb.details=GSLB \u8be6\u7ec6\u4fe1\u606f label.gslb.delete=\u5220\u9664 GSLB -label.opendaylight.controllers=OpenDaylight Controllers +label.opendaylight.controller=OpenDaylight \u63a7\u5236\u5668 +label.opendaylight.controllers=OpenDaylight \u63a7\u5236\u5668 label.portable.ip.ranges=\u53ef\u79fb\u690d IP \u8303\u56f4 label.add.portable.ip.range=\u6dfb\u52a0\u53ef\u79fb\u690d IP \u8303\u56f4 label.delete.portable.ip.range=\u5220\u9664\u53ef\u79fb\u690d IP \u8303\u56f4 -label.opendaylight.controllerdetail=OpenDaylight Controller Details +label.opendaylight.controllerdetail=OpenDaylight \u63a7\u5236\u5668\u8be6\u7ec6\u4fe1\u606f label.portable.ip.range.details=\u53ef\u79fb\u690d IP \u8303\u56f4\u8be6\u7ec6\u4fe1\u606f label.portable.ips=\u53ef\u79fb\u690d IP label.gslb.assigned.lb=\u5df2\u5206\u914d\u8d1f\u8f7d\u5e73\u8861 @@ -1305,22 +1307,183 @@ label.enable.autoscale=\u542f\u7528\u81ea\u52a8\u6269\u5c55 label.disable.autoscale=\u7981\u7528\u81ea\u52a8\u6269\u5c55 label.min.instances=\u6700\u5c0f\u5b9e\u4f8b\u6570 label.max.instances=\u6700\u5927\u5b9e\u4f8b\u6570 -label.add.OpenDaylight.device=Add OpenDaylight Controller +label.add.OpenDaylight.device=\u6dfb\u52a0 OpenDaylight \u63a7\u5236\u5668 label.show.advanced.settings=\u663e\u793a\u9ad8\u7ea7\u8bbe\u7f6e -label.delete.OpenDaylight.device=Delete OpenDaylight Controller +label.delete.OpenDaylight.device=\u5220\u9664 OpenDaylight \u63a7\u5236\u5668 label.polling.interval.sec=\u8f6e\u8be2\u65f6\u95f4\u95f4\u9694(\u79d2) label.quiet.time.sec=\u5b89\u9759\u65f6\u95f4(\u79d2) label.destroy.vm.graceperiod=\u9500\u6bc1 VM \u5bbd\u9650\u671f label.SNMP.community=SNMP \u793e\u533a label.SNMP.port=SNMP \u7aef\u53e3 label.add.ucs.manager=\u6dfb\u52a0 UCS \u7ba1\u7406\u5668 -label.ovm.traffic.label=OVM \u901a\u4fe1\u6807\u7b7e -label.lxc.traffic.label=LXC \u901a\u4fe1\u6807\u7b7e -label.hyperv.traffic.label=HyperV \u901a\u4fe1\u6807\u7b7e +label.ovm.traffic.label=OVM \u6d41\u91cf\u6807\u7b7e +label.lxc.traffic.label=LXC \u6d41\u91cf\u6807\u7b7e +label.hyperv.traffic.label=HyperV \u6d41\u91cf\u6807\u7b7e +label.resource.name=\u8d44\u6e90\u540d\u79f0 +label.reource.id=\u8d44\u6e90 ID +label.vnmc.devices=VNMC \u8bbe\u5907 +label.add.vnmc.provider=\u6dfb\u52a0 VNMC \u63d0\u4f9b\u7a0b\u5e8f +label.enable.vnmc.provider=\u542f\u7528 VNMC \u63d0\u4f9b\u7a0b\u5e8f +label.add.vnmc.device=\u6dfb\u52a0 VNMC \u8bbe\u5907 +label.ciscovnmc.resource.details=CiscoVNMC \u8d44\u6e90\u8be6\u7ec6\u4fe1\u606f +label.delete.ciscovnmc.resource=\u5220\u9664 CiscoVNMC \u8d44\u6e90 +label.enable.vnmc.device=\u542f\u7528 VNMC \u8bbe\u5907 +label.disbale.vnmc.device=\u7981\u7528 VNMC \u8bbe\u5907 +label.disable.vnmc.provider=\u7981\u7528 VNMC \u63d0\u4f9b\u7a0b\u5e8f +label.services=\u670d\u52a1 +label.secondary.staging.store=\u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8 +label.release.account=\u4ece\u5e10\u6237\u4e2d\u91ca\u653e +label.release.account.lowercase=\u4ece\u5e10\u6237\u4e2d\u91ca\u653e +label.vlan.vni.ranges=VLAN/VNI \u8303\u56f4 +label.dedicated.vlan.vni.ranges=VLAN/VNI \u8303\u56f4\u5df2\u4e13\u7528 +label.dedicate.vlan.vni.range=\u5c06 VLAN/VNI \u8303\u56f4\u4e13\u7528 +label.vlan.vni.range=VLAN/VNI \u8303\u56f4 +label.vlan.range.details=VLAN \u8303\u56f4\u8be6\u7ec6\u4fe1\u606f +label.release.dedicated.vlan.range=\u91ca\u653e\u4e13\u7528 VLAN \u8303\u56f4 +label.broadcat.uri=\u5e7f\u64ad URI +label.ipv4.cidr=IPv4 CIDR +label.guest.network.details=\u6765\u5bbe\u7f51\u7edc\u8be6\u7ec6\u4fe1\u606f +label.ipv4.gateway=IPv4 \u7f51\u5173 +label.release.dedicated.vlan.range=\u91ca\u653e\u4e13\u7528 VLAN \u8303\u56f4 +label.vlan.ranges=VLAN \u8303\u56f4 +label.virtual.appliance.details=\u865a\u62df\u8bbe\u5907\u8be6\u7ec6\u4fe1\u606f +label.start.lb.vm=\u542f\u52a8 LB VM +label.stop.lb.vm=\u505c\u6b62 LB VM +label.migrate.lb.vm=\u8fc1\u79fb LB VM +label.vpc.virtual.router=VPC \u865a\u62df\u8def\u7531\u5668 +label.ovs=OVS +label.gslb.service=GSLB \u670d\u52a1 +label.gslb.service.public.ip=GSLB \u670d\u52a1\u516c\u7528 IP +label.gslb.service.private.ip=GSLB \u670d\u52a1\u4e13\u7528 IP +label.baremetal.dhcp.provider=\u88f8\u673a DHCP \u63d0\u4f9b\u7a0b\u5e8f +label.add.baremetal.dhcp.device=\u6dfb\u52a0\u88f8\u673a DHCP \u8bbe\u5907 +label.baremetal.pxe.provider=\u88f8\u673a PXE \u63d0\u4f9b\u7a0b\u5e8f +label.baremetal.pxe.device=\u6dfb\u52a0\u88f8\u673a PXE \u8bbe\u5907 +label.tftp.root.directory=Tftp \u6839\u76ee\u5f55 +label.add.vmware.datacenter=\u6dfb\u52a0 VMware \u6570\u636e\u4e2d\u5fc3 +label.remove.vmware.datacenter=\u5220\u9664 VMware \u6570\u636e\u4e2d\u5fc3 +label.dc.name=\u6570\u636e\u4e2d\u5fc3\u540d\u79f0 +label.vcenter=vCenter +label.dedicate.zone=\u5c06\u8d44\u6e90\u57df\u4e13\u7528 +label.zone.dedicated=\u8d44\u6e90\u57df\u5df2\u4e13\u7528 +label.release.dedicated.zone=\u91ca\u653e\u4e13\u7528\u8d44\u6e90\u57df +label.ipv6.dns1=IPv6 DNS1 +label.ipv6.dns2=IPv6 DNS2 +label.vmware.datacenter.name=VMware \u6570\u636e\u4e2d\u5fc3\u540d\u79f0 +label.vmware.datacenter.vcenter=VMware \u6570\u636e\u4e2d\u5fc3 vCenter +label.vmware.datacenter.id=VMware \u6570\u636e\u4e2d\u5fc3 ID +label.system.vm.details=\u7cfb\u7edf VM \u8be6\u7ec6\u4fe1\u606f +label.system.vm.scaled.up=\u5df2\u6269\u5c55\u7cfb\u7edf VM +label.console.proxy.vm=\u63a7\u5236\u5668\u4ee3\u7406 VM +label.settings=\u8bbe\u7f6e +label.requires.upgrade=\u9700\u8981\u5347\u7ea7 +label.upgrade.router.newer.template=\u5347\u7ea7\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +label.router.vm.scaled.up=\u5df2\u6269\u5c55\u8def\u7531\u5668 VM +label.total.virtual.routers=\u865a\u62df\u8def\u7531\u5668\u603b\u6570 +label.upgrade.required=\u9700\u8981\u5347\u7ea7 +label.virtual.routers.group.zone=\u865a\u62df\u8def\u7531\u5668(\u6309\u8d44\u6e90\u57df\u5206\u7ec4) +label.total.virtual.routers.upgrade=\u9700\u8981\u5347\u7ea7\u7684\u865a\u62df\u8def\u7531\u5668\u603b\u6570 +label.virtual.routers.group.pod=\u865a\u62df\u8def\u7531\u5668(\u6309\u63d0\u4f9b\u70b9\u5206\u7ec4) +label.virtual.routers.group.cluster=\u865a\u62df\u8def\u7531\u5668(\u6309\u7fa4\u96c6\u5206\u7ec4) +label.zone.lower=\u8d44\u6e90\u57df +label.virtual.routers.group.account=\u865a\u62df\u8def\u7531\u5668(\u6309\u5e10\u6237\u5206\u7ec4) +label.netscaler.details=NetScaler \u8be6\u7ec6\u4fe1\u606f +label.baremetal.dhcp.devices=\u88f8\u673a DHCP \u8bbe\u5907 +label.baremetal.pxe.devices=\u88f8\u673a PXE \u8bbe\u5907 +label.addes.new.f5=\u5df2\u6dfb\u52a0\u65b0 F5 +label.f5.details=F5 \u8be6\u7ec6\u4fe1\u606f +label.srx.details=SRX \u8be6\u7ec6\u4fe1\u606f +label.palo.alto.details=Palo Alto \u8be6\u7ec6\u4fe1\u606f +label.added.nicira.nvp.controller=\u5df2\u6dfb\u52a0\u65b0 Nicira NVP \u63a7\u5236\u5668\ +label.nicira.nvp.details=Nicira NVP \u8be6\u7ec6\u4fe1\u606f +label.added.new.bigswitch.vns.controller=\u5df2\u6dfb\u52a0\u65b0 BigSwitch VNS \u63a7\u5236\u5668 +label.bigswitch.vns.details=BigSwitch VNS \u8be6\u7ec6\u4fe1\u606f +label.dedicate=\u4e13\u7528 +label.dedicate.pod=\u5c06\u63d0\u4f9b\u70b9\u4e13\u7528 +label.pod.dedicated=\u63d0\u4f9b\u70b9\u5df2\u4e13\u7528 +label.release.dedicated.pod=\u91ca\u653e\u4e13\u7528\u63d0\u4f9b\u70b9 +label.override.public.traffic=\u66ff\u4ee3\u516c\u5171\u6d41\u91cf +label.public.traffic.vswitch.type=\u516c\u5171\u6d41\u91cf\u865a\u62df\u4ea4\u6362\u673a\u7c7b\u578b +label.public.traffic.vswitch.name=\u516c\u5171\u6d41\u91cf\u865a\u62df\u4ea4\u6362\u673a\u540d\u79f0 +label.override.guest.traffic=\u66ff\u4ee3\u6765\u5bbe\u6d41\u91cf +label.guest.traffic.vswitch.type=\u6765\u5bbe\u6d41\u91cf\u865a\u62df\u4ea4\u6362\u673a\u7c7b\u578b +label.guest.traffic.vswitch.name=\u6765\u5bbe\u6d41\u91cf\u865a\u62df\u4ea4\u6362\u673a\u540d\u79f0 +label.cisco.nexus1000v.ip.address=Nexus 1000v IP \u5730\u5740 +label.cisco.nexus1000v.username=Nexus 1000v \u7528\u6237\u540d +label.cisco.nexus1000v.password=Nexus 1000v \u5bc6\u7801 +label.dedicate.cluster=\u5c06\u7fa4\u96c6\u4e13\u7528 +label.release.dedicated.cluster=\u91ca\u653e\u4e13\u7528\u7fa4\u96c6 +label.dedicate.host=\u5c06\u4e3b\u673a\u4e13\u7528 +label.release.dedicated.host=\u91ca\u653e\u4e13\u7528\u4e3b\u673a +label.number.of.cpu.sockets=CPU \u63d2\u69fd\u6570 +label.delete.ucs.manager=\u5220\u9664 UCS Manager +label.blades=\u5200\u7247\u5f0f\u670d\u52a1\u5668 +label.chassis=\u673a\u7bb1 +label.blade.id=\u5200\u7247\u5f0f\u670d\u52a1\u5668 ID +label.associated.profile=\u5df2\u5173\u8054\u914d\u7f6e\u6587\u4ef6 +label.refresh.blades=\u5237\u65b0\u5200\u7247\u5f0f\u670d\u52a1\u5668 +label.instanciate.template.associate.profile.blade=\u5c06\u6a21\u677f\u5b9e\u4f8b\u5316\u5e76\u5c06\u914d\u7f6e\u6587\u4ef6\u4e0e\u5200\u7247\u5f0f\u670d\u52a1\u5668\u5173\u8054 +label.select.template=\u9009\u62e9\u6a21\u677f +label.profile=\u914d\u7f6e\u6587\u4ef6 +label.delete.profile=\u5220\u9664\u914d\u7f6e\u6587\u4ef6 +label.disassociate.profile.blade=\u53d6\u6d88\u5c06\u914d\u7f6e\u6587\u4ef6\u4e0e\u5200\u7247\u5f0f\u670d\u52a1\u5668\u5173\u8054 +label.secondary.storage.details=\u4e8c\u7ea7\u5b58\u50a8\u8be6\u7ec6\u4fe1\u606f +label.secondary.staging.store.details=\u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8\u8be6\u7ec6\u4fe1\u606f +label.add.nfs.secondary.staging.store=\u6dfb\u52a0 NFS \u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8 +label.delete.secondary.staging.store=\u5220\u9664\u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8 +label.ipv4.start.ip=IPv4 \u8d77\u59cb IP +label.ipv4.end.ip=IPv4 \u7ed3\u675f IP +label.ipv6.start.ip=IPv6 \u8d77\u59cb IP +label.ipv6.end.ip=IPv6 \u7ed3\u675f IP +label.vm.password=VM \u7684\u5bc6\u7801 +label.group.by.zone=\u6309\u8d44\u6e90\u57df\u5206\u7ec4 +label.group.by.pod=\u6309\u63d0\u4f9b\u70b9\u5206\u7ec4 +label.group.by.cluster=\u6309\u7fa4\u96c6\u5206\u7ec4 +label.group.by.account=\u6309\u5e10\u6237\u5206\u7ec4 +label.no.grouping=(\u4e0d\u5206\u7ec4) +label.create.nfs.secondary.staging.storage=\u521b\u5efa NFS \u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8 +label.username.lower=\u7528\u6237\u540d +label.password.lower=\u5bc6\u7801 +label.email.lower=\u7535\u5b50\u90ae\u4ef6 +label.firstname.lower=\u540d\u5b57 +label.lastname.lower=\u59d3\u6c0f +label.domain.lower=\u57df +label.account.lower=\u5e10\u6237 +label.type.lower=\u7c7b\u578b +label.rule.number=\u89c4\u5219\u7f16\u53f7 +label.action=\u64cd\u4f5c +label.name.lower=\u540d\u79f0 +label.ucs=UCS +label.change.affinity=\u66f4\u6539\u5173\u8054\u6027 +label.persistent=\u6c38\u4e45 +label.broadcasturi=\u5e7f\u64ad URI +label.network.cidr=\u7f51\u7edc CIDR +label.reserved.ip.range=\u9884\u7559 IP \u8303\u56f4 +label.autoscale=\u81ea\u52a8\u6269\u5c55 +label.health.check=\u8fd0\u884c\u72b6\u51b5\u68c0\u67e5 +label.public.load.balancer.provider=\u516c\u7528\u8d1f\u8f7d\u5e73\u8861\u5668\u63d0\u4f9b\u7a0b\u5e8f +label.add.isolated.network=\u6dfb\u52a0\u9694\u79bb\u7f51\u7edc +label.vlan=VLAN +label.secondary.isolated.vlan.id=\u4e8c\u7ea7\u9694\u79bb VLAN ID +label.ipv4.netmask=IPv4 \u7f51\u7edc\u63a9\u7801 +label.custom=\u81ea\u5b9a\u4e49 +label.disable.network.offering=\u7981\u7528\u7f51\u7edc\u65b9\u6848 +label.enable.network.offering=\u542f\u7528\u7f51\u7edc\u65b9\u6848 +label.remove.network.offering=\u5220\u9664\u7f51\u7edc\u65b9\u6848 +label.system.offering.for.router=\u8def\u7531\u5668\u7684\u7cfb\u7edf\u65b9\u6848 +label.mode=\u6a21\u5f0f +label.associate.public.ip=\u5173\u8054\u516c\u7528 IP +label.acl=ACL +label.user.data=\u7528\u6237\u6570\u636e +label.virtual.networking=\u865a\u62df\u7f51\u7edc\u8fde\u63a5 +label.allow=\u5141\u8bb8 +label.deny=\u62d2\u7edd +label.default.egress.policy=\u9ed8\u8ba4\u51fa\u53e3\u89c4\u5219 +label.xenserver.tools.version.61.plus=XenServer Tools \u7248\u672c 6.1\+ managed.state=\u6258\u7ba1\u72b6\u6001 message.acquire.new.ip.vpc=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e3a\u6b64 VPC \u83b7\u53d6\u4e00\u4e2a\u65b0 IP\u3002 message.acquire.new.ip=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e3a\u6b64\u7f51\u7edc\u83b7\u53d6\u4e00\u4e2a\u65b0 IP\u3002 -message.acquire.public.ip=\u8bf7\u9009\u62e9\u4e00\u4e2a\u8981\u4ece\u4e2d\u83b7\u53d6\u65b0 IP \u7684\u533a\u57df\u3002 +message.acquire.public.ip=\u8bf7\u9009\u62e9\u4e00\u4e2a\u8981\u4ece\u4e2d\u83b7\u53d6\u65b0 IP \u7684\u8d44\u6e90\u57df\u3002 message.action.cancel.maintenance.mode=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u53d6\u6d88\u6b64\u7ef4\u62a4\u6a21\u5f0f\u3002 message.action.cancel.maintenance=\u5df2\u6210\u529f\u53d6\u6d88\u7ef4\u62a4\u60a8\u7684\u4e3b\u673a\u3002\u6b64\u8fc7\u7a0b\u53ef\u80fd\u9700\u8981\u957f\u8fbe\u51e0\u5206\u949f\u65f6\u95f4\u3002 message.action.change.service.warning.for.instance=\u5fc5\u987b\u5148\u7981\u7528\u60a8\u7684\u5b9e\u4f8b\uff0c\u7136\u540e\u518d\u5c1d\u8bd5\u66f4\u6539\u5176\u5f53\u524d\u7684\u670d\u52a1\u65b9\u6848\u3002 @@ -1331,22 +1494,22 @@ message.action.delete.domain=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9 message.action.delete.external.firewall=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5916\u90e8\u9632\u706b\u5899\u3002\u8b66\u544a\: \u5982\u679c\u60a8\u8ba1\u5212\u91cd\u65b0\u6dfb\u52a0\u540c\u4e00\u4e2a\u5916\u90e8\u9632\u706b\u5899\uff0c\u5219\u5fc5\u987b\u5728\u8bbe\u5907\u4e0a\u91cd\u7f6e\u4f7f\u7528\u6570\u636e\u3002 message.action.delete.external.load.balancer=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5916\u90e8\u8d1f\u8f7d\u5e73\u8861\u5668\u3002\u8b66\u544a\: \u5982\u679c\u60a8\u8ba1\u5212\u91cd\u65b0\u6dfb\u52a0\u540c\u4e00\u4e2a\u5916\u90e8\u8d1f\u8f7d\u5e73\u8861\u5668\uff0c\u5219\u5fc5\u987b\u5728\u8bbe\u5907\u4e0a\u91cd\u7f6e\u4f7f\u7528\u6570\u636e\u3002 message.action.delete.ingress.rule=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5165\u53e3\u89c4\u5219\u3002 -message.action.delete.ISO.for.all.zones=\u6b64 ISO \u7531\u6240\u6709\u533a\u57df\u4f7f\u7528\u3002\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5176\u4ece\u6240\u6709\u533a\u57df\u4e2d\u5220\u9664\u3002 +message.action.delete.ISO.for.all.zones=\u6b64 ISO \u7531\u6240\u6709\u8d44\u6e90\u57df\u4f7f\u7528\u3002\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5176\u4ece\u6240\u6709\u8d44\u6e90\u57df\u4e2d\u5220\u9664\u3002 message.action.delete.ISO=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64 ISO\u3002 message.action.delete.network=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u7f51\u7edc\u3002 message.action.delete.nexusVswitch=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64 Nexus 1000v message.action.delete.physical.network=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u7269\u7406\u7f51\u7edc message.action.delete.pod=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u63d0\u4f9b\u70b9\u3002 message.action.delete.primary.storage=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u4e3b\u5b58\u50a8\u3002 -message.action.delete.secondary.storage=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u8f85\u52a9\u5b58\u50a8\u3002 +message.action.delete.secondary.storage=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u4e8c\u7ea7\u5b58\u50a8\u3002 message.action.delete.security.group=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5b89\u5168\u7ec4\u3002 message.action.delete.service.offering=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u670d\u52a1\u65b9\u6848\u3002 message.action.delete.snapshot=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5feb\u7167\u3002 message.action.delete.system.service.offering=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u7cfb\u7edf\u670d\u52a1\u65b9\u6848\u3002 -message.action.delete.template.for.all.zones=\u6b64\u6a21\u677f\u7531\u6240\u6709\u533a\u57df\u4f7f\u7528\u3002\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5176\u4ece\u6240\u6709\u533a\u57df\u4e2d\u5220\u9664\u3002 +message.action.delete.template.for.all.zones=\u6b64\u6a21\u677f\u7531\u6240\u6709\u8d44\u6e90\u57df\u4f7f\u7528\u3002\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5176\u4ece\u6240\u6709\u8d44\u6e90\u57df\u4e2d\u5220\u9664\u3002 message.action.delete.template=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u6a21\u677f\u3002 message.action.delete.volume=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5377\u3002 -message.action.delete.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u533a\u57df\u3002 +message.action.delete.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u8d44\u6e90\u57df\u3002 message.action.destroy.instance=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u9500\u6bc1\u6b64\u5b9e\u4f8b\u3002 message.action.destroy.systemvm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u9500\u6bc1\u6b64\u7cfb\u7edf VM\u3002 message.action.disable.cluster=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u7fa4\u96c6\u3002 @@ -1354,7 +1517,7 @@ message.action.disable.nexusVswitch=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u message.action.disable.physical.network=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u7269\u7406\u7f51\u7edc\u3002 message.action.disable.pod=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u63d0\u4f9b\u70b9\u3002 message.action.disable.static.NAT=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u9759\u6001 NAT\u3002 -message.action.disable.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u533a\u57df\u3002 +message.action.disable.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u8d44\u6e90\u57df\u3002 message.action.download.iso=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e0b\u8f7d\u6b64 ISO\u3002 message.action.download.template=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e0b\u8f7d\u6b64\u6a21\u677f\u3002 message.action.enable.cluster=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u7fa4\u96c6\u3002 @@ -1362,7 +1525,7 @@ message.action.enable.maintenance=\u5df2\u6210\u529f\u51c6\u5907\u597d\u7ef4\u62 message.action.enable.nexusVswitch=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64 Nexus 1000v message.action.enable.physical.network=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u7269\u7406\u7f51\u7edc\u3002 message.action.enable.pod=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u63d0\u4f9b\u70b9\u3002 -message.action.enable.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u533a\u57df\u3002 +message.action.enable.zone=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u8d44\u6e90\u57df\u3002 message.action.expunge.instance=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5b9e\u4f8b\u3002 message.action.force.reconnect=\u5df2\u6210\u529f\u5f3a\u5236\u91cd\u65b0\u8fde\u63a5\u60a8\u7684\u4e3b\u673a\u3002\u6b64\u8fc7\u7a0b\u53ef\u80fd\u9700\u8981\u957f\u8fbe\u51e0\u5206\u949f\u65f6\u95f4\u3002 message.action.host.enable.maintenance.mode=\u542f\u7528\u7ef4\u62a4\u6a21\u5f0f\u4f1a\u5bfc\u81f4\u5c06\u6b64\u4e3b\u673a\u4e0a\u6b63\u5728\u8fd0\u884c\u7684\u6240\u6709\u5b9e\u4f8b\u5b9e\u65f6\u8fc1\u79fb\u5230\u4efb\u4f55\u53ef\u7528\u7684\u4e3b\u673a\u3002 @@ -1389,26 +1552,26 @@ message.action.unmanage.cluster=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u53d6 message.action.vmsnapshot.delete=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64 VM \u5feb\u7167\u3002 message.action.vmsnapshot.revert=\u8fd8\u539f VM \u5feb\u7167 message.activate.project=\u662f\u5426\u786e\u5b9e\u8981\u6fc0\u6d3b\u6b64\u9879\u76ee? -message.add.cluster.zone=\u5411\u533a\u57df \u4e2d\u6dfb\u52a0\u4e00\u4e2a\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u6258\u7ba1\u7684\u7fa4\u96c6 -message.add.cluster=\u5411\u533a\u57df \u3001\u63d0\u4f9b\u70b9 \u4e2d\u6dfb\u52a0\u4e00\u4e2a\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u6258\u7ba1\u7684\u7fa4\u96c6 +message.add.cluster.zone=\u5411\u8d44\u6e90\u57df \u4e2d\u6dfb\u52a0\u4e00\u4e2a\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u6258\u7ba1\u7684\u7fa4\u96c6 +message.add.cluster=\u5411\u8d44\u6e90\u57df \u3001\u63d0\u4f9b\u70b9 \u4e2d\u6dfb\u52a0\u4e00\u4e2a\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u6258\u7ba1\u7684\u7fa4\u96c6 message.add.disk.offering=\u8bf7\u6307\u5b9a\u4ee5\u4e0b\u53c2\u6570\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u7684\u78c1\u76d8\u65b9\u6848 message.add.domain=\u8bf7\u6307\u5b9a\u8981\u5728\u6b64\u57df\u4e0b\u521b\u5efa\u7684\u5b50\u57df -message.add.firewall=\u5411\u533a\u57df\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u9632\u706b\u5899 +message.add.firewall=\u5411\u8d44\u6e90\u57df\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u9632\u706b\u5899 message.add.guest.network=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u6dfb\u52a0\u4e00\u4e2a\u6765\u5bbe\u7f51\u7edc message.add.host=\u8bf7\u6307\u5b9a\u4ee5\u4e0b\u53c2\u6570\u4ee5\u6dfb\u52a0\u4e00\u53f0\u65b0\u4e3b\u673a -message.add.ip.range.direct.network=\u5411\u533a\u57df \u4e2d\u7684\u76f4\u63a5\u7f51\u7edc \u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4 +message.add.ip.range.direct.network=\u5411\u8d44\u6e90\u57df \u4e2d\u7684\u76f4\u63a5\u7f51\u7edc \u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4 message.add.ip.range.to.pod=

\u5411\u63d0\u4f9b\u70b9\u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4\:

-message.add.ip.range=\u5411\u533a\u57df\u4e2d\u7684\u516c\u7528\u7f51\u7edc\u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4 +message.add.ip.range=\u5411\u8d44\u6e90\u57df\u4e2d\u7684\u516c\u7528\u7f51\u7edc\u6dfb\u52a0\u4e00\u4e2a IP \u8303\u56f4 message.add.load.balancer.under.ip=\u5df2\u5728\u4ee5\u4e0b IP \u4e0b\u6dfb\u52a0\u8d1f\u8f7d\u5e73\u8861\u5668\u89c4\u5219\: -message.add.load.balancer=\u5411\u533a\u57df\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u8d1f\u8f7d\u5e73\u8861\u5668 -message.add.network=\u4e3a\u533a\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u7f51\u7edc +message.add.load.balancer=\u5411\u8d44\u6e90\u57df\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u8d1f\u8f7d\u5e73\u8861\u5668 +message.add.network=\u4e3a\u8d44\u6e90\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u7f51\u7edc message.add.new.gateway.to.vpc=\u8bf7\u6307\u5b9a\u5c06\u65b0\u7f51\u5173\u6dfb\u52a0\u5230\u6b64 VPC \u6240\u9700\u7684\u4fe1\u606f\u3002 -message.add.pod.during.zone.creation=\u6bcf\u4e2a\u533a\u57df\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u63d0\u4f9b\u70b9\u3002\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u60a8\u5c06\u5728\u968f\u540e\u7684\u67d0\u4e2a\u6b65\u9aa4\u4e2d\u6dfb\u52a0\u8fd9\u4e9b\u4e3b\u673a\u548c\u670d\u52a1\u5668\u3002\u9996\u5148\uff0c\u8bf7\u4e3a CloudStack \u7684\u5185\u90e8\u7ba1\u7406\u901a\u4fe1\u914d\u7f6e\u4e00\u4e2a\u9884\u7559 IP \u5730\u5740\u8303\u56f4\u3002\u9884\u7559\u7684 IP \u8303\u56f4\u5bf9\u4e91\u4e2d\u7684\u6bcf\u4e2a\u533a\u57df\u6765\u8bf4\u5fc5\u987b\u552f\u4e00\u3002 -message.add.pod=\u4e3a\u533a\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u63d0\u4f9b\u70b9 -message.add.primary.storage=\u4e3a\u533a\u57df \u3001\u63d0\u4f9b\u70b9 \u6dfb\u52a0\u4e00\u4e2a\u65b0\u4e3b\u5b58\u50a8 +message.add.pod.during.zone.creation=\u6bcf\u4e2a\u8d44\u6e90\u57df\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u63d0\u4f9b\u70b9\u3002\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u60a8\u5c06\u5728\u968f\u540e\u7684\u67d0\u4e2a\u6b65\u9aa4\u4e2d\u6dfb\u52a0\u8fd9\u4e9b\u4e3b\u673a\u548c\u670d\u52a1\u5668\u3002\u9996\u5148\uff0c\u8bf7\u4e3a CloudStack \u7684\u5185\u90e8\u7ba1\u7406\u6d41\u91cf\u914d\u7f6e\u4e00\u4e2a\u9884\u7559 IP \u5730\u5740\u8303\u56f4\u3002\u9884\u7559\u7684 IP \u8303\u56f4\u5bf9\u4e91\u4e2d\u7684\u6bcf\u4e2a\u8d44\u6e90\u57df\u6765\u8bf4\u5fc5\u987b\u552f\u4e00\u3002 +message.add.pod=\u4e3a\u8d44\u6e90\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u63d0\u4f9b\u70b9 +message.add.primary.storage=\u4e3a\u8d44\u6e90\u57df \u3001\u63d0\u4f9b\u70b9 \u6dfb\u52a0\u4e00\u4e2a\u65b0\u4e3b\u5b58\u50a8 message.add.primary=\u8bf7\u6307\u5b9a\u4ee5\u4e0b\u53c2\u6570\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u4e3b\u5b58\u50a8 -message.add.region=\u8bf7\u6307\u5b9a\u6dfb\u52a0\u65b0\u533a\u57df\u6240\u9700\u7684\u4fe1\u606f\u3002 -message.add.secondary.storage=\u4e3a\u533a\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u5b58\u50a8 +message.add.region=\u8bf7\u6307\u5b9a\u6dfb\u52a0\u65b0\u5730\u7406\u533a\u57df\u6240\u9700\u7684\u4fe1\u606f\u3002 +message.add.secondary.storage=\u4e3a\u8d44\u6e90\u57df \u6dfb\u52a0\u4e00\u4e2a\u65b0\u5b58\u50a8 message.add.service.offering=\u8bf7\u586b\u5199\u4ee5\u4e0b\u6570\u636e\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u8ba1\u7b97\u65b9\u6848\u3002 message.add.system.service.offering=\u8bf7\u586b\u5199\u4ee5\u4e0b\u6570\u636e\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u7684\u7cfb\u7edf\u670d\u52a1\u65b9\u6848\u3002 message.add.template=\u8bf7\u8f93\u5165\u4ee5\u4e0b\u6570\u636e\u4ee5\u521b\u5efa\u65b0\u6a21\u677f @@ -1420,8 +1583,8 @@ message.adding.Netscaler.provider=\u6b63\u5728\u6dfb\u52a0 Netscaler \u63d0\u4f9 message.additional.networks.desc=\u8bf7\u9009\u62e9\u865a\u62df\u673a\u8981\u8fde\u63a5\u5230\u7684\u5176\u4ed6\u7f51\u7edc\u3002 message.advanced.mode.desc=\u5982\u679c\u60a8\u5e0c\u671b\u542f\u7528 VLAN \u652f\u6301\uff0c\u8bf7\u9009\u62e9\u6b64\u7f51\u7edc\u6a21\u5f0f\u3002\u6b64\u7f51\u7edc\u6a21\u5f0f\u5728\u5141\u8bb8\u7ba1\u7406\u5458\u63d0\u4f9b\u9632\u706b\u5899\u3001VPN \u6216\u8d1f\u8f7d\u5e73\u8861\u5668\u652f\u6301\u7b49\u81ea\u5b9a\u4e49\u7f51\u7edc\u65b9\u6848\u4ee5\u53ca\u542f\u7528\u76f4\u63a5\u7f51\u7edc\u8fde\u63a5\u4e0e\u865a\u62df\u7f51\u7edc\u8fde\u63a5\u7b49\u65b9\u9762\u63d0\u4f9b\u4e86\u6700\u5927\u7684\u7075\u6d3b\u6027\u3002 message.advanced.security.group=\u5982\u679c\u8981\u4f7f\u7528\u5b89\u5168\u7ec4\u63d0\u4f9b\u6765\u5bbe VM \u9694\u79bb\uff0c\u8bf7\u9009\u62e9\u6b64\u6a21\u5f0f\u3002 -message.advanced.virtual=\u5982\u679c\u8981\u4f7f\u7528\u6574\u4e2a\u533a\u57df\u7684 VLAN \u63d0\u4f9b\u6765\u5bbe VM \u9694\u79bb\uff0c\u8bf7\u9009\u62e9\u6b64\u6a21\u5f0f\u3002 -message.after.enable.s3=\u5df2\u914d\u7f6e S3 \u652f\u6301\u7684\u8f85\u52a9\u5b58\u50a8\u3002\u6ce8\u610f\: \u9000\u51fa\u6b64\u9875\u9762\u540e\uff0c\u60a8\u5c06\u65e0\u6cd5\u518d\u6b21\u91cd\u65b0\u914d\u7f6e S3\u3002 +message.advanced.virtual=\u5982\u679c\u8981\u4f7f\u7528\u6574\u4e2a\u8d44\u6e90\u57df\u7684 VLAN \u63d0\u4f9b\u6765\u5bbe VM \u9694\u79bb\uff0c\u8bf7\u9009\u62e9\u6b64\u6a21\u5f0f\u3002 +message.after.enable.s3=\u5df2\u914d\u7f6e S3 \u652f\u6301\u7684\u4e8c\u7ea7\u5b58\u50a8\u3002\u6ce8\u610f\: \u9000\u51fa\u6b64\u9875\u9762\u540e\uff0c\u60a8\u5c06\u65e0\u6cd5\u518d\u6b21\u91cd\u65b0\u914d\u7f6e S3\u3002 message.after.enable.swift=\u5df2\u914d\u7f6e SWIFT\u3002\u6ce8\u610f\: \u9000\u51fa\u6b64\u9875\u9762\u540e\uff0c\u60a8\u5c06\u65e0\u6cd5\u518d\u6b21\u91cd\u65b0\u914d\u7f6e SWIFT\u3002 message.alert.state.detected=\u68c0\u6d4b\u5230\u8b66\u62a5\u72b6\u6001 message.allow.vpn.access=\u8bf7\u8f93\u5165\u8981\u5141\u8bb8\u8fdb\u884c VPN \u8bbf\u95ee\u7684\u7528\u6237\u7684\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002 @@ -1431,11 +1594,11 @@ message.attach.volume=\u8bf7\u586b\u5199\u4ee5\u4e0b\u6570\u636e\u4ee5\u9644\u52 message.basic.mode.desc=\u5982\u679c\u60a8*\u4e0d*\u5e0c\u671b\u542f\u7528\u4efb\u4f55 VLAN \u652f\u6301\uff0c\u8bf7\u9009\u62e9\u6b64\u7f51\u7edc\u6a21\u5f0f\u3002\u5c06\u76f4\u63a5\u4ece\u6b64\u7f51\u7edc\u4e2d\u4e3a\u5728\u6b64\u7f51\u7edc\u6a21\u5f0f\u4e0b\u521b\u5efa\u7684\u6240\u6709\u865a\u62df\u673a\u5b9e\u4f8b\u5206\u914d\u4e00\u4e2a IP\uff0c\u5e76\u4f7f\u7528\u5b89\u5168\u7ec4\u63d0\u4f9b\u5b89\u5168\u6027\u548c\u9694\u79bb\u3002 message.change.offering.confirm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u66f4\u6539\u6b64\u865a\u62df\u5b9e\u4f8b\u7684\u670d\u52a1\u65b9\u6848\u3002 message.change.password=\u8bf7\u66f4\u6539\u60a8\u7684\u5bc6\u7801\u3002 -message.configure.all.traffic.types=\u60a8\u6709\u591a\u4e2a\u7269\u7406\u7f51\u7edc\uff0c\u8bf7\u5355\u51fb\u201c\u7f16\u8f91\u201d\u6309\u94ae\u4e3a\u6bcf\u79cd\u901a\u4fe1\u7c7b\u578b\u914d\u7f6e\u6807\u7b7e\u3002 -message.configuring.guest.traffic=\u6b63\u5728\u914d\u7f6e\u6765\u5bbe\u901a\u4fe1 +message.configure.all.traffic.types=\u60a8\u6709\u591a\u4e2a\u7269\u7406\u7f51\u7edc\uff0c\u8bf7\u5355\u51fb\u201c\u7f16\u8f91\u201d\u6309\u94ae\u4e3a\u6bcf\u79cd\u6d41\u91cf\u7c7b\u578b\u914d\u7f6e\u6807\u7b7e\u3002 +message.configuring.guest.traffic=\u6b63\u5728\u914d\u7f6e\u6765\u5bbe\u6d41\u91cf message.configuring.physical.networks=\u6b63\u5728\u914d\u7f6e\u7269\u7406\u7f51\u7edc -message.configuring.public.traffic=\u6b63\u5728\u914d\u7f6e\u516c\u5171\u901a\u4fe1 -message.configuring.storage.traffic=\u6b63\u5728\u914d\u7f6e\u5b58\u50a8\u901a\u4fe1 +message.configuring.public.traffic=\u6b63\u5728\u914d\u7f6e\u516c\u5171\u6d41\u91cf +message.configuring.storage.traffic=\u6b63\u5728\u914d\u7f6e\u5b58\u50a8\u6d41\u91cf message.confirm.action.force.reconnect=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5f3a\u5236\u91cd\u65b0\u8fde\u63a5\u6b64\u4e3b\u673a\u3002 message.confirm.delete.F5=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 F5 message.confirm.delete.NetScaler=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 NetScaler @@ -1448,7 +1611,7 @@ message.confirm.join.project=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u52a0\u5 message.confirm.remove.IP.range=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64 IP \u8303\u56f4\u3002 message.confirm.shutdown.provider=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5173\u95ed\u6b64\u63d0\u4f9b\u7a0b\u5e8f message.copy.iso.confirm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06 ISO \u590d\u5236\u5230 -message.copy.template=\u5c06\u6a21\u677f XXX \u4ece\u533a\u57df \u590d\u5236\u5230 +message.copy.template=\u5c06\u6a21\u677f XXX \u4ece\u8d44\u6e90\u57df \u590d\u5236\u5230 message.create.template.vm=\u57fa\u4e8e\u6a21\u677f \u521b\u5efa VM message.create.template.volume=\u8bf7\u5148\u6307\u5b9a\u4ee5\u4e0b\u4fe1\u606f\uff0c\u7136\u540e\u518d\u521b\u5efa\u78c1\u76d8\u5377 \u7684\u6a21\u677f\u3002\u521b\u5efa\u6a21\u677f\u53ef\u80fd\u9700\u8981\u51e0\u5206\u949f\u5230\u66f4\u957f\u7684\u65f6\u95f4\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u78c1\u76d8\u5377\u7684\u5927\u5c0f\u3002 message.create.template=\u662f\u5426\u786e\u5b9e\u8981\u521b\u5efa\u6a21\u677f? @@ -1457,10 +1620,10 @@ message.creating.guest.network=\u6b63\u5728\u521b\u5efa\u6765\u5bbe\u7f51\u7edc message.creating.physical.networks=\u6b63\u5728\u521b\u5efa\u7269\u7406\u7f51\u7edc message.creating.pod=\u6b63\u5728\u521b\u5efa\u63d0\u4f9b\u70b9 message.creating.primary.storage=\u6b63\u5728\u521b\u5efa\u4e3b\u5b58\u50a8 -message.creating.secondary.storage=\u6b63\u5728\u521b\u5efa\u8f85\u52a9\u5b58\u50a8 -message.creating.zone=\u6b63\u5728\u521b\u5efa\u533a\u57df +message.creating.secondary.storage=\u6b63\u5728\u521b\u5efa\u4e8c\u7ea7\u5b58\u50a8 +message.creating.zone=\u6b63\u5728\u521b\u5efa\u8d44\u6e90\u57df message.decline.invitation=\u662f\u5426\u786e\u5b9e\u8981\u62d2\u7edd\u6b64\u9879\u76ee\u9080\u8bf7? -message.dedicate.zone=\u6b63\u5728\u5c06\u533a\u57df\u4e13\u6709\u5316 +message.dedicate.zone=\u6b63\u5728\u5c06\u8d44\u6e90\u57df\u4e13\u7528 message.delete.account=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5e10\u6237\u3002 message.delete.affinity.group=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u5173\u8054\u6027\u7ec4\u3002 message.delete.gateway=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u6b64\u7f51\u5173 @@ -1474,8 +1637,8 @@ message.desc.basic.zone=\u63d0\u4f9b\u4e00\u4e2a\u7f51\u7edc\uff0c\u5c06\u76f4\u message.desc.cluster=\u6bcf\u4e2a\u63d0\u4f9b\u70b9\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u7fa4\u96c6\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u7fa4\u96c6\u3002\u7fa4\u96c6\u63d0\u4f9b\u4e86\u4e00\u79cd\u7f16\u7ec4\u4e3b\u673a\u7684\u65b9\u6cd5\u3002\u7fa4\u96c6\u4e2d\u7684\u6240\u6709\u4e3b\u673a\u90fd\u5177\u6709\u76f8\u540c\u7684\u786c\u4ef6\uff0c\u8fd0\u884c\u76f8\u540c\u7684\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\uff0c\u4f4d\u4e8e\u76f8\u540c\u7684\u5b50\u7f51\u4e2d\uff0c\u5e76\u8bbf\u95ee\u76f8\u540c\u7684\u5171\u4eab\u5b58\u50a8\u3002\u6bcf\u4e2a\u7fa4\u96c6\u7531\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u673a\u4ee5\u53ca\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u7ec4\u6210\u3002 message.desc.host=\u6bcf\u4e2a\u7fa4\u96c6\u4e2d\u5fc5\u987b\u81f3\u5c11\u5305\u542b\u4e00\u4e2a\u4e3b\u673a\u4ee5\u4f9b\u6765\u5bbe VM \u5728\u4e0a\u9762\u8fd0\u884c\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u4e3b\u673a\u3002\u8981\u4f7f\u4e3b\u673a\u5728 CloudStack \u4e2d\u8fd0\u884c\uff0c\u5fc5\u987b\u5728\u6b64\u4e3b\u673a\u4e0a\u5b89\u88c5\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u8f6f\u4ef6\uff0c\u4e3a\u5176\u5206\u914d\u4e00\u4e2a IP \u5730\u5740\uff0c\u5e76\u786e\u4fdd\u5c06\u5176\u8fde\u63a5\u5230 CloudStack \u7ba1\u7406\u670d\u52a1\u5668\u3002

\u8bf7\u63d0\u4f9b\u4e3b\u673a\u7684 DNS \u6216 IP \u5730\u5740\u3001\u7528\u6237\u540d(\u901a\u5e38\u4e3a root)\u548c\u5bc6\u7801\uff0c\u4ee5\u53ca\u7528\u4e8e\u5bf9\u4e3b\u673a\u8fdb\u884c\u5206\u7c7b\u7684\u4efb\u4f55\u6807\u7b7e\u3002 message.desc.primary.storage=\u6bcf\u4e2a\u7fa4\u96c6\u4e2d\u5fc5\u987b\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u3002\u4e3b\u5b58\u50a8\u4e2d\u5305\u542b\u5728\u7fa4\u96c6\u4e2d\u7684\u4e3b\u673a\u4e0a\u8fd0\u884c\u7684\u6240\u6709 VM \u7684\u78c1\u76d8\u5377\u3002\u8bf7\u4f7f\u7528\u5e95\u5c42\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u652f\u6301\u7684\u7b26\u5408\u6807\u51c6\u7684\u534f\u8bae\u3002 -message.desc.secondary.storage=\u6bcf\u4e2a\u533a\u57df\u4e2d\u5fc5\u987b\u81f3\u5c11\u5305\u542b\u4e00\u4e2a NFS \u6216\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a NFS \u6216\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\u3002\u8f85\u52a9\u5b58\u50a8\u7528\u4e8e\u5b58\u50a8 VM \u6a21\u677f\u3001ISO \u6620\u50cf\u548c VM \u78c1\u76d8\u5377\u5feb\u7167\u3002\u6b64\u670d\u52a1\u5668\u5fc5\u987b\u5bf9\u533a\u57df\u4e2d\u7684\u6240\u6709\u670d\u52a1\u5668\u53ef\u7528\u3002

\u8bf7\u63d0\u4f9b IP \u5730\u5740\u548c\u5bfc\u51fa\u8def\u5f84\u3002 -message.desc.zone=\u533a\u57df\u662f CloudStack \u4e2d\u6700\u5927\u7684\u7ec4\u7ec7\u5355\u4f4d\uff0c\u4e00\u4e2a\u533a\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u533a\u57df\u53ef\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\u3002\u4e00\u4e2a\u533a\u57df\u7531\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\u4ee5\u53ca\u7531\u533a\u57df\u4e2d\u7684\u6240\u6709\u63d0\u4f9b\u70b9\u5171\u4eab\u7684\u4e00\u4e2a\u8f85\u52a9\u5b58\u50a8\u670d\u52a1\u5668\u7ec4\u6210\uff0c\u5176\u4e2d\u6bcf\u4e2a\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u591a\u4e2a\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u3002 +message.desc.secondary.storage=\u6bcf\u4e2a\u8d44\u6e90\u57df\u4e2d\u5fc5\u987b\u81f3\u5c11\u5305\u542b\u4e00\u4e2a NFS \u6216\u4e8c\u7ea7\u5b58\u50a8\u670d\u52a1\u5668\uff0c\u73b0\u5728\u6211\u4eec\u5c06\u6dfb\u52a0\u7b2c\u4e00\u4e2a NFS \u6216\u4e8c\u7ea7\u5b58\u50a8\u670d\u52a1\u5668\u3002\u4e8c\u7ea7\u5b58\u50a8\u7528\u4e8e\u5b58\u50a8 VM \u6a21\u677f\u3001ISO \u6620\u50cf\u548c VM \u78c1\u76d8\u5377\u5feb\u7167\u3002\u6b64\u670d\u52a1\u5668\u5fc5\u987b\u5bf9\u8d44\u6e90\u57df\u4e2d\u7684\u6240\u6709\u670d\u52a1\u5668\u53ef\u7528\u3002

\u8bf7\u63d0\u4f9b IP \u5730\u5740\u548c\u5bfc\u51fa\u8def\u5f84\u3002 +message.desc.zone=\u8d44\u6e90\u57df\u662f CloudStack \u4e2d\u6700\u5927\u7684\u7ec4\u7ec7\u5355\u4f4d\uff0c\u4e00\u4e2a\u8d44\u6e90\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u8d44\u6e90\u57df\u53ef\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\u3002\u4e00\u4e2a\u8d44\u6e90\u57df\u7531\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\u4ee5\u53ca\u7531\u8d44\u6e90\u57df\u4e2d\u7684\u6240\u6709\u63d0\u4f9b\u70b9\u5171\u4eab\u7684\u4e00\u4e2a\u4e8c\u7ea7\u5b58\u50a8\u670d\u52a1\u5668\u7ec4\u6210\uff0c\u5176\u4e2d\u6bcf\u4e2a\u63d0\u4f9b\u70b9\u4e2d\u5305\u542b\u591a\u4e2a\u4e3b\u673a\u548c\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u3002 message.detach.disk=\u662f\u5426\u786e\u5b9e\u8981\u53d6\u6d88\u9644\u52a0\u6b64\u78c1\u76d8? message.detach.iso.confirm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4ece\u6b64\u865a\u62df\u673a\u4e2d\u53d6\u6d88\u9644\u52a0\u6b64 ISO\u3002 message.disable.account=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528\u6b64\u5e10\u6237\u3002\u7981\u7528\u540e\uff0c\u6b64\u5e10\u6237\u7684\u6240\u6709\u7528\u6237\u5c06\u4e0d\u518d\u6709\u6743\u8bbf\u95ee\u5404\u81ea\u7684\u4e91\u8d44\u6e90\u3002\u6240\u6709\u6b63\u5728\u8fd0\u884c\u7684\u865a\u62df\u673a\u5c06\u7acb\u5373\u5173\u95ed\u3002 @@ -1490,7 +1653,7 @@ message.download.volume=\u8bf7\u5355\u51fb 00000 \u4e0b\u8f7d\ message.edit.account=\u7f16\u8f91(\u201c-1\u201d\u8868\u793a\u5bf9\u8981\u521b\u5efa\u7684\u8d44\u6e90\u6570\u91cf\u6ca1\u6709\u4efb\u4f55\u9650\u5236) message.edit.confirm=\u8bf7\u5148\u786e\u8ba4\u60a8\u6240\u505a\u7684\u66f4\u6539\uff0c\u7136\u540e\u5355\u51fb\u201c\u4fdd\u5b58\u201d\u3002 message.edit.limits=\u8bf7\u6307\u5b9a\u5bf9\u4ee5\u4e0b\u8d44\u6e90\u7684\u9650\u5236\u3002\u201c-1\u201d\u8868\u793a\u4e0d\u9650\u5236\u8981\u521b\u5efa\u7684\u8d44\u6e90\u6570\u3002 -message.edit.traffic.type=\u8bf7\u6307\u5b9a\u60a8\u5e0c\u671b\u4e0e\u6b64\u901a\u4fe1\u7c7b\u578b\u5173\u8054\u7684\u901a\u4fe1\u6807\u7b7e\u3002 +message.edit.traffic.type=\u8bf7\u6307\u5b9a\u60a8\u5e0c\u671b\u4e0e\u6b64\u6d41\u91cf\u7c7b\u578b\u5173\u8054\u7684\u6d41\u91cf\u6807\u7b7e\u3002 message.enable.account=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u5e10\u6237\u3002 message.enable.user=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528\u6b64\u7528\u6237\u3002 message.enable.vpn.access=\u5f53\u524d\u5df2\u5bf9\u6b64 IP \u5730\u5740\u7981\u7528\u4e86 VPN\u3002\u662f\u5426\u8981\u542f\u7528 VPN \u8bbf\u95ee? @@ -1498,51 +1661,51 @@ message.enable.vpn=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5bf9\u6b64 IP \u5 message.enabled.vpn.ip.sec=\u60a8\u7684 IPSec \u9884\u5171\u4eab\u5bc6\u94a5 message.enabled.vpn=\u60a8\u7684 VPN \u8bbf\u95ee\u529f\u80fd\u5f53\u524d\u5df2\u542f\u7528\uff0c\u53ef\u4ee5\u901a\u8fc7 IP \u8fdb\u884c\u8bbf\u95ee message.enabling.security.group.provider=\u6b63\u5728\u542f\u7528\u5b89\u5168\u7ec4\u63d0\u4f9b\u7a0b\u5e8f -message.enabling.zone=\u6b63\u5728\u542f\u7528\u533a\u57df +message.enabling.zone=\u6b63\u5728\u542f\u7528\u8d44\u6e90\u57df message.enter.token=\u8bf7\u8f93\u5165\u60a8\u5728\u9080\u8bf7\u7535\u5b50\u90ae\u4ef6\u4e2d\u6536\u5230\u7684\u4ee4\u724c\u3002 message.generate.keys=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e3a\u6b64\u7528\u6237\u751f\u6210\u65b0\u5bc6\u94a5\u3002 -message.guest.traffic.in.advanced.zone=\u6765\u5bbe\u7f51\u7edc\u901a\u4fe1\u662f\u6307\u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\u6307\u5b9a\u4e00\u4e2a VLAN ID \u8303\u56f4\u53ef\u4f20\u9001\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u7684\u6765\u5bbe\u901a\u4fe1\u3002 -message.guest.traffic.in.basic.zone=\u6765\u5bbe\u7f51\u7edc\u901a\u4fe1\u662f\u6307\u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\u5e94\u6307\u5b9a\u4e00\u4e2a CloudStack \u53ef\u4ee5\u5206\u914d\u7ed9\u6765\u5bbe VM \u7684 IP \u5730\u5740\u8303\u56f4\u3002\u8bf7\u786e\u4fdd\u6b64\u8303\u56f4\u4e0e\u9884\u7559\u7684\u7cfb\u7edf IP \u8303\u56f4\u4e0d\u91cd\u53e0\u3002 +message.guest.traffic.in.advanced.zone=\u6765\u5bbe\u7f51\u7edc\u6d41\u91cf\u662f\u6307\u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\u6307\u5b9a\u4e00\u4e2a VLAN ID \u8303\u56f4\u53ef\u4f20\u9001\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u7684\u6765\u5bbe\u6d41\u91cf\u3002 +message.guest.traffic.in.basic.zone=\u6765\u5bbe\u7f51\u7edc\u6d41\u91cf\u662f\u6307\u6700\u7ec8\u7528\u6237\u865a\u62df\u673a\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\u5e94\u6307\u5b9a\u4e00\u4e2a CloudStack \u53ef\u4ee5\u5206\u914d\u7ed9\u6765\u5bbe VM \u7684 IP \u5730\u5740\u8303\u56f4\u3002\u8bf7\u786e\u4fdd\u6b64\u8303\u56f4\u4e0e\u9884\u7559\u7684\u7cfb\u7edf IP \u8303\u56f4\u4e0d\u91cd\u53e0\u3002 message.installWizard.click.retry=\u8bf7\u5355\u51fb\u6b64\u6309\u94ae\u91cd\u65b0\u5c1d\u8bd5\u542f\u52a8\u3002 -message.installWizard.copy.whatIsACluster=\u7fa4\u96c6\u63d0\u4f9b\u4e86\u4e00\u79cd\u7f16\u7ec4\u4e3b\u673a\u7684\u65b9\u6cd5\u3002\u7fa4\u96c6\u4e2d\u7684\u6240\u6709\u4e3b\u673a\u90fd\u5177\u6709\u76f8\u540c\u7684\u786c\u4ef6\uff0c\u8fd0\u884c\u76f8\u540c\u7684\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\uff0c\u4f4d\u4e8e\u540c\u4e00\u5b50\u7f51\u4e2d\uff0c\u5e76\u8bbf\u95ee\u76f8\u540c\u7684\u5171\u4eab\u5b58\u50a8\u3002\u53ef\u4ee5\u5b9e\u65f6\u5c06\u865a\u62df\u673a\u5b9e\u4f8b(VM)\u4ece\u4e00\u53f0\u4e3b\u673a\u8fc1\u79fb\u5230\u540c\u4e00\u7fa4\u96c6\u5185\u7684\u5176\u4ed6\u4e3b\u673a\uff0c\u800c\u65e0\u9700\u4e2d\u65ad\u5411\u7528\u6237\u63d0\u4f9b\u670d\u52a1\u3002\u7fa4\u96c6\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u7684\u7b2c\u4e09\u5927\u7ec4\u7ec7\u5355\u4f4d\u3002\u7fa4\u96c6\u5305\u542b\u5728\u63d0\u4f9b\u70b9\u4e2d\uff0c\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u533a\u57df\u4e2d\u3002

CloudStack&\#8482; \u5141\u8bb8\u4e91\u90e8\u7f72\u4e2d\u5b58\u5728\u591a\u4e2a\u7fa4\u96c6\uff0c\u4f46\u5bf9\u4e8e\u57fa\u672c\u5b89\u88c5\uff0c\u6211\u4eec\u53ea\u9700\u8981\u4e00\u4e2a\u7fa4\u96c6\u3002 -message.installWizard.copy.whatIsAHost=\u4e3b\u673a\u662f\u6307\u4e00\u53f0\u8ba1\u7b97\u673a\u3002\u4e3b\u673a\u63d0\u4f9b\u8fd0\u884c\u6765\u5bbe\u865a\u62df\u673a\u7684\u8ba1\u7b97\u8d44\u6e90\u3002\u6bcf\u53f0\u4e3b\u673a\u4e0a\u90fd\u5b89\u88c5\u6709\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u8f6f\u4ef6\uff0c\u7528\u4e8e\u7ba1\u7406\u6765\u5bbe VM (\u88f8\u673a\u4e3b\u673a\u9664\u5916\uff0c\u5c06\u5728\u201c\u9ad8\u7ea7\u5b89\u88c5\u6307\u5357\u201d\u4e2d\u8ba8\u8bba\u8fd9\u4e00\u7279\u6b8a\u6848\u4f8b)\u3002\u4f8b\u5982\uff0c\u542f\u7528\u4e86 KVM \u7684 Linux \u670d\u52a1\u5668\u3001Citrix XenServer \u670d\u52a1\u5668\u548c ESXi \u670d\u52a1\u5668\u90fd\u53ef\u7528\u4f5c\u4e3b\u673a\u3002\u5728\u57fa\u672c\u5b89\u88c5\u4e2d\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u4e00\u53f0\u8fd0\u884c XenServer \u7684\u4e3b\u673a\u3002

\u4e3b\u673a\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u6700\u5c0f\u7684\u7ec4\u7ec7\u5355\u4f4d\u3002\u4e3b\u673a\u5305\u542b\u5728\u7fa4\u96c6\u4e2d\uff0c\u7fa4\u96c6\u5305\u542b\u5728\u63d0\u4f9b\u70b9\u4e2d\uff0c\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u533a\u57df\u4e2d\u3002 -message.installWizard.copy.whatIsAPod=\u4e00\u4e2a\u63d0\u4f9b\u70b9\u901a\u5e38\u4ee3\u8868\u4e00\u4e2a\u673a\u67b6\u3002\u540c\u4e00\u63d0\u4f9b\u70b9\u4e2d\u7684\u4e3b\u673a\u4f4d\u4e8e\u540c\u4e00\u5b50\u7f51\u4e2d\u3002

\u63d0\u4f9b\u70b9\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u7684\u7b2c\u4e8c\u5927\u7ec4\u7ec7\u5355\u4f4d\u3002\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u533a\u57df\u4e2d\u3002\u6bcf\u4e2a\u533a\u57df\u4e2d\u53ef\u4ee5\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\uff1b\u5728\u57fa\u672c\u5b89\u88c5\u4e2d\uff0c\u60a8\u7684\u533a\u57df\u4e2d\u5c06\u4ec5\u5305\u542b\u4e00\u4e2a\u63d0\u4f9b\u70b9\u3002 -message.installWizard.copy.whatIsAZone=\u533a\u57df\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u6700\u5927\u7684\u7ec4\u7ec7\u5355\u4f4d\u3002\u867d\u7136\u5141\u8bb8\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u4e2d\u5b58\u5728\u591a\u4e2a\u533a\u57df\uff0c\u4f46\u662f\u4e00\u4e2a\u533a\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u5c06\u57fa\u7840\u67b6\u6784\u7f16\u7ec4\u5230\u533a\u57df\u4e2d\u7684\u597d\u5904\u662f\u53ef\u4ee5\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\u3002\u4f8b\u5982\uff0c\u6bcf\u4e2a\u533a\u57df\u90fd\u53ef\u4ee5\u62e5\u6709\u5404\u81ea\u7684\u7535\u6e90\u4f9b\u5e94\u548c\u7f51\u7edc\u4e0a\u884c\u65b9\u6848\uff0c\u5e76\u4e14\u5404\u533a\u57df\u53ef\u4ee5\u5728\u5730\u7406\u4f4d\u7f6e\u4e0a\u76f8\u9694\u5f88\u8fdc(\u867d\u7136\u5e76\u975e\u5fc5\u987b\u76f8\u9694\u5f88\u8fdc)\u3002 -message.installWizard.copy.whatIsCloudStack=CloudStack&\#8482 \u662f\u4e00\u4e2a\u8f6f\u4ef6\u5e73\u53f0\uff0c\u53ef\u5c06\u8ba1\u7b97\u8d44\u6e90\u96c6\u4e2d\u5728\u4e00\u8d77\u4ee5\u6784\u5efa\u516c\u5171\u3001\u79c1\u6709\u548c\u6df7\u5408\u57fa\u7840\u8bbe\u65bd\u5373\u670d\u52a1(IaaS)\u4e91\u3002CloudStack&\#8482 \u8d1f\u8d23\u7ba1\u7406\u7ec4\u6210\u4e91\u57fa\u7840\u67b6\u6784\u7684\u7f51\u7edc\u3001\u5b58\u50a8\u548c\u8ba1\u7b97\u8282\u70b9\u3002\u4f7f\u7528 CloudStack&\#8482 \u53ef\u4ee5\u90e8\u7f72\u3001\u7ba1\u7406\u548c\u914d\u7f6e\u4e91\u8ba1\u7b97\u73af\u5883\u3002

CloudStack&\#8482 \u901a\u8fc7\u6269\u5c55\u5546\u7528\u786c\u4ef6\u4e0a\u8fd0\u884c\u7684\u6bcf\u4e2a\u865a\u62df\u673a\u6620\u50cf\u7684\u8303\u56f4\uff0c\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5b9e\u65f6\u53ef\u7528\u7684\u4e91\u57fa\u7840\u67b6\u6784\u8f6f\u4ef6\u5806\u6808\u7528\u4e8e\u4ee5\u670d\u52a1\u65b9\u5f0f\u4ea4\u4ed8\u865a\u62df\u6570\u636e\u4e2d\u5fc3\uff0c\u5373\u4ea4\u4ed8\u6784\u5efa\u3001\u90e8\u7f72\u548c\u7ba1\u7406\u591a\u5c42\u6b21\u548c\u591a\u79df\u6237\u4e91\u5e94\u7528\u7a0b\u5e8f\u5fc5\u9700\u7684\u6240\u6709\u7ec4\u4ef6\u3002\u5f00\u6e90\u7248\u672c\u548c Premium \u7248\u672c\u90fd\u5df2\u53ef\u7528\uff0c\u4e14\u63d0\u4f9b\u7684\u529f\u80fd\u51e0\u4e4e\u5b8c\u5168\u76f8\u540c\u3002 -message.installWizard.copy.whatIsPrimaryStorage=CloudStack&\#8482; \u4e91\u57fa\u7840\u67b6\u6784\u4f7f\u7528\u4ee5\u4e0b\u4e24\u79cd\u7c7b\u578b\u7684\u5b58\u50a8\: \u4e3b\u5b58\u50a8\u548c\u8f85\u52a9\u5b58\u50a8\u3002\u8fd9\u4e24\u79cd\u7c7b\u578b\u7684\u5b58\u50a8\u53ef\u4ee5\u662f iSCSI \u6216 NFS \u670d\u52a1\u5668\uff0c\u4e5f\u53ef\u4ee5\u662f\u672c\u5730\u78c1\u76d8\u3002

\u4e3b\u5b58\u50a8\u4e0e\u7fa4\u96c6\u76f8\u5173\u8054\uff0c\u7528\u4e8e\u5b58\u50a8\u8be5\u7fa4\u96c6\u4e2d\u7684\u4e3b\u673a\u4e0a\u6b63\u5728\u8fd0\u884c\u7684\u6240\u6709 VM \u5bf9\u5e94\u7684\u6bcf\u4e2a\u6765\u5bbe VM \u7684\u78c1\u76d8\u5377\u3002\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u901a\u5e38\u4f4d\u4e8e\u9760\u8fd1\u4e3b\u673a\u7684\u4f4d\u7f6e\u3002 -message.installWizard.copy.whatIsSecondaryStorage=\u8f85\u52a9\u5b58\u50a8\u4e0e\u533a\u57df\u76f8\u5173\u8054\uff0c\u7528\u4e8e\u5b58\u50a8\u4ee5\u4e0b\u9879\u76ee\:
  • \u6a21\u677f - \u53ef\u7528\u4e8e\u542f\u52a8 VM \u5e76\u53ef\u4ee5\u5305\u542b\u5176\u4ed6\u914d\u7f6e\u4fe1\u606f(\u4f8b\u5982\uff0c\u5df2\u5b89\u88c5\u7684\u5e94\u7528\u7a0b\u5e8f)\u7684\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf
  • ISO \u6620\u50cf - \u53ef\u91cd\u65b0\u542f\u52a8\u6216\u4e0d\u53ef\u91cd\u65b0\u542f\u52a8\u7684\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf
  • \u78c1\u76d8\u5377\u5feb\u7167 - \u5df2\u4fdd\u5b58\u7684 VM \u6570\u636e\u526f\u672c\uff0c\u53ef\u7528\u4e8e\u6267\u884c\u6570\u636e\u6062\u590d\u6216\u521b\u5efa\u65b0\u6a21\u677f
+message.installWizard.copy.whatIsACluster=\u7fa4\u96c6\u63d0\u4f9b\u4e86\u4e00\u79cd\u7f16\u7ec4\u4e3b\u673a\u7684\u65b9\u6cd5\u3002\u7fa4\u96c6\u4e2d\u7684\u6240\u6709\u4e3b\u673a\u90fd\u5177\u6709\u76f8\u540c\u7684\u786c\u4ef6\uff0c\u8fd0\u884c\u76f8\u540c\u7684\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\uff0c\u4f4d\u4e8e\u540c\u4e00\u5b50\u7f51\u4e2d\uff0c\u5e76\u8bbf\u95ee\u76f8\u540c\u7684\u5171\u4eab\u5b58\u50a8\u3002\u53ef\u4ee5\u5b9e\u65f6\u5c06\u865a\u62df\u673a\u5b9e\u4f8b(VM)\u4ece\u4e00\u53f0\u4e3b\u673a\u8fc1\u79fb\u5230\u540c\u4e00\u7fa4\u96c6\u5185\u7684\u5176\u4ed6\u4e3b\u673a\uff0c\u800c\u65e0\u9700\u4e2d\u65ad\u5411\u7528\u6237\u63d0\u4f9b\u670d\u52a1\u3002\u7fa4\u96c6\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u7684\u7b2c\u4e09\u5927\u7ec4\u7ec7\u5355\u4f4d\u3002\u7fa4\u96c6\u5305\u542b\u5728\u63d0\u4f9b\u70b9\u4e2d\uff0c\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u8d44\u6e90\u57df\u4e2d\u3002

CloudStack&\#8482; \u5141\u8bb8\u4e91\u90e8\u7f72\u4e2d\u5b58\u5728\u591a\u4e2a\u7fa4\u96c6\uff0c\u4f46\u5bf9\u4e8e\u57fa\u672c\u5b89\u88c5\uff0c\u6211\u4eec\u53ea\u9700\u8981\u4e00\u4e2a\u7fa4\u96c6\u3002 +message.installWizard.copy.whatIsAHost=\u4e3b\u673a\u662f\u6307\u4e00\u53f0\u8ba1\u7b97\u673a\u3002\u4e3b\u673a\u63d0\u4f9b\u8fd0\u884c\u6765\u5bbe\u865a\u62df\u673a\u7684\u8ba1\u7b97\u8d44\u6e90\u3002\u6bcf\u53f0\u4e3b\u673a\u4e0a\u90fd\u5b89\u88c5\u6709\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u8f6f\u4ef6\uff0c\u7528\u4e8e\u7ba1\u7406\u6765\u5bbe VM (\u88f8\u673a\u4e3b\u673a\u9664\u5916\uff0c\u5c06\u5728\u201c\u9ad8\u7ea7\u5b89\u88c5\u6307\u5357\u201d\u4e2d\u8ba8\u8bba\u8fd9\u4e00\u7279\u6b8a\u6848\u4f8b)\u3002\u4f8b\u5982\uff0c\u542f\u7528\u4e86 KVM \u7684 Linux \u670d\u52a1\u5668\u3001Citrix XenServer \u670d\u52a1\u5668\u548c ESXi \u670d\u52a1\u5668\u90fd\u53ef\u7528\u4f5c\u4e3b\u673a\u3002\u5728\u57fa\u672c\u5b89\u88c5\u4e2d\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u4e00\u53f0\u8fd0\u884c XenServer \u7684\u4e3b\u673a\u3002

\u4e3b\u673a\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u6700\u5c0f\u7684\u7ec4\u7ec7\u5355\u4f4d\u3002\u4e3b\u673a\u5305\u542b\u5728\u7fa4\u96c6\u4e2d\uff0c\u7fa4\u96c6\u5305\u542b\u5728\u63d0\u4f9b\u70b9\u4e2d\uff0c\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u8d44\u6e90\u57df\u4e2d\u3002 +message.installWizard.copy.whatIsAPod=\u4e00\u4e2a\u63d0\u4f9b\u70b9\u901a\u5e38\u4ee3\u8868\u4e00\u4e2a\u673a\u67b6\u3002\u540c\u4e00\u63d0\u4f9b\u70b9\u4e2d\u7684\u4e3b\u673a\u4f4d\u4e8e\u540c\u4e00\u5b50\u7f51\u4e2d\u3002

\u63d0\u4f9b\u70b9\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u7684\u7b2c\u4e8c\u5927\u7ec4\u7ec7\u5355\u4f4d\u3002\u63d0\u4f9b\u70b9\u5305\u542b\u5728\u8d44\u6e90\u57df\u4e2d\u3002\u6bcf\u4e2a\u8d44\u6e90\u57df\u4e2d\u53ef\u4ee5\u5305\u542b\u4e00\u4e2a\u6216\u591a\u4e2a\u63d0\u4f9b\u70b9\uff1b\u5728\u57fa\u672c\u5b89\u88c5\u4e2d\uff0c\u60a8\u7684\u8d44\u6e90\u57df\u4e2d\u5c06\u4ec5\u5305\u542b\u4e00\u4e2a\u63d0\u4f9b\u70b9\u3002 +message.installWizard.copy.whatIsAZone=\u8d44\u6e90\u57df\u662f CloudStack&\#8482; \u90e8\u7f72\u4e2d\u6700\u5927\u7684\u7ec4\u7ec7\u5355\u4f4d\u3002\u867d\u7136\u5141\u8bb8\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u4e2d\u5b58\u5728\u591a\u4e2a\u8d44\u6e90\u57df\uff0c\u4f46\u662f\u4e00\u4e2a\u8d44\u6e90\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u5c06\u57fa\u7840\u67b6\u6784\u7f16\u7ec4\u5230\u8d44\u6e90\u57df\u4e2d\u7684\u597d\u5904\u662f\u53ef\u4ee5\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\u3002\u4f8b\u5982\uff0c\u6bcf\u4e2a\u8d44\u6e90\u57df\u90fd\u53ef\u4ee5\u62e5\u6709\u5404\u81ea\u7684\u7535\u6e90\u4f9b\u5e94\u548c\u7f51\u7edc\u4e0a\u884c\u65b9\u6848\uff0c\u5e76\u4e14\u5404\u8d44\u6e90\u57df\u53ef\u4ee5\u5728\u5730\u7406\u4f4d\u7f6e\u4e0a\u76f8\u9694\u5f88\u8fdc(\u867d\u7136\u5e76\u975e\u5fc5\u987b\u76f8\u9694\u5f88\u8fdc)\u3002 +message.installWizard.copy.whatIsCloudStack=CloudStack&\#8482 \u662f\u4e00\u4e2a\u8f6f\u4ef6\u5e73\u53f0\uff0c\u53ef\u5c06\u8ba1\u7b97\u8d44\u6e90\u96c6\u4e2d\u5728\u4e00\u8d77\u4ee5\u6784\u5efa\u516c\u6709\u3001\u79c1\u6709\u548c\u6df7\u5408\u57fa\u7840\u8bbe\u65bd\u5373\u670d\u52a1(IaaS)\u4e91\u3002CloudStack&\#8482 \u8d1f\u8d23\u7ba1\u7406\u7ec4\u6210\u4e91\u57fa\u7840\u67b6\u6784\u7684\u7f51\u7edc\u3001\u5b58\u50a8\u548c\u8ba1\u7b97\u8282\u70b9\u3002\u4f7f\u7528 CloudStack&\#8482 \u53ef\u4ee5\u90e8\u7f72\u3001\u7ba1\u7406\u548c\u914d\u7f6e\u4e91\u8ba1\u7b97\u73af\u5883\u3002

CloudStack&\#8482 \u901a\u8fc7\u6269\u5c55\u5546\u7528\u786c\u4ef6\u4e0a\u8fd0\u884c\u7684\u6bcf\u4e2a\u865a\u62df\u673a\u6620\u50cf\u7684\u8303\u56f4\uff0c\u63d0\u4f9b\u4e86\u4e00\u4e2a\u5b9e\u65f6\u53ef\u7528\u7684\u4e91\u57fa\u7840\u67b6\u6784\u8f6f\u4ef6\u5806\u6808\u7528\u4e8e\u4ee5\u670d\u52a1\u65b9\u5f0f\u4ea4\u4ed8\u865a\u62df\u6570\u636e\u4e2d\u5fc3\uff0c\u5373\u4ea4\u4ed8\u6784\u5efa\u3001\u90e8\u7f72\u548c\u7ba1\u7406\u591a\u5c42\u6b21\u548c\u591a\u79df\u6237\u4e91\u5e94\u7528\u7a0b\u5e8f\u5fc5\u9700\u7684\u6240\u6709\u7ec4\u4ef6\u3002\u5f00\u6e90\u7248\u672c\u548c Premium \u7248\u672c\u90fd\u5df2\u53ef\u7528\uff0c\u4e14\u63d0\u4f9b\u7684\u529f\u80fd\u51e0\u4e4e\u5b8c\u5168\u76f8\u540c\u3002 +message.installWizard.copy.whatIsPrimaryStorage=CloudStack&\#8482; \u4e91\u57fa\u7840\u67b6\u6784\u4f7f\u7528\u4ee5\u4e0b\u4e24\u79cd\u7c7b\u578b\u7684\u5b58\u50a8\: \u4e3b\u5b58\u50a8\u548c\u4e8c\u7ea7\u5b58\u50a8\u3002\u8fd9\u4e24\u79cd\u7c7b\u578b\u7684\u5b58\u50a8\u53ef\u4ee5\u662f iSCSI \u6216 NFS \u670d\u52a1\u5668\uff0c\u4e5f\u53ef\u4ee5\u662f\u672c\u5730\u78c1\u76d8\u3002

\u4e3b\u5b58\u50a8\u4e0e\u7fa4\u96c6\u76f8\u5173\u8054\uff0c\u7528\u4e8e\u5b58\u50a8\u8be5\u7fa4\u96c6\u4e2d\u7684\u4e3b\u673a\u4e0a\u6b63\u5728\u8fd0\u884c\u7684\u6240\u6709 VM \u5bf9\u5e94\u7684\u6bcf\u4e2a\u6765\u5bbe VM \u7684\u78c1\u76d8\u5377\u3002\u4e3b\u5b58\u50a8\u670d\u52a1\u5668\u901a\u5e38\u4f4d\u4e8e\u9760\u8fd1\u4e3b\u673a\u7684\u4f4d\u7f6e\u3002 +message.installWizard.copy.whatIsSecondaryStorage=\u4e8c\u7ea7\u5b58\u50a8\u4e0e\u8d44\u6e90\u57df\u76f8\u5173\u8054\uff0c\u7528\u4e8e\u5b58\u50a8\u4ee5\u4e0b\u9879\u76ee\:
  • \u6a21\u677f - \u53ef\u7528\u4e8e\u542f\u52a8 VM \u5e76\u53ef\u4ee5\u5305\u542b\u5176\u4ed6\u914d\u7f6e\u4fe1\u606f(\u4f8b\u5982\uff0c\u5df2\u5b89\u88c5\u7684\u5e94\u7528\u7a0b\u5e8f)\u7684\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf
  • ISO \u6620\u50cf - \u53ef\u91cd\u65b0\u542f\u52a8\u6216\u4e0d\u53ef\u91cd\u65b0\u542f\u52a8\u7684\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf
  • \u78c1\u76d8\u5377\u5feb\u7167 - \u5df2\u4fdd\u5b58\u7684 VM \u6570\u636e\u526f\u672c\uff0c\u53ef\u7528\u4e8e\u6267\u884c\u6570\u636e\u6062\u590d\u6216\u521b\u5efa\u65b0\u6a21\u677f
message.installWizard.now.building=\u73b0\u5728\u6b63\u5728\u6784\u5efa\u60a8\u7684\u4e91... message.installWizard.tooltip.addCluster.name=\u7fa4\u96c6\u7684\u540d\u79f0\u3002\u6b64\u540d\u79f0\u53ef\u4ee5\u662f\u60a8\u9009\u62e9\u7684\u6587\u672c\uff0c\u4e14\u672a\u7531 CloudStack \u4f7f\u7528\u3002 message.installWizard.tooltip.addHost.hostname=\u4e3b\u673a\u7684 DNS \u540d\u79f0\u6216 IP \u5730\u5740\u3002 message.installWizard.tooltip.addHost.password=\u6b64\u4e3a\u4e0a\u8ff0\u7528\u6237\u7684\u5bc6\u7801(\u6765\u81ea XenServer \u5b89\u88c5)\u3002 message.installWizard.tooltip.addHost.username=\u901a\u5e38\u4e3a root\u3002 message.installWizard.tooltip.addPod.name=\u63d0\u4f9b\u70b9\u7684\u540d\u79f0 -message.installWizard.tooltip.addPod.reservedSystemEndIp=\u6b64\u4e3a CloudStack \u7528\u4e8e\u7ba1\u7406\u8f85\u52a9\u5b58\u50a8 VM \u548c\u63a7\u5236\u53f0\u4ee3\u7406 VM \u7684\u4e13\u7528\u7f51\u7edc\u4e2d\u7684 IP \u8303\u56f4\u3002\u8fd9\u4e9b IP \u5730\u5740\u6765\u81ea\u4e0e\u8ba1\u7b97\u670d\u52a1\u5668\u76f8\u540c\u7684\u5b50\u7f51\u3002 +message.installWizard.tooltip.addPod.reservedSystemEndIp=\u6b64\u4e3a CloudStack \u7528\u4e8e\u7ba1\u7406\u4e8c\u7ea7\u5b58\u50a8 VM \u548c\u63a7\u5236\u53f0\u4ee3\u7406 VM \u7684\u4e13\u7528\u7f51\u7edc\u4e2d\u7684 IP \u8303\u56f4\u3002\u8fd9\u4e9b IP \u5730\u5740\u6765\u81ea\u4e0e\u8ba1\u7b97\u670d\u52a1\u5668\u76f8\u540c\u7684\u5b50\u7f51\u3002 message.installWizard.tooltip.addPod.reservedSystemGateway=\u8be5\u63d0\u4f9b\u70b9\u4e2d\u7684\u4e3b\u673a\u7f51\u5173\u3002 message.installWizard.tooltip.addPod.reservedSystemNetmask=\u6765\u5bbe\u5c06\u8981\u4f7f\u7528\u7684\u5b50\u7f51\u4e0a\u6b63\u5728\u4f7f\u7528\u7684\u7f51\u7edc\u63a9\u7801\u3002 -message.installWizard.tooltip.addPod.reservedSystemStartIp=\u6b64\u4e3a CloudStack \u7528\u4e8e\u7ba1\u7406\u8f85\u52a9\u5b58\u50a8 VM \u548c\u63a7\u5236\u53f0\u4ee3\u7406 VM \u7684\u4e13\u7528\u7f51\u7edc\u4e2d\u7684 IP \u8303\u56f4\u3002\u8fd9\u4e9b IP \u5730\u5740\u6765\u81ea\u4e0e\u8ba1\u7b97\u670d\u52a1\u5668\u76f8\u540c\u7684\u5b50\u7f51\u3002 +message.installWizard.tooltip.addPod.reservedSystemStartIp=\u6b64\u4e3a CloudStack \u7528\u4e8e\u7ba1\u7406\u4e8c\u7ea7\u5b58\u50a8 VM \u548c\u63a7\u5236\u53f0\u4ee3\u7406 VM \u7684\u4e13\u7528\u7f51\u7edc\u4e2d\u7684 IP \u8303\u56f4\u3002\u8fd9\u4e9b IP \u5730\u5740\u6765\u81ea\u4e0e\u8ba1\u7b97\u670d\u52a1\u5668\u76f8\u540c\u7684\u5b50\u7f51\u3002 message.installWizard.tooltip.addPrimaryStorage.name=\u5b58\u50a8\u8bbe\u5907\u7684\u540d\u79f0\u3002 -message.installWizard.tooltip.addPrimaryStorage.path=(\u9002\u7528\u4e8e NFS)\u5728 NFS \u4e2d\uff0c\u6b64\u8def\u5f84\u4e3a\u670d\u52a1\u5668\u7684\u5bfc\u51fa\u8def\u5f84\u3002\u8def\u5f84(\u9488\u5bf9 SharedMountPoint)\u3002\u5bf9\u4e8e KVM\uff0c\u6b64\u8def\u5f84\u4e3a\u88c5\u8f7d\u4e86\u8f85\u52a9\u5b58\u50a8\u7684\u6bcf\u4e2a\u4e3b\u673a\u4e0a\u7684\u8def\u5f84\u3002\u4f8b\u5982\uff0c/mnt/primary\u3002 +message.installWizard.tooltip.addPrimaryStorage.path=(\u9002\u7528\u4e8e NFS)\u5728 NFS \u4e2d\uff0c\u6b64\u8def\u5f84\u4e3a\u670d\u52a1\u5668\u7684\u5bfc\u51fa\u8def\u5f84\u3002\u8def\u5f84(\u9488\u5bf9 SharedMountPoint)\u3002\u5bf9\u4e8e KVM\uff0c\u6b64\u8def\u5f84\u4e3a\u88c5\u8f7d\u4e86\u4e8c\u7ea7\u5b58\u50a8\u7684\u6bcf\u4e2a\u4e3b\u673a\u4e0a\u7684\u8def\u5f84\u3002\u4f8b\u5982\uff0c/mnt/primary\u3002 message.installWizard.tooltip.addPrimaryStorage.server=(\u9002\u7528\u4e8e NFS\u3001iSCSI \u6216 PreSetup)\u5b58\u50a8\u8bbe\u5907\u7684 IP \u5730\u5740\u6216 DNS \u540d\u79f0\u3002 -message.installWizard.tooltip.addSecondaryStorage.nfsServer=\u6258\u7ba1\u8f85\u52a9\u5b58\u50a8\u7684 NFS \u670d\u52a1\u5668\u7684 IP \u5730\u5740 +message.installWizard.tooltip.addSecondaryStorage.nfsServer=\u6258\u7ba1\u4e8c\u7ea7\u5b58\u50a8\u7684 NFS \u670d\u52a1\u5668\u7684 IP \u5730\u5740 message.installWizard.tooltip.addSecondaryStorage.path=\u5bfc\u51fa\u8def\u5f84(\u4f4d\u4e8e\u4e0a\u8ff0\u6307\u5b9a\u670d\u52a1\u5668\u4e0a) -message.installWizard.tooltip.addZone.dns1=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u533a\u57df\u4e2d\u7684\u6765\u5bbe VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u60a8\u7a0d\u540e\u8981\u6dfb\u52a0\u7684\u516c\u7528\u7f51\u7edc\u8fdb\u884c\u8bbf\u95ee\u3002\u6b64\u533a\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 -message.installWizard.tooltip.addZone.dns2=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u533a\u57df\u4e2d\u7684\u6765\u5bbe VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u60a8\u7a0d\u540e\u8981\u6dfb\u52a0\u7684\u516c\u7528\u7f51\u7edc\u8fdb\u884c\u8bbf\u95ee\u3002\u6b64\u533a\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 -message.installWizard.tooltip.addZone.internaldns1=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u533a\u57df\u4e2d\u7684\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u7cfb\u7edf VM \u7684\u4e13\u7528\u7f51\u7edc\u63a5\u53e3\u8fdb\u884c\u8bbf\u95ee\u3002\u60a8\u4e3a\u63d0\u4f9b\u70b9\u63d0\u4f9b\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 -message.installWizard.tooltip.addZone.internaldns2=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u533a\u57df\u4e2d\u7684\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u7cfb\u7edf VM \u7684\u4e13\u7528\u7f51\u7edc\u63a5\u53e3\u8fdb\u884c\u8bbf\u95ee\u3002\u60a8\u4e3a\u63d0\u4f9b\u70b9\u63d0\u4f9b\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 -message.installWizard.tooltip.addZone.name=\u533a\u57df\u540d\u79f0 +message.installWizard.tooltip.addZone.dns1=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u6765\u5bbe VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u60a8\u7a0d\u540e\u8981\u6dfb\u52a0\u7684\u516c\u7528\u7f51\u7edc\u8fdb\u884c\u8bbf\u95ee\u3002\u6b64\u8d44\u6e90\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 +message.installWizard.tooltip.addZone.dns2=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u6765\u5bbe VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u60a8\u7a0d\u540e\u8981\u6dfb\u52a0\u7684\u516c\u7528\u7f51\u7edc\u8fdb\u884c\u8bbf\u95ee\u3002\u6b64\u8d44\u6e90\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 +message.installWizard.tooltip.addZone.internaldns1=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u7cfb\u7edf VM \u7684\u4e13\u7528\u7f51\u7edc\u63a5\u53e3\u8fdb\u884c\u8bbf\u95ee\u3002\u60a8\u4e3a\u63d0\u4f9b\u70b9\u63d0\u4f9b\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 +message.installWizard.tooltip.addZone.internaldns2=\u8fd9\u4e9b\u670d\u52a1\u5668\u662f\u4f9b\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\uff0c\u5c06\u901a\u8fc7\u7cfb\u7edf VM \u7684\u4e13\u7528\u7f51\u7edc\u63a5\u53e3\u8fdb\u884c\u8bbf\u95ee\u3002\u60a8\u4e3a\u63d0\u4f9b\u70b9\u63d0\u4f9b\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u5728\u6b64\u5904\u6307\u5b9a\u7684 DNS \u670d\u52a1\u5668\u3002 +message.installWizard.tooltip.addZone.name=\u8d44\u6e90\u57df\u540d\u79f0 message.installWizard.tooltip.configureGuestTraffic.description=\u60a8\u7684\u7f51\u7edc\u8bf4\u660e -message.installWizard.tooltip.configureGuestTraffic.guestEndIp=\u80fd\u591f\u5206\u914d\u7ed9\u6b64\u533a\u57df\u4e2d\u7684\u6765\u5bbe\u7684 IP \u5730\u5740\u8303\u56f4\u3002\u5982\u679c\u4f7f\u7528\u4e00\u4e2a NIC\uff0c\u8fd9\u4e9b IP \u5e94\u4f4d\u4e8e\u4e0e\u63d0\u4f9b\u70b9 CIDR \u76f8\u540c\u7684 CIDR \u4e2d\u3002 +message.installWizard.tooltip.configureGuestTraffic.guestEndIp=\u80fd\u591f\u5206\u914d\u7ed9\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u6765\u5bbe\u7684 IP \u5730\u5740\u8303\u56f4\u3002\u5982\u679c\u4f7f\u7528\u4e00\u4e2a NIC\uff0c\u8fd9\u4e9b IP \u5e94\u4f4d\u4e8e\u4e0e\u63d0\u4f9b\u70b9 CIDR \u76f8\u540c\u7684 CIDR \u4e2d\u3002 message.installWizard.tooltip.configureGuestTraffic.guestGateway=\u6765\u5bbe\u5e94\u4f7f\u7528\u7684\u7f51\u5173 message.installWizard.tooltip.configureGuestTraffic.guestNetmask=\u6765\u5bbe\u5e94\u4f7f\u7528\u7684\u5b50\u7f51\u4e0a\u6b63\u5728\u4f7f\u7528\u7684\u7f51\u7edc\u63a9\u7801 -message.installWizard.tooltip.configureGuestTraffic.guestStartIp=\u80fd\u591f\u5206\u914d\u7ed9\u6b64\u533a\u57df\u4e2d\u7684\u6765\u5bbe\u7684 IP \u5730\u5740\u8303\u56f4\u3002\u5982\u679c\u4f7f\u7528\u4e00\u4e2a NIC\uff0c\u8fd9\u4e9b IP \u5e94\u4f4d\u4e8e\u4e0e\u63d0\u4f9b\u70b9 CIDR \u76f8\u540c\u7684 CIDR \u4e2d\u3002 +message.installWizard.tooltip.configureGuestTraffic.guestStartIp=\u80fd\u591f\u5206\u914d\u7ed9\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u6765\u5bbe\u7684 IP \u5730\u5740\u8303\u56f4\u3002\u5982\u679c\u4f7f\u7528\u4e00\u4e2a NIC\uff0c\u8fd9\u4e9b IP \u5e94\u4f4d\u4e8e\u4e0e\u63d0\u4f9b\u70b9 CIDR \u76f8\u540c\u7684 CIDR \u4e2d\u3002 message.installWizard.tooltip.configureGuestTraffic.name=\u60a8\u7684\u7f51\u7edc\u540d\u79f0 message.instanceWizard.noTemplates=\u60a8\u6ca1\u6709\u4efb\u4f55\u53ef\u7528\u6a21\u677f\uff1b\u8bf7\u6dfb\u52a0\u4e00\u4e2a\u517c\u5bb9\u7684\u6a21\u677f\uff0c\u7136\u540e\u91cd\u65b0\u542f\u52a8\u5b9e\u4f8b\u5411\u5bfc\u3002 message.ip.address.changed=\u60a8\u7684 IP \u5730\u5740\u53ef\u80fd\u5df2\u53d1\u751f\u53d8\u5316\uff1b\u662f\u5426\u8981\u5237\u65b0\u6b64\u5217\u8868? \u8bf7\u6ce8\u610f\uff0c\u5237\u65b0\u6b64\u5217\u8868\u65f6\uff0c\u201c\u8be6\u7ec6\u4fe1\u606f\u201d\u7a97\u683c\u5c06\u5173\u95ed\u3002 message.iso.desc=\u78c1\u76d8\u6620\u50cf\uff0c\u5176\u4e2d\u5305\u542b\u64cd\u4f5c\u7cfb\u7edf\u7684\u6570\u636e\u6216\u53ef\u542f\u52a8\u4ecb\u8d28 message.join.project=\u60a8\u73b0\u5728\u5df2\u52a0\u5165\u4e86\u4e00\u4e2a\u9879\u76ee\u3002\u8bf7\u5207\u6362\u5230\u201c\u9879\u76ee\u89c6\u56fe\u201d\u4ee5\u67e5\u770b\u9879\u76ee\u3002 message.launch.vm.on.private.network=\u662f\u5426\u8981\u5728\u60a8\u7684\u79c1\u4eba\u4e13\u7528\u7f51\u7edc\u4e2d\u542f\u52a8\u5b9e\u4f8b? -message.launch.zone=\u533a\u57df\u5df2\u51c6\u5907\u5c31\u7eea\uff0c\u53ef\u968f\u65f6\u542f\u52a8\uff1b\u8bf7\u7ee7\u7eed\u6267\u884c\u4e0b\u4e00\u6b65\u9aa4\u3002 +message.launch.zone=\u8d44\u6e90\u57df\u5df2\u51c6\u5907\u5c31\u7eea\uff0c\u53ef\u968f\u65f6\u542f\u52a8\uff1b\u8bf7\u7ee7\u7eed\u6267\u884c\u4e0b\u4e00\u6b65\u9aa4\u3002 message.lock.account=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u9501\u5b9a\u6b64\u5e10\u6237\u3002\u901a\u8fc7\u9501\u5b9a\u6b64\u5e10\u6237\uff0c\u6b64\u5e10\u6237\u7684\u6240\u6709\u7528\u6237\u5c06\u4e0d\u518d\u80fd\u591f\u7ba1\u7406\u5404\u81ea\u7684\u4e91\u8d44\u6e90\uff0c\u4f46\u4ecd\u7136\u53ef\u4ee5\u8bbf\u95ee\u73b0\u6709\u8d44\u6e90\u3002 message.migrate.instance.confirm=\u8bf7\u786e\u8ba4\u8981\u5c06\u865a\u62df\u5b9e\u4f8b\u8fc1\u79fb\u5230\u7684\u4e3b\u673a\u3002 message.migrate.instance.to.host=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5b9e\u4f8b\u8fc1\u79fb\u5230\u5176\u4ed6\u4e3b\u673a\u3002 @@ -1551,7 +1714,7 @@ message.migrate.router.confirm=\u8bf7\u786e\u8ba4\u60a8\u8981\u5c06\u8def\u7531\ message.migrate.systemvm.confirm=\u8bf7\u786e\u8ba4\u60a8\u8981\u5c06\u7cfb\u7edf VM \u8fc1\u79fb\u5230\u7684\u4e3b\u673a\: message.migrate.volume=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5c06\u5377\u8fc1\u79fb\u5230\u5176\u4ed6\u4e3b\u5b58\u50a8\u3002 message.new.user=\u8bf7\u6307\u5b9a\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u5411\u5e10\u6237\u4e2d\u6dfb\u52a0\u4e00\u4e2a\u65b0\u7528\u6237 -message.no.network.support.configuration.not.true=\u60a8\u7684\u6240\u6709\u533a\u57df\u90fd\u672a\u542f\u7528\u5b89\u5168\u7ec4\uff0c\u56e0\u6b64\u65e0\u5176\u4ed6\u7f51\u7edc\u529f\u80fd\u3002\u8bf7\u7ee7\u7eed\u6267\u884c\u6b65\u9aa4 5\u3002 +message.no.network.support.configuration.not.true=\u60a8\u7684\u6240\u6709\u8d44\u6e90\u57df\u90fd\u672a\u542f\u7528\u5b89\u5168\u7ec4\uff0c\u56e0\u6b64\u65e0\u5176\u4ed6\u7f51\u7edc\u529f\u80fd\u3002\u8bf7\u7ee7\u7eed\u6267\u884c\u6b65\u9aa4 5\u3002 message.no.network.support=\u60a8\u9009\u62e9\u7684\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f vSphere \u6ca1\u6709\u4efb\u4f55\u5176\u4ed6\u7f51\u7edc\u529f\u80fd\u3002\u8bf7\u7ee7\u7eed\u6267\u884c\u6b65\u9aa4 5\u3002 message.no.projects.adminOnly=\u60a8\u6ca1\u6709\u4efb\u4f55\u9879\u76ee\u3002
\u8bf7\u8981\u6c42\u7ba1\u7406\u5458\u521b\u5efa\u4e00\u4e2a\u65b0\u9879\u76ee\u3002 message.no.projects=\u60a8\u6ca1\u6709\u4efb\u4f55\u9879\u76ee\u3002
\u8bf7\u4ece\u201c\u9879\u76ee\u201d\u90e8\u5206\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u9879\u76ee\u3002 @@ -1559,20 +1722,20 @@ message.number.clusters=

\u7fa4\u96c6\u6570

message.number.hosts=

\u4e3b\u673a\u6570

message.number.pods=

\u63d0\u4f9b\u70b9\u6570

message.number.storage=

\u4e3b\u5b58\u50a8\u5377\u6570

-message.number.zones=

\u533a\u57df\u6570

+message.number.zones=

\u8d44\u6e90\u57df\u6570

message.pending.projects.1=\u60a8\u6709\u5f85\u5b9a\u9879\u76ee\u9080\u8bf7\: message.pending.projects.2=\u8981\u67e5\u770b\uff0c\u8bf7\u8f6c\u81f3\u201c\u9879\u76ee\u201d\u90e8\u5206\uff0c\u7136\u540e\u4ece\u4e0b\u62c9\u5217\u8868\u4e2d\u9009\u62e9\u201c\u9080\u8bf7\u201d\u3002 -message.please.add.at.lease.one.traffic.range=\u8bf7\u81f3\u5c11\u6dfb\u52a0\u4e00\u4e2a\u901a\u4fe1\u8303\u56f4\u3002 +message.please.add.at.lease.one.traffic.range=\u8bf7\u81f3\u5c11\u6dfb\u52a0\u4e00\u4e2a\u6d41\u91cf\u8303\u56f4\u3002 message.please.proceed=\u8bf7\u7ee7\u7eed\u6267\u884c\u4e0b\u4e2a\u6b65\u9aa4\u3002 -message.please.select.a.configuration.for.your.zone=\u8bf7\u4e3a\u60a8\u7684\u533a\u57df\u9009\u62e9\u4e00\u79cd\u914d\u7f6e\u3002 -message.please.select.a.different.public.and.management.network.before.removing=\u8bf7\u5148\u9009\u62e9\u5176\u4ed6\u516c\u5171\u7ba1\u7406\u7f51\u7edc\uff0c\u7136\u540e\u518d\u5220\u9664 +message.please.select.a.configuration.for.your.zone=\u8bf7\u4e3a\u60a8\u7684\u8d44\u6e90\u57df\u9009\u62e9\u4e00\u79cd\u914d\u7f6e\u3002 +message.please.select.a.different.public.and.management.network.before.removing=\u8bf7\u5148\u9009\u62e9\u5176\u4ed6\u516c\u7528\u7ba1\u7406\u7f51\u7edc\uff0c\u7136\u540e\u518d\u5220\u9664 message.please.select.networks=\u8bf7\u4e3a\u60a8\u7684\u865a\u62df\u673a\u9009\u62e9\u7f51\u7edc\u3002 -message.please.wait.while.zone.is.being.created=\u6b63\u5728\u521b\u5efa\u533a\u57df\uff0c\u8bf7\u7a0d\u5019\uff1b\u6b64\u64cd\u4f5c\u53ef\u80fd\u9700\u8981\u4e00\u6bb5\u65f6\u95f4\u624d\u80fd\u5b8c\u6210... +message.please.wait.while.zone.is.being.created=\u6b63\u5728\u521b\u5efa\u8d44\u6e90\u57df\uff0c\u8bf7\u7a0d\u5019\uff1b\u6b64\u64cd\u4f5c\u53ef\u80fd\u9700\u8981\u4e00\u6bb5\u65f6\u95f4\u624d\u80fd\u5b8c\u6210... message.project.invite.sent=\u53d1\u9001\u7ed9\u7528\u6237\u7684\u9080\u8bf7\uff1b\u7528\u6237\u63a5\u53d7\u9080\u8bf7\u540e\uff0c\u5c06\u52a0\u5165\u5230\u9879\u76ee\u4e2d -message.public.traffic.in.advanced.zone=\u4e91\u4e2d\u7684 VM \u8bbf\u95ee Internet \u65f6\u5c06\u751f\u6210\u516c\u5171\u901a\u4fe1\uff0c\u4f46\u5fc5\u987b\u5206\u914d\u53ef\u516c\u5f00\u8bbf\u95ee\u7684 IP \u624d\u80fd\u5b9e\u73b0\u3002\u6700\u7ec8\u7528\u6237\u53ef\u4ee5\u4f7f\u7528 CloudStack UI \u83b7\u53d6\u8fd9\u4e9b IP\uff0c\u4ee5\u5728\u5176\u6765\u5bbe\u7f51\u7edc\u4e0e\u516c\u7528\u7f51\u7edc\u4e4b\u95f4\u6267\u884c NAT\u3002

\u8bf7\u81f3\u5c11\u4e3a Internet \u901a\u4fe1\u63d0\u4f9b\u4e00\u4e2a IP \u5730\u5740\u8303\u56f4\u3002 -message.public.traffic.in.basic.zone=\u4e91\u4e2d\u7684 VM \u8bbf\u95ee Internet \u6216\u901a\u8fc7 Internet \u5411\u5ba2\u6237\u7aef\u63d0\u4f9b\u670d\u52a1\u65f6\u5c06\u751f\u6210\u516c\u5171\u901a\u4fe1\uff0c\u4f46\u5fc5\u987b\u5206\u914d\u53ef\u516c\u5f00\u8bbf\u95ee\u7684 IP \u624d\u80fd\u5b9e\u73b0\u3002\u521b\u5efa\u5b9e\u4f8b\u65f6\uff0c\u5c06\u628a\u8fd9\u4e00\u7ec4\u516c\u7528 IP \u4e2d\u7684 IP (\u6765\u5bbe IP \u5730\u5740\u9664\u5916)\u5206\u914d\u7ed9\u6b64\u5b9e\u4f8b\u3002\u9759\u6001 1-1 NAT \u5c06\u5728\u516c\u7528 IP \u4e0e\u6765\u5bbe IP \u4e4b\u95f4\u81ea\u52a8\u8bbe\u7f6e\u3002\u6700\u7ec8\u7528\u6237\u8fd8\u53ef\u4ee5\u4f7f\u7528 CloudStack UI \u83b7\u53d6\u5176\u4ed6 IP\uff0c\u4ee5\u5728\u5176\u5b9e\u4f8b\u4e0e\u516c\u7528 IP \u4e4b\u95f4\u6267\u884c\u9759\u6001 NAT\u3002 -message.redirecting.region=\u6b63\u5728\u91cd\u5b9a\u5411\u5230\u533a\u57df... -message.remove.region=\u662f\u5426\u786e\u5b9e\u8981\u4ece\u6b64\u7ba1\u7406\u670d\u52a1\u5668\u4e2d\u5220\u9664\u6b64\u533a\u57df? +message.public.traffic.in.advanced.zone=\u4e91\u4e2d\u7684 VM \u8bbf\u95ee Internet \u65f6\u5c06\u751f\u6210\u516c\u5171\u6d41\u91cf\uff0c\u4f46\u5fc5\u987b\u5206\u914d\u53ef\u516c\u5f00\u8bbf\u95ee\u7684 IP \u624d\u80fd\u5b9e\u73b0\u3002\u6700\u7ec8\u7528\u6237\u53ef\u4ee5\u4f7f\u7528 CloudStack UI \u83b7\u53d6\u8fd9\u4e9b IP\uff0c\u4ee5\u5728\u5176\u6765\u5bbe\u7f51\u7edc\u4e0e\u516c\u7528\u7f51\u7edc\u4e4b\u95f4\u6267\u884c NAT\u3002

\u8bf7\u81f3\u5c11\u4e3a Internet \u6d41\u91cf\u63d0\u4f9b\u4e00\u4e2a IP \u5730\u5740\u8303\u56f4\u3002 +message.public.traffic.in.basic.zone=\u4e91\u4e2d\u7684 VM \u8bbf\u95ee Internet \u6216\u901a\u8fc7 Internet \u5411\u5ba2\u6237\u7aef\u63d0\u4f9b\u670d\u52a1\u65f6\u5c06\u751f\u6210\u516c\u5171\u6d41\u91cf\uff0c\u4f46\u5fc5\u987b\u5206\u914d\u53ef\u516c\u5f00\u8bbf\u95ee\u7684 IP \u624d\u80fd\u5b9e\u73b0\u3002\u521b\u5efa\u5b9e\u4f8b\u65f6\uff0c\u5c06\u628a\u8fd9\u4e00\u7ec4\u516c\u7528 IP \u4e2d\u7684 IP (\u6765\u5bbe IP \u5730\u5740\u9664\u5916)\u5206\u914d\u7ed9\u6b64\u5b9e\u4f8b\u3002\u9759\u6001 1-1 NAT \u5c06\u5728\u516c\u7528 IP \u4e0e\u6765\u5bbe IP \u4e4b\u95f4\u81ea\u52a8\u8bbe\u7f6e\u3002\u6700\u7ec8\u7528\u6237\u8fd8\u53ef\u4ee5\u4f7f\u7528 CloudStack UI \u83b7\u53d6\u5176\u4ed6 IP\uff0c\u4ee5\u5728\u5176\u5b9e\u4f8b\u4e0e\u516c\u7528 IP \u4e4b\u95f4\u6267\u884c\u9759\u6001 NAT\u3002 +message.redirecting.region=\u6b63\u5728\u91cd\u5b9a\u5411\u5230\u5730\u7406\u533a\u57df... +message.remove.region=\u662f\u5426\u786e\u5b9e\u8981\u4ece\u6b64\u7ba1\u7406\u670d\u52a1\u5668\u4e2d\u5220\u9664\u6b64\u5730\u7406\u533a\u57df? message.remove.vpc=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 VPC message.remove.vpn.access=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u4ee5\u4e0b\u7528\u6237\u7684 VPN \u8bbf\u95ee\u3002 message.reset.password.warning.notPasswordEnabled=\u521b\u5efa\u6b64\u5b9e\u4f8b\u7684\u6a21\u677f\u65f6\u672a\u542f\u7528\u5bc6\u7801 @@ -1583,14 +1746,14 @@ message.restart.mgmt.usage.server=\u8bf7\u91cd\u65b0\u542f\u52a8\u7ba1\u7406\u67 message.restart.network=\u6b64\u7f51\u7edc\u63d0\u4f9b\u7684\u6240\u6709\u670d\u52a1\u90fd\u5c06\u4e2d\u65ad\u3002\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u91cd\u65b0\u542f\u52a8\u6b64\u7f51\u7edc\u3002 message.restart.vpc=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u91cd\u65b0\u542f\u52a8 VPC message.security.group.usage=(\u6309\u4f4f Ctrl \u952e\u5e76\u5355\u51fb\u9f20\u6807\u53ef\u9009\u62e9\u6240\u6709\u9002\u7528\u7684\u5b89\u5168\u7ec4) -message.select.a.zone=\u4e00\u4e2a\u533a\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u591a\u4e2a\u533a\u57df\u53ef\u4ee5\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\uff0c\u6709\u52a9\u4e8e\u4f7f\u4e91\u66f4\u52a0\u53ef\u9760\u3002 +message.select.a.zone=\u4e00\u4e2a\u8d44\u6e90\u57df\u901a\u5e38\u4e0e\u4e00\u4e2a\u6570\u636e\u4e2d\u5fc3\u76f8\u5bf9\u5e94\u3002\u591a\u4e2a\u8d44\u6e90\u57df\u53ef\u4ee5\u63d0\u4f9b\u7269\u7406\u9694\u79bb\u548c\u5197\u4f59\uff0c\u6709\u52a9\u4e8e\u4f7f\u4e91\u66f4\u52a0\u53ef\u9760\u3002 message.select.instance=\u8bf7\u9009\u62e9\u4e00\u4e2a\u5b9e\u4f8b\u3002 message.select.iso=\u8bf7\u4e3a\u60a8\u7684\u65b0\u865a\u62df\u5b9e\u4f8b\u9009\u62e9\u4e00\u4e2a ISO\u3002 message.select.item=\u8bf7\u9009\u62e9\u4e00\u4e2a\u9879\u76ee\u3002 message.select.security.groups=\u8bf7\u4e3a\u60a8\u7684\u65b0 VM \u9009\u62e9\u5b89\u5168\u7ec4 message.select.template=\u8bf7\u4e3a\u60a8\u7684\u65b0\u865a\u62df\u5b9e\u4f8b\u9009\u62e9\u4e00\u4e2a\u6a21\u677f\u3002 -message.setup.physical.network.during.zone.creation.basic=\u6dfb\u52a0\u57fa\u7840\u533a\u57df\u65f6\uff0c\u53ef\u4ee5\u8bbe\u7f6e\u4e00\u4e2a\u7269\u7406\u7f51\u7edc\uff0c\u6b64\u7f51\u7edc\u5e94\u4e0e\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u4e2d\u7684 NIC \u76f8\u5bf9\u5e94\u3002\u6b64\u7f51\u7edc\u53ef\u4ee5\u627f\u8f7d\u591a\u79cd\u901a\u4fe1\u7c7b\u578b\u3002

\u6b64\u5916\uff0c\u8fd8\u53ef\u4ee5\u5c06\u5176\u4ed6\u901a\u4fe1\u7c7b\u578b\u62d6\u653e\u5230\u6b64\u7269\u7406\u7f51\u7edc\u3002 -message.setup.physical.network.during.zone.creation=\u6dfb\u52a0\u9ad8\u7ea7\u533a\u57df\u65f6\uff0c\u9700\u8981\u8bbe\u7f6e\u4e00\u4e2a\u6216\u591a\u4e2a\u7269\u7406\u7f51\u7edc\u3002\u6bcf\u4e2a\u7f51\u7edc\u90fd\u4e0e\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u4e2d\u7684\u4e00\u4e2a NIC \u76f8\u5bf9\u5e94\u3002\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u4e2d\u53ef\u4ee5\u5305\u542b\u4e00\u79cd\u6216\u591a\u79cd\u901a\u4fe1\u7c7b\u578b\uff0c\u5e76\u5bf9\u8fd9\u4e9b\u901a\u4fe1\u7c7b\u578b\u53ef\u80fd\u7684\u7ec4\u5408\u65b9\u5f0f\u8bbe\u7f6e\u4e86\u67d0\u4e9b\u9650\u5236\u3002

\u53ef\u4ee5\u5c06\u4e00\u79cd\u6216\u591a\u79cd\u901a\u4fe1\u7c7b\u578b\u62d6\u653e\u5230\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u4e2d\u3002 +message.setup.physical.network.during.zone.creation.basic=\u6dfb\u52a0\u57fa\u7840\u8d44\u6e90\u57df\u65f6\uff0c\u53ef\u4ee5\u8bbe\u7f6e\u4e00\u4e2a\u7269\u7406\u7f51\u7edc\uff0c\u6b64\u7f51\u7edc\u5e94\u4e0e\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u4e2d\u7684 NIC \u76f8\u5bf9\u5e94\u3002\u6b64\u7f51\u7edc\u53ef\u4ee5\u627f\u8f7d\u591a\u79cd\u6d41\u91cf\u7c7b\u578b\u3002

\u6b64\u5916\uff0c\u8fd8\u53ef\u4ee5\u5c06\u5176\u4ed6\u6d41\u91cf\u7c7b\u578b\u62d6\u653e\u5230\u6b64\u7269\u7406\u7f51\u7edc\u3002 +message.setup.physical.network.during.zone.creation=\u6dfb\u52a0\u9ad8\u7ea7\u8d44\u6e90\u57df\u65f6\uff0c\u9700\u8981\u8bbe\u7f6e\u4e00\u4e2a\u6216\u591a\u4e2a\u7269\u7406\u7f51\u7edc\u3002\u6bcf\u4e2a\u7f51\u7edc\u90fd\u4e0e\u865a\u62df\u673a\u7ba1\u7406\u7a0b\u5e8f\u4e2d\u7684\u4e00\u4e2a NIC \u76f8\u5bf9\u5e94\u3002\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u4e2d\u53ef\u4ee5\u5305\u542b\u4e00\u79cd\u6216\u591a\u79cd\u6d41\u91cf\u7c7b\u578b\uff0c\u5e76\u5bf9\u8fd9\u4e9b\u6d41\u91cf\u7c7b\u578b\u53ef\u80fd\u7684\u7ec4\u5408\u65b9\u5f0f\u8bbe\u7f6e\u4e86\u67d0\u4e9b\u9650\u5236\u3002

\u53ef\u4ee5\u5c06\u4e00\u79cd\u6216\u591a\u79cd\u6d41\u91cf\u7c7b\u578b\u62d6\u653e\u5230\u6bcf\u4e2a\u7269\u7406\u7f51\u7edc\u4e2d\u3002 message.setup.successful=\u5df2\u6210\u529f\u8bbe\u7f6e\u4e91\! message.snapshot.schedule=\u53ef\u4ee5\u901a\u8fc7\u4ece\u4ee5\u4e0b\u53ef\u7528\u9009\u9879\u4e2d\u8fdb\u884c\u9009\u62e9\u5e76\u5e94\u7528\u60a8\u7684\u7b56\u7565\u9996\u9009\u9879\u6765\u8bbe\u7f6e\u91cd\u73b0\u5feb\u7167\u8ba1\u5212 message.specify.url=\u8bf7\u6307\u5b9a URL @@ -1602,18 +1765,18 @@ message.step.3.continue=\u8bf7\u9009\u62e9\u4e00\u79cd\u78c1\u76d8\u65b9\u6848\u message.step.3.desc= message.step.4.continue=\u8bf7\u81f3\u5c11\u9009\u62e9\u4e00\u4e2a\u7f51\u7edc\u4ee5\u7ee7\u7eed message.step.4.desc=\u8bf7\u9009\u62e9\u865a\u62df\u5b9e\u4f8b\u8981\u8fde\u63a5\u5230\u7684\u4e3b\u7f51\u7edc\u3002 -message.storage.traffic=CloudStack \u5185\u90e8\u8d44\u6e90(\u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u901a\u4fe1\u7684\u4efb\u4f55\u7ec4\u4ef6\uff0c\u4f8b\u5982\u4e3b\u673a\u548c CloudStack \u7cfb\u7edf VM)\u4e4b\u95f4\u7684\u901a\u4fe1\u3002\u8bf7\u5728\u6b64\u5904\u914d\u7f6e\u5b58\u50a8\u901a\u4fe1\u3002 +message.storage.traffic=CloudStack \u5185\u90e8\u8d44\u6e90(\u5305\u62ec\u4e0e\u7ba1\u7406\u670d\u52a1\u5668\u901a\u4fe1\u7684\u4efb\u4f55\u7ec4\u4ef6\uff0c\u4f8b\u5982\u4e3b\u673a\u548c CloudStack \u7cfb\u7edf VM)\u4e4b\u95f4\u7684\u6d41\u91cf\u3002\u8bf7\u5728\u6b64\u5904\u914d\u7f6e\u5b58\u50a8\u6d41\u91cf\u3002 message.suspend.project=\u662f\u5426\u786e\u5b9e\u8981\u6682\u505c\u6b64\u9879\u76ee? message.template.desc=\u64cd\u4f5c\u7cfb\u7edf\u6620\u50cf\uff0c\u53ef\u7528\u4e8e\u542f\u52a8 VM -message.tooltip.dns.1=\u4f9b\u533a\u57df\u4e2d\u7684 VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u533a\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 -message.tooltip.dns.2=\u4f9b\u533a\u57df\u4e2d\u7684 VM \u4f7f\u7528\u7684\u8f85\u52a9 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u533a\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 -message.tooltip.internal.dns.1=\u4f9b\u533a\u57df\u4e2d\u7684 CloudStack \u5185\u90e8\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u63d0\u4f9b\u70b9\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 -message.tooltip.internal.dns.2=\u4f9b\u533a\u57df\u4e2d\u7684 CloudStack \u5185\u90e8\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u63d0\u4f9b\u70b9\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 +message.tooltip.dns.1=\u4f9b\u8d44\u6e90\u57df\u4e2d\u7684 VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u8d44\u6e90\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 +message.tooltip.dns.2=\u4f9b\u8d44\u6e90\u57df\u4e2d\u7684 VM \u4f7f\u7528\u7684\u4e8c\u7ea7 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u8d44\u6e90\u57df\u7684\u516c\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 +message.tooltip.internal.dns.1=\u4f9b\u8d44\u6e90\u57df\u4e2d\u7684 CloudStack \u5185\u90e8\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u63d0\u4f9b\u70b9\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 +message.tooltip.internal.dns.2=\u4f9b\u8d44\u6e90\u57df\u4e2d\u7684 CloudStack \u5185\u90e8\u7cfb\u7edf VM \u4f7f\u7528\u7684 DNS \u670d\u52a1\u5668\u540d\u79f0\u3002\u63d0\u4f9b\u70b9\u7684\u4e13\u7528 IP \u5730\u5740\u5fc5\u987b\u8def\u7531\u5230\u6b64\u670d\u52a1\u5668\u3002 message.tooltip.network.domain=DNS \u540e\u7f00\uff0c\u5c06\u4e3a\u7531\u6765\u5bbe VM \u8bbf\u95ee\u7684\u7f51\u7edc\u521b\u5efa\u4e00\u4e2a\u81ea\u5b9a\u4e49\u57df\u540d\u3002 message.tooltip.pod.name=\u6b64\u63d0\u4f9b\u70b9\u7684\u540d\u79f0\u3002 message.tooltip.reserved.system.gateway=\u63d0\u4f9b\u70b9\u4e2d\u7684\u4e3b\u673a\u7f51\u5173\u3002 message.tooltip.reserved.system.netmask=\u7528\u4e8e\u5b9a\u4e49\u63d0\u4f9b\u70b9\u5b50\u7f51\u7684\u7f51\u7edc\u524d\u7f00\u3002\u8bf7\u4f7f\u7528 CIDR \u7b26\u53f7\u3002 -message.tooltip.zone.name=\u533a\u57df\u540d\u79f0\u3002 +message.tooltip.zone.name=\u8d44\u6e90\u57df\u540d\u79f0\u3002 message.update.os.preference=\u8bf7\u4e3a\u6b64\u4e3b\u673a\u9009\u62e9\u4e00\u4e2a\u64cd\u4f5c\u7cfb\u7edf\u9996\u9009\u9879\u3002\u9996\u5148\u5c06\u5177\u6709\u76f8\u4f3c\u9996\u9009\u9879\u7684\u6240\u6709\u865a\u62df\u5b9e\u4f8b\u5206\u914d\u81f3\u6b64\u4e3b\u673a\uff0c\u7136\u540e\u518d\u9009\u62e9\u5176\u4ed6\u5b9e\u4f8b\u3002 message.update.resource.count=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u66f4\u65b0\u6b64\u5e10\u6237\u7684\u8d44\u6e90\u6570\u3002 message.update.ssl=\u8bf7\u63d0\u4ea4\u4e00\u4e2a\u65b0\u7684 X.509 \u517c\u5bb9\u7684 SSL \u8bc1\u4e66\uff0c\u4ee5\u4fbf\u5c06\u5176\u66f4\u65b0\u5230\u6bcf\u4e2a\u63a7\u5236\u53f0\u4ee3\u7406\u865a\u62df\u5b9e\u4f8b\: @@ -1623,32 +1786,32 @@ message.vm.create.template.confirm=\u521b\u5efa\u6a21\u677f\u5c06\u81ea\u52a8\u9 message.vm.review.launch=\u8bf7\u5148\u6838\u5bf9\u4ee5\u4e0b\u4fe1\u606f\uff0c\u786e\u8ba4\u60a8\u7684\u865a\u62df\u5b9e\u4f8b\u6b63\u786e\u65e0\u8bef\uff0c\u7136\u540e\u518d\u542f\u52a8\u3002 message.volume.create.template.confirm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u4e3a\u6b64\u78c1\u76d8\u5377\u521b\u5efa\u4e00\u4e2a\u6a21\u677f\u3002\u521b\u5efa\u6a21\u677f\u53ef\u80fd\u9700\u8981\u51e0\u5206\u949f\u5230\u66f4\u957f\u7684\u65f6\u95f4\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u5377\u7684\u5927\u5c0f\u3002 message.you.must.have.at.least.one.physical.network=\u60a8\u5fc5\u987b\u81f3\u5c11\u62e5\u6709\u4e00\u4e2a\u7269\u7406\u7f51\u7edc -message.zone.creation.complete.would.you.like.to.enable.this.zone=\u5df2\u5b8c\u6210\u521b\u5efa\u533a\u57df\u3002\u662f\u5426\u8981\u542f\u7528\u6b64\u533a\u57df? -message.Zone.creation.complete=\u5df2\u5b8c\u6210\u521b\u5efa\u533a\u57df -message.zone.no.network.selection=\u6240\u9009\u533a\u57df\u65e0\u4efb\u4f55\u7f51\u7edc\u9009\u9879\u3002 -message.zone.step.1.desc=\u8bf7\u4e3a\u60a8\u7684\u533a\u57df\u9009\u62e9\u4e00\u79cd\u7f51\u7edc\u6a21\u5f0f\u3002 -message.zone.step.2.desc=\u8bf7\u8f93\u5165\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u533a\u57df +message.zone.creation.complete.would.you.like.to.enable.this.zone=\u5df2\u5b8c\u6210\u521b\u5efa\u8d44\u6e90\u57df\u3002\u662f\u5426\u8981\u542f\u7528\u6b64\u8d44\u6e90\u57df? +message.Zone.creation.complete=\u5df2\u5b8c\u6210\u521b\u5efa\u8d44\u6e90\u57df +message.zone.no.network.selection=\u6240\u9009\u8d44\u6e90\u57df\u65e0\u4efb\u4f55\u7f51\u7edc\u9009\u9879\u3002 +message.zone.step.1.desc=\u8bf7\u4e3a\u60a8\u7684\u8d44\u6e90\u57df\u9009\u62e9\u4e00\u79cd\u7f51\u7edc\u6a21\u5f0f\u3002 +message.zone.step.2.desc=\u8bf7\u8f93\u5165\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u8d44\u6e90\u57df message.zone.step.3.desc=\u8bf7\u8f93\u5165\u4ee5\u4e0b\u4fe1\u606f\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u65b0\u63d0\u4f9b\u70b9 -message.zoneWizard.enable.local.storage=\u8b66\u544a\: \u5982\u679c\u4e3a\u6b64\u533a\u57df\u542f\u7528\u4e86\u672c\u5730\u5b58\u50a8\uff0c\u5219\u5fc5\u987b\u6267\u884c\u4ee5\u4e0b\u64cd\u4f5c\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u5e0c\u671b\u542f\u52a8\u7cfb\u7edf VM \u7684\u4f4d\u7f6e\:

1. \u5982\u679c\u9700\u8981\u5728\u4e3b\u5b58\u50a8\u4e2d\u542f\u52a8\u7cfb\u7edf VM\uff0c\u5219\u5fc5\u987b\u5728\u5b8c\u6210\u521b\u5efa\u540e\u5c06\u4e3b\u5b58\u50a8\u6dfb\u52a0\u5230\u6b64\u533a\u57df\u4e2d\u3002

2. \u5982\u679c\u9700\u8981\u5728\u672c\u5730\u5b58\u50a8\u4e2d\u542f\u52a8\u7cfb\u7edf VM\uff0c\u5219\u5fc5\u987b\u5c06 system.vm.use.local.storage \u8bbe\u7f6e\u4e3a true\u3002


\u662f\u5426\u8981\u7ee7\u7eed? +message.zoneWizard.enable.local.storage=\u8b66\u544a\: \u5982\u679c\u4e3a\u6b64\u8d44\u6e90\u57df\u542f\u7528\u4e86\u672c\u5730\u5b58\u50a8\uff0c\u5219\u5fc5\u987b\u6267\u884c\u4ee5\u4e0b\u64cd\u4f5c\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u5e0c\u671b\u542f\u52a8\u7cfb\u7edf VM \u7684\u4f4d\u7f6e\:

1. \u5982\u679c\u9700\u8981\u5728\u4e3b\u5b58\u50a8\u4e2d\u542f\u52a8\u7cfb\u7edf VM\uff0c\u5219\u5fc5\u987b\u5728\u5b8c\u6210\u521b\u5efa\u540e\u5c06\u4e3b\u5b58\u50a8\u6dfb\u52a0\u5230\u6b64\u8d44\u6e90\u57df\u4e2d\u3002

2. \u5982\u679c\u9700\u8981\u5728\u672c\u5730\u5b58\u50a8\u4e2d\u542f\u52a8\u7cfb\u7edf VM\uff0c\u5219\u5fc5\u987b\u5c06 system.vm.use.local.storage \u8bbe\u7f6e\u4e3a true\u3002


\u662f\u5426\u8981\u7ee7\u7eed? message.validate.fieldrequired=\u6b64\u5b57\u6bb5\u4e3a\u5fc5\u586b\u5b57\u6bb5\u3002 message.validate.fixfield=\u8bf7\u4fee\u590d\u6b64\u5b57\u6bb5\u3002 -message.validate.email.address=Please enter a valid email address. -message.validate.URL=Please enter a valid URL. -message.validate.date=Please enter a valid date. -message.validate.date.ISO=Please enter a valid date (ISO). -message.validate.number=Please enter a valid number. -message.validate.digits=Please enter only digits. -message.validate.creditcard=Please enter a valid credit card number. -message.validate.equalto=Please enter the same value again. -message.validate.accept=Please enter a value with a valid extension. -message.validate.maxlength=Please enter no more than {0} characters. -message.validate.minlength=Please enter at least {0} characters. -message.validate.range.length=Please enter a value between {0} and {1} characters long. -message.validate.range=Please enter a value between {0} and {1}. -message.validate.max=Please enter a value less than or equal to {0}. -messgae.validate.min=Please enter a value greater than or equal to {0}. +message.validate.email.address=\u8bf7\u8f93\u5165\u4e00\u4e2a\u6709\u6548\u7684\u7535\u5b50\u90ae\u4ef6\u5730\u5740\u3002 +message.validate.URL=\u8bf7\u8f93\u5165\u6709\u6548\u7684 URL\u3002 +message.validate.date=\u8bf7\u8f93\u5165\u6709\u6548\u7684\u65e5\u671f\u3002 +message.validate.date.ISO=\u8bf7\u8f93\u5165\u6709\u6548\u7684\u65e5\u671f(ISO)\u3002 +message.validate.number=\u8bf7\u8f93\u5165\u4e00\u4e2a\u6709\u6548\u6570\u5b57\u3002 +message.validate.digits=\u8bf7\u4ec5\u8f93\u5165\u6570\u5b57\u3002 +message.validate.creditcard=\u8bf7\u8f93\u5165\u4e00\u4e2a\u6709\u6548\u7684\u4fe1\u7528\u5361\u5361\u53f7\u3002 +message.validate.equalto=\u8bf7\u91cd\u65b0\u8f93\u5165\u76f8\u540c\u7684\u503c\u3002 +message.validate.accept=\u8bf7\u8f93\u5165\u4e00\u4e2a\u5e26\u6709\u6709\u6548\u6269\u5c55\u540d\u7684\u503c\u3002 +message.validate.maxlength=\u8bf7\u6700\u591a\u8f93\u5165 {0} \u4e2a\u5b57\u7b26\u3002 +message.validate.minlength=\u8bf7\u81f3\u5c11\u8f93\u5165 {0} \u4e2a\u5b57\u7b26\u3002 +message.validate.range.length=\u8bf7\u8f93\u5165\u4e00\u4e2a\u957f\u5ea6\u4ecb\u4e8e {0} \u5230 {1} \u4e4b\u95f4\u7684\u503c\u3002 +message.validate.range=\u8bf7\u8f93\u5165\u4e00\u4e2a\u4ecb\u4e8e {0} \u5230 {1} \u4e4b\u95f4\u7684\u503c\u3002 +message.validate.max=\u8bf7\u8f93\u5165\u4e00\u4e2a\u5c0f\u4e8e\u6216\u7b49\u4e8e {0} \u7684\u503c\u3002 +messgae.validate.min=\u8bf7\u8f93\u5165\u4e00\u4e2a\u5927\u4e8e\u6216\u7b49\u4e8e {0} \u7684\u503c\u3002 message.creating.systemVM=\u6b63\u5728\u521b\u5efa\u7cfb\u7edf VM (\u6b64\u64cd\u4f5c\u53ef\u80fd\u9700\u8981\u4e00\u4e9b\u65f6\u95f4) -message.enabling.zone.dots=\u6b63\u5728\u542f\u7528\u533a\u57df... +message.enabling.zone.dots=\u6b63\u5728\u542f\u7528\u8d44\u6e90\u57df... message.restoreVM=\u662f\u5426\u8981\u8fd8\u539f\u6b64 VM? message.no.host.available=\u6ca1\u6709\u53ef\u7528\u4e8e\u8fc1\u79fb\u7684\u4e3b\u673a message.network.addVM.desc=\u8bf7\u6307\u5b9a\u8981\u5c06\u6b64 VM \u6dfb\u52a0\u5230\u7684\u7f51\u7edc\u3002\u5c06\u4e3a\u6b64\u7f51\u7edc\u6dfb\u52a0\u4e00\u4e2a\u65b0 NIC\u3002 @@ -1667,6 +1830,53 @@ message.tier.required=\u201c\u5c42\u201d\u4e3a\u5fc5\u586b\u9879 message.remove.ldap=\u662f\u5426\u786e\u5b9e\u8981\u5220\u9664 LDAP \u914d\u7f6e? message.action.downloading.template=\u6b63\u5728\u4e0b\u8f7d\u6a21\u677f\u3002 message.configure.ldap=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u914d\u7f6e LDAP\u3002 +message.confirm.delete.ciscovnmc.resource=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 CiscoVNMC \u8d44\u6e90 +message.confirm.add.vnmc.provider=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u6dfb\u52a0 VNMC \u63d0\u4f9b\u7a0b\u5e8f\u3002 +message.confirm.enable.vnmc.provider=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u7528 VNMC \u63d0\u4f9b\u7a0b\u5e8f\u3002 +message.confirm.disable.vnmc.provider=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u7981\u7528 VNMC \u63d0\u4f9b\u7a0b\u5e8f\u3002 +message.vnmc.available.list=\u63d0\u4f9b\u7a0b\u5e8f\u5217\u8868\u4e2d\u672a\u63d0\u4f9b VNMC\u3002 +message.vnmc.not.available.list=\u63d0\u4f9b\u7a0b\u5e8f\u5217\u8868\u4e2d\u672a\u63d0\u4f9b VNMC\u3002 +message.confirm.release.dedicate.vlan.range=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u91ca\u653e\u4e13\u7528 VLAN \u8303\u56f4 +message.confirm.start.lb.vm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u542f\u52a8 LB VM +message.confirm.stop.lb.vm=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u505c\u6b62 LB VM +message.confirm.remove.vmware.datacenter=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 VMware \u6570\u636e\u4e2d\u5fc3 +message.confirm.dedicate.zone=\u662f\u5426\u8981\u5c06\u6b64\u8d44\u6e90\u57df\u4e13\u7528\u4e8e\u57df/\u5e10\u6237? +message.confirm.release.dedicated.zone=\u662f\u5426\u8981\u91ca\u653e\u6b64\u4e13\u7528\u8d44\u6e90\u57df? +message.dedicated.zone.released=\u5df2\u91ca\u653e\u4e13\u7528\u8d44\u6e90\u57df +message.read.admin.guide.scaling.up=\u5f00\u59cb\u6269\u5c55\u4e4b\u524d\uff0c\u8bf7\u9605\u8bfb\u7ba1\u7406\u6307\u5357\u4e2d\u7684\u52a8\u6001\u6269\u5c55\u90e8\u5206\u3002 +message.confirm.scale.up.system.vm=\u662f\u5426\u786e\u5b9e\u8981\u6269\u5c55\u7cfb\u7edf VM? +message.confirm.upgrade.router.newer.template=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5347\u7ea7\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +message.confirm.scale.up.router.vm=\u662f\u5426\u786e\u5b9e\u8981\u6269\u5c55\u8def\u7531\u5668 VM? +message.confirm.upgrade.routers.newtemplate=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5347\u7ea7\u6b64\u8d44\u6e90\u57df\u4e2d\u7684\u6240\u6709\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +message.confirm.upgrade.routers.pod.newtemplate=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5347\u7ea7\u6b64\u63d0\u4f9b\u70b9\u4e2d\u7684\u6240\u6709\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +message.confirm.upgrade.routers.cluster.newtemplate=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5347\u7ea7\u6b64\u7fa4\u96c6\u4e2d\u7684\u6240\u6709\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +message.confirm.upgrade.routers.account.newtemplate=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5347\u7ea7\u6b64\u5e10\u6237\u4e2d\u7684\u6240\u6709\u8def\u7531\u5668\u4ee5\u4f7f\u7528\u66f4\u65b0\u7684\u6a21\u677f +message.confirm.dedicate.pod.domain.account=\u662f\u5426\u786e\u5b9e\u8981\u5c06\u6b64\u63d0\u4f9b\u70b9\u4e13\u7528\u4e8e\u57df/\u5e10\u6237? +message.confirm.release.dedicated.pod=\u662f\u5426\u8981\u91ca\u653e\u6b64\u4e13\u7528\u63d0\u4f9b\u70b9? +message.pod.dedication.released=\u5df2\u91ca\u653e\u4e13\u7528\u63d0\u4f9b\u70b9 +message.confirm.dedicate.cluster.domain.account=\u662f\u5426\u786e\u5b9e\u8981\u5c06\u6b64\u7fa4\u96c6\u4e13\u7528\u4e8e\u57df/\u5e10\u6237? +message.cluster.dedicated=\u7fa4\u96c6\u5df2\u4e13\u7528 +message.confirm.release.dedicated.cluster=\u662f\u5426\u8981\u91ca\u653e\u6b64\u4e13\u7528\u7fa4\u96c6? +message.cluster.dedication.released=\u5df2\u91ca\u653e\u4e13\u7528\u7fa4\u96c6 +message.confirm.dedicate.host.domain.account=\u662f\u5426\u786e\u5b9e\u8981\u5c06\u6b64\u4e3b\u673a\u4e13\u7528\u4e8e\u57df/\u5e10\u6237? +message.host.dedicated=\u4e3b\u673a\u5df2\u4e13\u7528 +message.confirm.release.dedicated.host=\u662f\u5426\u8981\u91ca\u653e\u6b64\u4e13\u7528\u4e3b\u673a? +message.host.dedication.released=\u5df2\u91ca\u653e\u4e13\u7528\u4e3b\u673a +message.confirm.delete.ucs.manager=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664 UCS Manager +message.confirm.refresh.blades=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5237\u65b0\u5200\u7247\u5f0f\u670d\u52a1\u5668\u3002 +message.confirm.delete.secondary.staging.store=\u8bf7\u786e\u8ba4\u60a8\u786e\u5b9e\u8981\u5220\u9664\u4e8c\u7ea7\u6682\u5b58\u5b58\u50a8\u3002 +message.select.tier=\u8bf7\u9009\u62e9\u4e00\u4e2a\u5c42 +message.disallowed.characters=\u7981\u7528\u5b57\u7b26: \<\,\> +message.waiting.for.builtin.templates.to.load=\u6b63\u5728\u7b49\u5f85\u52a0\u8f7d\u5185\u7f6e\u6a21\u677f... +message.systems.vms.ready=\u7cfb\u7edf VM \u5df2\u5c31\u7eea\u3002 +message.your.cloudstack.is.ready=\u60a8\u7684 CloudStack \u5df2\u5c31\u7eea\! +message.specifiy.tag.key.value=\u8bf7\u6307\u5b9a\u6807\u8bb0\u5bc6\u94a5\u548c\u503c +message.enter.seperated.list.multiple.cidrs=\u5982\u679c\u5b58\u5728\u591a\u4e2a CIDR\uff0c\u8bf7\u8f93\u5165\u7528\u9017\u53f7\u5206\u9694\u7684 CIDR \u5217\u8868 +message.disabling.network.offering=\u6b63\u5728\u7981\u7528\u7f51\u7edc\u65b9\u6848 +message.confirm.enable.network.offering=\u662f\u5426\u786e\u5b9e\u8981\u542f\u7528\u6b64\u7f51\u7edc\u65b9\u6848? +message.enabling.network.offering=\u6b63\u5728\u542f\u7528\u7f51\u7edc\u65b9\u6848 +message.confirm.remove.network.offering=\u662f\u5426\u786e\u5b9e\u8981\u5220\u9664\u6b64\u7f51\u7edc\u65b9\u6848? +message.confirm.disable.network.offering=\u662f\u5426\u786e\u5b9e\u8981\u7981\u7528\u6b64\u7f51\u7edc\u65b9\u6848? mode=\u6a21\u5f0f network.rate=\u7f51\u7edc\u901f\u7387 notification.reboot.instance=\u91cd\u65b0\u542f\u52a8\u5b9e\u4f8b diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index a91592e45aa..45debe41a98 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -110,6 +110,15 @@ extractIso=15 #### guest OS commands listOsTypes=15 listOsCategories=15 +addGuestOs=1 +updateGuestOs=1 +removeGuestOs=1 + +#### guest OS mapping commands +listGuestOsMapping=1 +addGuestOsMapping=1 +updateGuestOsMapping=1 +removeGuestOsMapping=1 #### service offering commands createServiceOffering=1 diff --git a/core/src/com/cloud/agent/api/ClusterSyncAnswer.java b/core/src/com/cloud/agent/api/ClusterSyncAnswer.java index 90ce02da6b7..7b8a525b69e 100644 --- a/core/src/com/cloud/agent/api/ClusterSyncAnswer.java +++ b/core/src/com/cloud/agent/api/ClusterSyncAnswer.java @@ -18,18 +18,18 @@ package com.cloud.agent.api; import java.util.HashMap; -import com.cloud.utils.Ternary; +import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine.State; public class ClusterSyncAnswer extends Answer { private long _clusterId; - private HashMap> _newStates; + private HashMap> _newStates; private boolean _isExecuted = false; // this is here because a cron command answer is being sent twice // AgentAttache.processAnswers // AgentManagerImpl.notifyAnswersToMonitors - public boolean isExceuted() { + public boolean isExecuted() { return _isExecuted; } @@ -37,7 +37,7 @@ public class ClusterSyncAnswer extends Answer { _isExecuted = true; } - public ClusterSyncAnswer(long clusterId, HashMap> newStates) { + public ClusterSyncAnswer(long clusterId, HashMap> newStates) { _clusterId = clusterId; _newStates = newStates; result = true; @@ -47,7 +47,7 @@ public class ClusterSyncAnswer extends Answer { return _clusterId; } - public HashMap> getNewStates() { + public HashMap> getNewStates() { return _newStates; } diff --git a/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncAnswer.java b/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncAnswer.java new file mode 100644 index 00000000000..04fd967285f --- /dev/null +++ b/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncAnswer.java @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.agent.api; + +import java.util.HashMap; + +public class ClusterVMMetaDataSyncAnswer extends Answer { + private long _clusterId; + private HashMap _vmMetaDatum; + private boolean _isExecuted=false; + + // this is here because a cron command answer is being sent twice + // AgentAttache.processAnswers + // AgentManagerImpl.notifyAnswersToMonitors + public boolean isExecuted(){ + return _isExecuted; + } + + public void setExecuted(){ + _isExecuted = true; + } + + + public ClusterVMMetaDataSyncAnswer(long clusterId, HashMap vmMetaDatum){ + _clusterId = clusterId; + _vmMetaDatum = vmMetaDatum; + result = true; + } + + public long getClusterId() { + return _clusterId; + } + + public HashMap getVMMetaDatum() { + return _vmMetaDatum; + } + +} diff --git a/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncCommand.java b/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncCommand.java new file mode 100644 index 00000000000..0f6ca50e036 --- /dev/null +++ b/core/src/com/cloud/agent/api/ClusterVMMetaDataSyncCommand.java @@ -0,0 +1,47 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.agent.api; + + +public class ClusterVMMetaDataSyncCommand extends Command implements CronCommand { + int _interval; + + long _clusterId; + + public ClusterVMMetaDataSyncCommand() { + } + + public ClusterVMMetaDataSyncCommand(int interval, long clusterId){ + _interval = interval; + _clusterId = clusterId; + } + + @Override + public int getInterval() { + return _interval; + } + + public long getClusterId() { + return _clusterId; + } + + @Override + public boolean executeInSequence() { + return false; + } + +} diff --git a/core/src/com/cloud/agent/api/GetGPUStatsAnswer.java b/core/src/com/cloud/agent/api/GetGPUStatsAnswer.java new file mode 100644 index 00000000000..566eed5ab63 --- /dev/null +++ b/core/src/com/cloud/agent/api/GetGPUStatsAnswer.java @@ -0,0 +1,36 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you 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 com.cloud.agent.api; + +import java.util.HashMap; + +import com.cloud.agent.api.LogLevel.Log4jLevel; + +@LogLevel(Log4jLevel.Trace) +public class GetGPUStatsAnswer extends Answer { + + private HashMap> groupDetails; + + public GetGPUStatsAnswer(GetGPUStatsCommand cmd, HashMap> groupDetails) { + super(cmd); + this.groupDetails = groupDetails; + } + + public HashMap> getGroupDetails() { + return this.groupDetails; + } +} diff --git a/core/src/com/cloud/agent/api/GetGPUStatsCommand.java b/core/src/com/cloud/agent/api/GetGPUStatsCommand.java new file mode 100644 index 00000000000..047f56283e0 --- /dev/null +++ b/core/src/com/cloud/agent/api/GetGPUStatsCommand.java @@ -0,0 +1,47 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you 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 com.cloud.agent.api; + +import com.cloud.agent.api.LogLevel.Log4jLevel; + +@LogLevel(Log4jLevel.Trace) +public class GetGPUStatsCommand extends Command +{ + String hostGuid; + String hostName; + + protected GetGPUStatsCommand() { + } + + public GetGPUStatsCommand(String hostGuid, String hostName) { + this.hostGuid = hostGuid; + this.hostName = hostName; + } + + public String getHostGuid(){ + return this.hostGuid; + } + + public String getHostName(){ + return this.hostName; + } + + @Override + public boolean executeInSequence() { + return false; + } +} diff --git a/core/src/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/com/cloud/agent/api/StartupRoutingCommand.java index 626c87f2a96..dc371133861 100755 --- a/core/src/com/cloud/agent/api/StartupRoutingCommand.java +++ b/core/src/com/cloud/agent/api/StartupRoutingCommand.java @@ -22,7 +22,7 @@ import java.util.Map; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Networks.RouterPrivateIpStrategy; -import com.cloud.utils.Ternary; +import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine.State; public class StartupRoutingCommand extends StartupCommand { @@ -63,13 +63,14 @@ public class StartupRoutingCommand extends StartupCommand { // TODO vmsync // deprecated, will delete after full replacement Map vms; - HashMap> _clusterVMStates; + HashMap> _clusterVMStates; String caps; String pool; HypervisorType hypervisorType; Map hostDetails; //stuff like host os, cpu capabilities String hypervisorVersion; + HashMap> groupDetails = new HashMap>(); public StartupRoutingCommand() { super(Host.Type.Routing); @@ -137,7 +138,7 @@ public class StartupRoutingCommand extends StartupCommand { } } - public void setClusterVMStateChanges(HashMap> allStates) { + public void setClusterVMStateChanges(HashMap> allStates) { _clusterVMStates = allStates; } @@ -169,7 +170,7 @@ public class StartupRoutingCommand extends StartupCommand { return vms; } - public HashMap> getClusterVMStateChanges() { + public HashMap> getClusterVMStateChanges() { return _clusterVMStates; } @@ -244,4 +245,12 @@ public class StartupRoutingCommand extends StartupCommand { public void setHostVmStateReport(Map hostVmStateReport) { this._hostVmStateReport = hostVmStateReport; } + + public HashMap> getGpuGroupDetails() { + return groupDetails; + } + + public void setGpuGroupDetails(HashMap> groupDetails) { + this.groupDetails = groupDetails; + } } diff --git a/core/src/com/cloud/agent/api/StopCommand.java b/core/src/com/cloud/agent/api/StopCommand.java index 6a29aa69ba9..00d7f5fff6c 100755 --- a/core/src/com/cloud/agent/api/StopCommand.java +++ b/core/src/com/cloud/agent/api/StopCommand.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.agent.api; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.vm.VirtualMachine; public class StopCommand extends RebootCommand { @@ -23,6 +24,7 @@ public class StopCommand extends RebootCommand { private String urlPort = null; private String publicConsoleProxyIpAddress = null; boolean executeInSequence = false; + private GPUDeviceTO gpuDevice; protected StopCommand() { } @@ -62,4 +64,11 @@ public class StopCommand extends RebootCommand { return this.publicConsoleProxyIpAddress; } + public GPUDeviceTO getGpuDevice() { + return this.gpuDevice; + } + + public void setGpuDevice(GPUDeviceTO gpuDevice) { + this.gpuDevice = gpuDevice; + } } diff --git a/core/src/com/cloud/agent/api/routing/AggregationControlCommand.java b/core/src/com/cloud/agent/api/routing/AggregationControlCommand.java new file mode 100644 index 00000000000..ef75360a70f --- /dev/null +++ b/core/src/com/cloud/agent/api/routing/AggregationControlCommand.java @@ -0,0 +1,44 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.agent.api.routing; + +public class AggregationControlCommand extends NetworkElementCommand{ + public enum Action { + Start, + Finish, + Cleanup, + } + + private Action action; + + protected AggregationControlCommand() { + super(); + } + + public AggregationControlCommand(Action action, String name, String ip, String guestIp) { + super(); + this.action = action; + this.setAccessDetail(NetworkElementCommand.ROUTER_NAME, name); + this.setAccessDetail(NetworkElementCommand.ROUTER_IP, ip); + this.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, guestIp); + } + + public Action getAction() { + return action; + } +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 9bebd4c635e..3712abad7d8 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -25,6 +25,8 @@ import com.cloud.agent.api.CheckS2SVpnConnectionsCommand; import com.cloud.agent.api.GetDomRVersionAnswer; import com.cloud.agent.api.GetDomRVersionCmd; import com.cloud.agent.api.SetupGuestNetworkCommand; +import com.cloud.agent.api.routing.AggregationControlCommand; +import com.cloud.agent.api.routing.AggregationControlCommand.Action; import com.cloud.agent.api.routing.CreateIpAliasCommand; import com.cloud.agent.api.routing.DeleteIpAliasCommand; import com.cloud.agent.api.routing.DhcpEntryCommand; @@ -72,6 +74,11 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Queue; +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * VirtualNetworkResource controls and configures virtual networking @@ -111,21 +118,38 @@ public class VirtualRoutingResource { protected static final String VPC_STATIC_NAT = "vpc_staticnat.sh"; protected static final String VPC_STATIC_ROUTE = "vpc_staticroute.sh"; protected static final String VPN_L2TP = "vpn_l2tp.sh"; + + protected static final String VR_CFG = "vr_cfg.sh"; } private static final Logger s_logger = Logger.getLogger(VirtualRoutingResource.class); private VirtualRouterDeployer _vrDeployer; + private Map _vrAggregateCommandsSet; + protected Map _vrLockMap = new HashMap(); private String _name; private int _sleep; private int _retry; private int _port; + private String _cfgVersion = "1.0"; + public VirtualRoutingResource(VirtualRouterDeployer deployer) { this._vrDeployer = deployer; } public Answer executeRequest(final NetworkElementCommand cmd) { + boolean aggregated = false; + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + Lock lock; + if (_vrLockMap.containsKey(routerName)) { + lock = _vrLockMap.get(routerName); + } else { + lock = new ReentrantLock(); + _vrLockMap.put(routerName, lock); + } + lock.lock(); + try { ExecutionResult rc = _vrDeployer.prepareCommand(cmd); if (!rc.isSuccess()) { @@ -139,50 +163,20 @@ public class VirtualRoutingResource { return executeQueryCommand(cmd); } - List cfg; - if (cmd instanceof SetPortForwardingRulesVpcCommand) { - cfg = generateConfig((SetPortForwardingRulesVpcCommand)cmd); - } else if (cmd instanceof SetPortForwardingRulesCommand) { - cfg = generateConfig((SetPortForwardingRulesCommand)cmd); - } else if (cmd instanceof SetStaticRouteCommand) { - cfg = generateConfig((SetStaticRouteCommand)cmd); - } else if (cmd instanceof SetStaticNatRulesCommand) { - cfg = generateConfig((SetStaticNatRulesCommand)cmd); - } else if (cmd instanceof LoadBalancerConfigCommand) { - cfg = generateConfig((LoadBalancerConfigCommand)cmd); - } else if (cmd instanceof SavePasswordCommand) { - cfg = generateConfig((SavePasswordCommand)cmd); - } else if (cmd instanceof DhcpEntryCommand) { - cfg = generateConfig((DhcpEntryCommand)cmd); - } else if (cmd instanceof CreateIpAliasCommand) { - cfg = generateConfig((CreateIpAliasCommand)cmd); - } else if (cmd instanceof DnsMasqConfigCommand) { - cfg = generateConfig((DnsMasqConfigCommand)cmd); - } else if (cmd instanceof DeleteIpAliasCommand) { - cfg = generateConfig((DeleteIpAliasCommand)cmd); - } else if (cmd instanceof VmDataCommand) { - cfg = generateConfig((VmDataCommand)cmd); - } else if (cmd instanceof SetFirewallRulesCommand) { - cfg = generateConfig((SetFirewallRulesCommand)cmd); - } else if (cmd instanceof BumpUpPriorityCommand) { - cfg = generateConfig((BumpUpPriorityCommand)cmd); - } else if (cmd instanceof RemoteAccessVpnCfgCommand) { - cfg = generateConfig((RemoteAccessVpnCfgCommand)cmd); - } else if (cmd instanceof VpnUsersCfgCommand) { - cfg = generateConfig((VpnUsersCfgCommand)cmd); - } else if (cmd instanceof Site2SiteVpnCfgCommand) { - cfg = generateConfig((Site2SiteVpnCfgCommand)cmd); - } else if (cmd instanceof SetMonitorServiceCommand) { - cfg = generateConfig((SetMonitorServiceCommand)cmd); - } else if (cmd instanceof SetupGuestNetworkCommand) { - cfg = generateConfig((SetupGuestNetworkCommand)cmd); - } else if (cmd instanceof SetNetworkACLCommand) { - cfg = generateConfig((SetNetworkACLCommand)cmd); - } else if (cmd instanceof SetSourceNatCommand) { - cfg = generateConfig((SetSourceNatCommand)cmd); - } else if (cmd instanceof IpAssocCommand) { - cfg = generateConfig((IpAssocCommand)cmd); - } else { + if (cmd instanceof AggregationControlCommand) { + return execute((AggregationControlCommand)cmd); + } + + if (_vrAggregateCommandsSet.containsKey(routerName)) { + _vrAggregateCommandsSet.get(routerName).add(cmd); + aggregated = true; + // Clean up would be done after command has been executed + //TODO: Deal with group answer as well + return new Answer(cmd); + } + + List cfg = generateCommandCfg(cmd); + if (cfg == null) { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -190,9 +184,12 @@ public class VirtualRoutingResource { } catch (final IllegalArgumentException e) { return new Answer(cmd, false, e.getMessage()); } finally { - ExecutionResult rc = _vrDeployer.cleanupCommand(cmd); - if (!rc.isSuccess()) { - s_logger.error("Failed to cleanup VR command due to " + rc.getDetails()); + lock.unlock(); + if (!aggregated) { + ExecutionResult rc = _vrDeployer.cleanupCommand(cmd); + if (!rc.isSuccess()) { + s_logger.error("Failed to cleanup VR command due to " + rc.getDetails()); + } } } } @@ -952,6 +949,8 @@ public class VirtualRoutingResource { if (_vrDeployer == null) { throw new ConfigurationException("Unable to find the resource for VirtualRouterDeployer!"); } + + _vrAggregateCommandsSet = new HashMap<>(); return true; } @@ -1030,4 +1029,121 @@ public class VirtualRoutingResource { return false; } + + private List generateCommandCfg(NetworkElementCommand cmd) { + List cfg; + if (cmd instanceof SetPortForwardingRulesVpcCommand) { + cfg = generateConfig((SetPortForwardingRulesVpcCommand)cmd); + } else if (cmd instanceof SetPortForwardingRulesCommand) { + cfg = generateConfig((SetPortForwardingRulesCommand)cmd); + } else if (cmd instanceof SetStaticRouteCommand) { + cfg = generateConfig((SetStaticRouteCommand)cmd); + } else if (cmd instanceof SetStaticNatRulesCommand) { + cfg = generateConfig((SetStaticNatRulesCommand)cmd); + } else if (cmd instanceof LoadBalancerConfigCommand) { + cfg = generateConfig((LoadBalancerConfigCommand)cmd); + } else if (cmd instanceof SavePasswordCommand) { + cfg = generateConfig((SavePasswordCommand)cmd); + } else if (cmd instanceof DhcpEntryCommand) { + cfg = generateConfig((DhcpEntryCommand)cmd); + } else if (cmd instanceof CreateIpAliasCommand) { + cfg = generateConfig((CreateIpAliasCommand)cmd); + } else if (cmd instanceof DnsMasqConfigCommand) { + cfg = generateConfig((DnsMasqConfigCommand)cmd); + } else if (cmd instanceof DeleteIpAliasCommand) { + cfg = generateConfig((DeleteIpAliasCommand)cmd); + } else if (cmd instanceof VmDataCommand) { + cfg = generateConfig((VmDataCommand)cmd); + } else if (cmd instanceof SetFirewallRulesCommand) { + cfg = generateConfig((SetFirewallRulesCommand)cmd); + } else if (cmd instanceof BumpUpPriorityCommand) { + cfg = generateConfig((BumpUpPriorityCommand)cmd); + } else if (cmd instanceof RemoteAccessVpnCfgCommand) { + cfg = generateConfig((RemoteAccessVpnCfgCommand)cmd); + } else if (cmd instanceof VpnUsersCfgCommand) { + cfg = generateConfig((VpnUsersCfgCommand)cmd); + } else if (cmd instanceof Site2SiteVpnCfgCommand) { + cfg = generateConfig((Site2SiteVpnCfgCommand)cmd); + } else if (cmd instanceof SetMonitorServiceCommand) { + cfg = generateConfig((SetMonitorServiceCommand)cmd); + } else if (cmd instanceof SetupGuestNetworkCommand) { + cfg = generateConfig((SetupGuestNetworkCommand)cmd); + } else if (cmd instanceof SetNetworkACLCommand) { + cfg = generateConfig((SetNetworkACLCommand)cmd); + } else if (cmd instanceof SetSourceNatCommand) { + cfg = generateConfig((SetSourceNatCommand)cmd); + } else if (cmd instanceof IpAssocCommand) { + cfg = generateConfig((IpAssocCommand)cmd); + } else { + return null; + } + return cfg; + } + + private Answer execute(AggregationControlCommand cmd) { + Action action = cmd.getAction(); + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + assert routerName != null; + assert cmd.getRouterAccessIp() != null; + + if (action == Action.Start) { + assert (!_vrAggregateCommandsSet.containsKey(routerName)); + + Queue queue = new LinkedBlockingQueue<>(); + _vrAggregateCommandsSet.put(routerName, queue); + return new Answer(cmd); + } else if (action == Action.Finish) { + Queue queue = _vrAggregateCommandsSet.get(routerName); + try { + StringBuilder sb = new StringBuilder(); + sb.append("#Apache CloudStack Virtual Router Config File\n"); + sb.append("\n" + _cfgVersion + "\n\n"); + for (NetworkElementCommand command : queue) { + List cfg = generateCommandCfg(command); + if (cfg == null) { + s_logger.warn("Unknown commands for VirtualRoutingResource, but continue: " + cmd.toString()); + continue; + } + + for (ConfigItem c : cfg) { + if (c.isFile()) { + sb.append("\n"); + sb.append(c.getFilePath() + c.getFileName() + "\n"); + sb.append(c.getFileContents() + "\n"); + sb.append("\n"); + } else { + sb.append("\n"); + } + } + } + String cfgFilePath = "/var/cache/cloud/"; + String cfgFileName = "VR-"+ UUID.randomUUID().toString() + ".cfg"; + ExecutionResult result = _vrDeployer.createFileInVR(cmd.getRouterAccessIp(), cfgFilePath, cfgFileName, sb.toString()); + if (!result.isSuccess()) { + return new Answer(cmd, false, result.getDetails()); + } + + result = _vrDeployer.executeInVR(cmd.getRouterAccessIp(), VRScripts.VR_CFG, "-c " + cfgFilePath + cfgFileName); + if (!result.isSuccess()) { + return new Answer(cmd, false, result.getDetails()); + } + return new Answer(cmd); + } finally { + queue.clear(); + _vrAggregateCommandsSet.remove(routerName); + } + } else if (action == Action.Cleanup) { + assert (_vrAggregateCommandsSet.containsKey(routerName)); + Queue queue = _vrAggregateCommandsSet.get(routerName); + if (queue != null) { + queue.clear(); + } + _vrAggregateCommandsSet.remove(routerName); + + return new Answer(cmd); + } + return new Answer(cmd, false, "Fail to recongize aggregation action " + action.toString()); + } } diff --git a/core/src/com/cloud/info/ConsoleProxyInfo.java b/core/src/com/cloud/info/ConsoleProxyInfo.java index 9a94474efda..0e57b7551fc 100644 --- a/core/src/com/cloud/info/ConsoleProxyInfo.java +++ b/core/src/com/cloud/info/ConsoleProxyInfo.java @@ -32,16 +32,17 @@ public class ConsoleProxyInfo { this.sslEnabled = sslEnabled; if (sslEnabled) { - StringBuffer sb = new StringBuffer(proxyIpAddress); - for (int i = 0; i < sb.length(); i++) - if (sb.charAt(i) == '.') - sb.setCharAt(i, '-'); - if (consoleProxyUrlDomain != null && consoleProxyUrlDomain.length() > 0) { - sb.append("."); + StringBuffer sb = new StringBuffer(); + if (consoleProxyUrlDomain.startsWith("*")) { + sb.append(proxyIpAddress); + for (int i = 0; i < proxyIpAddress.length(); i++) + if (sb.charAt(i) == '.') + sb.setCharAt(i, '-'); + sb.append(consoleProxyUrlDomain.substring(1));//skip the * + } else { + //LB address sb.append(consoleProxyUrlDomain); - } else - sb.append(".realhostip.com"); - + } proxyAddress = sb.toString(); proxyPort = port; this.proxyUrlPort = proxyUrlPort; diff --git a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java index 01c05f004f9..6a5d6793312 100644 --- a/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java +++ b/core/src/org/apache/cloudstack/storage/to/PrimaryDataStoreTO.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.storage.to; +import java.util.Map; + import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import com.cloud.agent.api.to.DataStoreTO; @@ -23,6 +25,16 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage.StoragePoolType; public class PrimaryDataStoreTO implements DataStoreTO { + public static final String MANAGED = PrimaryDataStore.MANAGED; + public static final String STORAGE_HOST = PrimaryDataStore.STORAGE_HOST; + public static final String MANAGED_STORE_TARGET = PrimaryDataStore.MANAGED_STORE_TARGET; + public static final String MANAGED_STORE_TARGET_ROOT_VOLUME = PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME; + public static final String CHAP_INITIATOR_USERNAME = PrimaryDataStore.CHAP_INITIATOR_USERNAME; + public static final String CHAP_INITIATOR_SECRET = PrimaryDataStore.CHAP_INITIATOR_SECRET; + public static final String CHAP_TARGET_USERNAME = PrimaryDataStore.CHAP_TARGET_USERNAME; + public static final String CHAP_TARGET_SECRET = PrimaryDataStore.CHAP_TARGET_SECRET; + public static final String VOLUME_SIZE = PrimaryDataStore.VOLUME_SIZE; + private final String uuid; private final String name; private String type; @@ -32,6 +44,7 @@ public class PrimaryDataStoreTO implements DataStoreTO { private String path; private int port; private final String url; + private Map details; public PrimaryDataStoreTO(PrimaryDataStore dataStore) { this.uuid = dataStore.getUuid(); @@ -42,6 +55,7 @@ public class PrimaryDataStoreTO implements DataStoreTO { this.setPath(dataStore.getPath()); this.setPort(dataStore.getPort()); this.url = dataStore.getUri(); + this.details = dataStore.getDetails(); } public long getId() { @@ -58,6 +72,10 @@ public class PrimaryDataStoreTO implements DataStoreTO { return this.url; } + public Map getDetails() { + return this.details; + } + public String getName() { return this.name; } diff --git a/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index c980c9dbe52..48da1bb58b0 100644 --- a/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java +++ b/core/test/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java @@ -19,6 +19,8 @@ package com.cloud.agent.resource.virtualnetwork; import com.cloud.agent.api.Answer; import com.cloud.agent.api.BumpUpPriorityCommand; import com.cloud.agent.api.SetupGuestNetworkCommand; +import com.cloud.agent.api.routing.AggregationControlCommand; +import com.cloud.agent.api.routing.AggregationControlCommand.Action; import com.cloud.agent.api.routing.CreateIpAliasCommand; import com.cloud.agent.api.routing.DeleteIpAliasCommand; import com.cloud.agent.api.routing.DhcpEntryCommand; @@ -67,6 +69,7 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import javax.naming.ConfigurationException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.UUID; @@ -82,7 +85,9 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { int _count; String _file; - String ROUTERIP = "10.2.3.4"; + String ROUTERIP = "169.254.3.4"; + String ROUTERGUESTIP = "10.200.1.1"; + String ROUTERNAME = "r-4-VM"; @Override public ExecutionResult executeInVR(String routerIp, String script, String args) { @@ -131,6 +136,14 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { } } + private void verifyFile(NetworkElementCommand cmd, String path, String filename, String content) { + if (cmd instanceof AggregationControlCommand) { + verifyFile((AggregationControlCommand)cmd, path, filename, content); + } else if (cmd instanceof LoadBalancerConfigCommand) { + verifyFile((LoadBalancerConfigCommand)cmd, path, filename, content); + } + } + protected void verifyCommand(NetworkElementCommand cmd, String script, String args) { if (cmd instanceof SetPortForwardingRulesVpcCommand) { verifyArgs((SetPortForwardingRulesVpcCommand) cmd, script, args); @@ -175,6 +188,10 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { } else if (cmd instanceof IpAssocCommand) { verifyArgs((IpAssocCommand)cmd, script, args); } + + if (cmd instanceof AggregationControlCommand) { + verifyArgs((AggregationControlCommand)cmd, script, args); + } } private void verifyArgs(VpnUsersCfgCommand cmd, String script, String args) { @@ -203,11 +220,7 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSetPortForwardingRulesVpcCommand() { - List pfRules = new ArrayList<>(); - pfRules.add(new PortForwardingRuleTO(1, "64.1.1.10", 22, 80, "10.10.1.10", 22, 80, "TCP", false, false)); - pfRules.add(new PortForwardingRuleTO(2, "64.1.1.11", 8080, 8080, "10.10.1.11", 8080, 8080, "UDP", true, false)); - SetPortForwardingRulesVpcCommand cmd = new SetPortForwardingRulesVpcCommand(pfRules); - assertEquals(cmd.getAnswersCount(), 2); + SetPortForwardingRulesVpcCommand cmd = generateSetPortForwardingRulesVpcCommand(); // Reset rule check count _count = 0; @@ -218,6 +231,16 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { assertTrue(answer.getResult()); } + protected SetPortForwardingRulesVpcCommand generateSetPortForwardingRulesVpcCommand() { + List pfRules = new ArrayList<>(); + pfRules.add(new PortForwardingRuleTO(1, "64.1.1.10", 22, 80, "10.10.1.10", 22, 80, "TCP", false, false)); + pfRules.add(new PortForwardingRuleTO(2, "64.1.1.11", 8080, 8080, "10.10.1.11", 8080, 8080, "UDP", true, false)); + SetPortForwardingRulesVpcCommand cmd = new SetPortForwardingRulesVpcCommand(pfRules); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + assertEquals(cmd.getAnswersCount(), 2); + return cmd; + } + private void verifyArgs(SetPortForwardingRulesVpcCommand cmd, String script, String args) { assertTrue(script.equals(VRScripts.VPC_PORTFORWARDING)); _count ++; @@ -235,12 +258,7 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSetPortForwardingRulesCommand() { - List pfRules = new ArrayList<>(); - pfRules.add(new PortForwardingRuleTO(1, "64.1.1.10", 22, 80, "10.10.1.10", 22, 80, "TCP", false, false)); - pfRules.add(new PortForwardingRuleTO(2, "64.1.1.11", 8080, 8080, "10.10.1.11", 8080, 8080, "UDP", true, false)); - SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(pfRules); - assertEquals(cmd.getAnswersCount(), 2); - + SetPortForwardingRulesCommand cmd = generateSetPortForwardingRulesCommand(); // Reset rule check count _count = 0; @@ -250,6 +268,16 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { assertTrue(answer.getResult()); } + protected SetPortForwardingRulesCommand generateSetPortForwardingRulesCommand() { + List pfRules = new ArrayList<>(); + pfRules.add(new PortForwardingRuleTO(1, "64.1.1.10", 22, 80, "10.10.1.10", 22, 80, "TCP", false, false)); + pfRules.add(new PortForwardingRuleTO(2, "64.1.1.11", 8080, 8080, "10.10.1.11", 8080, 8080, "UDP", true, false)); + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(pfRules); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + assertEquals(cmd.getAnswersCount(), 2); + return cmd; + } + private void verifyArgs(SetPortForwardingRulesCommand cmd, String script, String args) { assertTrue(script.equals(VRScripts.FIREWALL_NAT)); _count ++; @@ -267,14 +295,7 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testIpAssocCommand() { - List ips = new ArrayList(); - ips.add(new IpAddressTO(1, "64.1.1.10", true, true, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); - ips.add(new IpAddressTO(2, "64.1.1.11", false, false, false, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); - ips.add(new IpAddressTO(3, "65.1.1.11", true, false, false, "vlan://65", "65.1.1.1", "255.255.255.0", "11:23:45:67:89:AB", 1000, false)); - IpAddressTO[] ipArray = ips.toArray(new IpAddressTO[ips.size()]); - IpAssocCommand cmd = new IpAssocCommand(ipArray); - assertEquals(cmd.getAnswersCount(), 3); - + IpAssocCommand cmd = generateIpAssocCommand(); _count = 0; Answer answer = _resource.executeRequest(cmd); @@ -292,16 +313,22 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { return new ExecutionResult(true, null); } - @Test - public void testIpAssocVpcCommand() { - List ips = new ArrayList(); + protected IpAssocCommand generateIpAssocCommand() { + List ips = new ArrayList<>(); ips.add(new IpAddressTO(1, "64.1.1.10", true, true, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); - ips.add(new IpAddressTO(2, "64.1.1.11", false, false, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); + ips.add(new IpAddressTO(2, "64.1.1.11", false, false, false, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); ips.add(new IpAddressTO(3, "65.1.1.11", true, false, false, "vlan://65", "65.1.1.1", "255.255.255.0", "11:23:45:67:89:AB", 1000, false)); IpAddressTO[] ipArray = ips.toArray(new IpAddressTO[ips.size()]); - IpAssocVpcCommand cmd = new IpAssocVpcCommand(ipArray); - assertEquals(cmd.getAnswersCount(), 6); + IpAssocCommand cmd = new IpAssocCommand(ipArray); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + assertEquals(cmd.getAnswersCount(), 3); + return cmd; + } + + @Test + public void testIpAssocVpcCommand() { + IpAssocVpcCommand cmd = generateIpAssocVpcCommand(); _count = 0; Answer answer = _resource.executeRequest(cmd); @@ -319,6 +346,19 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { return new ExecutionResult(true, null); } + protected IpAssocVpcCommand generateIpAssocVpcCommand() { + List ips = new ArrayList(); + ips.add(new IpAddressTO(1, "64.1.1.10", true, true, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); + ips.add(new IpAddressTO(2, "64.1.1.11", false, false, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false)); + ips.add(new IpAddressTO(3, "65.1.1.11", true, false, false, "vlan://65", "65.1.1.1", "255.255.255.0", "11:23:45:67:89:AB", 1000, false)); + IpAddressTO[] ipArray = ips.toArray(new IpAddressTO[ips.size()]); + IpAssocVpcCommand cmd = new IpAssocVpcCommand(ipArray); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + assertEquals(cmd.getAnswersCount(), 6); + + return cmd; + } + private void verifyArgs(IpAssocCommand cmd, String script, String args) { if (cmd instanceof IpAssocVpcCommand) { _count ++; @@ -367,8 +407,7 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSourceNatCommand() { - IpAddressTO ip = new IpAddressTO(1, "64.1.1.10", true, true, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false); - SetSourceNatCommand cmd = new SetSourceNatCommand(ip, true); + SetSourceNatCommand cmd = generateSetSourceNatCommand(); Answer answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); } @@ -379,6 +418,13 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { return new ExecutionResult(true, null); } + protected SetSourceNatCommand generateSetSourceNatCommand() { + IpAddressTO ip = new IpAddressTO(1, "64.1.1.10", true, true, true, "vlan://64", "64.1.1.1", "255.255.255.0", "01:23:45:67:89:AB", 1000, false); + SetSourceNatCommand cmd = new SetSourceNatCommand(ip, true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; + } + private void verifyArgs(SetSourceNatCommand cmd, String script, String args) { assertEquals(script, VRScripts.VPC_SOURCE_NAT); assertEquals(args, "-A -l 64.1.1.10 -c eth1"); @@ -386,6 +432,18 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testNetworkACLCommand() { + SetNetworkACLCommand cmd = generateSetNetworkACLCommand(); + _count = 0; + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + + cmd.setAccessDetail(NetworkElementCommand.VPC_PRIVATE_GATEWAY, String.valueOf(VpcGateway.Type.Private)); + answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + protected SetNetworkACLCommand generateSetNetworkACLCommand() { List acls = new ArrayList<>(); List cidrs = new ArrayList<>(); cidrs.add("192.168.0.1/24"); @@ -397,16 +455,10 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { nic.setMac("01:23:45:67:89:AB"); nic.setIp("192.168.1.1"); nic.setNetmask("255.255.255.0"); - - _count = 0; - SetNetworkACLCommand cmd = new SetNetworkACLCommand(acls, nic); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); - cmd.setAccessDetail(NetworkElementCommand.VPC_PRIVATE_GATEWAY, String.valueOf(VpcGateway.Type.Private)); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; } private void verifyArgs(SetNetworkACLCommand cmd, String script, String args) { @@ -435,6 +487,18 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSetupGuestNetworkCommand() { + SetupGuestNetworkCommand cmd = generateSetupGuestNetworkCommand(); + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + private ExecutionResult prepareNetworkElementCommand(SetupGuestNetworkCommand cmd) { + NicTO nic = cmd.getNic(); + nic.setDeviceId(4); + return new ExecutionResult(true, null); + } + + protected SetupGuestNetworkCommand generateSetupGuestNetworkCommand() { NicTO nic = new NicTO(); nic.setMac("01:23:45:67:89:AB"); nic.setIp("10.1.1.1"); @@ -443,16 +507,9 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { SetupGuestNetworkCommand cmd = new SetupGuestNetworkCommand("10.1.1.10-10.1.1.20", "cloud.test", false, 0, "8.8.8.8", "8.8.4.4", true, nic); cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, "10.1.1.2"); cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, "10.1.1.1"); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); - - } - - private ExecutionResult prepareNetworkElementCommand(SetupGuestNetworkCommand cmd) { - NicTO nic = cmd.getNic(); - nic.setDeviceId(4); - return new ExecutionResult(true, null); + return cmd; } private void verifyArgs(SetupGuestNetworkCommand cmd, String script, String args) { @@ -462,13 +519,20 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSetMonitorServiceCommand() { + SetMonitorServiceCommand cmd = generateSetMonitorServiceCommand(); + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + protected SetMonitorServiceCommand generateSetMonitorServiceCommand() { List services = new ArrayList<>(); services.add(new MonitorServiceTO("service", "process", "name", "path", "file", true)); services.add(new MonitorServiceTO("service_2", "process_2", "name_2", "path_2", "file_2", false)); SetMonitorServiceCommand cmd = new SetMonitorServiceCommand(services); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + + return cmd; } private void verifyArgs(SetMonitorServiceCommand cmd, String script, String args) { @@ -481,14 +545,17 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { _count = 0; Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), true, false); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); Answer answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); cmd = new Site2SiteVpnCfgCommand(false, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); } @@ -516,20 +583,35 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { public void testRemoteAccessVpnCfgCommand() { _count = 0; + Answer answer = _resource.executeRequest(generateRemoteAccessVpnCfgCommand1()); + assertTrue(answer.getResult()); + + answer = _resource.executeRequest(generateRemoteAccessVpnCfgCommand2()); + assertTrue(answer.getResult()); + + answer = _resource.executeRequest(generateRemoteAccessVpnCfgCommand3()); + assertTrue(answer.getResult()); + } + + protected RemoteAccessVpnCfgCommand generateRemoteAccessVpnCfgCommand1() { RemoteAccessVpnCfgCommand cmd = new RemoteAccessVpnCfgCommand(true, "124.10.10.10", "10.10.1.1", "10.10.1.10-10.10.1.20", "sharedkey", false); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); cmd.setLocalCidr("10.1.1.1/24"); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; + } - cmd = new RemoteAccessVpnCfgCommand(false, "124.10.10.10", "10.10.1.1", "10.10.1.10-10.10.1.20", "sharedkey", false); + protected RemoteAccessVpnCfgCommand generateRemoteAccessVpnCfgCommand2() { + RemoteAccessVpnCfgCommand cmd = new RemoteAccessVpnCfgCommand(false, "124.10.10.10", "10.10.1.1", "10.10.1.10-10.10.1.20", "sharedkey", false); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); cmd.setLocalCidr("10.1.1.1/24"); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; + } - cmd = new RemoteAccessVpnCfgCommand(true, "124.10.10.10", "10.10.1.1", "10.10.1.10-10.10.1.20", "sharedkey", true); + protected RemoteAccessVpnCfgCommand generateRemoteAccessVpnCfgCommand3() { + RemoteAccessVpnCfgCommand cmd = new RemoteAccessVpnCfgCommand(true, "124.10.10.10", "10.10.1.1", "10.10.1.10-10.10.1.20", "sharedkey", true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); cmd.setLocalCidr("10.1.1.1/24"); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; } private void verifyArgs(RemoteAccessVpnCfgCommand cmd, String script, String args) { @@ -556,6 +638,15 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { public void testFirewallRulesCommand() { _count = 0; + Answer answer = _resource.executeRequest(generateSetFirewallRulesCommand()); + assertTrue(answer.getResult()); + assertTrue(answer instanceof GroupAnswer); + assertEquals(((GroupAnswer) answer).getResults().length, 3); + + //TODO Didn't test egress rule because not able to generate FirewallRuleVO object + } + + protected SetFirewallRulesCommand generateSetFirewallRulesCommand() { List rules = new ArrayList<>(); List sourceCidrs = new ArrayList<>(); sourceCidrs.add("10.10.1.1/24"); @@ -564,12 +655,9 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { rules.add(new FirewallRuleTO(2, "64.10.10.10", "ICMP", 0, 0, false, false, Purpose.Firewall, sourceCidrs, -1, -1)); rules.add(new FirewallRuleTO(3, "64.10.10.10", "ICMP", 0, 0, true, true, Purpose.Firewall, sourceCidrs, -1, -1)); SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); - assertTrue(answer instanceof GroupAnswer); - assertEquals(((GroupAnswer) answer).getResults().length, 3); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); - //TODO Didn't test egress rule because not able to generate FirewallRuleVO object + return cmd; } private void verifyArgs(SetFirewallRulesCommand cmd, String script, String args) { @@ -579,6 +667,11 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testVmDataCommand() { + Answer answer = _resource.executeRequest(generateVmDataCommand()); + assertTrue(answer.getResult()); + } + + protected VmDataCommand generateVmDataCommand() { VmDataCommand cmd = new VmDataCommand("10.1.10.4", "i-4-VM", true); cmd.addVmData("userdata", "user-data", "user-data"); cmd.addVmData("metadata", "service-offering", "serviceOffering"); @@ -592,8 +685,9 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { cmd.addVmData("metadata", "public-keys", "publickey"); cmd.addVmData("metadata", "cloud-identifier", "CloudStack-{test}"); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + + return cmd; } private void verifyArgs(VmDataCommand cmd, String script, String args) { @@ -608,11 +702,16 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testSavePasswordCommand() { - SavePasswordCommand cmd = new SavePasswordCommand("123pass", "10.1.10.4", "i-4-VM", true); - Answer answer = _resource.executeRequest(cmd); + Answer answer = _resource.executeRequest(generateSavePasswordCommand()); assertTrue(answer.getResult()); } + protected SavePasswordCommand generateSavePasswordCommand() { + SavePasswordCommand cmd = new SavePasswordCommand("123pass", "10.1.10.4", "i-4-VM", true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; + } + private void verifyArgs(SavePasswordCommand cmd, String script, String args) { assertEquals(script, VRScripts.PASSWORD); assertEquals(args, "-v 10.1.10.4 -p 123pass"); @@ -621,19 +720,35 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testDhcpEntryCommand() { _count = 0; + + Answer answer = _resource.executeRequest(generateDhcpEntryCommand1()); + assertTrue(answer.getResult()); + + answer = _resource.executeRequest(generateDhcpEntryCommand2()); + assertTrue(answer.getResult()); + + answer = _resource.executeRequest(generateDhcpEntryCommand3()); + assertTrue(answer.getResult()); + } + + protected DhcpEntryCommand generateDhcpEntryCommand1() { DhcpEntryCommand cmd = new DhcpEntryCommand("12:34:56:78:90:AB", "10.1.10.2", "vm1", null, true); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; + } - cmd = new DhcpEntryCommand("12:34:56:78:90:AB", null, "vm1", "2001:db8:0:0:0:ff00:42:8329", true); + protected DhcpEntryCommand generateDhcpEntryCommand2() { + DhcpEntryCommand cmd = new DhcpEntryCommand("12:34:56:78:90:AB", null, "vm1", "2001:db8:0:0:0:ff00:42:8329", true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); cmd.setDuid(NetUtils.getDuidLL(cmd.getVmMac())); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; + } - cmd = new DhcpEntryCommand("12:34:56:78:90:AB", "10.1.10.2", "vm1", "2001:db8:0:0:0:ff00:42:8329", true); + protected DhcpEntryCommand generateDhcpEntryCommand3() { + DhcpEntryCommand cmd = new DhcpEntryCommand("12:34:56:78:90:AB", "10.1.10.2", "vm1", "2001:db8:0:0:0:ff00:42:8329", true); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); cmd.setDuid(NetUtils.getDuidLL(cmd.getVmMac())); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + return cmd; } private void verifyArgs(DhcpEntryCommand cmd, String script, String args) { @@ -656,13 +771,19 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testCreateIpAliasCommand() { + Answer answer = _resource.executeRequest(generateCreateIpAliasCommand()); + assertTrue(answer.getResult()); + } + + protected CreateIpAliasCommand generateCreateIpAliasCommand() { List aliases = new ArrayList<>(); aliases.add(new IpAliasTO("169.254.3.10", "255.255.255.0", "1")); aliases.add(new IpAliasTO("169.254.3.11", "255.255.255.0", "2")); aliases.add(new IpAliasTO("169.254.3.12", "255.255.255.0", "3")); CreateIpAliasCommand cmd = new CreateIpAliasCommand("169.254.3.10", aliases); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + + return cmd; } private void verifyArgs(CreateIpAliasCommand cmd, String script, String args) { @@ -672,13 +793,18 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testDeleteIpAliasCommand() { + Answer answer = _resource.executeRequest(generateDeleteIpAliasCommand()); + assertTrue(answer.getResult()); + } + + protected DeleteIpAliasCommand generateDeleteIpAliasCommand() { List aliases = new ArrayList<>(); aliases.add(new IpAliasTO("169.254.3.10", "255.255.255.0", "1")); aliases.add(new IpAliasTO("169.254.3.11", "255.255.255.0", "2")); aliases.add(new IpAliasTO("169.254.3.12", "255.255.255.0", "3")); DeleteIpAliasCommand cmd = new DeleteIpAliasCommand("169.254.10.1", aliases, aliases); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; } private void verifyArgs(DeleteIpAliasCommand cmd, String script, String args) { @@ -688,12 +814,17 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { @Test public void testDnsMasqConfigCommand() { - List dhcps = new ArrayList(); + Answer answer = _resource.executeRequest(generateDnsMasqConfigCommand()); + assertTrue(answer.getResult()); + } + + protected DnsMasqConfigCommand generateDnsMasqConfigCommand() { + List dhcps = new ArrayList<>(); dhcps.add(new DhcpTO("10.1.20.2", "10.1.20.1", "255.255.255.0", "10.1.20.5")); dhcps.add(new DhcpTO("10.1.21.2", "10.1.21.1", "255.255.255.0", "10.1.21.5")); DnsMasqConfigCommand cmd = new DnsMasqConfigCommand(dhcps); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; } private void verifyArgs(DnsMasqConfigCommand cmd, String script, String args) { @@ -706,6 +837,14 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { _count = 0; _file = ""; + Answer answer = _resource.executeRequest(generateLoadBalancerConfigCommand1()); + assertTrue(answer.getResult()); + + answer = _resource.executeRequest(generateLoadBalancerConfigCommand2()); + assertTrue(answer.getResult()); + } + + protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand1() { List lbs = new ArrayList<>(); List dests = new ArrayList<>(); dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); @@ -716,19 +855,27 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { NicTO nic = new NicTO(); LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, null, "1000", false); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2"); - Answer answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); - - nic.setIp("10.1.10.2"); - cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false); - answer = _resource.executeRequest(cmd); - assertTrue(answer.getResult()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; } - private void verifyFile(NetworkElementCommand cmd, String path, String filename, String content) { - if (!(cmd instanceof LoadBalancerConfigCommand)) { - fail("Only LB command would call this!"); - } + protected LoadBalancerConfigCommand generateLoadBalancerConfigCommand2() { + List lbs = new ArrayList<>(); + List dests = new ArrayList<>(); + dests.add(new LbDestination(80, 8080, "10.1.10.2", false)); + dests.add(new LbDestination(80, 8080, "10.1.10.2", true)); + lbs.add(new LoadBalancerTO(UUID.randomUUID().toString(), "64.10.1.10", 80, "tcp", "algo", false, false, false, dests)); + LoadBalancerTO[] arrayLbs = new LoadBalancerTO[lbs.size()]; + lbs.toArray(arrayLbs); + NicTO nic = new NicTO(); + nic.setIp("10.1.10.2"); + LoadBalancerConfigCommand cmd = new LoadBalancerConfigCommand(arrayLbs, "64.10.2.10", "10.1.10.2", "192.168.1.2", nic, Long.valueOf(1), "1000", false); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, "10.1.10.2"); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); + return cmd; + } + + protected void verifyFile(LoadBalancerConfigCommand cmd, String path, String filename, String content) { _count ++; switch (_count) { case 1: @@ -795,5 +942,149 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { } } + @Test + public void testAggregationCommands() { + List cmds = new LinkedList<>(); + AggregationControlCommand startCmd = new AggregationControlCommand(Action.Start, ROUTERNAME, ROUTERIP, ROUTERGUESTIP); + cmds.add(startCmd); + cmds.add(generateIpAssocCommand()); + cmds.add(generateIpAssocVpcCommand()); + + cmds.add(generateSetFirewallRulesCommand()); + + cmds.add(generateSetPortForwardingRulesCommand()); + cmds.add(generateSetPortForwardingRulesVpcCommand()); + + cmds.add(generateCreateIpAliasCommand()); + cmds.add(generateDeleteIpAliasCommand()); + cmds.add(generateDnsMasqConfigCommand()); + + cmds.add(generateRemoteAccessVpnCfgCommand1()); + cmds.add(generateRemoteAccessVpnCfgCommand2()); + cmds.add(generateRemoteAccessVpnCfgCommand3()); + + //cmds.add(generateLoadBalancerConfigCommand1()); + //cmds.add(generateLoadBalancerConfigCommand2()); + + cmds.add(generateSetPortForwardingRulesCommand()); + cmds.add(generateSetPortForwardingRulesVpcCommand()); + + cmds.add(generateDhcpEntryCommand1()); + cmds.add(generateDhcpEntryCommand2()); + cmds.add(generateDhcpEntryCommand3()); + + cmds.add(generateSavePasswordCommand()); + cmds.add(generateVmDataCommand()); + + AggregationControlCommand finishCmd = new AggregationControlCommand(Action.Finish, ROUTERNAME, ROUTERIP, ROUTERGUESTIP); + cmds.add(finishCmd); + + for (NetworkElementCommand cmd : cmds) { + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + } + + private void verifyArgs(AggregationControlCommand cmd, String script, String args) { + assertEquals(script, VRScripts.VR_CFG); + assertTrue(args.startsWith("-c /var/cache/cloud/VR-")); + assertTrue(args.endsWith(".cfg")); + } + + protected void verifyFile(AggregationControlCommand cmd, String path, String filename, String content) { + assertEquals(path, "/var/cache/cloud/"); + assertTrue(filename.startsWith("VR-")); + assertTrue(filename.endsWith(".cfg")); + assertEquals(content, "#Apache CloudStack Virtual Router Config File\n" + + "\n" + + "1.0\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "" + + "\n"); + } + } diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java index 3535a320b4c..350f3960e35 100644 --- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java @@ -37,11 +37,11 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network; import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; import com.cloud.offering.ServiceOffering; import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; -import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.fsm.NoTransitionException; @@ -76,7 +76,7 @@ public interface VirtualMachineManager extends Manager { * @param hyperType Hypervisor type * @throws InsufficientCapacityException If there are insufficient capacity to deploy this vm. */ - void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, Pair rootDiskOffering, + void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, DiskOfferingInfo rootDiskOfferingInfo, LinkedHashMap dataDiskOfferings, LinkedHashMap> auxiliaryNetworks, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException; diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index f79d3361b97..2531074029b 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -106,7 +106,7 @@ public interface VolumeOrchestrationService { boolean canVmRestartOnAnotherServer(long vmId); - DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, VirtualMachineTemplate template, VirtualMachine vm, + DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, Account owner); String getVmNameFromVolumeId(long volumeId); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java index f20e18847fe..a8f8b308539 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionService.java @@ -26,6 +26,8 @@ import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; public interface DataMotionService { + void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback); + void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback); void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback callback); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java index 39a257d7262..b5601e9693c 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java @@ -30,6 +30,8 @@ public interface DataMotionStrategy { StrategyPriority canHandle(Map volumeMap, Host srcHost, Host destHost); + Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback); + Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback); Void copyAsync(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback callback); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java index 19893d07d1f..bb9f2dad1ff 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreInfo.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import java.util.Map; + import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -25,6 +27,16 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; public interface PrimaryDataStoreInfo extends StoragePool { + static final String MANAGED = "managed"; + static final String STORAGE_HOST= "storageHost"; + static final String MANAGED_STORE_TARGET = "managedStoreTarget"; + static final String MANAGED_STORE_TARGET_ROOT_VOLUME = "managedStoreTargetRootVolume"; + static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername"; + static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret"; + static final String CHAP_TARGET_USERNAME = "chapTargetUsername"; + static final String CHAP_TARGET_SECRET = "chapTargetSecret"; + static final String VOLUME_SIZE = "volumeSize"; + boolean isHypervisorSupported(HypervisorType hypervisor); boolean isLocalStorageSupported(); @@ -37,5 +49,11 @@ public interface PrimaryDataStoreInfo extends StoragePool { @Override StoragePoolType getPoolType(); + boolean isManaged(); + + void setDetails(Map details); + + Map getDetails(); + PrimaryDataStoreLifeCycle getLifeCycle(); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index 3f676ae73dc..b6e61069e77 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -78,7 +78,11 @@ public interface VolumeService { VolumeEntity getVolumeEntity(long volumeId); - AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template); + AsyncCallFuture createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, + TemplateInfo srcTemplateInfo, long destHostId); + + AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, + TemplateInfo template); AsyncCallFuture copyVolume(VolumeInfo srcVolume, DataStore destStore); diff --git a/engine/components-api/src/com/cloud/resource/ResourceManager.java b/engine/components-api/src/com/cloud/resource/ResourceManager.java index 95fb3853717..5a9bddb1863 100755 --- a/engine/components-api/src/com/cloud/resource/ResourceManager.java +++ b/engine/components-api/src/com/cloud/resource/ResourceManager.java @@ -17,15 +17,18 @@ // under the License. package com.cloud.resource; +import java.util.HashMap; import java.util.List; import java.util.Map; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.PodCluster; import com.cloud.exception.AgentUnavailableException; +import com.cloud.gpu.HostGpuGroupsVO; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostStats; @@ -137,4 +140,42 @@ public interface ResourceManager extends ResourceService { * @return */ List listAllUpAndEnabledNonHAHosts(Type type, Long clusterId, Long podId, long dcId); + + /** + * Check if host has GPU devices available + * @param hostId the host to be checked + * @param vgpuType the VGPU type + * @return true when the host has the capacity with given VGPU type + */ + boolean isGPUDeviceAvailable(long hostId, String vgpuType); + + /** + * Get available GPU device + * @param hostId the host to be checked + * @param vgpuType the VGPU type + * @return GPUDeviceTO[] + */ + GPUDeviceTO getGPUDevice(long hostId, String vgpuType); + + /** + * Return listof available GPU devices + * @param hostId, the host to be checked + * @param vgpuType the VGPU type + * @return List of HostGpuGroupsVO. + */ + List listAvailableGPUDevice(long hostId, String vgpuType); + + /** + * Update GPU device details (post VM deployment) + * @param hostId, the dest host Id + * @param groupDetails, capacity of GPU group. + */ + void updateGPUDetails(long hostId, HashMap> groupDetails); + + /** + * Get GPU details for a host + * @param host, the Host object + * @return Details of groupNames and enabled VGPU type with remaining capacity. + */ + HashMap> getGPUStatistics(HostVO host); } diff --git a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java index 354da4b1c48..9874ee41932 100755 --- a/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java +++ b/engine/orchestration/src/com/cloud/agent/manager/DirectAgentAttache.java @@ -34,7 +34,6 @@ import com.cloud.agent.transport.Request; import com.cloud.agent.transport.Response; import com.cloud.exception.AgentUnavailableException; import com.cloud.host.Status; -import com.cloud.host.Status.Event; import com.cloud.resource.ServerResource; public class DirectAgentAttache extends AgentAttache { @@ -145,7 +144,6 @@ public class DirectAgentAttache extends AgentAttache { PingCommand cmd = resource.getCurrentStatus(_id); if (cmd == null) { s_logger.warn("Unable to get current status on " + _id + "(" + _name + ")"); - _mgr.disconnectWithInvestigation(DirectAgentAttache.this, Event.AgentDisconnected); return; } if (s_logger.isDebugEnabled()) { diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 3c272e48e55..609aefa4b2a 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -80,6 +80,8 @@ import com.cloud.agent.api.CheckVirtualMachineAnswer; import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.ClusterSyncAnswer; import com.cloud.agent.api.ClusterSyncCommand; +import com.cloud.agent.api.ClusterVMMetaDataSyncCommand; +import com.cloud.agent.api.ClusterVMMetaDataSyncAnswer; import com.cloud.agent.api.Command; import com.cloud.agent.api.MigrateAnswer; import com.cloud.agent.api.MigrateCommand; @@ -101,6 +103,7 @@ import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; @@ -137,6 +140,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; +import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.ha.HighAvailabilityManager; import com.cloud.ha.HighAvailabilityManager.WorkType; import com.cloud.host.Host; @@ -152,6 +156,7 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.rules.RulesManager; import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.resource.ResourceManager; @@ -273,6 +278,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject protected AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject + protected VGPUTypesDao _vgpuTypesDao; + @Inject protected EntityManager _entityMgr; @Inject @@ -340,6 +347,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac static final ConfigKey ClusterDeltaSyncInterval = new ConfigKey("Advanced", Integer.class, "sync.interval", "60", "Cluster Delta sync interval in seconds", false); + static final ConfigKey ClusterVMMetaDataSyncInterval = new ConfigKey("Advanced", Integer.class, "vmmetadata.sync.interval", "180", "Cluster VM metadata sync interval in seconds", + false); static final ConfigKey VmJobEnabled = new ConfigKey("Advanced", Boolean.class, "vm.job.enabled", "true", @@ -368,7 +377,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override @DB public void allocate(String vmInstanceName, final VirtualMachineTemplate template, ServiceOffering serviceOffering, - final Pair rootDiskOffering, LinkedHashMap dataDiskOfferings, + final DiskOfferingInfo rootDiskOfferingInfo, LinkedHashMap dataDiskOfferings, final LinkedHashMap> auxiliaryNetworks, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException { @@ -408,11 +417,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } if (template.getFormat() == ImageFormat.ISO) { - volumeMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOffering.first(), rootDiskOffering.second(), vmFinal, template, owner); + volumeMgr.allocateRawVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(), vmFinal, + template, owner); } else if (template.getFormat() == ImageFormat.BAREMETAL) { // Do nothing } else { - volumeMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOffering.first(), rootDiskOffering.second(), template, vmFinal, owner); + volumeMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOfferingInfo.getDiskOffering(), rootDiskOfferingInfo.getSize(), + rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vmFinal, owner); } for (Map.Entry offering : dataDiskOfferingsFinal.entrySet()) { @@ -429,7 +440,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, LinkedHashMap> networks, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException { - allocate(vmInstanceName, template, serviceOffering, new Pair(serviceOffering, null), null, networks, plan, hyperType); + allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), null, networks, plan, hyperType); } private VirtualMachineGuru getVmGuru(VirtualMachine vm) { @@ -1019,6 +1030,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new ConcurrentOperationException("Unable to transition to a new state."); } + // Update GPU device capacity + GPUDeviceTO gpuDevice = startAnswer.getVirtualMachine().getGpuDevice(); + if (gpuDevice != null) { + _resourceMgr.updateGPUDetails(destHostId, gpuDevice.getGroupDetails()); + } + startedVm = vm; if (s_logger.isDebugEnabled()) { s_logger.debug("Start completed for VM " + vm); @@ -1214,6 +1231,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } + GPUDeviceTO gpuDevice = stop.getGpuDevice(); + if (gpuDevice != null) { + _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails()); + } if (!answer.getResult()) { s_logger.debug("Unable to stop VM due to " + answer.getDetails()); return false; @@ -1476,6 +1497,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new CloudRuntimeException("Unable to stop the virtual machine due to " + answer.getDetails()); } vmGuru.finalizeStop(profile, answer); + GPUDeviceTO gpuDevice = stop.getGpuDevice(); + if (gpuDevice != null) { + _resourceMgr.updateGPUDetails(vm.getHostId(), gpuDevice.getGroupDetails()); + } } else { throw new CloudRuntimeException("Invalid answer received in response to a StopCommand on " + vm.instanceName); } @@ -2521,7 +2546,35 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return commands; } - public void deltaSync(Map> newStates) { + // this is XenServer specific + public void syncVMMetaData(Map vmMetadatum) { + if (vmMetadatum == null || vmMetadatum.isEmpty()) { + return; + } + for (Map.Entry entry : vmMetadatum.entrySet()) { + String name = entry.getKey(); + String platform = entry.getValue(); + if (platform == null || platform.isEmpty()) { + continue; + } + VMInstanceVO vm = _vmDao.findVMByInstanceName(name); + if (vm != null && vm.getType() == VirtualMachine.Type.User) { + // track platform info + UserVmVO userVm = _userVmDao.findById(vm.getId()); + _userVmDao.loadDetails(userVm); + userVm.setDetail("platform", platform); + String pvdriver = "xenserver56"; + if ( platform.contains("device_id")) { + pvdriver = "xenserver61"; + } + userVm.setDetail("hypervisortoolsversion", pvdriver); + _userVmDao.saveDetails(userVm); + } + } + } + + + public void deltaSync(Map> newStates) { Map states = convertToInfos(newStates); for (Map.Entry entry : states.entrySet()) { @@ -2556,7 +2609,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - public void fullSync(final long clusterId, Map> newStates) { + public void fullSync(final long clusterId, Map> newStates) { if (newStates == null) return; Map infos = convertToInfos(newStates); @@ -2686,24 +2739,24 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } - protected Map convertToInfos(final Map> newStates) { + protected Map convertToInfos(final Map> newStates) { final HashMap map = new HashMap(); if (newStates == null) { return map; } boolean is_alien_vm = true; long alien_vm_count = -1; - for (Map.Entry> entry : newStates.entrySet()) { + for (Map.Entry> entry : newStates.entrySet()) { is_alien_vm = true; String name = entry.getKey(); VMInstanceVO vm = _vmDao.findVMByInstanceName(name); if (vm != null) { - map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vm, entry.getValue().second(), entry.getValue().first(), entry.getValue().third())); + map.put(vm.getId(), new AgentVmInfo(entry.getKey(), vm, entry.getValue().second(), entry.getValue().first())); is_alien_vm = false; } // alien VMs if (is_alien_vm) { - map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, entry.getValue().second(), entry.getValue().first(), entry.getValue().third())); + map.put(alien_vm_count--, new AgentVmInfo(entry.getKey(), null, entry.getValue().second(), entry.getValue().first())); s_logger.warn("Found an alien VM " + entry.getKey()); } } @@ -2784,15 +2837,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac ") stopped on host " + hostDesc + " due to storage failure", "Virtual Machine " + vm.getInstanceName() + " (id: " + vm.getId() + ") running on host [" + vm.getHostId() + "] stopped due to storage failure."); } - // track platform info - if (info.platform != null && !info.platform.isEmpty()) { - if (vm.getType() == VirtualMachine.Type.User) { - UserVmVO userVm = _userVmDao.findById(vm.getId()); - _userVmDao.loadDetails(userVm); - userVm.setDetail("platform", info.platform); - _userVmDao.saveDetails(userVm); - } - } if (trackExternalChange) { if (serverState == State.Starting) { @@ -3004,15 +3048,21 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean processAnswers(long agentId, long seq, Answer[] answers) { - if (!VmJobEnabled.value()) { - for (final Answer answer : answers) { - if (answer instanceof ClusterSyncAnswer) { + for (final Answer answer : answers) { + if (answer instanceof ClusterSyncAnswer) { + if (!VmJobEnabled.value()) { ClusterSyncAnswer hs = (ClusterSyncAnswer)answer; - if (!hs.isExceuted()) { + if (!hs.isExecuted()) { deltaSync(hs.getNewStates()); hs.setExecuted(); } } + } else if ( answer instanceof ClusterVMMetaDataSyncAnswer) { + ClusterVMMetaDataSyncAnswer cvms = (ClusterVMMetaDataSyncAnswer)answer; + if (!cvms.isExecuted()) { + syncVMMetaData(cvms.getVMMetaDatum()); + cvms.setExecuted(); + } } } return true; @@ -3046,7 +3096,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } } - if(VmJobEnabled.value()) { if (ping.getHostVmStateReport() != null) { _syncMgr.processHostVmStatePingReport(agentId, ping.getHostVmStateReport()); @@ -3101,11 +3150,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (agent.getHypervisorType() == HypervisorType.XenServer) { // only for Xen if (!VmJobEnabled.value()) { StartupRoutingCommand startup = (StartupRoutingCommand)cmd; - HashMap> allStates = startup.getClusterVMStateChanges(); + HashMap> allStates = startup.getClusterVMStateChanges(); if (allStates != null) { fullSync(clusterId, allStates); } - // initiate the cron job ClusterSyncCommand syncCmd = new ClusterSyncCommand(ClusterDeltaSyncInterval.value(), clusterId); try { @@ -3115,6 +3163,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.fatal("The Cluster VM sync process failed for cluster id " + clusterId + " with ", e); } } + // initiate the cron job + ClusterVMMetaDataSyncCommand syncVMMetaDataCmd = new ClusterVMMetaDataSyncCommand(ClusterVMMetaDataSyncInterval.value(), clusterId); + try { + long seq_no = _agentMgr.send(agentId, new Commands(syncVMMetaDataCmd), this); + s_logger.debug("Cluster VM metadata sync started with jobid " + seq_no); + } catch (AgentUnavailableException e) { + s_logger.fatal("The Cluster VM metadata sync process failed for cluster id " + clusterId + " with ", e); + } } else { // for others KVM and VMWare if (!VmJobEnabled.value()) { StartupRoutingCommand startup = (StartupRoutingCommand)cmd; @@ -3185,33 +3241,22 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac public State state; public String hostUuid; public VMInstanceVO vm; - public String platform; @SuppressWarnings("unchecked") - public AgentVmInfo(String name, VMInstanceVO vm, State state, String host, String platform) { + public AgentVmInfo(String name, VMInstanceVO vm, State state, String host) { this.name = name; this.state = state; this.vm = vm; hostUuid = host; - this.platform = platform; - - } - - public AgentVmInfo(String name, VMInstanceVO vm, State state, String host) { - this(name, vm, state, host, null); } public AgentVmInfo(String name, VMInstanceVO vm, State state) { - this(name, vm, state, null, null); + this(name, vm, state, null); } public String getHostUuid() { return hostUuid; } - - public String getPlatform() { - return platform; - } } @Override diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java index b00b5ad7499..fc1b85c0802 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java @@ -46,18 +46,19 @@ import com.cloud.network.Network; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.offering.DiskOffering; +import com.cloud.offering.DiskOfferingInfo; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.user.dao.AccountDao; -import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentContext; import com.cloud.vm.NicProfile; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; @Component @@ -78,6 +79,9 @@ public class CloudOrchestrator implements OrchestrationService { @Inject protected UserVmDao _userVmDao = null; + @Inject + protected UserVmDetailsDao _userVmDetailsDao = null; + @Inject protected ServiceOfferingDao _serviceOfferingDao; @@ -176,12 +180,25 @@ public class CloudOrchestrator implements OrchestrationService { // If the template represents an ISO, a disk offering must be passed in, and will be used to create the root disk // Else, a disk offering is optional, and if present will be used to create the data disk - Pair rootDiskOffering = new Pair(null, null); + DiskOfferingInfo rootDiskOfferingInfo = new DiskOfferingInfo(); LinkedHashMap dataDiskOfferings = new LinkedHashMap(); ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - rootDiskOffering.first(offering); - rootDiskOffering.second(rootDiskSize); + + rootDiskOfferingInfo.setDiskOffering(offering); + rootDiskOfferingInfo.setSize(rootDiskSize); + + if (offering.isCustomizedIops() != null && offering.isCustomizedIops()) { + Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + + if (userVmDetails != null) { + String minIops = userVmDetails.get("minIops"); + String maxIops = userVmDetails.get("maxIops"); + + rootDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null); + rootDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); + } + } if (vm.getDiskOfferingId() != null) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(vm.getDiskOfferingId()); @@ -199,7 +216,7 @@ public class CloudOrchestrator implements OrchestrationService { dataDiskOfferings.put(diskOffering, size); } - _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(templateId)), offering, rootDiskOffering, dataDiskOfferings, networkIpMap, plan, + _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(templateId)), offering, rootDiskOfferingInfo, dataDiskOfferings, networkIpMap, plan, hypervisorType); return vmEntity; @@ -217,9 +234,11 @@ public class CloudOrchestrator implements OrchestrationService { //load vm instance and offerings and call virtualMachineManagerImpl VMInstanceVO vm = _vmDao.findByUuid(id); - Pair rootDiskOffering = new Pair(null, null); ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - rootDiskOffering.first(offering); + + DiskOfferingInfo rootDiskOfferingInfo = new DiskOfferingInfo(); + + rootDiskOfferingInfo.setDiskOffering(offering); LinkedHashMap dataDiskOfferings = new LinkedHashMap(); Long diskOfferingId = vm.getDiskOfferingId(); @@ -238,8 +257,9 @@ public class CloudOrchestrator implements OrchestrationService { } _volumeMgr.validateVolumeSizeRange(size * 1024 * 1024 * 1024); } - rootDiskOffering.first(diskOffering); - rootDiskOffering.second(size); + + rootDiskOfferingInfo.setDiskOffering(diskOffering); + rootDiskOfferingInfo.setSize(size); LinkedHashMap> networkIpMap = new LinkedHashMap>(); for (String uuid : networkNicMap.keySet()) { @@ -251,7 +271,7 @@ public class CloudOrchestrator implements OrchestrationService { HypervisorType hypervisorType = HypervisorType.valueOf(hypervisor); - _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), offering, rootDiskOffering, dataDiskOfferings, networkIpMap, plan, hypervisorType); + _itMgr.allocate(vm.getInstanceName(), _templateDao.findById(new Long(isoId)), offering, rootDiskOfferingInfo, dataDiskOfferings, networkIpMap, plan, hypervisorType); return vmEntity; } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index 6e55bd2881c..6aa1e36ccd4 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.engine.orchestration; + import java.net.URI; import java.util.ArrayList; import java.util.Arrays; @@ -36,8 +37,10 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.acl.IAMEntityType; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.acl.IAMEntityType; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -49,7 +52,6 @@ import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.region.PortableIpDao; -import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -131,6 +133,7 @@ import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeDao; import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO; import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.element.AggregatedCommandExecutor; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.IpDeployer; import com.cloud.network.element.LoadBalancingServiceProvider; @@ -1082,6 +1085,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } + for (NetworkElement element : networkElements) { + if ((element instanceof AggregatedCommandExecutor) && (providersToImplement.contains(element.getProvider()))) { + ((AggregatedCommandExecutor)element).prepareAggregatedExecution(network, dest); + } + } + + try { // reapply all the firewall/staticNat/lb rules s_logger.debug("Reprogramming network " + network + " as a part of network implement"); if (!reprogramNetworkRules(network.getId(), CallContext.current().getCallingAccount(), network)) { @@ -1092,6 +1102,25 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid()); throw ex; } + for (NetworkElement element : networkElements) { + if ((element instanceof AggregatedCommandExecutor) && (providersToImplement.contains(element.getProvider()))) { + if (!((AggregatedCommandExecutor)element).completeAggregatedExecution(network, dest)) { + s_logger.warn("Failed to re-program the network as a part of network " + network + " implement due to aggregated commands execution failure!"); + // see DataCenterVO.java + ResourceUnavailableException ex = new ResourceUnavailableException("Unable to apply network rules as a part of network " + network + " implement", DataCenter.class, + network.getDataCenterId()); + ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid()); + throw ex; + } + } + } + } finally { + for (NetworkElement element : networkElements) { + if ((element instanceof AggregatedCommandExecutor) && (providersToImplement.contains(element.getProvider()))) { + ((AggregatedCommandExecutor)element).cleanupAggregatedExecution(network, dest); + } + } + } } // This method re-programs the rules/ips for existing network diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index d3eda8a3721..f3753a74176 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; @@ -604,15 +605,19 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } @Override - public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, VirtualMachineTemplate template, VirtualMachine vm, Account owner) { + public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm, Account owner) { assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template really...."; Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId()); if (rootDisksize != null) { size = (rootDisksize * 1024 * 1024 * 1024); } - VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, offering.getMinIops(), offering.getMaxIops(), - null); + + minIops = minIops != null ? minIops : offering.getMinIops(); + maxIops = maxIops != null ? maxIops : offering.getMaxIops(); + + VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, minIops, maxIops, null); + vol.setFormat(getSupportedImageFormatForCluster(template.getHypervisorType())); if (vm != null) { vol.setInstanceId(vm.getId()); @@ -1113,7 +1118,23 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati future = volService.createVolumeAsync(volume, destPool); } else { TemplateInfo templ = tmplFactory.getTemplate(templateId, DataStoreRole.Image); - future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); + + PrimaryDataStore primaryDataStore = (PrimaryDataStore)destPool; + + if (primaryDataStore.isManaged()) { + DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); + HypervisorType hyperType = vm.getVirtualMachine().getHypervisorType(); + + // update the volume's hypervisor_ss_reserve from its disk offering (used for managed storage) + updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType); + + long hostId = vm.getVirtualMachine().getHostId(); + + future = volService.createManagedStorageAndVolumeFromTemplateAsync(volume, destPool.getId(), templ, hostId); + } + else { + future = volService.createVolumeFromTemplateAsync(volume, destPool.getId(), templ); + } } VolumeApiResult result = null; try { diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 08efb8345f5..765d86c34ad 100644 --- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -316,6 +316,8 @@ + + diff --git a/engine/schema/src/com/cloud/event/EventVO.java b/engine/schema/src/com/cloud/event/EventVO.java index 0fa891387ab..3ae46d6244d 100644 --- a/engine/schema/src/com/cloud/event/EventVO.java +++ b/engine/schema/src/com/cloud/event/EventVO.java @@ -78,8 +78,8 @@ public class EventVO implements Event { @Column(name = "archived") private boolean archived; - @Column(name = "display_event", updatable = true, nullable = false) - protected boolean isDisplayEventEnabled = true; + @Column(name = "display", updatable = true, nullable = false) + protected boolean display = true; @Transient private int totalSize; @@ -214,12 +214,12 @@ public class EventVO implements Event { this.archived = archived; } - public boolean isDisplayEventEnabled() { - return isDisplayEventEnabled; + public boolean isDisplay() { + return display; } - public void setDisplayEventEnabled(boolean displayEventEnabled) { - isDisplayEventEnabled = displayEventEnabled; + public void setDisplay(boolean display) { + this.display = display; } @Override diff --git a/engine/schema/src/com/cloud/gpu/HostGpuGroupsVO.java b/engine/schema/src/com/cloud/gpu/HostGpuGroupsVO.java new file mode 100644 index 00000000000..1c186a162e3 --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/HostGpuGroupsVO.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.gpu; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name="host_gpu_groups") +public class HostGpuGroupsVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="group_name") + private String groupName; + + @Column(name="host_id") + private long hostId; + + protected HostGpuGroupsVO() { + } + + public HostGpuGroupsVO(long hostId, String groupName) { + this.hostId = hostId; + this.groupName = groupName; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + @Override + public long getId() { + return id; + } +} diff --git a/engine/schema/src/com/cloud/gpu/VGPUTypesVO.java b/engine/schema/src/com/cloud/gpu/VGPUTypesVO.java new file mode 100644 index 00000000000..586a0c92798 --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/VGPUTypesVO.java @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.gpu; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name="vgpu_types") +public class VGPUTypesVO implements InternalIdentity { + @Id + @GeneratedValue(strategy=GenerationType.IDENTITY) + @Column(name="id") + private long id; + + @Column(name="vgpu_type") + private String vgpuType; + + @Column(name="gpu_group_id") + private long gpuGroupId; + + @Column(name="remaining_vm_capacity") + private long remainingCapacity; + + protected VGPUTypesVO() { + } + + public VGPUTypesVO(String vgpuType, long gpuGroupId, long remainingCapacity) { + this.vgpuType = vgpuType; + this.gpuGroupId = gpuGroupId; + this.remainingCapacity = remainingCapacity; + } + + public String getVgpuType() { + return vgpuType; + } + + public void setVgpuType(String vgpuType) { + this.vgpuType = vgpuType; + } + + public long getGpuGroupId() { + return gpuGroupId; + } + + public void setGpuGroupId(long gpuGroupId) { + this.gpuGroupId = gpuGroupId; + } + + public long getRemainingCapacity() { + return remainingCapacity; + } + + public void setRemainingCapacity(long remainingCapacity) { + this.remainingCapacity = remainingCapacity; + } + + @Override + public long getId() { + return id; + } +} diff --git a/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDao.java b/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDao.java new file mode 100644 index 00000000000..58641443972 --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDao.java @@ -0,0 +1,60 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.gpu.dao; + +import java.util.List; + +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.utils.db.GenericDao; + +public interface HostGpuGroupsDao extends GenericDao { + + /** + * Find host device by hostId and PCI ID + * @param hostId the host + * @param groupName GPU group + * @return HostGpuGroupsVO + */ + HostGpuGroupsVO findByHostIdGroupName(long hostId, String groupName); + + /** + * List all the host Ids, that are GPU enabled. + * @return list of hostIds + */ + List listHostIds(); + + /** + * Return a list by hostId. + * @param hostId the host + * @return HostGpuGroupsVO + */ + List listByHostId(long hostId); + + /** + * Delete entries by hostId. + * @param hostId the host + */ + void deleteGpuEntries(long hostId); + + /** + * Save the list of GPU groups belonging to a host + * @param hostId the host + * @param gpuGroups the list of GPU groups to save + */ + void persist(long hostId, List gpuGroups); + +} diff --git a/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java b/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java new file mode 100644 index 00000000000..6bddea262ed --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/dao/HostGpuGroupsDaoImpl.java @@ -0,0 +1,94 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.gpu.dao; + +import java.util.List; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@Local(value = HostGpuGroupsDao.class) +public class HostGpuGroupsDaoImpl extends GenericDaoBase implements HostGpuGroupsDao { + private static final Logger s_logger = Logger.getLogger(HostGpuGroupsDaoImpl.class); + + private final SearchBuilder _hostIdGroupNameSearch; + private final SearchBuilder _searchByHostId; + private final GenericSearchBuilder _searchHostIds; + + public HostGpuGroupsDaoImpl() { + + _hostIdGroupNameSearch = createSearchBuilder(); + _hostIdGroupNameSearch.and("hostId", _hostIdGroupNameSearch.entity().getHostId(), SearchCriteria.Op.EQ); + _hostIdGroupNameSearch.and("groupName", _hostIdGroupNameSearch.entity().getGroupName(), SearchCriteria.Op.EQ); + _hostIdGroupNameSearch.done(); + + _searchByHostId = createSearchBuilder(); + _searchByHostId.and("hostId", _searchByHostId.entity().getHostId(), SearchCriteria.Op.EQ); + _searchByHostId.done(); + + _searchHostIds = createSearchBuilder(Long.class); + _searchHostIds.selectFields(_searchHostIds.entity().getHostId()); + _searchHostIds.done(); + } + + @Override + public HostGpuGroupsVO findByHostIdGroupName(long hostId, String groupName) { + SearchCriteria sc = _hostIdGroupNameSearch.create(); + sc.setParameters("hostId", hostId); + sc.setParameters("groupName", groupName); + return findOneBy(sc); + } + + @Override + public List listHostIds() { + SearchCriteria sc = _searchHostIds.create(); + return customSearch(sc, null); + } + + @Override + public List listByHostId(long hostId) { + SearchCriteria sc = _searchByHostId.create(); + sc.setParameters("hostId", hostId); + return listBy(sc); + } + + @Override + public void persist(long hostId, List gpuGroups) { + for (String groupName : gpuGroups) { + if (findByHostIdGroupName(hostId, groupName) == null) { + HostGpuGroupsVO record = new HostGpuGroupsVO(hostId, groupName); + persist(record); + } + } + } + + @Override + public void deleteGpuEntries(long hostId) { + SearchCriteria sc = _searchByHostId.create(); + sc.setParameters("hostId", hostId); + remove(sc); + } +} diff --git a/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDao.java b/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDao.java new file mode 100644 index 00000000000..2872cfda886 --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDao.java @@ -0,0 +1,48 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you 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 com.cloud.gpu.dao; + +import java.util.HashMap; +import java.util.List; + +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.utils.db.GenericDao; + +public interface VGPUTypesDao extends GenericDao { + + /** + * Find VGPU types by group Id + * @param groupId of the GPU group + * @return list of VGPUTypesVO + */ + List listByGroupId(long groupId); + + /** + * Find VGPU type by group Id and VGPU type + * @param groupId of the GPU group + * @param vgpuType name of VGPU type + * @return VGPUTypesVO + */ + VGPUTypesVO findByGroupIdVGPUType(long groupId, String vgpuType); + + /** + * Save the list of enabled VGPU types + * @param hostId the host + * @param groupDetails with enabled VGPU types + */ + void persist(long hostId, HashMap> groupDetails); +} diff --git a/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDaoImpl.java b/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDaoImpl.java new file mode 100644 index 00000000000..7cc83cc9eb8 --- /dev/null +++ b/engine/schema/src/com/cloud/gpu/dao/VGPUTypesDaoImpl.java @@ -0,0 +1,95 @@ +//Licensed to the Apache Software Foundation (ASF) under one +//or more contributor license agreements. See the NOTICE file +//distributed with this work for additional information +//regarding copyright ownership. The ASF licenses this file +//to you 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 com.cloud.gpu.dao; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +@Component +@Local(value = VGPUTypesDao.class) +public class VGPUTypesDaoImpl extends GenericDaoBase implements VGPUTypesDao { + private static final Logger s_logger = Logger.getLogger(VGPUTypesDaoImpl.class); + + private final SearchBuilder _searchByGroupId; + private final SearchBuilder _searchByGroupIdVGPUType; + // private final SearchBuilder _searchByHostId; + // private final SearchBuilder _searchForStaleEntries; + + @Inject protected HostGpuGroupsDao _hostGpuGroupsDao; + + public VGPUTypesDaoImpl() { + + _searchByGroupId = createSearchBuilder(); + _searchByGroupId.and("groupId", _searchByGroupId.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + _searchByGroupId.done(); + + _searchByGroupIdVGPUType = createSearchBuilder(); + _searchByGroupIdVGPUType.and("groupId", _searchByGroupIdVGPUType.entity().getGpuGroupId(), SearchCriteria.Op.EQ); + _searchByGroupIdVGPUType.and("vgpuType", _searchByGroupIdVGPUType.entity().getVgpuType(), SearchCriteria.Op.EQ); + _searchByGroupIdVGPUType.done(); + } + + @Override + public List listByGroupId(long groupId) { + SearchCriteria sc = _searchByGroupId.create(); + sc.setParameters("groupId", groupId); + return listBy(sc); + } + + @Override + public VGPUTypesVO findByGroupIdVGPUType(long groupId, String vgpuType) { + SearchCriteria sc = _searchByGroupIdVGPUType.create(); + sc.setParameters("groupId", groupId); + sc.setParameters("vgpuType", vgpuType); + return findOneBy(sc); + } + + @Override + public void persist(long hostId, HashMap> groupDetails) { + Iterator>> it1 = groupDetails.entrySet().iterator(); + while (it1.hasNext()) { + Entry> entry = it1.next(); + HostGpuGroupsVO gpuGroup = _hostGpuGroupsDao.findByHostIdGroupName(hostId, entry.getKey()); + HashMap values = entry.getValue(); + Iterator> it2 = values.entrySet().iterator(); + while (it2.hasNext()) { + Entry record = it2.next(); + VGPUTypesVO vgpuType = null; + if ((vgpuType = findByGroupIdVGPUType(gpuGroup.getId(), record.getKey())) == null) { + persist(new VGPUTypesVO(record.getKey(), gpuGroup.getId(), record.getValue())); + } else { + vgpuType.setRemainingCapacity(record.getValue()); + update(vgpuType.getId(), vgpuType); + } + } + } + } +} diff --git a/engine/schema/src/com/cloud/host/HostVO.java b/engine/schema/src/com/cloud/host/HostVO.java index 56c066b1b72..25cc9544da2 100755 --- a/engine/schema/src/com/cloud/host/HostVO.java +++ b/engine/schema/src/com/cloud/host/HostVO.java @@ -17,6 +17,7 @@ package com.cloud.host; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -153,6 +154,10 @@ public class HostVO implements Host { @Transient List hostTags; + // This value is only for saving and current cannot be loaded. + @Transient + HashMap> groupDetails = new HashMap>(); + @Override public String getStorageIpAddressDeux() { return storageIpAddressDeux; @@ -313,6 +318,14 @@ public class HostVO implements Host { this.hostTags = hostTags; } + public HashMap> getGpuGroupDetails() { + return groupDetails; + } + + public void setGpuGroups(HashMap> groupDetails) { + this.groupDetails = groupDetails; + } + @Column(name = "data_center_id", nullable = false) private long dataCenterId; diff --git a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java index 08a436644ba..c2a9826405d 100755 --- a/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/com/cloud/host/dao/HostDaoImpl.java @@ -38,6 +38,8 @@ import com.cloud.cluster.agentlb.HostTransferMapVO; import com.cloud.cluster.agentlb.dao.HostTransferMapDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; +import com.cloud.gpu.dao.HostGpuGroupsDao; +import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostTagVO; @@ -126,6 +128,10 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Inject protected HostDetailsDao _detailsDao; @Inject + protected HostGpuGroupsDao _hostGpuGroupsDao; + @Inject + protected VGPUTypesDao _vgpuTypesDao; + @Inject protected HostTagsDao _hostTagsDao; @Inject protected HostTransferMapDao _hostTransferDao; @@ -775,6 +781,16 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao _hostTagsDao.persist(host.getId(), hostTags); } + protected void saveGpuRecords(HostVO host) { + HashMap> groupDetails = host.getGpuGroupDetails(); + if (groupDetails != null) { + // Create/Update GPU group entries + _hostGpuGroupsDao.persist(host.getId(), new ArrayList(groupDetails.keySet())); + // Create/Update VGPU types entries + _vgpuTypesDao.persist(host.getId(), groupDetails); + } + } + @Override @DB public HostVO persist(HostVO host) { @@ -797,6 +813,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao loadDetails(dbHost); saveHostTags(host); loadHostTags(dbHost); + saveGpuRecords(host); txn.commit(); @@ -816,6 +833,7 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao saveDetails(host); saveHostTags(host); + saveGpuRecords(host); txn.commit(); diff --git a/engine/schema/src/com/cloud/network/vpc/NetworkACLItemVO.java b/engine/schema/src/com/cloud/network/vpc/NetworkACLItemVO.java index 24ec1b9f71b..8031b001b8a 100644 --- a/engine/schema/src/com/cloud/network/vpc/NetworkACLItemVO.java +++ b/engine/schema/src/com/cloud/network/vpc/NetworkACLItemVO.java @@ -71,7 +71,7 @@ public class NetworkACLItemVO implements NetworkACLItem { @Enumerated(value = EnumType.STRING) TrafficType trafficType; - @Column(name = "cidr") + @Column(name = "cidr", length = 2048) String sourceCidrs; @Column(name = "uuid") @@ -88,20 +88,20 @@ public class NetworkACLItemVO implements NetworkACLItem { protected boolean display = true; public NetworkACLItemVO() { - this.uuid = UUID.randomUUID().toString(); + uuid = UUID.randomUUID().toString(); } public NetworkACLItemVO(Integer portStart, Integer portEnd, String protocol, long aclId, List sourceCidrs, Integer icmpCode, Integer icmpType, TrafficType trafficType, Action action, int number) { - this.sourcePortStart = portStart; - this.sourcePortEnd = portEnd; + sourcePortStart = portStart; + sourcePortEnd = portEnd; this.protocol = protocol; this.aclId = aclId; - this.state = State.Staged; + state = State.Staged; this.icmpCode = icmpCode; this.icmpType = icmpType; setSourceCidrList(sourceCidrs); - this.uuid = UUID.randomUUID().toString(); + uuid = UUID.randomUUID().toString(); this.trafficType = trafficType; this.action = action; this.number = number; @@ -191,7 +191,7 @@ public class NetworkACLItemVO implements NetworkACLItem { @Override public String getUuid() { - return this.uuid; + return uuid; } @Override diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java index ed8090b3bc7..8dfe5273b94 100644 --- a/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingDetailsVO.java @@ -60,6 +60,10 @@ public class ServiceOfferingDetailsVO implements ResourceDetail { return resourceId; } + public void setResourceId(long serviceOfferingId) { + this.resourceId = serviceOfferingId; + } + @Override public String getName() { return name; diff --git a/engine/schema/src/com/cloud/storage/GuestOSHypervisorVO.java b/engine/schema/src/com/cloud/storage/GuestOSHypervisorVO.java index 8210abf8c3d..1fbc9d7178a 100644 --- a/engine/schema/src/com/cloud/storage/GuestOSHypervisorVO.java +++ b/engine/schema/src/com/cloud/storage/GuestOSHypervisorVO.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.storage; +import java.util.Date; +import java.util.UUID; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -23,6 +26,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "guest_os_hypervisor") public class GuestOSHypervisorVO implements GuestOSHypervisor { @@ -32,7 +37,7 @@ public class GuestOSHypervisorVO implements GuestOSHypervisor { long id; @Column(name = "guest_os_id") - private long guestOsId; + long guestOsId; @Column(name = "guest_os_name") String guestOsName; @@ -40,11 +45,28 @@ public class GuestOSHypervisorVO implements GuestOSHypervisor { @Column(name = "hypervisor_type") String hypervisorType; + @Column(name = "hypervisor_version") + String hypervisorVersion; + + @Column(name = "uuid") + String uuid = UUID.randomUUID().toString(); + + @Column(name = GenericDao.REMOVED_COLUMN) + Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + Date created; + @Override public long getId() { return id; } + @Override + public String getHypervisorVersion() { + return hypervisorVersion; + } + @Override public String getHypervisorType() { return hypervisorType; @@ -60,4 +82,42 @@ public class GuestOSHypervisorVO implements GuestOSHypervisor { return guestOsId; } + @Override + public String getUuid() { + return uuid; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + public void setGuestOsId(long guestOsId) { + this.guestOsId = guestOsId; + } + + public void setGuestOsName(String guestOsName) { + this.guestOsName = guestOsName; + } + + public void setHypervisorType(String hypervisorType) { + this.hypervisorType = hypervisorType; + } + + public void setHypervisorVersion(String hypervisorVersion) { + this.hypervisorVersion = hypervisorVersion; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } } diff --git a/engine/schema/src/com/cloud/storage/GuestOSVO.java b/engine/schema/src/com/cloud/storage/GuestOSVO.java index 57b852953a9..a0040beeafe 100644 --- a/engine/schema/src/com/cloud/storage/GuestOSVO.java +++ b/engine/schema/src/com/cloud/storage/GuestOSVO.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage; +import java.util.Date; import java.util.UUID; import javax.persistence.Column; @@ -25,6 +26,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.GenericDao; + @Entity @Table(name = "guest_os") public class GuestOSVO implements GuestOS { @@ -45,6 +48,12 @@ public class GuestOSVO implements GuestOS { @Column(name = "uuid") String uuid = UUID.randomUUID().toString(); + @Column(name = GenericDao.REMOVED_COLUMN) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + @Override public long getId() { return id; @@ -79,10 +88,24 @@ public class GuestOSVO implements GuestOS { @Override public String getUuid() { - return this.uuid; + return uuid; } public void setUuid(String uuid) { this.uuid = uuid; } + + @Override + public Date getCreated() { + return created; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public Date getRemoved() { + return removed; + } } diff --git a/engine/schema/src/com/cloud/storage/dao/GuestOSHypervisorDao.java b/engine/schema/src/com/cloud/storage/dao/GuestOSHypervisorDao.java index 651cfb56700..5b6e7198f0d 100644 --- a/engine/schema/src/com/cloud/storage/dao/GuestOSHypervisorDao.java +++ b/engine/schema/src/com/cloud/storage/dao/GuestOSHypervisorDao.java @@ -24,4 +24,7 @@ public interface GuestOSHypervisorDao extends GenericDao implements GuestOSHypervisorDao { protected final SearchBuilder guestOsSearch; + protected final SearchBuilder mappingSearch; protected GuestOSHypervisorDaoImpl() { guestOsSearch = createSearchBuilder(); guestOsSearch.and("guest_os_id", guestOsSearch.entity().getGuestOsId(), SearchCriteria.Op.EQ); guestOsSearch.done(); + + mappingSearch = createSearchBuilder(); + mappingSearch.and("guest_os_id", mappingSearch.entity().getGuestOsId(), SearchCriteria.Op.EQ); + mappingSearch.and("hypervisor_type", mappingSearch.entity().getHypervisorType(), SearchCriteria.Op.EQ); + mappingSearch.and("hypervisor_version", mappingSearch.entity().getHypervisorVersion(), SearchCriteria.Op.EQ); + mappingSearch.done(); } @Override @@ -46,4 +55,22 @@ public class GuestOSHypervisorDaoImpl extends GenericDaoBase sc = mappingSearch.create(); + sc.setParameters("guest_os_id", guestOsId); + sc.setParameters("hypervisor_type", hypervisorType); + sc.setParameters("hypervisor_version", hypervisorVersion); + return findOneBy(sc); + } + + @Override + public boolean removeGuestOsMapping(Long id) { + GuestOSHypervisorVO guestOsHypervisor = findById(id); + createForUpdate(id); + guestOsHypervisor.setRemoved(new Date()); + update(id, guestOsHypervisor); + return super.remove(id); + } + } diff --git a/engine/schema/src/com/cloud/upgrade/dao/DatabaseAccessObject.java b/engine/schema/src/com/cloud/upgrade/dao/DatabaseAccessObject.java new file mode 100644 index 00000000000..836a5376865 --- /dev/null +++ b/engine/schema/src/com/cloud/upgrade/dao/DatabaseAccessObject.java @@ -0,0 +1,99 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.upgrade.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.apache.log4j.Logger; + +public class DatabaseAccessObject { + + private static Logger s_logger = Logger.getLogger(DatabaseAccessObject.class); + + public void dropKey(Connection conn, String tableName, String key, boolean isForeignKey) { + PreparedStatement pstmt = null; + try { + if (isForeignKey) { + pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP FOREIGN KEY " + key); + } else { + pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP KEY " + key); + } + pstmt.executeUpdate(); + s_logger.debug("Key " + key + " is dropped successfully from the table " + tableName); + } catch (SQLException e) { + s_logger.warn("Ignored SQL Exception when trying to drop " + (isForeignKey ? "foreign " : "") + "key " + key + " on table " + tableName, e); + } finally { + closePreparedStatement(pstmt, "Ignored SQL Exception when trying to close PreparedStatement atfer dropping " + (isForeignKey ? "foreign " : "") + "key " + key + + " on table " + tableName); + } + } + + public void dropPrimaryKey(Connection conn, String tableName) { + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP PRIMARY KEY "); + pstmt.executeUpdate(); + s_logger.debug("Primary key is dropped successfully from the table " + tableName); + } catch (SQLException e) { + s_logger.warn("Ignored SQL Exception when trying to drop primary key on table " + tableName, e); + } finally { + closePreparedStatement(pstmt, "Ignored SQL Exception when trying to close PreparedStatement atfer dropping primary key on table " + tableName); + } + } + + public void dropColumn(Connection conn, String tableName, String columnName) { + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP COLUMN " + columnName); + pstmt.executeUpdate(); + s_logger.debug("Column " + columnName + " is dropped successfully from the table " + tableName); + } catch (SQLException e) { + s_logger.warn("Unable to drop columns using query " + pstmt + " due to exception", e); + } finally { + closePreparedStatement(pstmt, "Ignored SQL Exception when trying to close PreparedStatement after dropping column " + columnName + " on table " + tableName); + } + } + + public boolean columnExists(Connection conn, String tableName, String columnName) { + boolean columnExists = false; + PreparedStatement pstmt = null; + try { + pstmt = conn.prepareStatement("SELECT " + columnName + " FROM " + tableName); + pstmt.executeQuery(); + columnExists = true; + } catch (SQLException e) { + s_logger.warn("Field " + columnName + " doesn't exist in " + tableName, e); + } finally { + closePreparedStatement(pstmt, "Ignored SQL Exception when trying to close PreparedStatement atfer checking if column " + columnName + " existed on table " + tableName); + } + + return columnExists; + } + + protected static void closePreparedStatement(PreparedStatement pstmt, String errorMessage) { + try { + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + s_logger.warn(errorMessage, e); + } + } + +} diff --git a/engine/schema/src/com/cloud/upgrade/dao/DbUpgradeUtils.java b/engine/schema/src/com/cloud/upgrade/dao/DbUpgradeUtils.java index af23b8784a9..38ca5c9c272 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/DbUpgradeUtils.java +++ b/engine/schema/src/com/cloud/upgrade/dao/DbUpgradeUtils.java @@ -17,88 +17,28 @@ package com.cloud.upgrade.dao; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; import java.util.List; -import org.apache.log4j.Logger; - -import com.cloud.utils.exception.CloudRuntimeException; - public class DbUpgradeUtils { - final static Logger s_logger = Logger.getLogger(DbUpgradeUtils.class); + + private static DatabaseAccessObject dao = new DatabaseAccessObject(); public static void dropKeysIfExist(Connection conn, String tableName, List keys, boolean isForeignKey) { for (String key : keys) { - PreparedStatement pstmt = null; - try { - if (isForeignKey) { - pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP FOREIGN KEY " + key); - } else { - pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP KEY " + key); - } - pstmt.executeUpdate(); - s_logger.debug("Key " + key + " is dropped successfully from the table " + tableName); - } catch (SQLException e) { - // do nothing here - - continue; - } finally { - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { - } - } + dao.dropKey(conn, tableName, key, isForeignKey); } } public static void dropPrimaryKeyIfExists(Connection conn, String tableName) { - PreparedStatement pstmt = null; - try { - pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP PRIMARY KEY "); - pstmt.executeUpdate(); - s_logger.debug("Primary key is dropped successfully from the table " + tableName); - } catch (SQLException e) { - // do nothing here - } finally { - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { - } - } + dao.dropPrimaryKey(conn, tableName); } public static void dropTableColumnsIfExist(Connection conn, String tableName, List columns) { - PreparedStatement pstmt = null; - try { - for (String column : columns) { - try { - pstmt = conn.prepareStatement("SELECT " + column + " FROM " + tableName); - pstmt.executeQuery(); - } catch (SQLException e) { - // if there is an exception, it means that field doesn't exist, so do nothing here - s_logger.trace("Field " + column + " doesn't exist in " + tableName); - continue; - } - - pstmt = conn.prepareStatement("ALTER TABLE " + tableName + " DROP COLUMN " + column); - pstmt.executeUpdate(); - s_logger.debug("Column " + column + " is dropped successfully from the table " + tableName); - } - } catch (SQLException e) { - s_logger.warn("Unable to drop columns using query " + pstmt + " due to exception", e); - throw new CloudRuntimeException("Unable to drop columns due to ", e); - } finally { - try { - if (pstmt != null) { - pstmt.close(); - } - } catch (SQLException e) { + for (String columnName : columns) { + if (dao.columnExists(conn, tableName, columnName)) { + dao.dropColumn(conn, tableName, columnName); } } } + } diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java index 8b68d6c9038..75b1d75dd6a 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.log4j.Logger; + import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; @@ -44,6 +46,8 @@ import com.cloud.utils.fsm.StateObject; @Entity @Table(name = "snapshot_store_ref") public class SnapshotDataStoreVO implements StateObject, DataObjectInStore { + private static final Logger s_logger = Logger.getLogger(SnapshotDataStoreVO.class); + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; @@ -272,7 +276,12 @@ public class SnapshotDataStoreVO implements StateObject 0) { + refCnt--; + } + else { + s_logger.warn("We should not try to decrement a zero reference count even though our code has guarded"); + } } public Long getVolumeId() { diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java index 897fee0e4de..bb40bf5d11a 100755 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.log4j.Logger; + import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; @@ -45,6 +47,8 @@ import com.cloud.utils.fsm.StateObject; @Entity @Table(name = "template_store_ref") public class TemplateDataStoreVO implements StateObject, DataObjectInStore { + private static final Logger s_logger = Logger.getLogger(TemplateDataStoreVO.class); + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; @@ -367,7 +371,12 @@ public class TemplateDataStoreVO implements StateObject 0) { + refCnt--; + } + else{ + s_logger.warn("We should not try to decrement a zero reference count even though our code has guarded"); + } } } diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java index fe6aca4c3d2..588eae8ba57 100755 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java @@ -29,6 +29,8 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import org.apache.log4j.Logger; + import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; @@ -44,6 +46,8 @@ import com.cloud.utils.fsm.StateObject; @Entity @Table(name = "volume_store_ref") public class VolumeDataStoreVO implements StateObject, DataObjectInStore { + private static final Logger s_logger = Logger.getLogger(VolumeDataStoreVO.class); + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) Long id; @@ -342,7 +346,12 @@ public class VolumeDataStoreVO implements StateObject 0) { + refCnt--; + } + else { + s_logger.warn("We should not try to decrement a zero reference count even though our code has guarded"); + } } public void incrRefCnt() { diff --git a/engine/schema/test/com/cloud/upgrade/dao/DatabaseAccessObjectTest.java b/engine/schema/test/com/cloud/upgrade/dao/DatabaseAccessObjectTest.java new file mode 100644 index 00000000000..5d37fbf326a --- /dev/null +++ b/engine/schema/test/com/cloud/upgrade/dao/DatabaseAccessObjectTest.java @@ -0,0 +1,463 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.upgrade.dao; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.contains; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.internal.util.reflection.Whitebox; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class DatabaseAccessObjectTest { + + @Mock + private PreparedStatement preparedStatementMock; + + @Mock + private Connection connectionMock; + + @Mock + private Logger loggerMock; + + private final DatabaseAccessObject dao = new DatabaseAccessObject(); + + @Before + public void setup() { + Whitebox.setInternalState(dao, "s_logger", loggerMock); + } + + @Test + public void testDropKey() throws Exception { + when(connectionMock.prepareStatement(contains("DROP KEY"))).thenReturn(preparedStatementMock); + + Connection conn = connectionMock; + String tableName = "tableName"; + String key = "key"; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).debug(anyString()); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @Test(expected = NullPointerException.class) + public void testDropKeyWhenConnectionIsNull() throws Exception { + Connection conn = null; + String tableName = "tableName"; + String key = "key"; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + } + + @Test + public void testDropKeyWhenTableNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("null DROP KEY"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + String key = "key"; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropKeyWhenKeyIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("DROP KEY null"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String key = null; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropKeyWhenKeysAreForeignKeys() throws Exception { + when(connectionMock.prepareStatement(contains("DROP FOREIGN KEY"))).thenReturn(preparedStatementMock); + + Connection conn = connectionMock; + String tableName = "tableName"; + String key = "key"; + boolean isForeignKey = true; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).debug(anyString()); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @Test + public void testDropKeyWhenPrepareStatementResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(any(String.class))).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String key = "key"; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(0)).executeUpdate(); + verify(preparedStatementMock, times(0)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropKeyWhenExecuteUpdateResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("DROP KEY"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String key = "key"; + boolean isForeignKey = false; + + dao.dropKey(conn, tableName, key, isForeignKey); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @SuppressWarnings("static-access") + @Test + public void testClosePreparedStatementWhenPreparedStatementIsNull() throws Exception { + PreparedStatement preparedStatement = null; + String errorMessage = "some message"; + + dao.closePreparedStatement(preparedStatement, errorMessage); + + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @SuppressWarnings("static-access") + @Test + public void testClosePreparedStatementWhenPreparedStatementIsNotNullAndThereIsNoException() throws Exception { + PreparedStatement preparedStatement = preparedStatementMock; + String errorMessage = "some message"; + + dao.closePreparedStatement(preparedStatement, errorMessage); + + verify(preparedStatement, times(1)).close(); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @SuppressWarnings("static-access") + @Test + public void testClosePreparedStatementWhenPreparedStatementIsNotNullAndThereIsException() throws Exception { + SQLException sqlException = new SQLException(); + doThrow(sqlException).when(preparedStatementMock).close(); + + PreparedStatement preparedStatement = preparedStatementMock; + String errorMessage = "some message"; + + dao.closePreparedStatement(preparedStatement, errorMessage); + + verify(preparedStatement, times(1)).close(); + verify(loggerMock, times(1)).warn(errorMessage, sqlException); + } + + @Test + public void testDropPrimaryKey() throws Exception { + when(connectionMock.prepareStatement(contains("DROP PRIMARY KEY"))).thenReturn(preparedStatementMock); + + Connection conn = connectionMock; + String tableName = "tableName"; + + dao.dropPrimaryKey(conn, tableName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).debug(anyString()); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @Test(expected = NullPointerException.class) + public void testDropPrimaryKeyWhenConnectionIsNull() throws Exception { + Connection conn = null; + String tableName = "tableName"; + + dao.dropPrimaryKey(conn, tableName); + } + + @Test + public void testDropPrimaryKeyWhenTableNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("null DROP PRIMARY KEY"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + + dao.dropPrimaryKey(conn, tableName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropPrimaryKeyWhenPrepareStatementResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("DROP PRIMARY KEY"))).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + + dao.dropPrimaryKey(conn, tableName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(0)).executeUpdate(); + verify(preparedStatementMock, times(0)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropPrimaryKeyWhenExecuteUpdateResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("DROP PRIMARY KEY"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + + dao.dropPrimaryKey(conn, tableName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testColumnExists() throws Exception { + when(connectionMock.prepareStatement(contains("SELECT"))).thenReturn(preparedStatementMock); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.columnExists(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeQuery(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @Test(expected = NullPointerException.class) + public void testColumnExistsWhenConnectionIsNull() throws Exception { + Connection conn = null; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.columnExists(conn, tableName, columnName); + } + + @Test + public void testColumnExistsWhenTableNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("FROM null"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeQuery()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + String columnName = "columnName"; + + dao.columnExists(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeQuery(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testColumnExistsWhenColumnNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("SELECT null"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeQuery()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = null; + + dao.columnExists(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeQuery(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropColumn() throws Exception { + when(connectionMock.prepareStatement(anyString())).thenReturn(preparedStatementMock); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.dropColumn(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(0)).executeQuery(); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(1)).debug(anyString()); + verify(loggerMock, times(0)).warn(anyString(), any(Throwable.class)); + } + + @Test(expected = NullPointerException.class) + public void testDropColumnWhenConnectionIsNull() throws Exception { + + Connection conn = null; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.dropColumn(conn, tableName, columnName); + } + + @Test + public void testDropColumnWhenTableNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("ALTER TABLE null"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = null; + String columnName = "columnName"; + + dao.dropColumn(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropColumnWhenColumnNameIsNull() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(contains("DROP COLUMN null"))).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = null; + + dao.dropColumn(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropColumnWhenPrepareStatementResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(anyString())).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.dropColumn(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(0)).executeUpdate(); + verify(preparedStatementMock, times(0)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + + @Test + public void testDropColumnWhenexecuteUpdateResultsInException() throws Exception { + SQLException sqlException = new SQLException(); + when(connectionMock.prepareStatement(anyString())).thenReturn(preparedStatementMock); + when(preparedStatementMock.executeUpdate()).thenThrow(sqlException); + + Connection conn = connectionMock; + String tableName = "tableName"; + String columnName = "columnName"; + + dao.dropColumn(conn, tableName, columnName); + + verify(connectionMock, times(1)).prepareStatement(anyString()); + verify(preparedStatementMock, times(1)).executeUpdate(); + verify(preparedStatementMock, times(1)).close(); + verify(loggerMock, times(0)).debug(anyString()); + verify(loggerMock, times(1)).warn(anyString(), eq(sqlException)); + } + +} diff --git a/engine/schema/test/com/cloud/upgrade/dao/DbUpgradeUtilsTest.java b/engine/schema/test/com/cloud/upgrade/dao/DbUpgradeUtilsTest.java new file mode 100644 index 00000000000..d248cfb254e --- /dev/null +++ b/engine/schema/test/com/cloud/upgrade/dao/DbUpgradeUtilsTest.java @@ -0,0 +1,162 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.upgrade.dao; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +@RunWith(PowerMockRunner.class) +public class DbUpgradeUtilsTest { + + @Mock + private Connection connectionMock; + + @Mock + private DatabaseAccessObject daoMock; + + @Before + public void setupClass() { + Whitebox.setInternalState(DbUpgradeUtils.class, "dao", daoMock); + } + + @Test + public void testDropKeyIfExistWhenNoKeysAreSupplied() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + List keys = new ArrayList(); + boolean isForeignKey = false; + + DbUpgradeUtils.dropKeysIfExist(conn, tableName, keys, isForeignKey); + + verify(daoMock, times(0)).dropKey(eq(conn), eq(tableName), anyString(), eq(isForeignKey)); + } + + @Test + public void testDropKeyIfExistWhenOneKeysIsSupplied() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + String key = "key"; + List keys = Arrays.asList(new String[] {key}); + boolean isForeignKey = false; + + DbUpgradeUtils.dropKeysIfExist(conn, tableName, keys, isForeignKey); + + verify(daoMock, times(1)).dropKey(conn, tableName, key, isForeignKey); + } + + @Test + public void testDropKeyIfExistWhenThreeKeysAreSupplied() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + String key1 = "key1"; + String key2 = "key2"; + List keys = Arrays.asList(new String[] {key1, key2}); + boolean isForeignKey = false; + + DbUpgradeUtils.dropKeysIfExist(conn, tableName, keys, isForeignKey); + + verify(daoMock, times(1)).dropKey(conn, tableName, key1, isForeignKey); + verify(daoMock, times(1)).dropKey(conn, tableName, key2, isForeignKey); + } + + @Test + public void testDropPrimaryKey() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + + DbUpgradeUtils.dropPrimaryKeyIfExists(conn, tableName); + + verify(daoMock, times(1)).dropPrimaryKey(conn, tableName); + } + + @Test + public void testDropTableColumnsIfExistWhenNoKeysAreSupplied() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + List columns = new ArrayList(); + + DbUpgradeUtils.dropTableColumnsIfExist(conn, tableName, columns); + + verify(daoMock, times(0)).columnExists(eq(conn), eq(tableName), anyString()); + verify(daoMock, times(0)).dropColumn(eq(conn), eq(tableName), anyString()); + } + + @Test + public void testDropTableColumnsIfExistWhenOneKeysIsSuppliedAndColumnExists() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + String column = "column"; + when(daoMock.columnExists(conn, tableName, column)).thenReturn(true); + List columns = Arrays.asList(new String[] {column}); + + DbUpgradeUtils.dropTableColumnsIfExist(conn, tableName, columns); + + verify(daoMock, times(1)).columnExists(conn, tableName, column); + verify(daoMock, times(1)).dropColumn(conn, tableName, column); + } + + @Test + public void testDropTableColumnsIfExistWhenOneKeysIsSuppliedAndColumnDoesNotExists() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + String column = "column"; + when(daoMock.columnExists(conn, tableName, column)).thenReturn(false); + List columns = Arrays.asList(new String[] {column}); + + DbUpgradeUtils.dropTableColumnsIfExist(conn, tableName, columns); + + verify(daoMock, times(1)).columnExists(conn, tableName, column); + verify(daoMock, times(0)).dropColumn(conn, tableName, column); + } + + @Test + public void testDropTableColumnsIfExistWhenThreeKeysAreSuppliedAnOneDoesnotExist() throws Exception { + Connection conn = connectionMock; + String tableName = "tableName"; + String column1 = "column1"; + String column2 = "column2"; + String column3 = "column3"; + when(daoMock.columnExists(conn, tableName, column1)).thenReturn(true); + when(daoMock.columnExists(conn, tableName, column2)).thenReturn(false); + when(daoMock.columnExists(conn, tableName, column3)).thenReturn(true); + List keys = Arrays.asList(new String[] {column1, column2, column3}); + + DbUpgradeUtils.dropTableColumnsIfExist(conn, tableName, keys); + + verify(daoMock, times(1)).columnExists(conn, tableName, column1); + verify(daoMock, times(1)).dropColumn(conn, tableName, column1); + verify(daoMock, times(1)).columnExists(conn, tableName, column2); + verify(daoMock, times(0)).dropColumn(conn, tableName, column2); + verify(daoMock, times(1)).columnExists(conn, tableName, column3); + verify(daoMock, times(1)).dropColumn(conn, tableName, column3); + } +} diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 0556479e072..df81199461e 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -46,6 +46,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.RemoteHostEndPoint; import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity; @@ -94,15 +95,17 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { protected boolean needCacheStorage(DataObject srcData, DataObject destData) { DataTO srcTO = srcData.getTO(); - DataTO destTO = destData.getTO(); DataStoreTO srcStoreTO = srcTO.getDataStore(); - DataStoreTO destStoreTO = destTO.getDataStore(); + if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache) { //|| // (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) { return false; } + DataTO destTO = destData.getTO(); + DataStoreTO destStoreTO = destTO.getDataStore(); + if (destStoreTO instanceof NfsTO || destStoreTO.getRole() == DataStoreRole.ImageCache) { return false; } @@ -147,7 +150,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return selectedScope; } - protected Answer copyObject(DataObject srcData, DataObject destData) { + protected Answer copyObject(DataObject srcData, DataObject destData, Host destHost) { String value = configDao.getValue(Config.PrimaryStorageDownloadWait.toString()); int _primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue())); Answer answer = null; @@ -160,7 +163,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } CopyCommand cmd = new CopyCommand(srcForCopy.getTO(), destData.getTO(), _primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(srcForCopy, destData); + EndPoint ep = destHost != null ? RemoteHostEndPoint.getHypervisorHostEndPoint(destHost) : selector.select(srcForCopy, destData); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -193,6 +196,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } } + protected Answer copyObject(DataObject srcData, DataObject destData) { + return copyObject(srcData, destData, null); + } + protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) { DataObject leafData = null; DataStore store = cacheMgr.getCacheStorage(snapshot, scope); @@ -392,8 +399,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return answer; } + // Note: destHost is currently only used if the copyObject method is invoked @Override - public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { Answer answer = null; String errMsg = null; try { @@ -416,7 +424,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.SNAPSHOT) { answer = copySnapshot(srcData, destData); } else { - answer = copyObject(srcData, destData); + answer = copyObject(srcData, destData, destHost); } if (answer != null && !answer.getResult()) { @@ -432,6 +440,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return null; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + return copyAsync(srcData, destData, null, callback); + } + @DB protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) { diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java index 37e8baabbe9..eed1e08005f 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/DataMotionServiceImpl.java @@ -46,7 +46,7 @@ public class DataMotionServiceImpl implements DataMotionService { StorageStrategyFactory storageStrategyFactory; @Override - public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + public void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { if (srcData.getDataStore() == null || destData.getDataStore() == null) { throw new CloudRuntimeException("can't find data store"); } @@ -65,7 +65,12 @@ public class DataMotionServiceImpl implements DataMotionService { destData.getType().name() + " '" + destData.getUuid() + "'"); } - strategy.copyAsync(srcData, destData, callback); + strategy.copyAsync(srcData, destData, destHost, callback); + } + + @Override + public void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + copyAsync(srcData, destData, null, callback); } @Override diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index 166b523e387..4e6ab6ba300 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -777,12 +777,21 @@ public class TemplateServiceImpl implements TemplateService { String scheme = "http"; boolean _sslCopy = false; String sslCfg = _configDao.getValue(Config.SecStorageEncryptCopy.toString()); + String _ssvmUrlDomain = _configDao.getValue("secstorage.ssl.cert.domain"); if (sslCfg != null) { _sslCopy = Boolean.parseBoolean(sslCfg); } + if(_sslCopy && (_ssvmUrlDomain == null || _ssvmUrlDomain.isEmpty())){ + s_logger.warn("Empty secondary storage url domain, ignoring SSL"); + _sslCopy = false; + } if (_sslCopy) { - hostname = ipAddress.replace(".", "-"); - hostname = hostname + ".realhostip.com"; + if(_ssvmUrlDomain.startsWith("*")) { + hostname = ipAddress.replace(".", "-"); + hostname = hostname + _ssvmUrlDomain.substring(1); + } else { + hostname = _ssvmUrlDomain; + } scheme = "https"; } return scheme + "://" + hostname + "/copy/SecStorage/" + dir + "/" + path; diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java index 8db21ccbf7a..34db48146be 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; @@ -299,6 +300,20 @@ public class TemplateObject implements TemplateInfo { if (dataStore == null) { return null; } + + // managed primary data stores should not have an install path + if (dataStore instanceof PrimaryDataStore) { + PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore; + + Map details = primaryDataStore.getDetails(); + + boolean managed = details != null && Boolean.parseBoolean(details.get(PrimaryDataStore.MANAGED)); + + if (managed) { + return null; + } + } + DataObjectInStore obj = objectInStoreMgr.findObject(this, dataStore); return obj.getInstallPath(); } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java index 2a11e1337e6..accfeb5318e 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java @@ -57,6 +57,11 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.HIGHEST; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCmdAnswer answer = null; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index 649e6896c8e..4fb37e2a181 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import javax.inject.Inject; @@ -67,6 +68,7 @@ import com.cloud.utils.storage.encoding.EncodingType; public class PrimaryDataStoreImpl implements PrimaryDataStore { private static final Logger s_logger = Logger.getLogger(PrimaryDataStoreImpl.class); + protected PrimaryDataStoreDriver driver; protected StoragePoolVO pdsv; @Inject @@ -86,6 +88,7 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { @Inject private VolumeDao volumeDao; + private Map _details; public PrimaryDataStoreImpl() { @@ -135,6 +138,16 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return pdsv.getId(); } + @Override + public void setDetails(Map details) { + _details = details; + } + + @Override + public Map getDetails() { + return _details; + } + @Override public String getUri() { String path = pdsv.getPath().replaceFirst("/*", ""); @@ -222,10 +235,15 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return null; } + @Override + public boolean isManaged() { + return pdsv.isManaged(); + } + @Override public DataObject create(DataObject obj) { // create template on primary storage - if (obj.getType() == DataObjectType.TEMPLATE) { + if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) { try { String templateIdPoolIdString = "templateId:" + obj.getId() + "poolId:" + getId(); VMTemplateStoragePoolVO templateStoragePoolRef; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java index 4838bf678e0..fffd1e815c4 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java @@ -85,7 +85,7 @@ public class DefaultHostListener implements HypervisorHostListener { poolVO.setCapacityBytes(mspAnswer.getPoolInfo().getCapacityBytes()); primaryStoreDao.update(pool.getId(), poolVO); - s_logger.info("Connection established between " + pool + " host + " + hostId); + s_logger.info("Connection established between storage pool " + pool + " and host " + hostId); return true; } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index fa0fd956a7a..d47ee27d4a5 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.storage.volume; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -40,6 +41,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; @@ -60,6 +62,7 @@ import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ListVolumeAnswer; @@ -72,6 +75,7 @@ import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceAllocationException; +import com.cloud.host.dao.HostDao; import com.cloud.host.Host; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; @@ -122,6 +126,8 @@ public class VolumeServiceImpl implements VolumeService { VolumeDao _volumeDao; @Inject EndPointSelector _epSelector; + @Inject + HostDao _hostDao; public VolumeServiceImpl() { } @@ -349,6 +355,39 @@ public class VolumeServiceImpl implements VolumeService { return null; } + private class ManagedCreateBaseImageContext extends AsyncRpcContext { + private final VolumeInfo _volumeInfo; + private final PrimaryDataStore _primaryDataStore; + private final TemplateInfo _templateInfo; + private final AsyncCallFuture _future; + + public ManagedCreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volumeInfo, + PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, AsyncCallFuture future) { + super(callback); + + _volumeInfo = volumeInfo; + _primaryDataStore = primaryDatastore; + _templateInfo = templateInfo; + _future = future; + } + + public VolumeInfo getVolumeInfo() { + return _volumeInfo; + } + + public PrimaryDataStore getPrimaryDataStore() { + return _primaryDataStore; + } + + public TemplateInfo getTemplateInfo() { + return _templateInfo; + } + + public AsyncCallFuture getFuture() { + return _future; + } + } + class CreateBaseImageContext extends AsyncRpcContext { private final VolumeInfo volume; private final PrimaryDataStore dataStore; @@ -411,7 +450,6 @@ public class VolumeServiceImpl implements VolumeService { @DB protected void createBaseImageAsync(VolumeInfo volume, PrimaryDataStore dataStore, TemplateInfo template, AsyncCallFuture future) { - DataObject templateOnPrimaryStoreObj = dataStore.create(template); VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(dataStore.getId(), template.getId()); @@ -476,6 +514,37 @@ public class VolumeServiceImpl implements VolumeService { return; } + protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher callback, + ManagedCreateBaseImageContext context) { + CopyCommandResult result = callback.getResult(); + VolumeInfo volumeInfo = context.getVolumeInfo(); + VolumeApiResult res = new VolumeApiResult(volumeInfo); + + if (result.isSuccess()) { + // volumeInfo.processEvent(Event.OperationSuccessed, result.getAnswer()); + + VolumeVO volume = volDao.findById(volumeInfo.getId()); + CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer(); + TemplateObjectTO templateObjectTo = (TemplateObjectTO)answer.getNewData(); + + volume.setPath(templateObjectTo.getPath()); + volume.setFormat(templateObjectTo.getFormat()); + + volDao.update(volume.getId(), volume); + } + else { + volumeInfo.processEvent(Event.DestroyRequested); + + res.setResult(result.getResult()); + } + + AsyncCallFuture future = context.getFuture(); + + future.complete(res); + + return null; + } + @DB protected Void copyBaseImageCallback(AsyncCallbackDispatcher callback, CreateBaseImageContext context) { CopyCommandResult result = callback.getResult(); @@ -577,6 +646,91 @@ public class VolumeServiceImpl implements VolumeService { return null; } + @Override + public AsyncCallFuture createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, + TemplateInfo srcTemplateInfo, long destHostId) { + PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId); + TemplateInfo destTemplateInfo = (TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo); + Host destHost = _hostDao.findById(destHostId); + + if (destHost == null) { + throw new CloudRuntimeException("Destinatin host should not be null."); + } + + AsyncCallFuture future = new AsyncCallFuture(); + + try { + // must call driver to have a volume created + AsyncCallFuture createVolumeFuture = createVolumeAsync(volumeInfo, destPrimaryDataStore); + + VolumeApiResult createVolumeResult = createVolumeFuture.get(); + + if (createVolumeResult.isFailed()) { + throw new CloudRuntimeException("Creation of a volume failed: " + createVolumeResult.getResult()); + } + + // refresh the volume from the DB + volumeInfo = volFactory.getVolume(volumeInfo.getId(), destPrimaryDataStore); + + connectVolumeToHost(volumeInfo, destHost, destPrimaryDataStore); + + ManagedCreateBaseImageContext context = new ManagedCreateBaseImageContext(null, volumeInfo, + destPrimaryDataStore, srcTemplateInfo, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, null)).setContext(context); + + Map details = new HashMap(); + + details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString()); + details.put(PrimaryDataStore.STORAGE_HOST, destPrimaryDataStore.getHostAddress()); + // for managed storage, the storage repository (XenServer) or datastore (ESX) name is based off of the iScsiName property of a volume + details.put(PrimaryDataStore.MANAGED_STORE_TARGET, volumeInfo.get_iScsiName()); + details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, volumeInfo.getName()); + details.put(PrimaryDataStore.VOLUME_SIZE, String.valueOf(volumeInfo.getSize())); + + ChapInfo chapInfo = getChapInfo(volumeInfo, destPrimaryDataStore); + + if (chapInfo != null) { + details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); + details.put(PrimaryDataStore.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret()); + details.put(PrimaryDataStore.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername()); + details.put(PrimaryDataStore.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); + } + + destPrimaryDataStore.setDetails(details); + + motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller); + } + catch (Throwable t) { + String errMsg = t.toString(); + + volumeInfo.processEvent(Event.DestroyRequested); + + disconnectVolumeFromHost(volumeInfo, destHost, destPrimaryDataStore); + + try { + AsyncCallFuture expungeVolumeFuture = expungeVolumeAsync(volumeInfo); + + VolumeApiResult expungeVolumeResult = expungeVolumeFuture.get(); + + if (expungeVolumeResult.isFailed()) { + errMsg += " : Failed to expunge a volume that was created"; + } + } + catch (Exception ex) { + errMsg += " : " + ex.getMessage(); + } + + VolumeApiResult result = new VolumeApiResult(volumeInfo); + + result.setResult(errMsg); + + future.complete(result); + } + + return future; + } + @DB @Override public AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template) { diff --git a/packaging/centos63/cloud-agent.rc b/packaging/centos63/cloud-agent.rc index 40c358e36b6..ece4c2732f8 100755 --- a/packaging/centos63/cloud-agent.rc +++ b/packaging/centos63/cloud-agent.rc @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=/var/run/"$SHORTNAME".pid LOCKFILE=/var/lock/subsys/"$SHORTNAME" LOGDIR=/var/log/cloudstack/agent diff --git a/packaging/centos63/cloud-ipallocator.rc b/packaging/centos63/cloud-ipallocator.rc index 9a24d8dfc8a..d26287dcb3c 100755 --- a/packaging/centos63/cloud-ipallocator.rc +++ b/packaging/centos63/cloud-ipallocator.rc @@ -25,7 +25,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=/var/run/"$SHORTNAME".pid LOCKFILE=/var/lock/subsys/"$SHORTNAME" LOGFILE=/var/log/cloudstack/ipallocator/ipallocator.log diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java index 70bdb6f62df..b729d9bb3eb 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BareMetalResourceBase.java @@ -340,8 +340,7 @@ public class BareMetalResourceBase extends ManagerBase implements ServerResource vmDao = ComponentContext.getComponent(VMInstanceDao.class); final List vms = vmDao.listByHostId(hostId); for (VMInstanceVO vm : vms) { - states.put(vm.getInstanceName(), new HostVmStateReportEntry(vm.getState() == State.Running ? PowerState.PowerOn : PowerState.PowerOff, "host-" + hostId, - null)); + states.put(vm.getInstanceName(), new HostVmStateReportEntry(vm.getState() == State.Running ? PowerState.PowerOn : PowerState.PowerOff, "host-" + hostId)); } } /* diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java index 1d9e7f619f9..bf0795df059 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/guru/HypervGuru.java @@ -53,7 +53,7 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { @Inject HypervManager _hypervMgr; @Inject NetworkDao _networkDao; @Inject NetworkModel _networkMgr; - + int MaxNicSupported = 8; @Override public final HypervisorType getHypervisorType() { return HypervisorType.Hyperv; @@ -85,7 +85,7 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { NicTO[] nics = to.getNics(); // reserve extra NICs - NicTO[] expandedNics = new NicTO[nics.length + _hypervMgr.getRouterExtraPublicNics()]; + NicTO[] expandedNics = new NicTO[MaxNicSupported]; int i = 0; int deviceId = -1; for(i = 0; i < nics.length; i++) { @@ -97,8 +97,9 @@ public class HypervGuru extends HypervisorGuruBase implements HypervisorGuru { long networkId = publicNicProfile.getNetworkId(); NetworkVO network = _networkDao.findById(networkId); - - for(; i < nics.length + _hypervMgr.getRouterExtraPublicNics(); i++) { + // for Hyperv Hot Nic plug is not supported and it will support upto 8 nics. + // creating the VR with extra nics (actual nics(3) + extra nics) will be 8 + for(; i < MaxNicSupported; i++) { NicTO nicTo = new NicTO(); nicTo.setDeviceId(deviceId++); nicTo.setBroadcastType(publicNicProfile.getBroadcastType()); diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index c79373a0eee..a3ffa758f8e 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -83,6 +83,7 @@ import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PingTestCommand; +import com.cloud.agent.api.SetupGuestNetworkCommand; import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; @@ -98,6 +99,7 @@ import com.cloud.agent.api.routing.DnsMasqConfigCommand; import com.cloud.agent.api.routing.IpAliasTO; import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.IpAssocVpcCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; @@ -105,6 +107,7 @@ import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetFirewallRulesAnswer; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetMonitorServiceCommand; +import com.cloud.agent.api.routing.SetNetworkACLCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetSourceNatAnswer; @@ -119,9 +122,12 @@ import com.cloud.agent.api.routing.VpnUsersCfgCommand; import com.cloud.agent.api.to.DhcpTO; import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.agent.resource.virtualnetwork.VirtualRouterDeployer; +import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.exception.InternalErrorException; import com.cloud.host.Host.Type; @@ -135,6 +141,7 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.serializer.GsonHelper; +import com.cloud.utils.ExecutionResult; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.net.NetUtils; @@ -148,7 +155,7 @@ import com.cloud.vm.VirtualMachineName; * Implementation of dummy resource to be returned from discoverer. **/ @Local(value = ServerResource.class) -public class HypervDirectConnectResource extends ServerResourceBase implements ServerResource { +public class HypervDirectConnectResource extends ServerResourceBase implements ServerResource, VirtualRouterDeployer { public static final int DEFAULT_AGENT_PORT = 8250; public static final String HOST_VM_STATE_REPORT_COMMAND = "org.apache.cloudstack.HostVmStateReportCommand"; private static final Logger s_logger = Logger.getLogger(HypervDirectConnectResource.class.getName()); @@ -178,6 +185,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S private static HypervManager s_hypervMgr; @Inject HypervManager _hypervMgr; + protected VirtualRoutingResource _vrResource; @PostConstruct void init() { @@ -358,7 +366,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S for (Map vmMap : vmList) { String name = (String)vmMap.keySet().toArray()[0]; - vmStates.put(name, new HostVmStateReportEntry(PowerState.valueOf(vmMap.get(name)), _guid, null)); + vmStates.put(name, new HostVmStateReportEntry(PowerState.valueOf(vmMap.get(name)), _guid)); } return vmStates; } @@ -421,8 +429,9 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S s_logger.error(errMsg, e); return null; } - - if (clazz == CheckSshCommand.class) { + if (cmd instanceof NetworkElementCommand) { + return _vrResource.executeRequest((NetworkElementCommand)cmd); + }if (clazz == CheckSshCommand.class) { answer = execute((CheckSshCommand)cmd); } else if (clazz == GetDomRVersionCmd.class) { answer = execute((GetDomRVersionCmd)cmd); @@ -499,6 +508,203 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } return answer; } + + @Override + public ExecutionResult executeInVR(String routerIP, String script, String args) { + Pair result; + + //TODO: Password should be masked, cannot output to log directly + if (s_logger.isDebugEnabled()) { + s_logger.debug("Run command on VR: " + routerIP + ", script: " + script + " with args: " + args); + } + + try { + result = SshHelper.sshExecute(routerIP, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/" + script + " " + args); + } catch (Exception e) { + String msg = "Command failed due to " + e ; + s_logger.error(msg); + result = new Pair(false, msg); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug(script + " execution result: " + result.first().toString()); + } + return new ExecutionResult(result.first(), result.second()); + } + + @Override + public ExecutionResult createFileInVR(String routerIp, String filePath, String fileName, String content) { + File keyFile = getSystemVMKeyFile(); + try { + SshHelper.scpTo(routerIp, 3922, "root", keyFile, null, filePath, content.getBytes(), fileName, null); + } catch (Exception e) { + s_logger.warn("Fail to create file " + filePath + fileName + " in VR " + routerIp, e); + return new ExecutionResult(false, e.getMessage()); + } + return new ExecutionResult(true, null); + } + + @Override + public ExecutionResult prepareCommand(NetworkElementCommand cmd) { + //Update IP used to access router + cmd.setRouterAccessIp(getRouterSshControlIp(cmd)); + assert cmd.getRouterAccessIp() != null; + + if (cmd instanceof IpAssocVpcCommand) { + return prepareNetworkElementCommand((IpAssocVpcCommand)cmd); + } else if (cmd instanceof IpAssocCommand) { + return prepareNetworkElementCommand((IpAssocCommand)cmd); + } else if (cmd instanceof SetSourceNatCommand) { + return prepareNetworkElementCommand((SetSourceNatCommand)cmd); + } else if (cmd instanceof SetupGuestNetworkCommand) { + return prepareNetworkElementCommand((SetupGuestNetworkCommand)cmd); + } else if (cmd instanceof SetNetworkACLCommand) { + return prepareNetworkElementCommand((SetNetworkACLCommand)cmd); + } + return new ExecutionResult(true, null); + } + + private ExecutionResult prepareNetworkElementCommand(IpAssocCommand cmd) { + try { + + IpAddressTO[] ips = cmd.getIpAddresses(); + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String controlIp = getRouterSshControlIp(cmd); + + for (IpAddressTO ip : ips) { + /** + * TODO support other networks + */ + URI broadcastUri = BroadcastDomainType.fromString(ip.getBroadcastUri()); + if (BroadcastDomainType.getSchemeValue(broadcastUri) != BroadcastDomainType.Vlan) { + throw new InternalErrorException("Unable to assign a public IP to a VIF on network " + ip.getBroadcastUri()); + } + int vlanId = Integer.parseInt(BroadcastDomainType.getValue(broadcastUri)); + int publicNicInfo = -1; + publicNicInfo = getVmNics(routerName, vlanId); + + boolean addVif = false; + if (ip.isAdd() && publicNicInfo == -1) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Plug new NIC to associate" + controlIp + " to " + ip.getPublicIp()); + } + addVif = true; + } + + if (addVif) { + Pair nicdevice = findRouterFreeEthDeviceIndex(controlIp); + publicNicInfo = nicdevice.first(); + if (publicNicInfo > 0) { + modifyNicVlan(routerName, vlanId, nicdevice.second()); + // After modifying the vnic on VR, check the VR VNics config in the host and get the device position + publicNicInfo = getVmNics(routerName, vlanId); + // As a new nic got activated in the VR. add the entry in the NIC's table. + networkUsage(controlIp, "addVif", "eth" + publicNicInfo); + } + else { + // we didn't find any eth device available in VR to configure the ip range with new VLAN + String msg = "No Nic is available on DomR VIF to associate/disassociate IP with."; + s_logger.error(msg); + throw new InternalErrorException(msg); + } + ip.setNicDevId(publicNicInfo); + ip.setNewNic(addVif); + } else { + ip.setNicDevId(publicNicInfo); + } + } + } catch (Throwable e) { + s_logger.error("Unexpected exception: " + e.toString() + " will shortcut rest of IPAssoc commands", e); + return new ExecutionResult(false, e.toString()); + } + return new ExecutionResult(true, null); + } + + protected ExecutionResult prepareNetworkElementCommand(SetupGuestNetworkCommand cmd) { + NicTO nic = cmd.getNic(); + String routerIp = getRouterSshControlIp(cmd); + String domrName = + cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + + try { + int ethDeviceNum = findRouterEthDeviceIndex(domrName, routerIp, + nic.getMac()); + nic.setDeviceId(ethDeviceNum); + } catch (Exception e) { + String msg = "Prepare SetupGuestNetwork failed due to " + e.toString(); + s_logger.warn(msg, e); + return new ExecutionResult(false, msg); + } + return new ExecutionResult(true, null); + } + + + private ExecutionResult prepareNetworkElementCommand(IpAssocVpcCommand cmd) { + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + + try { + IpAddressTO[] ips = cmd.getIpAddresses(); + for (IpAddressTO ip : ips) { + + int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, ip.getVifMacAddress()); + if (ethDeviceNum < 0) { + if (ip.isAdd()) { + throw new InternalErrorException("Failed to find DomR VIF to associate/disassociate IP with."); + } else { + s_logger.debug("VIF to deassociate IP with does not exist, return success"); + continue; + } + } + + ip.setNicDevId(ethDeviceNum); + } + } catch (Exception e) { + s_logger.error("Prepare Ip Assoc failure on applying one ip due to exception: ", e); + return new ExecutionResult(false, e.toString()); + } + + return new ExecutionResult(true, null); + } + + protected ExecutionResult prepareNetworkElementCommand(SetSourceNatCommand cmd) { + String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + IpAddressTO pubIp = cmd.getIpAddress(); + + try { + int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, pubIp.getVifMacAddress()); + pubIp.setNicDevId(ethDeviceNum); + } catch (Exception e) { + String msg = "Prepare Ip SNAT failure due to " + e.toString(); + s_logger.error(msg, e); + return new ExecutionResult(false, e.toString()); + } + return new ExecutionResult(true, null); + } + + private ExecutionResult prepareNetworkElementCommand(SetNetworkACLCommand cmd) { + NicTO nic = cmd.getNic(); + String routerName = + cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME); + String routerIp = getRouterSshControlIp(cmd); + + try { + int ethDeviceNum = findRouterEthDeviceIndex(routerName, routerIp, + nic.getMac()); + nic.setDeviceId(ethDeviceNum); + } catch (Exception e) { + String msg = "Prepare SetNetworkACL failed due to " + e.toString(); + s_logger.error(msg, e); + return new ExecutionResult(false, msg); + } + return new ExecutionResult(true, null); + } + + @Override + public ExecutionResult cleanupCommand(NetworkElementCommand cmd) { + return new ExecutionResult(true, null); + } + protected Answer execute(final RemoteAccessVpnCfgCommand cmd) { String controlIp = getRouterSshControlIp(cmd); StringBuffer argsBuf = new StringBuffer(); @@ -1926,6 +2132,10 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S _configureCalled = true; } + _vrResource = new VirtualRoutingResource(this); + if (!_vrResource.configure(name, new HashMap())) { + throw new ConfigurationException("Unable to configure VirtualRoutingResource"); + } return true; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 1056bcf21f2..3a5d27c1478 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -254,11 +254,14 @@ import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -306,6 +309,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private String _pod; private String _clusterId; private int _migrateSpeed; + private int _migrateDowntime; + private int _migratePauseAfter; private long _hvVersion; private long _kernelVersion; @@ -889,6 +894,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv _mountPoint = "/mnt"; } + value = (String) params.get("vm.migrate.downtime"); + _migrateDowntime = NumbersUtil.parseInt(value, -1); + + value = (String) params.get("vm.migrate.pauseafter"); + _migratePauseAfter = NumbersUtil.parseInt(value, -1); + value = (String)params.get("vm.migrate.speed"); _migrateSpeed = NumbersUtil.parseInt(value, -1); if (_migrateSpeed == -1) { @@ -1793,8 +1804,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv try { Connect conn = LibvirtConnection.getConnection(); StorageVol v = conn.storageVolLookupByPath(path); + int flags = 0; - int flags = 1; + if (conn.getLibVirVersion() > 1001000 && vol.getFormat() == PhysicalDiskFormat.RAW) { + flags = 1; + } if (shrinkOk) { flags = 4; } @@ -2986,7 +3000,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv Connect conn = null; String xmlDesc = null; try { - conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName()); + conn = LibvirtConnection.getConnectionByVmName(vmName); ifaces = getInterfaces(conn, vmName); disks = getDisks(conn, vmName); dm = conn.domainLookupByName(vmName); @@ -3006,17 +3020,65 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv xmlDesc = dm.getXMLDesc(0).replace(_privateIp, cmd.getDestinationIp()); dconn = new Connect("qemu+tcp://" + cmd.getDestinationIp() + "/system"); - /* - * Hard code lm flag: VIR_MIGRATE_LIVE(1<<0) - */ - destDomain = dm.migrate(dconn, (1 << 0), xmlDesc, vmName, "tcp:" + cmd.getDestinationIp(), _migrateSpeed); - for (DiskDef disk : disks) { - cleanupDisk(disk); + //run migration in thread so we can monitor it + s_logger.info("Live migration of instance " + vmName + " initiated"); + ExecutorService executor = Executors.newFixedThreadPool(1); + Callable worker = new MigrateKVMAsync(dm, dconn, vmName, cmd.getDestinationIp()); + Future migrateThread = executor.submit(worker); + executor.shutdown(); + long sleeptime = 0; + while (!executor.isTerminated()) { + Thread.sleep(100); + sleeptime += 100; + if (sleeptime == 1000) { // wait 1s before attempting to set downtime on migration, since I don't know of a VIR_DOMAIN_MIGRATING state + if (_migrateDowntime > 0 ) { + try { + int setDowntime = dm.migrateSetMaxDowntime(_migrateDowntime); + if (setDowntime == 0 ) { + s_logger.debug("Set max downtime for migration of " + vmName + " to " + String.valueOf(_migrateDowntime) + "ms"); + } + } catch (LibvirtException e) { + s_logger.debug("Failed to set max downtime for migration, perhaps migration completed? Error: " + e.getMessage()); + } + } + } + if ((sleeptime % 1000) == 0) { + s_logger.info("Waiting for migration of " + vmName + " to complete, waited " + sleeptime + "ms"); + } + + // pause vm if we meet the vm.migrate.pauseafter threshold and not already paused + if (_migratePauseAfter > 0 && sleeptime > _migratePauseAfter && dm.getInfo().state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING ) { + s_logger.info("Pausing VM " + vmName + " due to property vm.migrate.pauseafter setting to " + _migratePauseAfter+ "ms to complete migration"); + try { + dm.suspend(); + } catch (LibvirtException e) { + // pause could be racy if it attempts to pause right when vm is finished, simply warn + s_logger.info("Failed to pause vm " + vmName + " : " + e.getMessage()); + } + } + } + s_logger.info("Migration thread for " + vmName + " is done"); + + destDomain = migrateThread.get(10, TimeUnit.SECONDS); + + if (destDomain != null) { + for (DiskDef disk : disks) { + cleanupDisk(disk); + } } } catch (LibvirtException e) { s_logger.debug("Can't migrate domain: " + e.getMessage()); result = e.getMessage(); + } catch (InterruptedException e) { + s_logger.debug("Interrupted while migrating domain: " + e.getMessage()); + result = e.getMessage(); + } catch (ExecutionException e) { + s_logger.debug("Failed to execute while migrating domain: " + e.getMessage()); + result = e.getMessage(); + } catch (TimeoutException e) { + s_logger.debug("Timed out while migrating domain: " + e.getMessage()); + result = e.getMessage(); } finally { try { if (dm != null) { @@ -3054,6 +3116,30 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv return new MigrateAnswer(cmd, result == null, result, null); } + private class MigrateKVMAsync implements Callable { + Domain dm = null; + Connect dconn = null; + String vmName = ""; + String destIp = ""; + + MigrateKVMAsync(Domain dm, Connect dconn, String vmName, String destIp) { + this.dm = dm; + this.dconn = dconn; + this.vmName = vmName; + this.destIp = destIp; + } + + @Override + public Domain call() throws LibvirtException { + // set compression flag for migration if libvirt version supports it + if (dconn.getLibVirVersion() < 1003000) { + return dm.migrate(dconn, (1 << 0), vmName, "tcp:" + destIp, _migrateSpeed); + } else { + return dm.migrate(dconn, (1 << 0)|(1 << 11), vmName, "tcp:" + destIp, _migrateSpeed); + } + } + } + private synchronized Answer execute(PrepareForMigrationCommand cmd) { VirtualMachineTO vm = cmd.getVirtualMachine(); @@ -4298,6 +4384,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv try { conn = LibvirtConnection.getConnectionByType(HypervisorType.LXC.toString()); vmStates.putAll(getAllVms(conn)); + conn = LibvirtConnection.getConnectionByType(HypervisorType.KVM.toString()); + vmStates.putAll(getAllVms(conn)); } catch (LibvirtException e) { s_logger.debug("Failed to get connection: " + e.getMessage()); } @@ -4394,6 +4482,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv try { conn = LibvirtConnection.getConnectionByType(HypervisorType.LXC.toString()); vmStates.putAll(getHostVmStateReport(conn)); + conn = LibvirtConnection.getConnectionByType(HypervisorType.KVM.toString()); + vmStates.putAll(getHostVmStateReport(conn)); } catch (LibvirtException e) { s_logger.debug("Failed to get connection: " + e.getMessage()); } @@ -4447,7 +4537,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv // Stopped VM around, to work-around that, reporting only powered-on VM // if (state == PowerState.PowerOn) - vmStates.put(vmName, new HostVmStateReportEntry(state, conn.getHostName(), null)); + vmStates.put(vmName, new HostVmStateReportEntry(state, conn.getHostName())); } catch (final LibvirtException e) { s_logger.warn("Unable to get vms", e); } finally { @@ -4476,7 +4566,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv // Stopped VM around, to work-around that, reporting only powered-on VM // if (state == PowerState.PowerOn) - vmStates.put(vmName, new HostVmStateReportEntry(state, conn.getHostName(), null)); + vmStates.put(vmName, new HostVmStateReportEntry(state, conn.getHostName())); } catch (final LibvirtException e) { s_logger.warn("Unable to get vms", e); } finally { diff --git a/plugins/hypervisors/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java b/plugins/hypervisors/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java index 238ac518a92..14145025a5a 100755 --- a/plugins/hypervisors/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java +++ b/plugins/hypervisors/ovm/src/com/cloud/ovm/hypervisor/OvmResourceBase.java @@ -808,7 +808,7 @@ public class OvmResourceBase implements ServerResource, HypervisorResource { Map vms = OvmHost.getAllVms(_conn); for (final Map.Entry entry : vms.entrySet()) { PowerState state = toPowerState(entry.getKey(), entry.getValue()); - vmStates.put(entry.getKey(), new HostVmStateReportEntry(state, _conn.getIp(), null)); + vmStates.put(entry.getKey(), new HostVmStateReportEntry(state, _conn.getIp())); } return vmStates; } diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentRoutingResource.java b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentRoutingResource.java index bb2851fef72..2fca291c3e1 100644 --- a/plugins/hypervisors/simulator/src/com/cloud/resource/AgentRoutingResource.java +++ b/plugins/hypervisors/simulator/src/com/cloud/resource/AgentRoutingResource.java @@ -280,7 +280,7 @@ public class AgentRoutingResource extends AgentStorageResource { HashMap report = new HashMap(); for (String vmName : _runningVms.keySet()) { - report.put(vmName, new HostVmStateReportEntry(PowerState.PowerOn, agentHost.getName(), null)); + report.put(vmName, new HostVmStateReportEntry(PowerState.PowerOn, agentHost.getName())); } return report; diff --git a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java index 3eb2cf60b7d..40909629c70 100644 --- a/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java +++ b/plugins/hypervisors/simulator/src/org/apache/cloudstack/storage/motion/SimulatorDataMotionStrategy.java @@ -42,6 +42,11 @@ public class SimulatorDataMotionStrategy implements DataMotionStrategy { return StrategyPriority.HYPERVISOR; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult("something", null); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 3eac0fc8b7b..4693f867ea8 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -5238,7 +5238,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa name = VMInternalCSName; if (!isTemplate) { - newStates.put(name, new HostVmStateReportEntry(convertPowerState(powerState), hyperHost.getHyperHostName(), null)); + newStates.put(name, new HostVmStateReportEntry(convertPowerState(powerState), hyperHost.getHyperHostName())); } } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java b/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java index c33af27d244..0e2a9117f10 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java +++ b/plugins/hypervisors/vmware/src/com/cloud/network/element/CiscoNexusVSMElement.java @@ -79,6 +79,8 @@ public class CiscoNexusVSMElement extends CiscoNexusVSMDeviceManagerImpl impleme ClusterDao _clusterDao; @Inject ClusterVSMMapDao _clusterVSMDao; + @Inject + ManagementService _mgr; @Override public Map> getCapabilities() { @@ -190,7 +192,8 @@ public class CiscoNexusVSMElement extends CiscoNexusVSMDeviceManagerImpl impleme // Else if there is only a zoneId defined, get a list of all vmware clusters // in the zone, and then for each cluster, pull the VSM and prepare a list. if (zoneId != null && zoneId.longValue() != 0) { - ManagementService ref = cmd.getMgmtServiceRef(); + ManagementService ref = _mgr; + ; List clusterList = ref.searchForClusters(zoneId, cmd.getStartIndex(), cmd.getPageSizeVal(), "VMware"); if (clusterList.size() == 0) { diff --git a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java index 09f10688a68..24efde79a99 100644 --- a/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java +++ b/plugins/hypervisors/vmware/src/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java @@ -85,6 +85,11 @@ public class VmwareStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.CANT_HANDLE; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult(null, null); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java index 529e26125f3..059e6e4139a 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java @@ -34,7 +34,6 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.GuestOSVO; -import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.VolumeDao; @@ -103,30 +102,30 @@ public class XenServerGuru extends HypervisorGuruBase implements HypervisorGuru List volumes = _volumeDao.findByInstance(vm.getId()); + // it's OK in this case to send a detach command to the host for a root volume as this + // will simply lead to the SR that supports the root volume being removed if (volumes != null) { for (VolumeVO volume : volumes) { - if (volume.getVolumeType() == Volume.Type.DATADISK) { - StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); + StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); - // storagePool should be null if we are expunging a volume that was never - // attached to a VM that was started (the "trick" for storagePool to be null - // is that none of the VMs this volume may have been attached to were ever started, - // so the volume was never assigned to a storage pool) - if (storagePool != null && storagePool.isManaged()) { - DataTO volTO = _volFactory.getVolume(volume.getId()).getTO(); - DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getPath(), volume.getVolumeType()); + // storagePool should be null if we are expunging a volume that was never + // attached to a VM that was started (the "trick" for storagePool to be null + // is that none of the VMs this volume may have been attached to were ever started, + // so the volume was never assigned to a storage pool) + if (storagePool != null && storagePool.isManaged()) { + DataTO volTO = _volFactory.getVolume(volume.getId()).getTO(); + DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getPath(), volume.getVolumeType()); - DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName()); + DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName()); - cmd.setManaged(true); + cmd.setManaged(true); - cmd.setStorageHost(storagePool.getHostAddress()); - cmd.setStoragePort(storagePool.getPort()); + cmd.setStorageHost(storagePool.getHostAddress()); + cmd.setStoragePort(storagePool.getPort()); - cmd.set_iScsiName(volume.get_iScsiName()); + cmd.set_iScsiName(volume.get_iScsiName()); - commands.add(cmd); - } + commands.add(cmd); } } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index c0d691ad5ba..d80bddc3773 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -56,11 +56,13 @@ import com.trilead.ssh2.SCPClient; import com.xensource.xenapi.Bond; import com.xensource.xenapi.Connection; import com.xensource.xenapi.Console; +import com.xensource.xenapi.GPUGroup; import com.xensource.xenapi.Host; import com.xensource.xenapi.HostCpu; import com.xensource.xenapi.HostMetrics; import com.xensource.xenapi.Network; import com.xensource.xenapi.PBD; +import com.xensource.xenapi.PGPU; import com.xensource.xenapi.PIF; import com.xensource.xenapi.Pool; import com.xensource.xenapi.SR; @@ -73,6 +75,8 @@ import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VBD; import com.xensource.xenapi.VBDMetrics; import com.xensource.xenapi.VDI; +import com.xensource.xenapi.VGPU; +import com.xensource.xenapi.VGPUType; import com.xensource.xenapi.VIF; import com.xensource.xenapi.VLAN; import com.xensource.xenapi.VM; @@ -99,6 +103,8 @@ import com.cloud.agent.api.CheckVirtualMachineCommand; import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.ClusterSyncAnswer; import com.cloud.agent.api.ClusterSyncCommand; +import com.cloud.agent.api.ClusterVMMetaDataSyncCommand; +import com.cloud.agent.api.ClusterVMMetaDataSyncAnswer; import com.cloud.agent.api.Command; import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.CreateVMSnapshotAnswer; @@ -106,6 +112,8 @@ import com.cloud.agent.api.CreateVMSnapshotCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.DeleteVMSnapshotAnswer; import com.cloud.agent.api.DeleteVMSnapshotCommand; +import com.cloud.agent.api.GetGPUStatsAnswer; +import com.cloud.agent.api.GetGPUStatsCommand; import com.cloud.agent.api.GetHostStatsAnswer; import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.GetStorageStatsAnswer; @@ -199,6 +207,7 @@ import com.cloud.agent.api.storage.ResizeVolumeCommand; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NicTO; @@ -431,6 +440,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((GetHostStatsCommand)cmd); } else if (clazz == GetVmStatsCommand.class) { return execute((GetVmStatsCommand)cmd); + } else if (clazz == GetGPUStatsCommand.class) { + return execute((GetGPUStatsCommand) cmd); } else if (clazz == GetVmDiskStatsCommand.class) { return execute((GetVmDiskStatsCommand)cmd); } else if (clazz == CheckHealthCommand.class) { @@ -511,6 +522,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((UpdateHostPasswordCommand)cmd); } else if (cmd instanceof ClusterSyncCommand) { return execute((ClusterSyncCommand)cmd); + } else if (cmd instanceof ClusterVMMetaDataSyncCommand) { + return execute((ClusterVMMetaDataSyncCommand)cmd); } else if (clazz == CheckNetworkCommand.class) { return execute((CheckNetworkCommand)cmd); } else if (clazz == PlugNicCommand.class) { @@ -1284,6 +1297,65 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return dynamicMinRam; } + private HashMap> getGPUGroupDetails(Connection conn) throws XenAPIException, XmlRpcException { + HashMap> groupDetails = new HashMap>(); + Host host = Host.getByUuid(conn, _host.uuid); + Set pgpus = host.getPGPUs(conn); + Iterator iter = pgpus.iterator(); + while (iter.hasNext()) { + PGPU pgpu = iter.next(); + GPUGroup gpuGroup = pgpu.getGPUGroup(conn); + Set enabledVGPUTypes = gpuGroup.getEnabledVGPUTypes(conn); + String groupName = gpuGroup.getNameLabel(conn); + HashMap gpuCapacity = new HashMap(); + if (groupDetails.get(groupName) != null) { + gpuCapacity = groupDetails.get(groupName); + } + // Get remaining capacity of all the enabled VGPU in a PGPU + if(enabledVGPUTypes != null) { + Iterator it = enabledVGPUTypes.iterator(); + while (it.hasNext()) { + VGPUType type = it.next(); + String modelName = type.getModelName(conn); + Long remainingCapacity = pgpu.getRemainingCapacity(conn, type); + if (gpuCapacity.get(modelName) != null) { + long newRemainingCapacity = gpuCapacity.get(modelName) + remainingCapacity; + gpuCapacity.put(modelName, newRemainingCapacity); + } else { + gpuCapacity.put(modelName, remainingCapacity); + } + } + } + groupDetails.put(groupName, gpuCapacity); + } + return groupDetails; + } + + protected void createVGPU(Connection conn, StartCommand cmd, VM vm, GPUDeviceTO gpuDevice) throws XenAPIException, XmlRpcException { + Set groups = GPUGroup.getByNameLabel(conn, gpuDevice.getGpuGroup()); + assert groups.size() == 1 : "Should only have 1 group but found " + groups.size(); + GPUGroup gpuGroup = groups.iterator().next(); + + Set vgpuTypes = gpuGroup.getEnabledVGPUTypes(conn); + Iterator iter = vgpuTypes.iterator(); + VGPUType vgpuType = null; + while (iter.hasNext()) { + VGPUType entry = iter.next(); + if (entry.getModelName(conn).equals(gpuDevice.getVgpuType())) { + vgpuType = entry; + } + } + String device = "0"; // Only allow device = "0" for now, as XenServer supports just a single vGPU per VM. + Map other_config = new HashMap(); + VGPU.create(conn, vm, gpuGroup, device, other_config, vgpuType); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Created VGPU of VGPU type [ " + gpuDevice.getVgpuType() + " ] for VM " + cmd.getVirtualMachine().getName()); + } + // Calculate and set remaining GPU capacity in the host. + cmd.getVirtualMachine().getGpuDevice().setGroupDetails(getGPUGroupDetails(conn)); + } + protected VM createVmFromTemplate(Connection conn, VirtualMachineTO vmSpec, Host host) throws XenAPIException, XmlRpcException { String guestOsTypeName = getGuestOsType(vmSpec.getOs(), vmSpec.getBootloader() == BootloaderType.CD); Set templates = VM.getByNameLabel(conn, guestOsTypeName); @@ -1432,14 +1504,12 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe platform.put("cores-per-socket", coresPerSocket); vm.setPlatform(conn, platform); } - - String xentoolsversion = details.get("hypervisortoolsversion"); - if (xentoolsversion == null || !xentoolsversion.equalsIgnoreCase("xenserver61")) { - Map platform = vm.getPlatform(conn); - platform.remove("device_id"); - vm.setPlatform(conn, platform); - } - + } + String xentoolsversion = details.get("hypervisortoolsversion"); + if (xentoolsversion == null || !xentoolsversion.equalsIgnoreCase("xenserver61")) { + Map platform = vm.getPlatform(conn); + platform.remove("device_id"); + vm.setPlatform(conn, platform); } } } @@ -1719,6 +1789,13 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Host host = Host.getByUuid(conn, _host.uuid); vm = createVmFromTemplate(conn, vmSpec, host); + GPUDeviceTO gpuDevice = vmSpec.getGpuDevice(); + if (gpuDevice != null) { + s_logger.debug("Creating VGPU for of VGPU type: " + gpuDevice.getVgpuType() + " in GPU group " + + gpuDevice.getGpuGroup() + " for VM " + vmName ); + createVGPU(conn, cmd, vm, gpuDevice); + } + for (DiskTO disk : vmSpec.getDisks()) { VDI newVdi = prepareManagedDisk(conn, disk, vmName); @@ -1869,18 +1946,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return prepareManagedStorage(conn, details, null, vdiNameLabel); } - protected VDI prepareManagedStorage(Connection conn, Map details, String path, String vdiNameLabel) throws Exception { + protected SR prepareManagedSr(Connection conn, Map details) { String iScsiName = details.get(DiskTO.IQN); String storageHost = details.get(DiskTO.STORAGE_HOST); String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME); String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET); - Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE)); - SR sr = getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true); + return getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true); + } + + protected VDI prepareManagedStorage(Connection conn, Map details, String path, String vdiNameLabel) throws Exception { + SR sr = prepareManagedSr(conn, details); VDI vdi = getVDIbyUuid(conn, path, false); if (vdi == null) { + Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE)); + vdi = createVdi(sr, vdiNameLabel, volumeSize); } @@ -2238,6 +2320,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return hostStats; } + protected GetGPUStatsAnswer execute(GetGPUStatsCommand cmd) { + Connection conn = getConnection(); + HashMap> groupDetails = new HashMap>(); + try { + groupDetails = getGPUGroupDetails(conn); + } catch (Exception e) { + String msg = "Unable to get GPU stats" + e.toString(); + s_logger.warn(msg, e); + } + return new GetGPUStatsAnswer(cmd, groupDetails); + } + protected GetVmStatsAnswer execute(GetVmStatsCommand cmd) { Connection conn = getConnection(); List vmNames = cmd.getVmNames(); @@ -2354,9 +2448,15 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe double diskReadKBs = 0; double diskWriteKBs = 0; for (VBD vbd : vm.getVBDs(conn)) { - VBDMetrics record = vbd.getMetrics(conn); - diskReadKBs += record.getIoReadKbs(conn); - diskWriteKBs += record.getIoWriteKbs(conn); + VBDMetrics vbdmetrics = vbd.getMetrics(conn); + if (!isRefNull(vbdmetrics)) { + try { + diskReadKBs += vbdmetrics.getIoReadKbs(conn); + diskWriteKBs += vbdmetrics.getIoWriteKbs(conn); + } catch (Types.HandleInvalid e) { + s_logger.debug("vbdmetrics doesn't exist "); + } + } } if (stats == null) { stats = new VmStatsEntry(); @@ -2533,7 +2633,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe VmPowerState ps = record.powerState; Host host = record.residentOn; - String xstoolsversion = getVMXenToolsVersion(record.platform); String host_uuid = null; if (!isRefNull(host)) { try { @@ -2549,7 +2648,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (host_uuid.equalsIgnoreCase(_host.uuid)) { vmStates.put( record.nameLabel, - new HostVmStateReportEntry(convertPowerState(ps), host_uuid, xstoolsversion) + new HostVmStateReportEntry(convertPowerState(ps), host_uuid) ); } } @@ -2559,8 +2658,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } // TODO vmsync { - protected HashMap> getAllVms(Connection conn) { - final HashMap> vmStates = new HashMap>(); + protected HashMap> getAllVms(Connection conn) { + final HashMap> vmStates = new HashMap>(); Map vm_map = null; for (int i = 0; i < 2; i++) { try { @@ -2590,7 +2689,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe s_logger.trace("VM " + record.nameLabel + ": powerstate = " + ps + "; vm state=" + state.toString()); } Host host = record.residentOn; - String platformstring = StringUtils.mapToString(record.platform); String host_uuid = null; if (!isRefNull(host)) { try { @@ -2602,7 +2700,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } catch (XmlRpcException e) { s_logger.error("Failed to get host uuid for host " + host.toWireString(), e); } - vmStates.put(record.nameLabel, new Ternary(host_uuid, state, platformstring)); + vmStates.put(record.nameLabel, new Pair(host_uuid, state)); } } @@ -3555,6 +3653,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe try { if (vm.getPowerState(conn) == VmPowerState.HALTED) { + Set vGPUs = null; + // Get updated GPU details + try { + vGPUs = vm.getVGPUs(conn); + } catch (XenAPIException e2) { + s_logger.debug("VM " + vmName + " does not have GPU support."); + } + if (vGPUs != null && !vGPUs.isEmpty()) { + HashMap> groupDetails = getGPUGroupDetails(conn); + cmd.setGpuDevice(new GPUDeviceTO(null, null, groupDetails)); + } + Set vifs = vm.getVIFs(conn); List networks = new ArrayList(); for (VIF vif : vifs) { @@ -3597,11 +3707,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new StopAnswer(cmd, "Stop VM failed", platformstring, false); } - /*Override by subclass*/ - protected String getVMXenToolsVersion(Map platform) { - return "xenserver56"; - } - private List getVdis(Connection conn, VM vm) { List vdis = new ArrayList(); try { @@ -4475,7 +4580,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Host.Record hostr = poolr.master.getRecord(conn); if (_host.uuid.equals(hostr.uuid)) { - HashMap> allStates = fullClusterSync(conn); + HashMap> allStates = fullClusterSync(conn); cmd.setClusterVMStateChanges(allStates); } } catch (Throwable e) { @@ -5394,6 +5499,15 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe details.put("private.network.device", _privateNetworkName); } + try { + HashMap> groupDetails = getGPUGroupDetails(conn); + cmd.setGpuGroupDetails(groupDetails); + } catch (Exception e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("GPU device not found in host " + hr.hostname); + } + } + cmd.setHostDetails(details); cmd.setName(hr.nameLabel); cmd.setGuid(_host.uuid); @@ -7047,11 +7161,49 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe s_logger.warn("Check for master failed, failing the Cluster sync command"); return new Answer(cmd); } - HashMap> newStates = deltaClusterSync(conn); + HashMap> newStates = deltaClusterSync(conn); return new ClusterSyncAnswer(cmd.getClusterId(), newStates); } - protected HashMap> fullClusterSync(Connection conn) { + + protected ClusterVMMetaDataSyncAnswer execute(final ClusterVMMetaDataSyncCommand cmd) { + Connection conn = getConnection(); + //check if this is master + Pool pool; + try { + pool = Pool.getByUuid(conn, _host.pool); + Pool.Record poolr = pool.getRecord(conn); + Host.Record hostr = poolr.master.getRecord(conn); + if (!_host.uuid.equals(hostr.uuid)) { + return new ClusterVMMetaDataSyncAnswer(cmd.getClusterId(), null); + } + } catch (Throwable e) { + s_logger.warn("Check for master failed, failing the Cluster sync VMMetaData command"); + return new ClusterVMMetaDataSyncAnswer(cmd.getClusterId(), null); + } + HashMap vmMetadatum = clusterVMMetaDataSync(conn); + return new ClusterVMMetaDataSyncAnswer(cmd.getClusterId(), vmMetadatum); + } + + protected HashMap clusterVMMetaDataSync(Connection conn) { + final HashMap vmMetaDatum = new HashMap(); + try { + Map vm_map = VM.getAllRecords(conn); //USE THIS TO GET ALL VMS FROM A CLUSTER + for (VM.Record record: vm_map.values()) { + if (record.isControlDomain || record.isASnapshot || record.isATemplate) { + continue; // Skip DOM0 + } + vmMetaDatum.put(record.nameLabel, StringUtils.mapToString(record.platform)); + } + } catch (final Throwable e) { + String msg = "Unable to get vms through host " + _host.uuid + " due to to " + e.toString(); + s_logger.warn(msg, e); + throw new CloudRuntimeException(msg); + } + return vmMetaDatum; + } + + protected HashMap> fullClusterSync(Connection conn) { synchronized (_cluster.intern()) { s_vms.clear(_cluster); } @@ -7069,7 +7221,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (!isRefNull(host)) { host_uuid = host.getUuid(conn); synchronized (_cluster.intern()) { - s_vms.put(_cluster, host_uuid, vm_name, state, null); + s_vms.put(_cluster, host_uuid, vm_name, state); } } if (s_logger.isTraceEnabled()) { @@ -7084,44 +7236,32 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return s_vms.getClusterVmState(_cluster); } - protected HashMap> deltaClusterSync(Connection conn) { - final HashMap> changes = new HashMap>(); + protected HashMap> deltaClusterSync(Connection conn) { + final HashMap> changes = new HashMap>(); synchronized (_cluster.intern()) { - HashMap> newStates = getAllVms(conn); + HashMap> newStates = getAllVms(conn); if (newStates == null) { s_logger.warn("Unable to get the vm states so no state sync at this point."); return null; } - HashMap> oldStates = new HashMap>(s_vms.size(_cluster)); + HashMap> oldStates = new HashMap>(s_vms.size(_cluster)); oldStates.putAll(s_vms.getClusterVmState(_cluster)); - for (final Map.Entry> entry : newStates.entrySet()) { + for (final Map.Entry> entry : newStates.entrySet()) { final String vm = entry.getKey(); - String platform = entry.getValue().third(); State newState = entry.getValue().second(); String host_uuid = entry.getValue().first(); - final Ternary oldState = oldStates.remove(vm); + final Pair oldState = oldStates.remove(vm); - // check if platform changed - if (platform != null && oldState != null) { - if (!platform.equals(oldState.third()) && newState != State.Stopped && newState != State.Stopping) { - s_logger.warn("Detecting a change in platform for " + vm); - changes.put(vm, new Ternary(host_uuid, newState, platform)); - - s_logger.debug("11. The VM " + vm + " is in " + newState + " state"); - s_vms.put(_cluster, host_uuid, vm, newState, platform); - continue; - } - } //check if host is changed if (host_uuid != null && oldState != null) { if (!host_uuid.equals(oldState.first()) && newState != State.Stopped && newState != State.Stopping) { s_logger.warn("Detecting a change in host for " + vm); - changes.put(vm, new Ternary(host_uuid, newState, platform)); + changes.put(vm, new Pair(host_uuid, newState)); s_logger.debug("11. The VM " + vm + " is in " + newState + " state"); - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); continue; } } @@ -7139,42 +7279,42 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe continue; } if (oldState == null) { - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); s_logger.warn("Detecting a new state but couldn't find a old state so adding it to the changes: " + vm); - changes.put(vm, new Ternary(host_uuid, newState, platform)); + changes.put(vm, new Pair(host_uuid, newState)); } else if (oldState.second() == State.Starting) { if (newState == State.Running) { s_logger.debug("12. The VM " + vm + " is in " + State.Running + " state"); - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); } else if (newState == State.Stopped) { s_logger.warn("Ignoring vm " + vm + " because of a lag in starting the vm."); } } else if (oldState.second() == State.Migrating) { if (newState == State.Running) { s_logger.debug("Detected that an migrating VM is now running: " + vm); - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); } } else if (oldState.second() == State.Stopping) { if (newState == State.Stopped) { s_logger.debug("13. The VM " + vm + " is in " + State.Stopped + " state"); - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); } else if (newState == State.Running) { s_logger.warn("Ignoring vm " + vm + " because of a lag in stopping the vm. "); } } else if (oldState.second() != newState) { s_logger.debug("14. The VM " + vm + " is in " + newState + " state was " + oldState.second()); - s_vms.put(_cluster, host_uuid, vm, newState, platform); + s_vms.put(_cluster, host_uuid, vm, newState); if (newState == State.Stopped) { /* * if (s_vmsKilled.remove(vm)) { s_logger.debug("VM " + vm + " has been killed for storage. "); * newState = State.Error; } */ } - changes.put(vm, new Ternary(host_uuid, newState, null)); + changes.put(vm, new Pair(host_uuid, newState)); } } - for (final Map.Entry> entry : oldStates.entrySet()) { + for (final Map.Entry> entry : oldStates.entrySet()) { final String vm = entry.getKey(); final State oldState = entry.getValue().second(); String host_uuid = entry.getValue().first(); @@ -7196,7 +7336,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } else { State newState = State.Stopped; s_logger.warn("The VM is now missing marking it as Stopped " + vm); - changes.put(vm, new Ternary(host_uuid, newState, null)); + changes.put(vm, new Pair(host_uuid, newState)); } } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java index b7199c8c89f..ffedaff3432 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer610Resource.java @@ -446,12 +446,4 @@ public class XenServer610Resource extends XenServer56FP1Resource { protected void plugDom0Vif(Connection conn, VIF dom0Vif) throws XmlRpcException, XenAPIException { // do nothing. In xenserver 6.1 and beyond this step isn't needed. } - - @Override - protected String getVMXenToolsVersion(Map platform) { - if (platform.containsKey("device_id")) { - return "xenserver61"; - } - return "xenserver56"; - } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java index 767d77e1b42..488acec1f9d 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java @@ -22,18 +22,18 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; -import com.cloud.utils.Ternary; +import com.cloud.utils.Pair; import com.cloud.vm.VirtualMachine.State; public class XenServerPoolVms { private static final Logger s_logger = Logger.getLogger(XenServerPoolVms.class); - private final Map>> _clusterVms = - new ConcurrentHashMap>>(); + private final Map>> _clusterVms = + new ConcurrentHashMap>>(); - public HashMap> getClusterVmState(String clusterId) { - HashMap> _vms = _clusterVms.get(clusterId); + public HashMap> getClusterVmState(String clusterId) { + HashMap> _vms = _clusterVms.get(clusterId); if (_vms == null) { - HashMap> vmStates = new HashMap>(); + HashMap> vmStates = new HashMap>(); _clusterVms.put(clusterId, vmStates); return vmStates; } else @@ -41,50 +41,45 @@ public class XenServerPoolVms { } public void clear(String clusterId) { - HashMap> _vms = getClusterVmState(clusterId); + HashMap> _vms = getClusterVmState(clusterId); _vms.clear(); } public State getState(String clusterId, String name) { - HashMap> vms = getClusterVmState(clusterId); - Ternary pv = vms.get(name); + HashMap> vms = getClusterVmState(clusterId); + Pair pv = vms.get(name); return pv == null ? State.Stopped : pv.second(); // if a VM is absent on the cluster, it is effectively in stopped state. } - public Ternary get(String clusterId, String name) { - HashMap> vms = getClusterVmState(clusterId); + public Pair get(String clusterId, String name) { + HashMap> vms = getClusterVmState(clusterId); return vms.get(name); } - public void put(String clusterId, String hostUuid, String name, State state, String platform) { - HashMap> vms = getClusterVmState(clusterId); - vms.put(name, new Ternary(hostUuid, state, platform)); - } - public void put(String clusterId, String hostUuid, String name, State state) { - HashMap> vms = getClusterVmState(clusterId); - vms.put(name, new Ternary(hostUuid, state, null)); + HashMap> vms = getClusterVmState(clusterId); + vms.put(name, new Pair(hostUuid, state)); } public void remove(String clusterId, String hostUuid, String name) { - HashMap> vms = getClusterVmState(clusterId); + HashMap> vms = getClusterVmState(clusterId); vms.remove(name); } - public void putAll(String clusterId, HashMap> newVms) { - HashMap> vms = getClusterVmState(clusterId); + public void putAll(String clusterId, HashMap> newVms) { + HashMap> vms = getClusterVmState(clusterId); vms.putAll(newVms); } public int size(String clusterId) { - HashMap> vms = getClusterVmState(clusterId); + HashMap> vms = getClusterVmState(clusterId); return vms.size(); } @Override public String toString() { StringBuilder sbuf = new StringBuilder("PoolVms="); - for (HashMap> clusterVM : _clusterVms.values()) { + for (HashMap> clusterVM : _clusterVms.values()) { for (String vmname : clusterVM.keySet()) { sbuf.append(vmname).append("-").append(clusterVM.get(vmname).second()).append(","); } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java index f2abff74f80..e512046b35b 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java @@ -59,6 +59,7 @@ import org.apache.cloudstack.storage.command.ForgetObjectCmd; import org.apache.cloudstack.storage.command.IntroduceObjectAnswer; import org.apache.cloudstack.storage.command.IntroduceObjectCmd; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -887,58 +888,121 @@ public class XenServerStorageProcessor implements StorageProcessor { @Override public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { - DataTO srcData = cmd.getSrcTO(); - DataTO destData = cmd.getDestTO(); + DataTO srcDataTo = cmd.getSrcTO(); + DataTO destDataTo = cmd.getDestTO(); int wait = cmd.getWait(); - DataStoreTO srcStore = srcData.getDataStore(); - try { - if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) { - NfsTO srcImageStore = (NfsTO)srcStore; - TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData; - String storeUrl = srcImageStore.getUrl(); + DataStoreTO srcDataStoreTo = srcDataTo.getDataStore(); + try { + if ((srcDataStoreTo instanceof NfsTO) && (srcDataTo.getObjectType() == DataObjectType.TEMPLATE)) { + NfsTO srcImageStore = (NfsTO)srcDataStoreTo; + TemplateObjectTO srcTemplateObjectTo = (TemplateObjectTO)srcDataTo; + String storeUrl = srcImageStore.getUrl(); URI uri = new URI(storeUrl); - String tmplpath = uri.getHost() + ":" + uri.getPath() + "/" + srcData.getPath(); - String poolName = destData.getDataStore().getUuid(); + String tmplPath = uri.getHost() + ":" + uri.getPath() + "/" + srcDataTo.getPath(); + DataStoreTO destDataStoreTo = destDataTo.getDataStore(); + + boolean managed = false; + String storageHost = null; + String managedStoragePoolName = null; + String managedStoragePoolRootVolumeName = null; + String managedStoragePoolRootVolumeSize = null; + String chapInitiatorUsername = null; + String chapInitiatorSecret = null; + + if (destDataStoreTo instanceof PrimaryDataStoreTO) { + PrimaryDataStoreTO destPrimaryDataStoreTo = (PrimaryDataStoreTO)destDataStoreTo; + + Map details = destPrimaryDataStoreTo.getDetails(); + + if (details != null) { + managed = Boolean.parseBoolean(details.get(PrimaryDataStoreTO.MANAGED)); + + if (managed) { + storageHost = details.get(PrimaryDataStoreTO.STORAGE_HOST); + managedStoragePoolName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET); + managedStoragePoolRootVolumeName = details.get(PrimaryDataStoreTO.MANAGED_STORE_TARGET_ROOT_VOLUME); + managedStoragePoolRootVolumeSize = details.get(PrimaryDataStoreTO.VOLUME_SIZE); + chapInitiatorUsername = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_USERNAME); + chapInitiatorSecret = details.get(PrimaryDataStoreTO.CHAP_INITIATOR_SECRET); + } + } + } + Connection conn = hypervisorResource.getConnection(); - SR poolsr = null; - Set srs = SR.getByNameLabel(conn, poolName); - if (srs.size() != 1) { - String msg = "There are " + srs.size() + " SRs with same name: " + poolName; - s_logger.warn(msg); - return new CopyCmdAnswer(msg); - } else { - poolsr = srs.iterator().next(); + final SR sr; + + if (managed) { + Map details = new HashMap(); + + details.put(DiskTO.STORAGE_HOST, storageHost); + details.put(DiskTO.IQN, managedStoragePoolName); + details.put(DiskTO.VOLUME_SIZE, managedStoragePoolRootVolumeSize); + details.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInitiatorUsername); + details.put(DiskTO.CHAP_INITIATOR_SECRET, chapInitiatorSecret); + + sr = hypervisorResource.prepareManagedSr(conn, details); } - String pUuid = poolsr.getUuid(conn); - boolean isISCSI = IsISCSI(poolsr.getType(conn)); - String uuid = copy_vhd_from_secondarystorage(conn, tmplpath, pUuid, wait); - VDI tmpl = getVDIbyUuid(conn, uuid); - VDI snapshotvdi = tmpl.snapshot(conn, new HashMap()); - String snapshotUuid = snapshotvdi.getUuid(conn); - snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName()); - String parentuuid = getVhdParent(conn, pUuid, snapshotUuid, isISCSI); - VDI parent = getVDIbyUuid(conn, parentuuid); - Long phySize = parent.getPhysicalUtilisation(conn); - tmpl.destroy(conn); - poolsr.scan(conn); + else { + String srName = destDataStoreTo.getUuid(); + Set srs = SR.getByNameLabel(conn, srName); + + if (srs.size() != 1) { + String msg = "There are " + srs.size() + " SRs with same name: " + srName; + + s_logger.warn(msg); + + return new CopyCmdAnswer(msg); + } else { + sr = srs.iterator().next(); + } + } + + String srUuid = sr.getUuid(conn); + String tmplUuid = copy_vhd_from_secondarystorage(conn, tmplPath, srUuid, wait); + VDI tmplVdi = getVDIbyUuid(conn, tmplUuid); + + final String uuidToReturn; + + if (managed) { + uuidToReturn = tmplUuid; + + tmplVdi.setNameLabel(conn, managedStoragePoolRootVolumeName); + } + else { + VDI snapshotVdi = tmplVdi.snapshot(conn, new HashMap()); + + uuidToReturn = snapshotVdi.getUuid(conn); + + snapshotVdi.setNameLabel(conn, "Template " + srcTemplateObjectTo.getName()); + + tmplVdi.destroy(conn); + } + + sr.scan(conn); + try { Thread.sleep(5000); } catch (Exception e) { } TemplateObjectTO newVol = new TemplateObjectTO(); - newVol.setUuid(snapshotvdi.getUuid(conn)); - newVol.setPath(newVol.getUuid()); + + newVol.setUuid(uuidToReturn); + newVol.setPath(uuidToReturn); newVol.setFormat(ImageFormat.VHD); + return new CopyCmdAnswer(newVol); } } catch (Exception e) { String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString(); + s_logger.warn(msg, e); + return new CopyCmdAnswer(msg); } + return new CopyCmdAnswer("not implemented yet"); } diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java index 438a392cac4..ec203d86ed5 100644 --- a/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java +++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java @@ -40,7 +40,7 @@ import com.cloud.agent.api.ClusterSyncAnswer; import com.cloud.agent.api.ClusterSyncCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.hypervisor.xen.resource.XenServer610Resource; -import com.cloud.utils.Ternary; +import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineName; @@ -148,13 +148,13 @@ public class XenServerResourceNewBase extends XenServer610Resource { return new Answer(cmd); } - HashMap> newStates = _listener.getChanges(); + HashMap> newStates = _listener.getChanges(); return new ClusterSyncAnswer(cmd.getClusterId(), newStates); } protected class VmEventListener extends Thread { boolean _stop = false; - HashMap> _changes = new HashMap>(); + HashMap> _changes = new HashMap>(); boolean _isMaster; Set _classes; String _token = ""; @@ -232,12 +232,12 @@ public class XenServerResourceNewBase extends XenServer610Resource { // NOTE: For now we only record change when the VM is stopped. We don't find out any VMs starting for now. synchronized (_cluster.intern()) { - Ternary oldState = s_vms.get(_cluster, vm); + Pair oldState = s_vms.get(_cluster, vm); if (oldState == null) { if (s_logger.isTraceEnabled()) { s_logger.trace("Unable to find " + vm + " from previous map. Assuming it was in Stopped state."); } - oldState = new Ternary(null, VirtualMachine.State.Stopped, null); + oldState = new Pair(null, VirtualMachine.State.Stopped); } if (s_logger.isTraceEnabled()) { @@ -281,7 +281,7 @@ public class XenServerResourceNewBase extends XenServer610Resource { } } if (reportChange) { - Ternary change = _changes.get(vm); + Pair change = _changes.get(vm); if (hostUuid == null) { // This is really strange code. It looks like the sync // code wants this to be set, which is extremely weird @@ -293,7 +293,7 @@ public class XenServerResourceNewBase extends XenServer610Resource { } } if (change == null) { - change = new Ternary(hostUuid, currentState, null); + change = new Pair(hostUuid, currentState); } else { change.first(hostUuid); change.second(currentState); @@ -325,13 +325,13 @@ public class XenServerResourceNewBase extends XenServer610Resource { return _isMaster; } - public HashMap> getChanges() { + public HashMap> getChanges() { synchronized (_cluster.intern()) { if (_changes.size() == 0) { return null; } - HashMap> diff = _changes; - _changes = new HashMap>(); + HashMap> diff = _changes; + _changes = new HashMap>(); return diff; } } diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java index e8217f7216f..975deec531f 100644 --- a/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java +++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/storage/motion/XenServerStorageMotionStrategy.java @@ -90,6 +90,11 @@ public class XenServerStorageMotionStrategy implements DataMotionStrategy { return StrategyPriority.CANT_HANDLE; } + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) { + throw new UnsupportedOperationException(); + } + @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { CopyCommandResult result = new CopyCommandResult(null, null); diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/api/command/CreateServiceInstanceCmd.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/api/command/CreateServiceInstanceCmd.java index 86e29497d64..d2cb4dee176 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/api/command/CreateServiceInstanceCmd.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/api/command/CreateServiceInstanceCmd.java @@ -186,7 +186,7 @@ public class CreateServiceInstanceCmd extends BaseAsyncCreateCmd { @Override public long getEntityOwnerId() { - Long accountId = finalyzeAccountId(accountName, domainId, projectId, true); + Long accountId = _accountService.finalyzeAccountId(accountName, domainId, projectId, true); if (accountId == null) { return CallContext.current().getCallingAccount().getId(); } diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java index 6e2d70db359..c0b9ee5a750 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/MockAccountManager.java @@ -373,7 +373,12 @@ public class MockAccountManager extends ManagerBase implements AccountManager { public void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException { // TODO Auto-generated method stub + } + @Override + public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) { + // TODO Auto-generated method stub + return null; } } diff --git a/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java b/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java index d6e1a01b49c..c2e26d52f65 100644 --- a/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java +++ b/plugins/storage/image/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackImageStoreDriverImpl.java @@ -93,12 +93,16 @@ public class CloudStackImageStoreDriverImpl extends BaseImageStoreDriverImpl { if (sslCfg != null) { _sslCopy = Boolean.parseBoolean(sslCfg); } + if(_sslCopy && (_ssvmUrlDomain == null || _ssvmUrlDomain.isEmpty())){ + s_logger.warn("Empty secondary storage url domain, ignoring SSL"); + _sslCopy = false; + } if (_sslCopy) { - hostname = ipAddress.replace(".", "-"); - if (_ssvmUrlDomain != null && _ssvmUrlDomain.length() > 0) { - hostname = hostname + "." + _ssvmUrlDomain; + if(_ssvmUrlDomain.startsWith("*")) { + hostname = ipAddress.replace(".", "-"); + hostname = hostname + _ssvmUrlDomain.substring(1); } else { - hostname = hostname + ".realhostip.com"; + hostname = _ssvmUrlDomain; } scheme = "https"; } diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java index 2dacafebb75..afdf97540e5 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapUserManager.java @@ -29,6 +29,8 @@ import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import org.apache.commons.lang.StringUtils; + public class LdapUserManager { @Inject @@ -185,6 +187,10 @@ public class LdapUserManager { controls.setSearchScope(_ldapConfiguration.getScope()); controls.setReturningAttributes(_ldapConfiguration.getReturnAttributes()); - return context.search(_ldapConfiguration.getBaseDn(), generateSearchFilter(username), controls); + String basedn = _ldapConfiguration.getBaseDn(); + if (StringUtils.isBlank(basedn)) { + throw new IllegalArgumentException("ldap basedn is not configured"); + } + return context.search(basedn, generateSearchFilter(username), controls); } } \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy index fa735d3f67e..9fbc81f6230 100644 --- a/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy +++ b/plugins/user-authenticators/ldap/test/groovy/org/apache/cloudstack/ldap/LdapUserManagerSpec.groovy @@ -21,10 +21,9 @@ import org.apache.cloudstack.ldap.LdapUserManager import spock.lang.Shared import javax.naming.NamingException -import javax.naming.NamingEnumeration import javax.naming.directory.Attribute import javax.naming.directory.Attributes -import javax.naming.directory.DirContext +import javax.naming.directory.InitialDirContext import javax.naming.directory.SearchControls import javax.naming.directory.SearchResult import javax.naming.ldap.LdapContext @@ -51,83 +50,83 @@ class LdapUserManagerSpec extends spock.lang.Specification { private def createGroupSearchContext() { - def umSearchResult = Mock(SearchResult) - umSearchResult.getName() >> principal; - umSearchResult.getAttributes() >> principal + def umSearchResult = Mock(SearchResult) + umSearchResult.getName() >> principal; + umSearchResult.getAttributes() >> principal - def uniqueMembers = new BasicNamingEnumerationImpl() - uniqueMembers.add(umSearchResult); - def attributes = Mock(Attributes) - def uniqueMemberAttribute = Mock(Attribute) - uniqueMemberAttribute.getId() >> "uniquemember" - uniqueMemberAttribute.getAll() >> uniqueMembers - attributes.get("uniquemember") >> uniqueMemberAttribute + def uniqueMembers = new BasicNamingEnumerationImpl() + uniqueMembers.add(umSearchResult); + def attributes = Mock(Attributes) + def uniqueMemberAttribute = Mock(Attribute) + uniqueMemberAttribute.getId() >> "uniquemember" + uniqueMemberAttribute.getAll() >> uniqueMembers + attributes.get("uniquemember") >> uniqueMemberAttribute - def groupSearchResult = Mock(SearchResult) - groupSearchResult.getName() >> principal; - groupSearchResult.getAttributes() >> attributes + def groupSearchResult = Mock(SearchResult) + groupSearchResult.getName() >> principal; + groupSearchResult.getAttributes() >> attributes - def searchGroupResults = new BasicNamingEnumerationImpl() - searchGroupResults.add(groupSearchResult); + def searchGroupResults = new BasicNamingEnumerationImpl() + searchGroupResults.add(groupSearchResult); - attributes = createUserAttributes(username, email, firstname, lastname) - SearchResult userSearchResult = createSearchResult(attributes) - def searchUsersResults = new BasicNamingEnumerationImpl() - searchUsersResults.add(userSearchResult); + attributes = createUserAttributes(username, email, firstname, lastname) + SearchResult userSearchResult = createSearchResult(attributes) + def searchUsersResults = new BasicNamingEnumerationImpl() + searchUsersResults.add(userSearchResult); - def context = Mock(LdapContext) - context.search(_, _, _) >>> [searchGroupResults, searchUsersResults]; + def context = Mock(LdapContext) + context.search(_, _, _) >>> [searchGroupResults, searchUsersResults]; - return context + return context } private def createContext() { - Attributes attributes = createUserAttributes(username, email, firstname, lastname) - SearchResult searchResults = createSearchResult(attributes) - def searchUsersResults = new BasicNamingEnumerationImpl() - searchUsersResults.add(searchResults); + Attributes attributes = createUserAttributes(username, email, firstname, lastname) + SearchResult searchResults = createSearchResult(attributes) + def searchUsersResults = new BasicNamingEnumerationImpl() + searchUsersResults.add(searchResults); - def context = Mock(LdapContext) - context.search(_, _, _) >> searchUsersResults; + def context = Mock(LdapContext) + context.search(_, _, _) >> searchUsersResults; - return context + return context } private SearchResult createSearchResult(attributes) { - def search = Mock(SearchResult) + def search = Mock(SearchResult) - search.getName() >> "cn=" + attributes.getAt("uid").get(); + search.getName() >> "cn=" + attributes.getAt("uid").get(); - search.getAttributes() >> attributes - search.getNameInNamespace() >> principal + search.getAttributes() >> attributes + search.getNameInNamespace() >> principal - return search + return search } private Attributes createUserAttributes(String username, String email, String firstname, String lastname) { - def attributes = Mock(Attributes) + def attributes = Mock(Attributes) - def nameAttribute = Mock(Attribute) - nameAttribute.getId() >> "uid" - nameAttribute.get() >> username - attributes.get("uid") >> nameAttribute + def nameAttribute = Mock(Attribute) + nameAttribute.getId() >> "uid" + nameAttribute.get() >> username + attributes.get("uid") >> nameAttribute - def mailAttribute = Mock(Attribute) - mailAttribute.getId() >> "mail" - mailAttribute.get() >> email - attributes.get("mail") >> mailAttribute + def mailAttribute = Mock(Attribute) + mailAttribute.getId() >> "mail" + mailAttribute.get() >> email + attributes.get("mail") >> mailAttribute - def givennameAttribute = Mock(Attribute) - givennameAttribute.getId() >> "givenname" - givennameAttribute.get() >> firstname - attributes.get("givenname") >> givennameAttribute + def givennameAttribute = Mock(Attribute) + givennameAttribute.getId() >> "givenname" + givennameAttribute.get() >> firstname + attributes.get("givenname") >> givennameAttribute - def snAttribute = Mock(Attribute) - snAttribute.getId() >> "sn" - snAttribute.get() >> lastname - attributes.get("sn") >> snAttribute + def snAttribute = Mock(Attribute) + snAttribute.getId() >> "sn" + snAttribute.get() >> lastname + attributes.get("sn") >> snAttribute - return attributes + return attributes } def setupSpec() { @@ -140,144 +139,158 @@ class LdapUserManagerSpec extends spock.lang.Specification { ldapConfiguration.getFirstnameAttribute() >> "givenname" ldapConfiguration.getLastnameAttribute() >> "sn" ldapConfiguration.getBaseDn() >> "dc=cloudstack,dc=org" - ldapConfiguration.getCommonNameAttribute() >> "cn" - ldapConfiguration.getGroupObject() >> "groupOfUniqueNames" - ldapConfiguration.getGroupUniqueMemeberAttribute() >> "uniquemember" + ldapConfiguration.getCommonNameAttribute() >> "cn" + ldapConfiguration.getGroupObject() >> "groupOfUniqueNames" + ldapConfiguration.getGroupUniqueMemeberAttribute() >> "uniquemember" username = "rmurphy" email = "rmurphy@test.com" firstname = "Ryan" lastname = "Murphy" - principal = "cn=" + username + "," + ldapConfiguration.getBaseDn() + principal = "cn=" + username + "," + ldapConfiguration.getBaseDn() } def "Test successfully creating an Ldap User from Search result"() { - given: "We have attributes, a search and a user manager" - def attributes = createUserAttributes(username, email, firstname, lastname) + given: "We have attributes, a search and a user manager" + def attributes = createUserAttributes(username, email, firstname, lastname) def search = createSearchResult(attributes) def userManager = new LdapUserManager(ldapConfiguration) def result = userManager.createUser(search) - expect: "The crated user the data supplied from LDAP" + expect: "The crated user the data supplied from LDAP" result.username == username result.email == email result.firstname == firstname result.lastname == lastname - result.principal == principal + result.principal == principal } def "Test successfully returning a list from get users"() { - given: "We have a LdapUserManager" + given: "We have a LdapUserManager" def userManager = new LdapUserManager(ldapConfiguration) - when: "A request for users is made" + when: "A request for users is made" def result = userManager.getUsers(username, createContext()) - then: "A list of users is returned" + then: "A list of users is returned" result.size() == 1 } def "Test successfully returning a list from get users when no username is given"() { - given: "We have a LdapUserManager" + given: "We have a LdapUserManager" def userManager = new LdapUserManager(ldapConfiguration) - when: "Get users is called without a username" + when: "Get users is called without a username" def result = userManager.getUsers(createContext()) - then: "All users are returned" - result.size() == 1 + then: "All users are returned" + result.size() == 1 } def "Test successfully returning a NamingEnumeration from searchUsers"() { - given: "We have a LdapUserManager" - def userManager = new LdapUserManager(ldapConfiguration) + given: "We have a LdapUserManager" + def userManager = new LdapUserManager(ldapConfiguration) - when: "We search for users" + when: "We search for users" def result = userManager.searchUsers(createContext()) - then: "A list of users are returned." + then: "A list of users are returned." result.next().getName() + "," + ldapConfiguration.getBaseDn() == principal } def "Test successfully returning an Ldap user from a get user request"() { - given: "We have a LdapUserMaanger" + given: "We have a LdapUserMaanger" - def userManager = new LdapUserManager(ldapConfiguration) + def userManager = new LdapUserManager(ldapConfiguration) - when: "A request for a user is made" - def result = userManager.getUser(username, createContext()) + when: "A request for a user is made" + def result = userManager.getUser(username, createContext()) - then: "The user is returned" - result.username == username - result.email == email - result.firstname == firstname - result.lastname == lastname - result.principal == principal + then: "The user is returned" + result.username == username + result.email == email + result.firstname == firstname + result.lastname == lastname + result.principal == principal } def "Test successfully throwing an exception when no users are found with getUser"() { - given: "We have a seachResult of users and a User Manager" + given: "We have a seachResult of users and a User Manager" - def searchUsersResults = new BasicNamingEnumerationImpl() + def searchUsersResults = new BasicNamingEnumerationImpl() - def context = Mock(LdapContext) - context.search(_, _, _) >> searchUsersResults; + def context = Mock(LdapContext) + context.search(_, _, _) >> searchUsersResults; - def userManager = new LdapUserManager(ldapConfiguration) + def userManager = new LdapUserManager(ldapConfiguration) - when: "a get user request is made and no user is found" - def result = userManager.getUser(username, context) + when: "a get user request is made and no user is found" + def result = userManager.getUser(username, context) - then: "An exception is thrown." - thrown NamingException + then: "An exception is thrown." + thrown NamingException } def "Test that a newly created Ldap User Manager is not null"() { - given: "You have created a new Ldap user manager object" - def result = new LdapUserManager(); - expect: "The result is not null" - result != null + given: "You have created a new Ldap user manager object" + def result = new LdapUserManager(); + expect: "The result is not null" + result != null } def "test successful generateGroupSearchFilter"() { - given: "ldap user manager and ldap config" - def ldapUserManager = new LdapUserManager(ldapConfiguration) - def groupName = varGroupName == null ? "*" : varGroupName - def expectedResult = "(&(objectClass=groupOfUniqueNames)(cn="+groupName+"))"; + given: "ldap user manager and ldap config" + def ldapUserManager = new LdapUserManager(ldapConfiguration) + def groupName = varGroupName == null ? "*" : varGroupName + def expectedResult = "(&(objectClass=groupOfUniqueNames)(cn=" + groupName + "))"; - def result = ldapUserManager.generateGroupSearchFilter(varGroupName) - expect: - result == expectedResult - where: "The group name passed is set to " - varGroupName << ["", null, "Murphy"] + def result = ldapUserManager.generateGroupSearchFilter(varGroupName) + expect: + result == expectedResult + where: "The group name passed is set to " + varGroupName << ["", null, "Murphy"] } - def "test successful getUsersInGroup"(){ - given: "ldap user manager and ldap config" - def ldapUserManager = new LdapUserManager(ldapConfiguration) + def "test successful getUsersInGroup"() { + given: "ldap user manager and ldap config" + def ldapUserManager = new LdapUserManager(ldapConfiguration) - when: "A request for users is made" - def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContext()) - then: "one user is returned" - result.size() == 1 + when: "A request for users is made" + def result = ldapUserManager.getUsersInGroup("engineering", createGroupSearchContext()) + then: "one user is returned" + result.size() == 1 } - def "test successful getUserForDn"(){ - given: "ldap user manager and ldap config" - def ldapUserManager = new LdapUserManager(ldapConfiguration) + def "test successful getUserForDn"() { + given: "ldap user manager and ldap config" + def ldapUserManager = new LdapUserManager(ldapConfiguration) - when: "A request for users is made" - def result = ldapUserManager.getUserForDn("cn=Ryan Murphy,ou=engineering,dc=cloudstack,dc=org",createContext()) - then: "A list of users is returned" - result != 1 - result.username == username - result.email == email - result.firstname == firstname - result.lastname == lastname - result.principal == principal + when: "A request for users is made" + def result = ldapUserManager.getUserForDn("cn=Ryan Murphy,ou=engineering,dc=cloudstack,dc=org", createContext()) + then: "A list of users is returned" + result != 1 + result.username == username + result.email == email + result.firstname == firstname + result.lastname == lastname + result.principal == principal } + + def "test searchUsers when ldap basedn in not set"() { + given: "ldap configuration where basedn is not set" + def ldapconfig = Mock(LdapConfiguration) + ldapconfig.getBaseDn() >> null + def ldapUserManager = new LdapUserManager(ldapconfig) + + when: "A request for search users is made" + def result = ldapUserManager.searchUsers(new InitialDirContext()) + + then: "An exception with no basedn defined is returned" + def e = thrown(IllegalArgumentException) + e.message == "ldap basedn is not configured" + } } diff --git a/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index b6720f5b639..3921484e54c 100755 --- a/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -25,7 +25,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@IPALOCATORLOG@ diff --git a/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index aa7e8cf8820..23ec8f3cd66 100755 --- a/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -25,7 +25,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in index 7aa456df51c..558f5a2ee98 100755 --- a/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/opensuse/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -32,7 +32,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in b/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in index aa7e8cf8820..23ec8f3cd66 100644 --- a/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in +++ b/python/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-ipallocator.in @@ -25,7 +25,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in index 7aa456df51c..558f5a2ee98 100755 --- a/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/sles/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -32,7 +32,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in b/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in index ec84962b669..e2cb36197a6 100755 --- a/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in +++ b/python/distro/ubuntu/SYSCONFDIR/init.d/cloud-ipallocator.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@AGENTLOG@ diff --git a/scripts/vm/hypervisor/xenserver/createipAlias.sh b/scripts/vm/hypervisor/xenserver/createipAlias.sh deleted file mode 100755 index 4ef6618794b..00000000000 --- a/scripts/vm/hypervisor/xenserver/createipAlias.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - - -usage() { - printf " %s routerip \n" $(basename $0) >&2 -} - -cert="/root/.ssh/id_rsa.cloud" -ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$1 "/root/createIpAlias.sh $2" diff --git a/scripts/vm/hypervisor/xenserver/deleteipAlias.sh b/scripts/vm/hypervisor/xenserver/deleteipAlias.sh deleted file mode 100644 index 7604172910a..00000000000 --- a/scripts/vm/hypervisor/xenserver/deleteipAlias.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. - -usage() { - printf " %s routerip \n" $(basename $0) >&2 -} - -cert="/root/.ssh/id_rsa.cloud" -ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$1 "/root/deleteIpAlias.sh $2 $3" diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops index 2f0f347c783..4174ef24f74 100755 --- a/scripts/vm/hypervisor/xenserver/vmops +++ b/scripts/vm/hypervisor/xenserver/vmops @@ -162,21 +162,6 @@ def pingtest(session, args): return txt -@echo -def savePassword(session, args): - sargs = args['args'] - cmd = sargs.split(' ') - cmd.insert(0, "/opt/cloud/bin/save_password_to_domr.sh") - cmd.insert(0, "/bin/bash") - try: - txt = util.pread2(cmd) - txt = 'success' - except: - logging.debug(" save password to domr failed " ) - txt = '' - - return txt - @echo def setLinkLocalIP(session, args): brName = args['brName'] @@ -1520,7 +1505,6 @@ if __name__ == "__main__": XenAPIPlugin.dispatch({"pingtest": pingtest, "setup_iscsi":setup_iscsi, "gethostvmstats": gethostvmstats, "getgateway": getgateway, "preparemigration": preparemigration, "setIptables": setIptables, "pingdomr": pingdomr, "pingxenserver": pingxenserver, - "savePassword": savePassword, "routerProxy": routerProxy, "createFile": createFile, "deleteFile": deleteFile, "network_rules":network_rules, diff --git a/scripts/vm/hypervisor/xenserver/xcposs/patch b/scripts/vm/hypervisor/xenserver/xcposs/patch index 2f902c23b44..08da883e546 100644 --- a/scripts/vm/hypervisor/xenserver/xcposs/patch +++ b/scripts/vm/hypervisor/xenserver/xcposs/patch @@ -39,13 +39,6 @@ setupxenserver.sh=..,0755,/opt/cloud/bin make_migratable.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -ipassoc.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -networkUsage.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin -l2tp_vpn.sh=../../../../network/domr/,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/cloud/bin copy_vhd_from_secondarystorage.sh=..,0755,/opt/cloud/bin @@ -60,12 +53,7 @@ create_privatetemplate_from_snapshot.sh=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -getRouterStatus.sh=../../../../network/domr/,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin -getDomRVersion.sh=../../../../network/domr/,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin ###add cloudstack plugin script for XCP cloudstack_plugins.conf=..,0644,/etc/xensource diff --git a/scripts/vm/hypervisor/xenserver/xcpserver/patch b/scripts/vm/hypervisor/xenserver/xcpserver/patch index 01cd73b605c..2376424ff57 100644 --- a/scripts/vm/hypervisor/xenserver/xcpserver/patch +++ b/scripts/vm/hypervisor/xenserver/xcpserver/patch @@ -39,13 +39,7 @@ setupxenserver.sh=..,0755,/opt/cloud/bin make_migratable.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/cloud/bin copy_vhd_from_secondarystorage.sh=..,0755,/opt/cloud/bin @@ -60,9 +54,6 @@ create_privatetemplate_from_snapshot.sh=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -getRouterStatus.sh=../../../../network/domr/,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin -getDomRVersion.sh=../../../../network/domr/,0755,/opt/cloud/bin add_to_vcpus_params_live.sh=..,0755,/opt/cloud/bin ###add cloudstack plugin script for XCP diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch index 9473bca8d5c..16dcb5746a1 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch @@ -39,12 +39,6 @@ make_migratable.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/cloud/bin copy_vhd_from_secondarystorage.sh=..,0755,/opt/cloud/bin @@ -61,7 +55,6 @@ create_privatetemplate_from_snapshot.sh=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin swift=..,0755,/opt/cloud/bin swiftxen=..,0755,/etc/xapi.d/plugins s3xen=..,0755,/etc/xapi.d/plugins diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index c91aa73821a..11bda07369e 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -38,12 +38,6 @@ setupxenserver.sh=..,0755,/opt/cloud/bin make_migratable.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/cloud/bin @@ -60,7 +54,6 @@ create_privatetemplate_from_snapshot.sh=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin swift=..,0755,/opt/cloud/bin swiftxen=..,0755,/etc/xapi.d/plugins s3xen=..,0755,/etc/xapi.d/plugins diff --git a/scripts/vm/hypervisor/xenserver/xenserver60/patch b/scripts/vm/hypervisor/xenserver/xenserver60/patch index 5a648e0990f..662327b6a88 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver60/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver60/patch @@ -40,14 +40,8 @@ id_rsa.cloud=../../../systemvm,0600,/root/.ssh network_info.sh=..,0755,/opt/cloud/bin setupxenserver.sh=..,0755,/opt/cloud/bin make_migratable.sh=..,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin copy_vhd_to_secondarystorage.sh=..,0755,/opt/cloud/bin @@ -64,7 +58,6 @@ create_privatetemplate_from_snapshot.sh=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin swift=..,0755,/opt/cloud/bin swiftxen=..,0755,/etc/xapi.d/plugins s3xen=..,0755,/etc/xapi.d/plugins diff --git a/scripts/vm/hypervisor/xenserver/xenserver62/patch b/scripts/vm/hypervisor/xenserver/xenserver62/patch index 70b86b4ffe5..05c619b70cb 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver62/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver62/patch @@ -40,14 +40,8 @@ id_rsa.cloud=../../../systemvm,0600,/root/.ssh network_info.sh=..,0755,/opt/cloud/bin setupxenserver.sh=..,0755,/opt/cloud/bin make_migratable.sh=..,0755,/opt/cloud/bin -createipAlias.sh=..,0755,/opt/cloud/bin -deleteipAlias.sh=..,0755,/opt/cloud/bin setup_iscsi.sh=..,0755,/opt/cloud/bin pingtest.sh=../../..,0755,/opt/cloud/bin -dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin -save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin -call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin cloud-setup-bonding.sh=..,0755,/opt/cloud/bin kill_copy_process.sh=..,0755,/opt/cloud/bin @@ -60,7 +54,6 @@ vhd-util=..,0755,/opt/cloud/bin upgrade_snapshot.sh=..,0755,/opt/cloud/bin cloud-clean-vlan.sh=..,0755,/opt/cloud/bin cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin -bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin swift=..,0755,/opt/cloud/bin swiftxen=..,0755,/etc/xapi.d/plugins s3xen=..,0755,/etc/xapi.d/plugins diff --git a/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml b/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml index fd2f5fbb3fd..91401e33549 100644 --- a/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml +++ b/server/resources/META-INF/cloudstack/core/spring-server-core-misc-context.xml @@ -50,6 +50,18 @@ + + + + + + + + + + + + diff --git a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java index b77f8acce4d..8abb5cd316f 100755 --- a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java @@ -26,11 +26,10 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; - import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.capacity.CapacityManager; import com.cloud.dc.ClusterDetailsDao; @@ -38,6 +37,7 @@ import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.gpu.GPU; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.Host.Type; @@ -47,7 +47,9 @@ import com.cloud.host.dao.HostDetailsDao; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.resource.ResourceManager; +import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.VMTemplateVO; @@ -99,6 +101,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { ClusterDao _clusterDao; @Inject ClusterDetailsDao _clusterDetailsDao; + @Inject + ServiceOfferingDetailsDao _serviceOfferingDetailsDao; float _factor = 1; boolean _checkHvm = true; protected String _allocationAlgorithm = "random"; @@ -264,7 +268,9 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { s_logger.debug("Looking for speed=" + (offering.getCpu() * offering.getSpeed()) + "Mhz, Ram=" + offering.getRamSize()); } + long serviceOfferingId = offering.getId(); List suitableHosts = new ArrayList(); + ServiceOfferingDetailsVO offeringDetails = null; for (Host host : hosts) { if (suitableHosts.size() == returnUpTo) { @@ -286,6 +292,13 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { continue; } + // Check if GPU device is required by offering and host has the availability + if ((offeringDetails = _serviceOfferingDetailsDao.findDetail(serviceOfferingId, GPU.Keys.vgpuType.toString())) != null + && !_resourceMgr.isGPUDeviceAvailable(host.getId(), offeringDetails.getValue())){ + s_logger.info("Host name: " + host.getName() + ", hostId: "+ host.getId() +" does not have required GPU devices available"); + continue; + } + int cpu_requested = offering.getCpu() * offering.getSpeed(); long ram_requested = offering.getRamSize() * 1024L * 1024L; Cluster cluster = _clusterDao.findById(host.getClusterId()); diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 859ca0198f5..432eb941ab1 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -131,6 +131,10 @@ import com.cloud.domain.dao.DomainDao; import com.cloud.event.Event; import com.cloud.event.dao.EventJoinDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.gpu.dao.HostGpuGroupsDao; +import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.Host; import com.cloud.host.HostStats; @@ -221,8 +225,10 @@ import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.StatsCollector; import com.cloud.server.TaggedResourceService; +import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSCategoryVO; @@ -325,6 +331,7 @@ public class ApiDBUtils { static NetworkRuleConfigDao s_networkRuleConfigDao; static HostPodDao s_podDao; static ServiceOfferingDao s_serviceOfferingDao; + static ServiceOfferingDetailsDao s_serviceOfferingDetailsDao; static SnapshotDao s_snapshotDao; static PrimaryDataStoreDao s_storagePoolDao; static VMTemplateDao s_templateDao; @@ -400,6 +407,8 @@ public class ApiDBUtils { static NetworkACLDao s_networkACLDao; static AccountService s_accountService; static ResourceMetaDataService s_resourceDetailsService; + static HostGpuGroupsDao s_hostGpuGroupsDao; + static VGPUTypesDao s_vgpuTypesDao; @Inject private ManagementServer ms; @@ -467,6 +476,8 @@ public class ApiDBUtils { @Inject private ServiceOfferingDao serviceOfferingDao; @Inject + private ServiceOfferingDetailsDao serviceOfferingDetailsDao; + @Inject private SnapshotDao snapshotDao; @Inject private PrimaryDataStoreDao storagePoolDao; @@ -616,6 +627,10 @@ public class ApiDBUtils { private ConfigurationManager configMgr; @Inject private ResourceMetaDataService resourceDetailsService; + @Inject + private HostGpuGroupsDao hostGpuGroupsDao; + @Inject + private VGPUTypesDao vgpuTypesDao; @PostConstruct void init() { @@ -649,6 +664,7 @@ public class ApiDBUtils { s_networkRuleConfigDao = networkRuleConfigDao; s_podDao = podDao; s_serviceOfferingDao = serviceOfferingDao; + s_serviceOfferingDetailsDao = serviceOfferingDetailsDao; s_serviceOfferingJoinDao = serviceOfferingJoinDao; s_snapshotDao = snapshotDao; s_storagePoolDao = storagePoolDao; @@ -727,6 +743,8 @@ public class ApiDBUtils { s_networkACLDao = networkACLDao; s_accountService = accountService; s_resourceDetailsService = resourceDetailsService; + s_hostGpuGroupsDao = hostGpuGroupsDao; + s_vgpuTypesDao = vgpuTypesDao; } // /////////////////////////////////////////////////////////// @@ -956,6 +974,10 @@ public class ApiDBUtils { return s_serviceOfferingDao.findByIdIncludingRemoved(serviceOfferingId); } + public static ServiceOfferingDetailsVO findServiceOfferingDetail(long serviceOfferingId, String key) { + return s_serviceOfferingDetailsDao.findDetail(serviceOfferingId, key); + } + public static Snapshot findSnapshotById(long snapshotId) { SnapshotVO snapshot = s_snapshotDao.findById(snapshotId); if (snapshot != null && snapshot.getRemoved() == null && snapshot.getState() == Snapshot.State.BackedUp) { @@ -1053,6 +1075,14 @@ public class ApiDBUtils { return type; } + public static List getGpuGroups(long hostId) { + return s_hostGpuGroupsDao.listByHostId(hostId); + } + + public static List getVgpus(long groupId) { + return s_vgpuTypesDao.listByGroupId(groupId); + } + public static List listUserStatsBy(Long accountId) { return s_userStatsDao.listBy(accountId); } diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 4bc6578465d..95074e24bb0 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -16,95 +16,71 @@ // under the License. package com.cloud.api; -import static org.apache.commons.lang.StringUtils.isNotBlank; - -import java.lang.reflect.Field; -import java.text.DateFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.StringTokenizer; -import java.util.regex.Matcher; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.InfrastructureEntity; -import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.BaseAsyncCustomIdCmd; import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.BaseCmd.CommandType; import org.apache.cloudstack.api.BaseCustomIdCmd; -import org.apache.cloudstack.api.BaseListCmd; -import org.apache.cloudstack.api.EntityReference; -import org.apache.cloudstack.api.InternalIdentity; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; -import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; -import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; -import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd; -import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.log4j.Logger; -import com.cloud.exception.InvalidParameterValueException; +import com.cloud.api.dispatch.DispatchChain; +import com.cloud.api.dispatch.DispatchChainFactory; +import com.cloud.api.dispatch.DispatchTask; +import com.cloud.event.EventTypes; import com.cloud.user.Account; import com.cloud.user.AccountManager; -import com.cloud.utils.DateUtil; import com.cloud.utils.ReflectUtil; -import com.cloud.utils.db.EntityManager; -import com.cloud.utils.exception.CSExceptionErrorCode; -import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; public class ApiDispatcher { private static final Logger s_logger = Logger.getLogger(ApiDispatcher.class.getName()); Long _createSnapshotQueueSizeLimit; - @Inject - AsyncJobManager _asyncMgr = null; - @Inject - AccountManager _accountMgr = null; - @Inject - EntityManager _entityMgr = null; - private static ApiDispatcher s_instance; + @Inject + AsyncJobManager _asyncMgr; - public static ApiDispatcher getInstance() { - return s_instance; - } + @Inject + AccountManager _accountMgr; + + @Inject() + protected DispatchChainFactory dispatchChainFactory; + + protected DispatchChain standardDispatchChain; + + protected DispatchChain asyncCreationDispatchChain; public ApiDispatcher() { } @PostConstruct - void init() { - s_instance = this; + public void setup() { + standardDispatchChain = dispatchChainFactory.getStandardDispatchChain(); + asyncCreationDispatchChain = dispatchChainFactory.getAsyncCreationDispatchChain(); } - public void setCreateSnapshotQueueSizeLimit(Long snapshotLimit) { + public void setCreateSnapshotQueueSizeLimit(final Long snapshotLimit) { _createSnapshotQueueSizeLimit = snapshotLimit; } - public void dispatchCreateCmd(BaseAsyncCreateCmd cmd, Map params) throws Exception { - processParameters(cmd, params); - CallContext.current().setEventDisplayEnabled(cmd.isDisplayResourceEnabled()); - cmd.create(); + public void dispatchCreateCmd(final BaseAsyncCreateCmd cmd, final Map params) throws Exception { + asyncCreationDispatchChain.dispatch(new DispatchTask(cmd, params)); + CallContext.current().setEventDisplayEnabled(cmd.isDisplayResourceEnabled()); } private void doAccessChecks(BaseCmd cmd, Map entitiesToAccess) { @@ -124,17 +100,31 @@ public class ApiDispatcher { } } - public void dispatch(BaseCmd cmd, Map params, boolean execute) throws Exception { - processParameters(cmd, params); - CallContext ctx = CallContext.current(); + public void dispatch(final BaseCmd cmd, final Map params, final boolean execute) throws Exception { + // Let the chain of responsibility dispatch gradually + standardDispatchChain.dispatch(new DispatchTask(cmd, params)); + + final CallContext ctx = CallContext.current(); ctx.setEventDisplayEnabled(cmd.isDisplayResourceEnabled()); + // TODO This if shouldn't be here. Use polymorphism and move it to validateSpecificParameters if (cmd instanceof BaseAsyncCmd) { - BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmd; - String startEventId = params.get("ctxStartEventId"); + final BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmd; + final String startEventId = params.get(ApiConstants.CTX_START_EVENT_ID); + String uuid = params.get(ApiConstants.UUID); ctx.setStartEventId(Long.valueOf(startEventId)); + // Fow now use the key from EventTypes.java rather than getInstanceType bcz the later doesn't refer to the interfaces + // Add the resource id in the call context, also add some other first class object ids (for now vm) if available. + // TODO - this should be done for all the uuids passed in the cmd - so should be moved where uuid to id conversion happens. + if(EventTypes.getEntityForEvent(asyncCmd.getEventType()) != null){ + ctx.putContextParameter(EventTypes.getEntityForEvent(asyncCmd.getEventType()), uuid); + } + if(params.get(ApiConstants.VIRTUAL_MACHINE_ID) != null){ + ctx.putContextParameter(ReflectUtil.getEntityName(VirtualMachine.class), params.get(ApiConstants.VIRTUAL_MACHINE_ID)); + } + // Synchronise job on the object if needed if (asyncCmd.getJob() != null && asyncCmd.getSyncObjId() != null && asyncCmd.getSyncObjType() != null) { Long queueSizeLimit = null; @@ -156,6 +146,7 @@ public class ApiDispatcher { } } + // TODO This if shouldn't be here. Use polymorphism and move it to validateSpecificParameters if (cmd instanceof BaseAsyncCustomIdCmd) { ((BaseAsyncCustomIdCmd)cmd).checkUuid(); } else if (cmd instanceof BaseCustomIdCmd) { @@ -163,392 +154,6 @@ public class ApiDispatcher { } cmd.execute(); - - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - public static void processParameters(BaseCmd cmd, Map params) { - Map entitiesToAccess = new HashMap(); - Map unpackedParams = cmd.unpackParams(params); - - if (cmd instanceof BaseListCmd) { - Object pageSizeObj = unpackedParams.get(ApiConstants.PAGE_SIZE); - Long pageSize = null; - if (pageSizeObj != null) { - pageSize = Long.valueOf((String)pageSizeObj); - } - - if ((unpackedParams.get(ApiConstants.PAGE) == null) && (pageSize != null && !pageSize.equals(BaseListCmd.s_pageSizeUnlimited))) { - ServerApiException ex = new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"page\" parameter is required when \"pagesize\" is specified"); - ex.setCSErrorCode(CSExceptionErrorCode.getCSErrCode(ex.getClass().getName())); - throw ex; - } else if (pageSize == null && (unpackedParams.get(ApiConstants.PAGE) != null)) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "\"pagesize\" parameter is required when \"page\" is specified"); - } - } - - List fields = ReflectUtil.getAllFieldsForClass(cmd.getClass(), BaseCmd.class); - - for (Field field : fields) { - Parameter parameterAnnotation = field.getAnnotation(Parameter.class); - if ((parameterAnnotation == null) || !parameterAnnotation.expose()) { - continue; - } - - //TODO: Annotate @Validate on API Cmd classes, FIXME how to process Validate - RoleType[] allowedRoles = parameterAnnotation.authorized(); - if (allowedRoles.length > 0) { - boolean permittedParameter = false; - Account caller = CallContext.current().getCallingAccount(); - for (RoleType allowedRole : allowedRoles) { - if (allowedRole.getValue() == caller.getType()) { - permittedParameter = true; - break; - } - } - if (!permittedParameter) { - s_logger.debug("Ignoring paremeter " + parameterAnnotation.name() + " as the caller is not authorized to pass it in"); - continue; - } - } - - Object paramObj = unpackedParams.get(parameterAnnotation.name()); - if (paramObj == null) { - if (parameterAnnotation.required()) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + - cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to missing parameter " + parameterAnnotation.name()); - } - continue; - } - - // marshall the parameter into the correct type and set the field value - try { - setFieldValue(field, cmd, paramObj, parameterAnnotation); - } catch (IllegalArgumentException argEx) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " + - parameterAnnotation.name()); - } - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + - cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value " + paramObj + " for parameter " + - parameterAnnotation.name()); - } catch (ParseException parseEx) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)); - } - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " + - cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + ", please pass dates in the format mentioned in the api documentation"); - } catch (InvalidParameterValueException invEx) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + - cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage()); - } catch (CloudRuntimeException cloudEx) { - s_logger.error("CloudRuntimeException", cloudEx); - // FIXME: Better error message? This only happens if the API command is not executable, which typically - //means - // there was - // and IllegalAccessException setting one of the parameters. - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Internal error executing API command " + - cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)); - } - - //check access on the resource this field points to - try { - ACL checkAccess = field.getAnnotation(ACL.class); - CommandType fieldType = parameterAnnotation.type(); - - if (checkAccess != null) { - // Verify that caller can perform actions in behalf of vm owner - //acumulate all Controlled Entities together. - - //parse the array of resource types and in case of map check access on key or value or both as specified in @acl - //implement external dao for classes that need findByName - //for maps, specify access to be checkd on key or value. - - // find the controlled entity DBid by uuid - if (parameterAnnotation.entityType() != null) { - Class[] entityList = parameterAnnotation.entityType()[0].getAnnotation(EntityReference.class).value(); - - for (Class entity : entityList) { - // Check if the parameter type is a single - // Id or list of id's/name's - switch (fieldType) { - case LIST: - CommandType listType = parameterAnnotation.collectionType(); - switch (listType) { - case LONG: - case UUID: - List listParam = (List)field.get(cmd); - for (Long entityId : listParam) { - Object entityObj = s_instance._entityMgr.findById(entity, entityId); - entitiesToAccess.put(entityObj, checkAccess.accessType()); - } - break; - /* - * case STRING: List listParam = - * new ArrayList(); listParam = - * (List)field.get(cmd); for(String - * entityName: listParam){ - * ControlledEntity entityObj = - * (ControlledEntity - * )daoClassInstance(entityId); - * entitiesToAccess.add(entityObj); } - * break; - */ - default: - break; - } - break; - case LONG: - case UUID: - Object entityObj = s_instance._entityMgr.findById(entity, (Long)field.get(cmd)); - entitiesToAccess.put(entityObj, checkAccess.accessType()); - break; - default: - break; } - if (ControlledEntity.class.isAssignableFrom(entity)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("ControlledEntity name is:" + entity.getName()); - } - } - - if (InfrastructureEntity.class.isAssignableFrom(entity)) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("InfrastructureEntity name is:" + entity.getName()); - } - } - } - - } - - } - - } catch (IllegalArgumentException e) { - s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + - " is not accessible]"); - } catch (IllegalAccessException e) { - s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + - " is not accessible]"); - } - - } - - //check access on the entities. - getInstance().doAccessChecks(cmd, entitiesToAccess); - - } - - private static Long translateUuidToInternalId(String uuid, Parameter annotation) { - if (uuid.equals("-1")) { - // FIXME: This is to handle a lot of hardcoded special cases where -1 is sent - // APITODO: Find and get rid of all hardcoded params in API Cmds and service layer - return -1L; - } - Long internalId = null; - // If annotation's empty, the cmd existed before 3.x try conversion to long - boolean isPre3x = annotation.since().isEmpty(); - // Match against Java's UUID regex to check if input is uuid string - boolean isUuid = uuid.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); - // Enforce that it's uuid for newly added apis from version 3.x - if (!isPre3x && !isUuid) - return null; - // Allow both uuid and internal id for pre3x apis - if (isPre3x && !isUuid) { - try { - internalId = Long.parseLong(uuid); - } catch (NumberFormatException e) { - internalId = null; - } - if (internalId != null) - return internalId; - } - // There may be multiple entities defined on the @EntityReference of a Response.class - // UUID CommandType would expect only one entityType, so use the first entityType - Class[] entities = annotation.entityType()[0].getAnnotation(EntityReference.class).value(); - // Go through each entity which is an interface to a VO class and get a VO object - // Try to getId() for the object using reflection, break on first non-null value - for (Class entity : entities) { - // For backward compatibility, we search within removed entities and let service layer deal - // with removed ones, return empty response or error - Object objVO = s_instance._entityMgr.findByUuidIncludingRemoved(entity, uuid); - if (objVO == null) { - continue; - } - // Invoke the getId method, get the internal long ID - // If that fails hide exceptions as the uuid may not exist - try { - internalId = ((InternalIdentity)objVO).getId(); - } catch (IllegalArgumentException e) { - } catch (NullPointerException e) { - } - // Return on first non-null Id for the uuid entity - if (internalId != null) - break; - } - if (internalId == null) { - if (s_logger.isDebugEnabled()) - s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database."); - throw new InvalidParameterValueException("Invalid parameter " + annotation.name() + " value=" + uuid + - " due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class."); - } - return internalId; - } - - @SuppressWarnings({"unchecked", "rawtypes"}) - private static void setFieldValue(Field field, BaseCmd cmdObj, Object paramObj, Parameter annotation) throws IllegalArgumentException, ParseException { - try { - field.setAccessible(true); - CommandType fieldType = annotation.type(); - switch (fieldType) { - case BOOLEAN: - field.set(cmdObj, Boolean.valueOf(paramObj.toString())); - break; - case DATE: - // This piece of code is for maintaining backward compatibility - // and support both the date formats(Bug 9724) - // Do the date messaging for ListEventsCmd only - if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd || cmdObj instanceof ArchiveEventsCmd || - cmdObj instanceof ArchiveAlertsCmd || cmdObj instanceof DeleteAlertsCmd) { - boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString()); - if (isObjInNewDateFormat) { - DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT; - synchronized (newFormat) { - field.set(cmdObj, newFormat.parse(paramObj.toString())); - } - } else { - DateFormat format = BaseCmd.INPUT_FORMAT; - synchronized (format) { - Date date = format.parse(paramObj.toString()); - if (field.getName().equals("startDate")) { - date = messageDate(date, 0, 0, 0); - } else if (field.getName().equals("endDate")) { - date = messageDate(date, 23, 59, 59); - } - field.set(cmdObj, date); - } - } - } else { - DateFormat format = BaseCmd.INPUT_FORMAT; - synchronized (format) { - format.setLenient(false); - field.set(cmdObj, format.parse(paramObj.toString())); - } - } - break; - case FLOAT: - // Assuming that the parameters have been checked for required before now, - // we ignore blank or null values and defer to the command to set a default - // value for optional parameters ... - if (paramObj != null && isNotBlank(paramObj.toString())) { - field.set(cmdObj, Float.valueOf(paramObj.toString())); - } - break; - case INTEGER: - // Assuming that the parameters have been checked for required before now, - // we ignore blank or null values and defer to the command to set a default - // value for optional parameters ... - if (paramObj != null && isNotBlank(paramObj.toString())) { - field.set(cmdObj, Integer.valueOf(paramObj.toString())); - } - break; - case LIST: - List listParam = new ArrayList(); - StringTokenizer st = new StringTokenizer(paramObj.toString(), ","); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - CommandType listType = annotation.collectionType(); - switch (listType) { - case INTEGER: - listParam.add(Integer.valueOf(token)); - break; - case UUID: - if (token.isEmpty()) - break; - Long internalId = translateUuidToInternalId(token, annotation); - listParam.add(internalId); - break; - case LONG: { - listParam.add(Long.valueOf(token)); - } - break; - case SHORT: - listParam.add(Short.valueOf(token)); - case STRING: - listParam.add(token); - break; - } - } - field.set(cmdObj, listParam); - break; - case UUID: - if (paramObj.toString().isEmpty()) - break; - Long internalId = translateUuidToInternalId(paramObj.toString(), annotation); - field.set(cmdObj, internalId); - break; - case LONG: - field.set(cmdObj, Long.valueOf(paramObj.toString())); - break; - case SHORT: - field.set(cmdObj, Short.valueOf(paramObj.toString())); - break; - case STRING: - if ((paramObj != null) && paramObj.toString().length() > annotation.length()) { - s_logger.error("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName()); - throw new InvalidParameterValueException("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName()); - } - field.set(cmdObj, paramObj.toString()); - break; - case TZDATE: - field.set(cmdObj, DateUtil.parseTZDateString(paramObj.toString())); - break; - case MAP: - default: - field.set(cmdObj, paramObj); - break; - } - } catch (IllegalAccessException ex) { - s_logger.error("Error initializing command " + cmdObj.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error initializing parameters for command " + cmdObj.getCommandName() + " [field " + field.getName() + - " is not accessible]"); - } - } - - private static boolean isObjInNewDateFormat(String string) { - Matcher matcher = BaseCmd.newInputDateFormat.matcher(string); - return matcher.matches(); - } - - private static Date messageDate(Date date, int hourOfDay, int minute, int second) { - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - cal.set(Calendar.HOUR_OF_DAY, hourOfDay); - cal.set(Calendar.MINUTE, minute); - cal.set(Calendar.SECOND, second); - return cal.getTime(); - } - - public static void plugService(Field field, BaseCmd cmd) { - - Class fc = field.getType(); - Object instance = null; - - if (instance == null) { - throw new CloudRuntimeException("Unable to plug service " + fc.getSimpleName() + " in command " + cmd.getClass().getSimpleName()); - } - - try { - field.setAccessible(true); - field.set(cmd, instance); - } catch (IllegalArgumentException e) { - s_logger.error("IllegalArgumentException at plugService for command " + cmd.getCommandName() + ", field " + field.getName()); - throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [Illegal argumet at field " + field.getName() + "]"); - } catch (IllegalAccessException e) { - s_logger.error("Error at plugService for command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); - throw new CloudRuntimeException("Internal error at plugService for command " + cmd.getCommandName() + " [field " + field.getName() + " is not accessible]"); - } - } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 4b5550cd39c..f5009097785 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -30,6 +30,8 @@ import java.util.TimeZone; import javax.inject.Inject; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -65,6 +67,7 @@ import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; import org.apache.cloudstack.api.response.GuestOSResponse; +import org.apache.cloudstack.api.response.GuestOsMappingResponse; import org.apache.cloudstack.api.response.GuestVlanRangeResponse; import org.apache.cloudstack.api.response.HostForMigrationResponse; import org.apache.cloudstack.api.response.HostResponse; @@ -146,7 +149,6 @@ import org.apache.cloudstack.region.Region; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageTypes; -import org.apache.log4j.Logger; import com.cloud.api.query.ViewResponseHelper; import com.cloud.api.query.vo.AccountJoinVO; @@ -264,6 +266,7 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSCategoryVO; +import com.cloud.storage.GuestOSHypervisor; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; @@ -1204,8 +1207,11 @@ public class ApiResponseHelper implements ResponseGenerator { NetworkOffering networkOffering = ApiDBUtils.findNetworkOfferingById(network.getNetworkOfferingId()); if (networkOffering.getElasticIp()) { IpAddress ip = ApiDBUtils.findIpByAssociatedVmId(vm.getId()); + Vlan vlan = ApiDBUtils.findVlanById(ip.getVlanId()); if (ip != null) { vmResponse.setPublicIp(ip.getAddress().addr()); + vmResponse.setPublicNetmask(vlan.getVlanNetmask()); + vmResponse.setGateway(vlan.getVlanGateway()); } } else { vmResponse.setPublicIp(singleNicProfile.getIp4Address()); @@ -3144,6 +3150,23 @@ public class ApiResponseHelper implements ResponseGenerator { return response; } + @Override + public GuestOsMappingResponse createGuestOSMappingResponse(GuestOSHypervisor guestOSHypervisor) { + GuestOsMappingResponse response = new GuestOsMappingResponse(); + response.setId(guestOSHypervisor.getUuid()); + response.setHypervisor(guestOSHypervisor.getHypervisorType()); + response.setHypervisorVersion(guestOSHypervisor.getHypervisorVersion()); + response.setOsNameForHypervisor((guestOSHypervisor.getGuestOsName())); + GuestOS guestOs = ApiDBUtils.findGuestOSById(guestOSHypervisor.getGuestOsId()); + if (guestOs != null) { + response.setOsStdName(guestOs.getDisplayName()); + response.setOsTypeId(guestOs.getUuid()); + } + + response.setObjectName("guestosmapping"); + return response; + } + @Override public SnapshotScheduleResponse createSnapshotScheduleResponse(SnapshotSchedule snapshotSchedule) { SnapshotScheduleResponse response = new SnapshotScheduleResponse(); @@ -3707,4 +3730,5 @@ public class ApiResponseHelper implements ResponseGenerator { response.setResponses(responses); return response; } + } diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java index 5ab65f4e0a4..e4486c1fee7 100755 --- a/server/src/com/cloud/api/ApiServer.java +++ b/server/src/com/cloud/api/ApiServer.java @@ -86,6 +86,7 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.acl.APIChecker; import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.BaseAsyncCreateCmd; @@ -123,11 +124,14 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import com.cloud.api.dispatch.DispatchChainFactory; +import com.cloud.api.dispatch.DispatchTask; import com.cloud.api.response.ApiResponseSerializer; import com.cloud.configuration.Config; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.event.ActionEventUtils; +import com.cloud.event.EventTypes; import com.cloud.exception.AccountLimitException; import com.cloud.exception.CloudAuthenticationException; import com.cloud.exception.InsufficientCapacityException; @@ -144,6 +148,7 @@ import com.cloud.user.UserAccount; import com.cloud.user.UserVO; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.ReflectUtil; import com.cloud.utils.StringUtils; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; @@ -154,17 +159,26 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionProxyObject; +import com.cloud.vm.VirtualMachine; @Component public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiServerService { + private static final String UTF_8 = "UTF-8"; private static final Logger s_logger = Logger.getLogger(ApiServer.class.getName()); private static final Logger s_accessLogger = Logger.getLogger("apiserver." + ApiServer.class.getName()); - private static boolean encodeApiResponse = false; - private static String jsonContentType = "text/javascript"; - private static String controlCharacters = "[\000-\011\013-\014\016-\037\177]"; // Non-printable ASCII characters - numbers 0 to 31 and 127 decimal - @Inject ApiDispatcher _dispatcher; + public static boolean encodeApiResponse = false; + public static String jsonContentType = "text/javascript"; + /** + * Non-printable ASCII characters - numbers 0 to 31 and 127 decimal + */ + public static final String CONTROL_CHARACTERS = "[\000-\011\013-\014\016-\037\177]"; + + @Inject + protected ApiDispatcher _dispatcher; + @Inject + protected DispatchChainFactory dispatchChainFactory; @Inject private AccountManager _accountMgr; @Inject @@ -193,27 +207,27 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Override - public boolean configure(String name, Map params) throws ConfigurationException { + public boolean configure(final String name, final Map params) throws ConfigurationException { return true; } @Override public boolean start() { Integer apiPort = null; // api port, null by default - SearchCriteria sc = _configDao.createSearchCriteria(); + final SearchCriteria sc = _configDao.createSearchCriteria(); sc.addAnd("name", SearchCriteria.Op.EQ, Config.IntegrationAPIPort.key()); - List values = _configDao.search(sc, null); + final List values = _configDao.search(sc, null); if ((values != null) && (values.size() > 0)) { - ConfigurationVO apiPortConfig = values.get(0); + final ConfigurationVO apiPortConfig = values.get(0); if (apiPortConfig.getValue() != null) { apiPort = Integer.parseInt(apiPortConfig.getValue()); } } - Map configs = _configDao.getConfiguration(); - String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key()); + final Map configs = _configDao.getConfiguration(); + final String strSnapshotLimit = configs.get(Config.ConcurrentSnapshotsThresholdPerHost.key()); if (strSnapshotLimit != null) { - Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L); + final Long snapshotLimit = NumbersUtil.parseLong(strSnapshotLimit, 1L); if (snapshotLimit.longValue() <= 0) { s_logger.debug("Global config parameter " + Config.ConcurrentSnapshotsThresholdPerHost.toString() + " is less or equal 0; defaulting to unlimited"); } else { @@ -221,19 +235,20 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } - Set> cmdClasses = new HashSet>(); - for (PluggableService pluggableService : _pluggableServices) { + final Set> cmdClasses = new HashSet>(); + for (final PluggableService pluggableService : _pluggableServices) { cmdClasses.addAll(pluggableService.getCommands()); if (s_logger.isDebugEnabled()) { s_logger.debug("Discovered plugin " + pluggableService.getClass().getSimpleName()); } } - for (Class cmdClass : cmdClasses) { - APICommand at = cmdClass.getAnnotation(APICommand.class); + for (final Class cmdClass : cmdClasses) { + final APICommand at = cmdClass.getAnnotation(APICommand.class); if (at == null) { throw new CloudRuntimeException(String.format("%s is claimed as a API command, but it doesn't have @APICommand annotation", cmdClass.getName())); } + String apiName = at.name(); List> apiCmdList = s_apiNameCmdClassMap.get(apiName); if (apiCmdList == null) { @@ -245,13 +260,13 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } setEncodeApiResponse(Boolean.valueOf(_configDao.getValue(Config.EncodeApiResponse.key()))); - String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key()); + final String jsonType = _configDao.getValue(Config.JavaScriptDefaultContentType.key()); if (jsonType != null) { jsonContentType = jsonType; } if (apiPort != null) { - ListenerThread listenerThread = new ListenerThread(this, apiPort); + final ListenerThread listenerThread = new ListenerThread(this, apiPort); listenerThread.start(); } @@ -262,13 +277,13 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // If integration api port is not configured, actual OTW requests will be received by ApiServlet @SuppressWarnings({"unchecked", "rawtypes"}) @Override - public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + public void handle(final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { // Create StringBuffer to log information in access log - StringBuffer sb = new StringBuffer(); - HttpServerConnection connObj = (HttpServerConnection)context.getAttribute("http.connection"); + final StringBuffer sb = new StringBuffer(); + final HttpServerConnection connObj = (HttpServerConnection)context.getAttribute("http.connection"); if (connObj instanceof SocketHttpServerConnection) { - InetAddress remoteAddr = ((SocketHttpServerConnection)connObj).getRemoteAddress(); + final InetAddress remoteAddr = ((SocketHttpServerConnection)connObj).getRemoteAddress(); sb.append(remoteAddr.toString() + " -- "); } sb.append(StringUtils.cleanString(request.getRequestLine().toString())); @@ -276,8 +291,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer try { List paramList = null; try { - paramList = URLEncodedUtils.parse(new URI(request.getRequestLine().getUri()), "UTF-8"); - } catch (URISyntaxException e) { + paramList = URLEncodedUtils.parse(new URI(request.getRequestLine().getUri()), UTF_8); + } catch (final URISyntaxException e) { s_logger.error("Error parsing url request", e); } @@ -286,9 +301,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // APITODO: Use Guava's (import com.google.common.collect.Multimap;) // (Immutable)Multimap paramMultiMap = HashMultimap.create(); // Map> parameterMap = paramMultiMap.asMap(); - Map parameterMap = new HashMap(); + final Map parameterMap = new HashMap(); String responseType = BaseCmd.RESPONSE_TYPE_XML; - for (NameValuePair param : paramList) { + for (final NameValuePair param : paramList) { if (param.getName().equalsIgnoreCase("response")) { responseType = param.getValue(); continue; @@ -308,15 +323,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // always trust commands from API port, user context will always be UID_SYSTEM/ACCOUNT_ID_SYSTEM CallContext.register(_accountMgr.getSystemUser(), _accountMgr.getSystemAccount()); sb.insert(0, "(userId=" + User.UID_SYSTEM + " accountId=" + Account.ACCOUNT_ID_SYSTEM + " sessionId=" + null + ") "); - String responseText = handleRequest(parameterMap, responseType, sb); + final String responseText = handleRequest(parameterMap, responseType, sb); sb.append(" 200 " + ((responseText == null) ? 0 : responseText.length())); writeResponse(response, responseText, HttpStatus.SC_OK, responseType, null); - } catch (ServerApiException se) { - String responseText = getSerializedApiError(se, parameterMap, responseType); + } catch (final ServerApiException se) { + final String responseText = getSerializedApiError(se, parameterMap, responseType); writeResponse(response, responseText, se.getErrorCode().getHttpCode(), responseType, se.getDescription()); sb.append(" " + se.getErrorCode() + " " + se.getDescription()); - } catch (RuntimeException e) { + } catch (final RuntimeException e) { // log runtime exception like NullPointerException to help identify the source easier s_logger.error("Unhandled exception, ", e); throw e; @@ -327,9 +342,32 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } + @SuppressWarnings({"unchecked", "rawtypes"}) + public void checkCharacterInkParams(final Map params) { + final Map stringMap = new HashMap(); + final Set keys = params.keySet(); + final Iterator keysIter = keys.iterator(); + while (keysIter.hasNext()) { + final String key = (String)keysIter.next(); + final String[] value = (String[])params.get(key); + // fail if parameter value contains ASCII control (non-printable) characters + if (value[0] != null) { + final Pattern pattern = Pattern.compile(CONTROL_CHARACTERS); + final Matcher matcher = pattern.matcher(value[0]); + if (matcher.find()) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Received value " + value[0] + " for parameter " + key + + " is invalid, contains illegal ASCII non-printable characters"); + } + } + stringMap.put(key, value[0]); + } + } + @Override @SuppressWarnings("rawtypes") - public String handleRequest(Map params, String responseType, StringBuffer auditTrailSb) throws ServerApiException { + public String handleRequest(final Map params, final String responseType, final StringBuffer auditTrailSb) throws ServerApiException { + checkCharacterInkParams(params); + String response = null; String[] command = null; @@ -339,37 +377,27 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer s_logger.error("invalid request, no command sent"); if (s_logger.isTraceEnabled()) { s_logger.trace("dumping request parameters"); - for (Object key : params.keySet()) { - String keyStr = (String)key; - String[] value = (String[])params.get(key); + for (final Object key : params.keySet()) { + final String keyStr = (String)key; + final String[] value = (String[])params.get(key); s_logger.trace(" key: " + keyStr + ", value: " + ((value == null) ? "'null'" : value[0])); } } throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "Invalid request, no command sent"); } else { - Map paramMap = new HashMap(); - Set keys = params.keySet(); - Iterator keysIter = keys.iterator(); + final Map paramMap = new HashMap(); + final Set keys = params.keySet(); + final Iterator keysIter = keys.iterator(); while (keysIter.hasNext()) { - String key = (String)keysIter.next(); + final String key = (String)keysIter.next(); if ("command".equalsIgnoreCase(key)) { continue; } - String[] value = (String[])params.get(key); - // fail if parameter value contains ASCII control (non-printable) characters - if (value[0] != null) { - Pattern pattern = Pattern.compile(controlCharacters); - Matcher matcher = pattern.matcher(value[0]); - if (matcher.find()) { - throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Received value " + value[0] + " for parameter " + key + - " is invalid, contains illegal ASCII non-printable characters"); - } - } + final String[] value = (String[])params.get(key); paramMap.put(key, value[0]); } Class cmdClass = getCmdClass(command[0]); - if (cmdClass != null) { APICommand annotation = cmdClass.getAnnotation(APICommand.class); if (annotation == null) { @@ -382,7 +410,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer cmdObj.configure(); cmdObj.setFullUrlParams(paramMap); cmdObj.setResponseType(responseType); - cmdObj.setHttpMethod(paramMap.get("httpmethod").toString()); + cmdObj.setHttpMethod(paramMap.get(ApiConstants.HTTPMETHOD).toString()); // This is where the command is either serialized, or directly dispatched response = queueCommand(cmdObj, paramMap); @@ -395,24 +423,24 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer buildAuditTrail(auditTrailSb, command[0], response); } else { if (!command[0].equalsIgnoreCase("login") && !command[0].equalsIgnoreCase("logout")) { - String errorString = "Unknown API command: " + command[0]; + final String errorString = "Unknown API command: " + command[0]; s_logger.warn(errorString); auditTrailSb.append(" " + errorString); throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, errorString); } } } - } catch (InvalidParameterValueException ex) { + } catch (final InvalidParameterValueException ex) { s_logger.info(ex.getMessage()); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex); - } catch (IllegalArgumentException ex) { + } catch (final IllegalArgumentException ex) { s_logger.info(ex.getMessage()); throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ex.getMessage(), ex); - } catch (PermissionDeniedException ex) { - ArrayList idList = ex.getIdProxyList(); + } catch (final PermissionDeniedException ex) { + final ArrayList idList = ex.getIdProxyList(); if (idList != null) { - StringBuffer buf = new StringBuffer(); - for (ExceptionProxyObject obj : idList) { + final StringBuffer buf = new StringBuffer(); + for (final ExceptionProxyObject obj : idList) { buf.append(obj.getDescription()); buf.append(":"); buf.append(obj.getUuid()); @@ -423,10 +451,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer s_logger.info("PermissionDenied: " + ex.getMessage()); } throw new ServerApiException(ApiErrorCode.ACCOUNT_ERROR, ex.getMessage(), ex); - } catch (AccountLimitException ex) { + } catch (final AccountLimitException ex) { s_logger.info(ex.getMessage()); throw new ServerApiException(ApiErrorCode.ACCOUNT_RESOURCE_LIMIT_ERROR, ex.getMessage(), ex); - } catch (InsufficientCapacityException ex) { + } catch (final InsufficientCapacityException ex) { s_logger.info(ex.getMessage()); String errorMsg = ex.getMessage(); if (!_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) { @@ -434,10 +462,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer errorMsg = BaseCmd.USER_ERROR_MESSAGE; } throw new ServerApiException(ApiErrorCode.INSUFFICIENT_CAPACITY_ERROR, errorMsg, ex); - } catch (ResourceAllocationException ex) { + } catch (final ResourceAllocationException ex) { s_logger.info(ex.getMessage()); throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage(), ex); - } catch (ResourceUnavailableException ex) { + } catch (final ResourceUnavailableException ex) { s_logger.info(ex.getMessage()); String errorMsg = ex.getMessage(); if (!_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) { @@ -445,11 +473,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer errorMsg = BaseCmd.USER_ERROR_MESSAGE; } throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, errorMsg, ex); - } catch (ServerApiException ex) { + } catch (final ServerApiException ex) { s_logger.info(ex.getDescription()); throw ex; - } catch (Exception ex) { - s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command[0]), ex); + } catch (final Exception ex) { + s_logger.error("unhandled exception executing api command: " + ((command == null) ? "null" : command), ex); String errorMsg = ex.getMessage(); if (!_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())) { // hide internal details to non-admin user for security reason @@ -461,29 +489,29 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer return response; } - private String getBaseAsyncResponse(long jobId, BaseAsyncCmd cmd) { - AsyncJobResponse response = new AsyncJobResponse(); + private String getBaseAsyncResponse(final long jobId, final BaseAsyncCmd cmd) { + final AsyncJobResponse response = new AsyncJobResponse(); - AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); + final AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); response.setJobId(job.getUuid()); response.setResponseName(cmd.getCommandName()); return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType()); } - private String getBaseAsyncCreateResponse(long jobId, BaseAsyncCreateCmd cmd, String objectUuid) { - CreateCmdResponse response = new CreateCmdResponse(); - AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); + private String getBaseAsyncCreateResponse(final long jobId, final BaseAsyncCreateCmd cmd, final String objectUuid) { + final CreateCmdResponse response = new CreateCmdResponse(); + final AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); response.setJobId(job.getUuid()); response.setId(objectUuid); response.setResponseName(cmd.getCommandName()); return ApiResponseSerializer.toSerializedString(response, cmd.getResponseType()); } - private String queueCommand(BaseCmd cmdObj, Map params) throws Exception { - CallContext ctx = CallContext.current(); - Long callerUserId = ctx.getCallingUserId(); - Account caller = ctx.getCallingAccount(); - ctx.setEventDisplayEnabled(cmdObj.isDisplayResourceEnabled()); + private String queueCommand(final BaseCmd cmdObj, final Map params) throws Exception { + final CallContext ctx = CallContext.current(); + final Long callerUserId = ctx.getCallingUserId(); + final Account caller = ctx.getCallingAccount(); + String vmUUID = params.get(ApiConstants.VIRTUAL_MACHINE_ID); // Queue command based on Cmd super class: // BaseCmd: cmd is dispatched to ApiDispatcher, executed, serialized and returned. @@ -493,16 +521,18 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer Long objectId = null; String objectUuid = null; if (cmdObj instanceof BaseAsyncCreateCmd) { - BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd)cmdObj; + final BaseAsyncCreateCmd createCmd = (BaseAsyncCreateCmd)cmdObj; _dispatcher.dispatchCreateCmd(createCmd, params); objectId = createCmd.getEntityId(); objectUuid = createCmd.getEntityUuid(); params.put("id", objectId.toString()); } else { - ApiDispatcher.processParameters(cmdObj, params); + // Extract the uuid before params are processed and id reflects internal db id + objectUuid = params.get(ApiConstants.ID); + dispatchChainFactory.getStandardDispatchChain().dispatch(new DispatchTask(cmdObj, params)); } - BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmdObj; + final BaseAsyncCmd asyncCmd = (BaseAsyncCmd)cmdObj; if (callerUserId != null) { params.put("ctxUserId", callerUserId.toString()); @@ -510,12 +540,24 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer if (caller != null) { params.put("ctxAccountId", String.valueOf(caller.getId())); } + if(objectUuid != null){ + params.put("uuid", objectUuid); + } long startEventId = ctx.getStartEventId(); asyncCmd.setStartEventId(startEventId); + // Add the resource id in the call context, also add some other first class object ids (for now vm) if available. + // TODO - this should be done for all the uuids passed in the cmd - so should be moved where uuid to id conversion happens. + if(EventTypes.getEntityForEvent(asyncCmd.getEventType()) != null){ + ctx.putContextParameter(EventTypes.getEntityForEvent(asyncCmd.getEventType()), objectUuid); + } + if(vmUUID != null){ + ctx.putContextParameter(ReflectUtil.getEntityName(VirtualMachine.class), vmUUID); + } + // save the scheduled event - Long eventId = + final Long eventId = ActionEventUtils.onScheduledActionEvent((callerUserId == null) ? User.UID_SYSTEM : callerUserId, asyncCmd.getEntityOwnerId(), asyncCmd.getEventType(), asyncCmd.getEventDescription(), asyncCmd.isDisplayResourceEnabled(), startEventId); if (startEventId == 0) { @@ -532,16 +574,16 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer asyncCmd.getInstanceType() != null ? asyncCmd.getInstanceType().toString() : null); job.setDispatcher(_asyncDispatcher.getName()); - long jobId = _asyncMgr.submitAsyncJob(job); + final long jobId = _asyncMgr.submitAsyncJob(job); if (jobId == 0L) { - String errorMsg = "Unable to schedule async job for command " + job.getCmd(); + final String errorMsg = "Unable to schedule async job for command " + job.getCmd(); s_logger.warn(errorMsg); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, errorMsg); } if (objectId != null) { - String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid; + final String objUuid = (objectUuid == null) ? objectId.toString() : objectUuid; return getBaseAsyncCreateResponse(jobId, (BaseAsyncCreateCmd)asyncCmd, objUuid); } else { SerializationContext.current().setUuidTranslation(true); @@ -553,23 +595,12 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // if the command is of the listXXXCommand, we will need to also return the // the job id and status if possible // For those listXXXCommand which we have already created DB views, this step is not needed since async job is joined in their db views. - if (cmdObj instanceof BaseListCmd && !(cmdObj instanceof ListVMsCmd) && !(cmdObj instanceof ListRoutersCmd) - && !(cmdObj instanceof ListSecurityGroupsCmd) - && !(cmdObj instanceof ListTagsCmd) - && !(cmdObj instanceof ListEventsCmd) - && !(cmdObj instanceof ListVMGroupsCmd) - && !(cmdObj instanceof ListProjectsCmd) - && !(cmdObj instanceof ListProjectAccountsCmd) - && !(cmdObj instanceof ListProjectInvitationsCmd) - && !(cmdObj instanceof ListHostsCmd) - && !(cmdObj instanceof ListVolumesCmd) - && !(cmdObj instanceof ListUsersCmd) - && !(cmdObj instanceof ListAccountsCmd) - && !(cmdObj instanceof ListStoragePoolsCmd) - && !(cmdObj instanceof ListDiskOfferingsCmd) - && !(cmdObj instanceof ListServiceOfferingsCmd) - && !(cmdObj instanceof ListZonesCmd) - ) { + if (cmdObj instanceof BaseListCmd && !(cmdObj instanceof ListVMsCmd) && !(cmdObj instanceof ListRoutersCmd) && !(cmdObj instanceof ListSecurityGroupsCmd) && + !(cmdObj instanceof ListTagsCmd) && !(cmdObj instanceof ListEventsCmd) && !(cmdObj instanceof ListVMGroupsCmd) && !(cmdObj instanceof ListProjectsCmd) && + !(cmdObj instanceof ListProjectAccountsCmd) && !(cmdObj instanceof ListProjectInvitationsCmd) && !(cmdObj instanceof ListHostsCmd) && + !(cmdObj instanceof ListVolumesCmd) && !(cmdObj instanceof ListUsersCmd) && !(cmdObj instanceof ListAccountsCmd) && + !(cmdObj instanceof ListStoragePoolsCmd) && !(cmdObj instanceof ListDiskOfferingsCmd) && !(cmdObj instanceof ListServiceOfferingsCmd) && + !(cmdObj instanceof ListZonesCmd)) { buildAsyncListResponse((BaseListCmd) cmdObj, caller); } @@ -579,8 +610,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @SuppressWarnings("unchecked") - private void buildAsyncListResponse(BaseListCmd command, Account account) { - List responses = ((ListResponse)command.getResponseObject()).getResponses(); + private void buildAsyncListResponse(final BaseListCmd command, final Account account) { + final List responses = ((ListResponse)command.getResponseObject()).getResponses(); if (responses != null && responses.size() > 0) { List jobs = null; @@ -595,18 +626,18 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer return; } - Map objectJobMap = new HashMap(); - for (AsyncJob job : jobs) { + final Map objectJobMap = new HashMap(); + for (final AsyncJob job : jobs) { if (job.getInstanceId() == null) { continue; } - String instanceUuid = ApiDBUtils.findJobInstanceUuid(job); + final String instanceUuid = ApiDBUtils.findJobInstanceUuid(job); objectJobMap.put(instanceUuid, job); } - for (ResponseObject response : responses) { + for (final ResponseObject response : responses) { if (response.getObjectId() != null && objectJobMap.containsKey(response.getObjectId())) { - AsyncJob job = objectJobMap.get(response.getObjectId()); + final AsyncJob job = objectJobMap.get(response.getObjectId()); response.setJobId(job.getUuid()); response.setJobStatus(job.getStatus().ordinal()); } @@ -614,7 +645,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } - private void buildAuditTrail(StringBuffer auditTrailSb, String command, String result) { + private void buildAuditTrail(final StringBuffer auditTrailSb, final String command, final String result) { if (result == null) { return; } @@ -627,31 +658,31 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Override - public boolean verifyRequest(Map requestParameters, Long userId) throws ServerApiException { + public boolean verifyRequest(final Map requestParameters, final Long userId) throws ServerApiException { try { String apiKey = null; String secretKey = null; String signature = null; String unsignedRequest = null; - String[] command = (String[])requestParameters.get("command"); + final String[] command = (String[])requestParameters.get(ApiConstants.COMMAND); if (command == null) { s_logger.info("missing command, ignoring request..."); return false; } - String commandName = command[0]; + final String commandName = command[0]; // if userId not null, that mean that user is logged in if (userId != null) { - User user = ApiDBUtils.findUserById(userId); + final User user = ApiDBUtils.findUserById(userId); try { checkCommandAvailable(user, commandName); - } catch (RequestLimitException ex) { + } catch (final RequestLimitException ex) { s_logger.debug(ex.getMessage()); throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage()); - } catch (PermissionDeniedException ex) { + } catch (final PermissionDeniedException ex) { s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user with id:" + userId); throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command does not exist or it is not available for user"); } @@ -666,9 +697,9 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // - build a request string with sorted params, make sure it's all lowercase // - sign the request, verify the signature is the same - List parameterNames = new ArrayList(); + final List parameterNames = new ArrayList(); - for (Object paramNameObj : requestParameters.keySet()) { + for (final Object paramNameObj : requestParameters.keySet()) { parameterNames.add((String)paramNameObj); // put the name in a list that we'll sort later } @@ -677,25 +708,25 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer String signatureVersion = null; String expires = null; - for (String paramName : parameterNames) { + for (final String paramName : parameterNames) { // parameters come as name/value pairs in the form String/String[] - String paramValue = ((String[])requestParameters.get(paramName))[0]; + final String paramValue = ((String[])requestParameters.get(paramName))[0]; - if ("signature".equalsIgnoreCase(paramName)) { + if (ApiConstants.SIGNATURE.equalsIgnoreCase(paramName)) { signature = paramValue; } else { - if ("apikey".equalsIgnoreCase(paramName)) { + if (ApiConstants.API_KEY.equalsIgnoreCase(paramName)) { apiKey = paramValue; - } else if ("signatureversion".equalsIgnoreCase(paramName)) { + } else if (ApiConstants.SIGNATURE_VERSION.equalsIgnoreCase(paramName)) { signatureVersion = paramValue; - } else if ("expires".equalsIgnoreCase(paramName)) { + } else if (ApiConstants.EXPIRES.equalsIgnoreCase(paramName)) { expires = paramValue; } if (unsignedRequest == null) { - unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + unsignedRequest = paramName + "=" + URLEncoder.encode(paramValue, UTF_8).replaceAll("\\+", "%20"); } else { - unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, "UTF-8").replaceAll("\\+", "%20"); + unsignedRequest = unsignedRequest + "&" + paramName + "=" + URLEncoder.encode(paramValue, UTF_8).replaceAll("\\+", "%20"); } } } @@ -717,30 +748,30 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer synchronized (DateFormatToUse) { try { expiresTS = DateFormatToUse.parse(expires); - } catch (ParseException pe) { + } catch (final ParseException pe) { s_logger.debug("Incorrect date format for Expires parameter", pe); return false; } } - Date now = new Date(System.currentTimeMillis()); + final Date now = new Date(System.currentTimeMillis()); if (expiresTS.before(now)) { s_logger.debug("Request expired -- ignoring ...sig: " + signature + ", apiKey: " + apiKey); return false; } } - TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); + final TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.CLOUD_DB); txn.close(); User user = null; // verify there is a user with this api key - Pair userAcctPair = _accountMgr.findUserByApiKey(apiKey); + final Pair userAcctPair = _accountMgr.findUserByApiKey(apiKey); if (userAcctPair == null) { s_logger.debug("apiKey does not map to a valid user -- ignoring request, apiKey: " + apiKey); return false; } user = userAcctPair.first(); - Account account = userAcctPair.second(); + final Account account = userAcctPair.second(); if (user.getState() != Account.State.enabled || !account.getState().equals(Account.State.enabled)) { s_logger.info("disabled or locked user accessing the api, userid = " + user.getId() + "; name = " + user.getUsername() + "; state: " + user.getState() + @@ -750,10 +781,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer try { checkCommandAvailable(user, commandName); - } catch (RequestLimitException ex) { + } catch (final RequestLimitException ex) { s_logger.debug(ex.getMessage()); throw new ServerApiException(ApiErrorCode.API_LIMIT_EXCEED, ex.getMessage()); - } catch (PermissionDeniedException ex) { + } catch (final PermissionDeniedException ex) { s_logger.debug("The given command:" + commandName + " does not exist or it is not available for user"); throw new ServerApiException(ApiErrorCode.UNSUPPORTED_ACTION_ERROR, "The given command:" + commandName + " does not exist or it is not available for user with id:" + userId); @@ -768,30 +799,30 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer unsignedRequest = unsignedRequest.toLowerCase(); - Mac mac = Mac.getInstance("HmacSHA1"); - SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); + final Mac mac = Mac.getInstance("HmacSHA1"); + final SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1"); mac.init(keySpec); mac.update(unsignedRequest.getBytes()); - byte[] encryptedBytes = mac.doFinal(); - String computedSignature = Base64.encodeBase64String(encryptedBytes); - boolean equalSig = signature.equals(computedSignature); + final byte[] encryptedBytes = mac.doFinal(); + final String computedSignature = Base64.encodeBase64String(encryptedBytes); + final boolean equalSig = signature.equals(computedSignature); if (!equalSig) { s_logger.info("User signature: " + signature + " is not equaled to computed signature: " + computedSignature); } else { CallContext.register(user, account); } return equalSig; - } catch (ServerApiException ex) { + } catch (final ServerApiException ex) { throw ex; - } catch (Exception ex) { + } catch (final Exception ex) { s_logger.error("unable to verify request signature"); } return false; } @Override - public Long fetchDomainId(String domainUUID) { - Domain domain = _domainMgr.getDomain(domainUUID); + public Long fetchDomainId(final String domainUUID) { + final Domain domain = _domainMgr.getDomain(domainUUID); if (domain != null) return domain.getId(); else @@ -799,15 +830,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Override - public void loginUser(HttpSession session, String username, String password, Long domainId, String domainPath, String loginIpAddress, - Map requestParameters) throws CloudAuthenticationException { + public void loginUser(final HttpSession session, final String username, final String password, Long domainId, final String domainPath, final String loginIpAddress, + final Map requestParameters) throws CloudAuthenticationException { // We will always use domainId first. If that does not exist, we will use domain name. If THAT doesn't exist // we will default to ROOT if (domainId == null) { if (domainPath == null || domainPath.trim().length() == 0) { domainId = Domain.ROOT_DOMAIN; } else { - Domain domainObj = _domainMgr.findDomainByPath(domainPath); + final Domain domainObj = _domainMgr.findDomainByPath(domainPath); if (domainObj != null) { domainId = domainObj.getId(); } else { // if an unknown path is passed in, fail the login call @@ -816,26 +847,26 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } - UserAccount userAcct = _accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters); + final UserAccount userAcct = _accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters); if (userAcct != null) { - String timezone = userAcct.getTimezone(); + final String timezone = userAcct.getTimezone(); float offsetInHrs = 0f; if (timezone != null) { - TimeZone t = TimeZone.getTimeZone(timezone); + final TimeZone t = TimeZone.getTimeZone(timezone); s_logger.info("Current user logged in under " + timezone + " timezone"); - java.util.Date date = new java.util.Date(); - long longDate = date.getTime(); - float offsetInMs = (t.getOffset(longDate)); + final java.util.Date date = new java.util.Date(); + final long longDate = date.getTime(); + final float offsetInMs = (t.getOffset(longDate)); offsetInHrs = offsetInMs / (1000 * 60 * 60); s_logger.info("Timezone offset from UTC is: " + offsetInHrs); } - Account account = _accountMgr.getAccount(userAcct.getAccountId()); + final Account account = _accountMgr.getAccount(userAcct.getAccountId()); // set the userId and account object for everyone session.setAttribute("userid", userAcct.getId()); - UserVO user = (UserVO)_accountMgr.getActiveUser(userAcct.getId()); + final UserVO user = (UserVO)_accountMgr.getActiveUser(userAcct.getId()); if (user.getUuid() != null) { session.setAttribute("user_UUID", user.getUuid()); } @@ -847,7 +878,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer session.setAttribute("account", account.getAccountName()); session.setAttribute("domainid", account.getDomainId()); - DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId()); + final DomainVO domain = (DomainVO)_domainMgr.getDomain(account.getDomainId()); if (domain.getUuid() != null) { session.setAttribute("domain_UUID", domain.getUuid()); } @@ -863,10 +894,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer // (bug 5483) generate a session key that the user must submit on every request to prevent CSRF, add that // to the login response so that session-based authenticators know to send the key back - SecureRandom sesssionKeyRandom = new SecureRandom(); - byte sessionKeyBytes[] = new byte[20]; + final SecureRandom sesssionKeyRandom = new SecureRandom(); + final byte sessionKeyBytes[] = new byte[20]; sesssionKeyRandom.nextBytes(sessionKeyBytes); - String sessionKey = Base64.encodeBase64String(sessionKeyBytes); + final String sessionKey = Base64.encodeBase64String(sessionKeyBytes); session.setAttribute("sessionkey", sessionKey); return; @@ -875,14 +906,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Override - public void logoutUser(long userId) { + public void logoutUser(final long userId) { _accountMgr.logoutUser(userId); return; } @Override - public boolean verifyUser(Long userId) { - User user = _accountMgr.getUserIncludingRemoved(userId); + public boolean verifyUser(final Long userId) { + final User user = _accountMgr.getUserIncludingRemoved(userId); Account account = null; if (user != null) { account = _accountMgr.getAccount(user.getAccountId()); @@ -896,12 +927,12 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer return true; } - private void checkCommandAvailable(User user, String commandName) throws PermissionDeniedException { + private void checkCommandAvailable(final User user, final String commandName) throws PermissionDeniedException { if (user == null) { throw new PermissionDeniedException("User is null for role based API access check for command" + commandName); } - for (APIChecker apiChecker : _apiAccessCheckers) { + for (final APIChecker apiChecker : _apiAccessCheckers) { apiChecker.checkAccess(user, commandName); } } @@ -937,30 +968,30 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } // FIXME: rather than isError, we might was to pass in the status code to give more flexibility - private void writeResponse(HttpResponse resp, final String responseText, final int statusCode, String responseType, String reasonPhrase) { + private void writeResponse(final HttpResponse resp, final String responseText, final int statusCode, final String responseType, final String reasonPhrase) { try { resp.setStatusCode(statusCode); resp.setReasonPhrase(reasonPhrase); - BasicHttpEntity body = new BasicHttpEntity(); + final BasicHttpEntity body = new BasicHttpEntity(); if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { // JSON response body.setContentType(jsonContentType); if (responseText == null) { - body.setContent(new ByteArrayInputStream("{ \"error\" : { \"description\" : \"Internal Server Error\" } }".getBytes("UTF-8"))); + body.setContent(new ByteArrayInputStream("{ \"error\" : { \"description\" : \"Internal Server Error\" } }".getBytes(UTF_8))); } } else { body.setContentType("text/xml"); if (responseText == null) { - body.setContent(new ByteArrayInputStream("Internal Server Error".getBytes("UTF-8"))); + body.setContent(new ByteArrayInputStream("Internal Server Error".getBytes(UTF_8))); } } if (responseText != null) { - body.setContent(new ByteArrayInputStream(responseText.getBytes("UTF-8"))); + body.setContent(new ByteArrayInputStream(responseText.getBytes(UTF_8))); } resp.setEntity(body); - } catch (Exception ex) { + } catch (final Exception ex) { s_logger.error("error!", ex); } } @@ -975,10 +1006,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer private ServerSocket _serverSocket = null; private HttpParams _params = null; - public ListenerThread(ApiServer requestHandler, int port) { + public ListenerThread(final ApiServer requestHandler, final int port) { try { _serverSocket = new ServerSocket(port); - } catch (IOException ioex) { + } catch (final IOException ioex) { s_logger.error("error initializing api server", ioex); return; } @@ -991,14 +1022,14 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1"); // Set up the HTTP protocol processor - BasicHttpProcessor httpproc = new BasicHttpProcessor(); + final BasicHttpProcessor httpproc = new BasicHttpProcessor(); httpproc.addInterceptor(new ResponseDate()); httpproc.addInterceptor(new ResponseServer()); httpproc.addInterceptor(new ResponseContent()); httpproc.addInterceptor(new ResponseConnControl()); // Set up request handlers - HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); + final HttpRequestHandlerRegistry reqistry = new HttpRequestHandlerRegistry(); reqistry.register("*", requestHandler); // Set up the HTTP service @@ -1013,15 +1044,15 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer while (!Thread.interrupted()) { try { // Set up HTTP connection - Socket socket = _serverSocket.accept(); - DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); + final Socket socket = _serverSocket.accept(); + final DefaultHttpServerConnection conn = new DefaultHttpServerConnection(); conn.bind(socket, _params); // Execute a new worker task to handle the request s_executor.execute(new WorkerTask(_httpService, conn, s_workerCount++)); - } catch (InterruptedIOException ex) { + } catch (final InterruptedIOException ex) { break; - } catch (IOException e) { + } catch (final IOException e) { s_logger.error("I/O error initializing connection thread", e); break; } @@ -1040,33 +1071,33 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer @Override protected void runInContext() { - HttpContext context = new BasicHttpContext(null); + final HttpContext context = new BasicHttpContext(null); try { while (!Thread.interrupted() && _conn.isOpen()) { _httpService.handleRequest(_conn, context); _conn.close(); } - } catch (ConnectionClosedException ex) { + } catch (final ConnectionClosedException ex) { if (s_logger.isTraceEnabled()) { s_logger.trace("ApiServer: Client closed connection"); } - } catch (IOException ex) { + } catch (final IOException ex) { if (s_logger.isTraceEnabled()) { s_logger.trace("ApiServer: IOException - " + ex); } - } catch (HttpException ex) { + } catch (final HttpException ex) { s_logger.warn("ApiServer: Unrecoverable HTTP protocol violation" + ex); } finally { try { _conn.shutdown(); - } catch (IOException ignore) { + } catch (final IOException ignore) { } } } } @Override - public String getSerializedApiError(int errorCode, String errorText, Map apiCommandParams, String responseType) { + public String getSerializedApiError(final int errorCode, final String errorText, final Map apiCommandParams, final String responseType) { String responseName = null; Class cmdClass = null; String responseText = null; @@ -1075,10 +1106,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer if (apiCommandParams == null || apiCommandParams.isEmpty()) { responseName = "errorresponse"; } else { - Object cmdObj = apiCommandParams.get("command"); + final Object cmdObj = apiCommandParams.get(ApiConstants.COMMAND); // cmd name can be null when "command" parameter is missing in the request if (cmdObj != null) { - String cmdName = ((String[])cmdObj)[0]; + final String cmdName = ((String[])cmdObj)[0]; cmdClass = getCmdClass(cmdName); if (cmdClass != null) { responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName(); @@ -1087,21 +1118,21 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } } - ExceptionResponse apiResponse = new ExceptionResponse(); + final ExceptionResponse apiResponse = new ExceptionResponse(); apiResponse.setErrorCode(errorCode); apiResponse.setErrorText(errorText); apiResponse.setResponseName(responseName); SerializationContext.current().setUuidTranslation(true); responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType); - } catch (Exception e) { + } catch (final Exception e) { s_logger.error("Exception responding to http request", e); } return responseText; } @Override - public String getSerializedApiError(ServerApiException ex, Map apiCommandParams, String responseType) { + public String getSerializedApiError(final ServerApiException ex, final Map apiCommandParams, final String responseType) { String responseName = null; Class cmdClass = null; String responseText = null; @@ -1114,11 +1145,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer if (ex.getErrorCode() == ApiErrorCode.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) { responseName = "errorresponse"; } else { - Object cmdObj = apiCommandParams.get("command"); + final Object cmdObj = apiCommandParams.get(ApiConstants.COMMAND); // cmd name can be null when "command" parameter is missing in // the request if (cmdObj != null) { - String cmdName = ((String[])cmdObj)[0]; + final String cmdName = ((String[])cmdObj)[0]; cmdClass = getCmdClass(cmdName); if (cmdClass != null) { responseName = ((BaseCmd)cmdClass.newInstance()).getCommandName(); @@ -1127,11 +1158,11 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } } } - ExceptionResponse apiResponse = new ExceptionResponse(); + final ExceptionResponse apiResponse = new ExceptionResponse(); apiResponse.setErrorCode(ex.getErrorCode().getHttpCode()); apiResponse.setErrorText(ex.getDescription()); apiResponse.setResponseName(responseName); - ArrayList idList = ex.getIdProxyList(); + final ArrayList idList = ex.getIdProxyList(); if (idList != null) { for (int i = 0; i < idList.size(); i++) { apiResponse.addProxyObject(idList.get(i)); @@ -1144,7 +1175,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer SerializationContext.current().setUuidTranslation(true); responseText = ApiResponseSerializer.toSerializedString(apiResponse, responseType); - } catch (Exception e) { + } catch (final Exception e) { s_logger.error("Exception responding to http request", e); } return responseText; @@ -1155,7 +1186,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Inject - public void setPluggableServices(List pluggableServices) { + public void setPluggableServices(final List pluggableServices) { _pluggableServices = pluggableServices; } @@ -1164,7 +1195,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer } @Inject - public void setApiAccessCheckers(List apiAccessCheckers) { + public void setApiAccessCheckers(final List apiAccessCheckers) { _apiAccessCheckers = apiAccessCheckers; } @@ -1172,7 +1203,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer return encodeApiResponse; } - private static void setEncodeApiResponse(boolean encodeApiResponse) { + private static void setEncodeApiResponse(final boolean encodeApiResponse) { ApiServer.encodeApiResponse = encodeApiResponse; } diff --git a/server/src/com/cloud/api/ApiServlet.java b/server/src/com/cloud/api/ApiServlet.java index 46f7eba8bee..37a2549edb8 100755 --- a/server/src/com/cloud/api/ApiServlet.java +++ b/server/src/com/cloud/api/ApiServlet.java @@ -35,6 +35,7 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import org.springframework.web.context.support.SpringBeanAutowiringSupport; +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ServerApiException; @@ -67,40 +68,40 @@ public class ApiServlet extends HttpServlet { } @Override - public void init(ServletConfig config) throws ServletException { + public void init(final ServletConfig config) throws ServletException { SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, config.getServletContext()); } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) { processRequest(req, resp); } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) { processRequest(req, resp); } - private void utf8Fixup(HttpServletRequest req, Map params) { + private void utf8Fixup(final HttpServletRequest req, final Map params) { if (req.getQueryString() == null) { return; } - String[] paramsInQueryString = req.getQueryString().split("&"); + final String[] paramsInQueryString = req.getQueryString().split("&"); if (paramsInQueryString != null) { - for (String param : paramsInQueryString) { - String[] paramTokens = param.split("=", 2); + for (final String param : paramsInQueryString) { + final String[] paramTokens = param.split("=", 2); if (paramTokens != null && paramTokens.length == 2) { String name = paramTokens[0]; String value = paramTokens[1]; try { name = URLDecoder.decode(name, "UTF-8"); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { } try { value = URLDecoder.decode(value, "UTF-8"); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { } params.put(name, new String[] {value}); } else { @@ -119,13 +120,13 @@ public class ApiServlet extends HttpServlet { }); } - private void processRequestInContext(HttpServletRequest req, HttpServletResponse resp) { - StringBuffer auditTrailSb = new StringBuffer(); + private void processRequestInContext(final HttpServletRequest req, final HttpServletResponse resp) { + final StringBuffer auditTrailSb = new StringBuffer(); auditTrailSb.append(" " + req.getRemoteAddr()); auditTrailSb.append(" -- " + req.getMethod() + " "); // get the response format since we'll need it in a couple of places String responseType = BaseCmd.RESPONSE_TYPE_XML; - Map params = new HashMap(); + final Map params = new HashMap(); params.putAll(req.getParameterMap()); // For HTTP GET requests, it seems that HttpServletRequest.getParameterMap() actually tries @@ -143,19 +144,19 @@ public class ApiServlet extends HttpServlet { try { HttpSession session = req.getSession(false); - Object[] responseTypeParam = params.get("response"); + final Object[] responseTypeParam = params.get(ApiConstants.RESPONSE); if (responseTypeParam != null) { responseType = (String)responseTypeParam[0]; } - Object[] commandObj = params.get("command"); + final Object[] commandObj = params.get(ApiConstants.COMMAND); if (commandObj != null) { - String command = (String)commandObj[0]; + final String command = (String)commandObj[0]; if ("logout".equalsIgnoreCase(command)) { // if this is just a logout, invalidate the session and return if (session != null) { - Long userId = (Long)session.getAttribute("userid"); - Account account = (Account)session.getAttribute("accountobj"); + final Long userId = (Long)session.getAttribute("userid"); + final Account account = (Account)session.getAttribute("accountobj"); Long accountId = null; if (account != null) { accountId = account.getId(); @@ -166,7 +167,7 @@ public class ApiServlet extends HttpServlet { } try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } } auditTrailSb.append("command=logout"); @@ -179,18 +180,18 @@ public class ApiServlet extends HttpServlet { if (session != null) { try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } } session = req.getSession(true); - String[] username = (String[])params.get("username"); - String[] password = (String[])params.get("password"); - String[] domainIdArr = (String[])params.get("domainid"); + final String[] username = (String[])params.get(ApiConstants.USERNAME); + final String[] password = (String[])params.get(ApiConstants.PASSWORD); + String[] domainIdArr = (String[])params.get(ApiConstants.DOMAIN_ID); if (domainIdArr == null) { - domainIdArr = (String[])params.get("domainId"); + domainIdArr = (String[])params.get(ApiConstants.DOMAIN__ID); } - String[] domainName = (String[])params.get("domain"); + final String[] domainName = (String[])params.get(ApiConstants.DOMAIN); Long domainId = null; if ((domainIdArr != null) && (domainIdArr.length > 0)) { try { @@ -200,10 +201,10 @@ public class ApiServlet extends HttpServlet { domainId = new Long(Long.parseLong(domainIdArr[0])); } auditTrailSb.append(" domainid=" + domainId);// building the params for POST call - } catch (NumberFormatException e) { + } catch (final NumberFormatException e) { s_logger.warn("Invalid domain id entered by user"); auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "Invalid domain id entered, please enter a valid one"); - String serializedResponse = + final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid domain id entered, please enter a valid one", params, responseType); writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType); @@ -225,24 +226,24 @@ public class ApiServlet extends HttpServlet { } if (username != null) { - String pwd = ((password == null) ? null : password[0]); + final String pwd = ((password == null) ? null : password[0]); try { _apiServer.loginUser(session, username[0], pwd, domainId, domain, req.getRemoteAddr(), params); auditTrailSb.insert(0, "(userId=" + session.getAttribute("userid") + " accountId=" + ((Account)session.getAttribute("accountobj")).getId() + " sessionId=" + session.getId() + ")"); - String loginResponse = getLoginSuccessResponse(session, responseType); + final String loginResponse = getLoginSuccessResponse(session, responseType); writeResponse(resp, loginResponse, HttpServletResponse.SC_OK, responseType); return; - } catch (CloudAuthenticationException ex) { + } catch (final CloudAuthenticationException ex) { // TODO: fall through to API key, or just fail here w/ auth error? (HTTP 401) try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } auditTrailSb.append(" " + ApiErrorCode.ACCOUNT_ERROR + " " + ex.getMessage() != null ? ex.getMessage() : "failed to authenticate user, check if username/password are correct"); - String serializedResponse = + final String serializedResponse = _apiServer.getSerializedApiError(ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), ex.getMessage() != null ? ex.getMessage() : "failed to authenticate user, check if username/password are correct", params, responseType); writeResponse(resp, serializedResponse, ApiErrorCode.ACCOUNT_ERROR.getHttpCode(), responseType); @@ -252,7 +253,7 @@ public class ApiServlet extends HttpServlet { } } auditTrailSb.append(req.getQueryString()); - boolean isNew = ((session == null) ? true : session.isNew()); + final boolean isNew = ((session == null) ? true : session.isNew()); // Initialize an empty context and we will update it after we have verified the request below, // we no longer rely on web-session here, verifyRequest will populate user/account information @@ -261,17 +262,17 @@ public class ApiServlet extends HttpServlet { if (!isNew) { userId = (Long)session.getAttribute("userid"); - String account = (String)session.getAttribute("account"); - Object accountObj = session.getAttribute("accountobj"); - String sessionKey = (String)session.getAttribute("sessionkey"); - String[] sessionKeyParam = (String[])params.get("sessionkey"); + final String account = (String)session.getAttribute("account"); + final Object accountObj = session.getAttribute("accountobj"); + final String sessionKey = (String)session.getAttribute("sessionkey"); + final String[] sessionKeyParam = (String[])params.get(ApiConstants.SESSIONKEY); if ((sessionKeyParam == null) || (sessionKey == null) || !sessionKey.equals(sessionKeyParam[0])) { try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials"); - String serializedResponse = + final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType); writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType); return; @@ -279,26 +280,26 @@ public class ApiServlet extends HttpServlet { // Do a sanity check here to make sure the user hasn't already been deleted if ((userId != null) && (account != null) && (accountObj != null) && _apiServer.verifyUser(userId)) { - String[] command = (String[])params.get("command"); + final String[] command = (String[])params.get(ApiConstants.COMMAND); if (command == null) { s_logger.info("missing command, ignoring request..."); auditTrailSb.append(" " + HttpServletResponse.SC_BAD_REQUEST + " " + "no command specified"); - String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_BAD_REQUEST, "no command specified", params, responseType); + final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_BAD_REQUEST, "no command specified", params, responseType); writeResponse(resp, serializedResponse, HttpServletResponse.SC_BAD_REQUEST, responseType); return; } - User user = _entityMgr.findById(User.class, userId); + final User user = _entityMgr.findById(User.class, userId); CallContext.register(user, (Account)accountObj); } else { // Invalidate the session to ensure we won't allow a request across management server // restarts if the userId was serialized to the stored session try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials"); - String serializedResponse = + final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials", params, responseType); writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType); return; @@ -326,29 +327,29 @@ public class ApiServlet extends HttpServlet { // Add the HTTP method (GET/POST/PUT/DELETE) as well into the params map. params.put("httpmethod", new String[] {req.getMethod()}); - String response = _apiServer.handleRequest(params, responseType, auditTrailSb); + final String response = _apiServer.handleRequest(params, responseType, auditTrailSb); writeResponse(resp, response != null ? response : "", HttpServletResponse.SC_OK, responseType); } else { if (session != null) { try { session.invalidate(); - } catch (IllegalStateException ise) { + } catch (final IllegalStateException ise) { } } auditTrailSb.append(" " + HttpServletResponse.SC_UNAUTHORIZED + " " + "unable to verify user credentials and/or request signature"); - String serializedResponse = + final String serializedResponse = _apiServer.getSerializedApiError(HttpServletResponse.SC_UNAUTHORIZED, "unable to verify user credentials and/or request signature", params, responseType); writeResponse(resp, serializedResponse, HttpServletResponse.SC_UNAUTHORIZED, responseType); } - } catch (ServerApiException se) { - String serializedResponseText = _apiServer.getSerializedApiError(se, params, responseType); + } catch (final ServerApiException se) { + final String serializedResponseText = _apiServer.getSerializedApiError(se, params, responseType); resp.setHeader("X-Description", se.getDescription()); writeResponse(resp, serializedResponseText, se.getErrorCode().getHttpCode(), responseType); auditTrailSb.append(" " + se.getErrorCode() + " " + se.getDescription()); - } catch (Exception ex) { + } catch (final Exception ex) { s_logger.error("unknown exception writing api response", ex); auditTrailSb.append(" unknown exception writing api response"); } finally { @@ -372,7 +373,7 @@ public class ApiServlet extends HttpServlet { */ // FIXME: rather than isError, we might was to pass in the status code to give more flexibility - private void writeResponse(HttpServletResponse resp, String response, int responseCode, String responseType) { + private void writeResponse(final HttpServletResponse resp, final String response, final int responseCode, final String responseType) { try { if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { resp.setContentType(ApiServer.getJsonContentType() + "; charset=UTF-8"); @@ -382,11 +383,11 @@ public class ApiServlet extends HttpServlet { resp.setStatus(responseCode); resp.getWriter().print(response); - } catch (IOException ioex) { + } catch (final IOException ioex) { if (s_logger.isTraceEnabled()) { s_logger.trace("exception writing response: " + ioex); } - } catch (Exception ex) { + } catch (final Exception ex) { if (!(ex instanceof IllegalStateException)) { s_logger.error("unknown exception writing api response", ex); } @@ -394,29 +395,29 @@ public class ApiServlet extends HttpServlet { } @SuppressWarnings("rawtypes") - private String getLoginSuccessResponse(HttpSession session, String responseType) { - StringBuffer sb = new StringBuffer(); - int inactiveInterval = session.getMaxInactiveInterval(); + private String getLoginSuccessResponse(final HttpSession session, final String responseType) { + final StringBuffer sb = new StringBuffer(); + final int inactiveInterval = session.getMaxInactiveInterval(); - String user_UUID = (String)session.getAttribute("user_UUID"); + final String user_UUID = (String)session.getAttribute("user_UUID"); session.removeAttribute("user_UUID"); - String domain_UUID = (String)session.getAttribute("domain_UUID"); + final String domain_UUID = (String)session.getAttribute("domain_UUID"); session.removeAttribute("domain_UUID"); if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { sb.append("{ \"loginresponse\" : { "); - Enumeration attrNames = session.getAttributeNames(); + final Enumeration attrNames = session.getAttributeNames(); if (attrNames != null) { sb.append("\"timeout\" : \"" + inactiveInterval + "\""); while (attrNames.hasMoreElements()) { - String attrName = (String)attrNames.nextElement(); + final String attrName = (String)attrNames.nextElement(); if ("userid".equalsIgnoreCase(attrName)) { sb.append(", \"" + attrName + "\" : \"" + user_UUID + "\""); } else if ("domainid".equalsIgnoreCase(attrName)) { sb.append(", \"" + attrName + "\" : \"" + domain_UUID + "\""); } else { - Object attrObj = session.getAttribute(attrName); + final Object attrObj = session.getAttribute(attrName); if ((attrObj instanceof String) || (attrObj instanceof Long)) { sb.append(", \"" + attrName + "\" : \"" + attrObj.toString() + "\""); } @@ -428,16 +429,16 @@ public class ApiServlet extends HttpServlet { sb.append(""); sb.append(""); sb.append("" + inactiveInterval + ""); - Enumeration attrNames = session.getAttributeNames(); + final Enumeration attrNames = session.getAttributeNames(); if (attrNames != null) { while (attrNames.hasMoreElements()) { - String attrName = (String)attrNames.nextElement(); - if ("userid".equalsIgnoreCase(attrName)) { + final String attrName = (String)attrNames.nextElement(); + if (ApiConstants.USER_ID.equalsIgnoreCase(attrName)) { sb.append("<" + attrName + ">" + user_UUID + ""); } else if ("domainid".equalsIgnoreCase(attrName)) { sb.append("<" + attrName + ">" + domain_UUID + ""); } else { - Object attrObj = session.getAttribute(attrName); + final Object attrObj = session.getAttribute(attrName); if (attrObj instanceof String || attrObj instanceof Long || attrObj instanceof Short) { sb.append("<" + attrName + ">" + attrObj.toString() + ""); } @@ -450,8 +451,8 @@ public class ApiServlet extends HttpServlet { return sb.toString(); } - private String getLogoutSuccessResponse(String responseType) { - StringBuffer sb = new StringBuffer(); + private String getLogoutSuccessResponse(final String responseType) { + final StringBuffer sb = new StringBuffer(); if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) { sb.append("{ \"logoutresponse\" : { \"description\" : \"success\" } }"); } else { diff --git a/server/src/com/cloud/api/dispatch/CommandCreationWorker.java b/server/src/com/cloud/api/dispatch/CommandCreationWorker.java new file mode 100644 index 00000000000..30d4637d373 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/CommandCreationWorker.java @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ServerApiException; + +import com.cloud.exception.ResourceAllocationException; + + +/** + * This worker invokes create on the {@link BaseCmd} itself + * + * @author afornie + */ +public class CommandCreationWorker implements DispatchWorker { + + private static final String ATTEMP_TO_CREATE_NON_CREATION_CMD = + "Trying to invoke creation on a Command that is not " + + BaseAsyncCreateCmd.class.getName(); + + @Override + public void handle(final DispatchTask task) { + final BaseCmd cmd = task.getCmd(); + + if (cmd instanceof BaseAsyncCreateCmd) { + try { + ((BaseAsyncCreateCmd)cmd).create(); + } catch (final ResourceAllocationException e) { + throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, + e.getMessage(), e); + } + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, + ATTEMP_TO_CREATE_NON_CREATION_CMD); + } + } + +} diff --git a/server/src/com/cloud/api/dispatch/DispatchChain.java b/server/src/com/cloud/api/dispatch/DispatchChain.java new file mode 100644 index 00000000000..0ce0ce011e3 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/DispatchChain.java @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.util.ArrayList; +import java.util.List; +import org.apache.cloudstack.api.ServerApiException; + +public class DispatchChain { + + protected List workers = new ArrayList(); + + public DispatchChain add(final DispatchWorker worker) { + workers.add(worker); + return this; + } + + public void dispatch(final DispatchTask task) + throws ServerApiException { + + for (final DispatchWorker worker : workers) { + worker.handle(task); + } + } +} diff --git a/server/src/com/cloud/api/dispatch/DispatchChainFactory.java b/server/src/com/cloud/api/dispatch/DispatchChainFactory.java new file mode 100644 index 00000000000..135829ec4b8 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/DispatchChainFactory.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +import com.cloud.user.AccountManager; + +public class DispatchChainFactory { + + @Inject + protected AccountManager _accountMgr; + + @Inject + protected ParamGenericValidationWorker paramGenericValidationWorker; + + @Inject + protected ParamUnpackWorker paramUnpackWorker; + + @Inject + protected ParamProcessWorker paramProcessWorker; + + @Inject + protected SpecificCmdValidationWorker specificCmdValidationWorker; + + @Inject + protected CommandCreationWorker commandCreationWorker; + + protected DispatchChain standardDispatchChain; + + protected DispatchChain asyncCreationDispatchChain; + + @PostConstruct + public void setup() { + standardDispatchChain = new DispatchChain(). + add(paramUnpackWorker). + add(paramProcessWorker). + add(paramGenericValidationWorker). + add(specificCmdValidationWorker); + + asyncCreationDispatchChain = new DispatchChain(). + add(paramUnpackWorker). + add(paramProcessWorker). + add(paramGenericValidationWorker). + add(specificCmdValidationWorker). + add(commandCreationWorker); + } + + public DispatchChain getStandardDispatchChain() { + return standardDispatchChain; + } + + public DispatchChain getAsyncCreationDispatchChain() { + return asyncCreationDispatchChain; + } +} diff --git a/server/src/com/cloud/api/dispatch/DispatchTask.java b/server/src/com/cloud/api/dispatch/DispatchTask.java new file mode 100644 index 00000000000..f6cb1833150 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/DispatchTask.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.util.Map; + +import org.apache.cloudstack.api.BaseCmd; + +/** + * This class wraps all the data that any worker could need. If we don't wrap it this + * way and we pass the parameters one by one, in the end we could end up having all the + * N workers receiving plenty of parameters and changing the signature, each time one + * of them changes. This way, if a certain worker needs something else, you just need + * to change it in this wrapper class and the worker itself. + */ +@SuppressWarnings("rawtypes") +public class DispatchTask { + + protected BaseCmd cmd; + + protected Map params; + + public DispatchTask(final BaseCmd cmd, final Map params) { + this.cmd = cmd; + this.params = params; + } + + public BaseCmd getCmd() { + return cmd; + } + + public void setCmd(final BaseCmd cmd) { + this.cmd = cmd; + } + + public Map getParams() { + return params; + } + + public void setParams(final Map params) { + this.params = params; + } +} diff --git a/server/src/com/cloud/api/dispatch/DispatchWorker.java b/server/src/com/cloud/api/dispatch/DispatchWorker.java new file mode 100644 index 00000000000..a0dbc6a88a5 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/DispatchWorker.java @@ -0,0 +1,30 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import org.apache.cloudstack.api.ServerApiException; + +/** + * Describes the behavior of the workers in the Chain of Responsibility, that receive and + * work on a {@link DispatchTask} which will then be passed to next workers. + */ +public interface DispatchWorker { + + public void handle(DispatchTask task) + throws ServerApiException; +} diff --git a/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java b/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java new file mode 100644 index 00000000000..f456468948b --- /dev/null +++ b/server/src/com/cloud/api/dispatch/ParamGenericValidationWorker.java @@ -0,0 +1,103 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.log4j.Logger; + +/** + * This worker validates parameters in a generic way, by using annotated + * restrictions without involving the {@Link BaseCmd}. This worker doesn't + * know or care about the meaning of the parameters and that's why we can + * have it out of the {@Link BaseCmd} + * + * @author afornie + */ +public class ParamGenericValidationWorker implements DispatchWorker { + + static Logger s_logger = Logger.getLogger(ParamGenericValidationWorker.class.getName()); + + protected static final List defaultParamNames = new ArrayList(); + + static { + defaultParamNames.add(ApiConstants.CTX_START_EVENT_ID); + defaultParamNames.add(ApiConstants.COMMAND); + defaultParamNames.add(ApiConstants.CMD_EVENT_TYPE); + defaultParamNames.add(ApiConstants.USERNAME); + defaultParamNames.add(ApiConstants.USER_ID); + defaultParamNames.add(ApiConstants.PASSWORD); + defaultParamNames.add(ApiConstants.DOMAIN); + defaultParamNames.add(ApiConstants.DOMAIN_ID); + defaultParamNames.add(ApiConstants.DOMAIN__ID); + defaultParamNames.add(ApiConstants.SESSIONKEY); + defaultParamNames.add(ApiConstants.RESPONSE); + defaultParamNames.add(ApiConstants.PAGE); + defaultParamNames.add(ApiConstants.USER_API_KEY); + defaultParamNames.add(ApiConstants.API_KEY); + defaultParamNames.add(ApiConstants.PAGE_SIZE); + defaultParamNames.add(ApiConstants.HTTPMETHOD); + defaultParamNames.add(ApiConstants.SIGNATURE); + defaultParamNames.add(ApiConstants.CTX_ACCOUNT_ID); + defaultParamNames.add(ApiConstants.CTX_START_EVENT_ID); + defaultParamNames.add(ApiConstants.CTX_USER_ID); + defaultParamNames.add("_"); + } + + protected static final String ERROR_MSG_PREFIX = "Unknown parameters :"; + + @SuppressWarnings("rawtypes") + @Override + public void handle(final DispatchTask task) { + final BaseCmd cmd = task.getCmd(); + final Map params = task.getParams(); + + final List expectedParamNames = getParamNamesForCommand(cmd); + + final StringBuilder errorMsg = new StringBuilder(ERROR_MSG_PREFIX); + boolean foundUnknownParam = false; + for (final Object paramName : params.keySet()) { + if (!expectedParamNames.contains(paramName)) { + errorMsg.append(" ").append(paramName); + foundUnknownParam= true; + } + } + + if (foundUnknownParam) { + s_logger.warn(String.format("Received unknown parameters for command %s. %s", cmd.getActualCommandName(), errorMsg)); + } + } + + protected List getParamNamesForCommand(final BaseCmd cmd) { + final List paramNames = new ArrayList(); + // The expected param names are all the specific for the current command class ... + for (final Field field : cmd.getParamFields()) { + final Parameter parameterAnnotation = field.getAnnotation(Parameter.class); + paramNames.add(parameterAnnotation.name()); + } + // ... plus the default ones + paramNames.addAll(defaultParamNames); + return paramNames; + } +} diff --git a/server/src/com/cloud/api/dispatch/ParamProcessWorker.java b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java new file mode 100644 index 00000000000..e9bdd8b107b --- /dev/null +++ b/server/src/com/cloud/api/dispatch/ParamProcessWorker.java @@ -0,0 +1,428 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import static org.apache.commons.lang.StringUtils.isNotBlank; + +import java.lang.reflect.Field; +import java.text.DateFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.regex.Matcher; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.InfrastructureEntity; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCreateCmd; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.api.InternalIdentity; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.BaseCmd.CommandType; +import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; +import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; +import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; +import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd; +import org.apache.cloudstack.api.command.user.event.ListEventsCmd; +import org.apache.cloudstack.context.CallContext; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; + +public class ParamProcessWorker implements DispatchWorker { + + private static final Logger s_logger = Logger.getLogger(ParamProcessWorker.class.getName()); + + @Inject + protected AccountManager _accountMgr; + + @Inject + protected EntityManager _entityMgr; + + @Override + public void handle(final DispatchTask task) { + processParameters(task.getCmd(), task.getParams()); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public void processParameters(final BaseCmd cmd, final Map params) { + final Map entitiesToAccess = new HashMap(); + + final List cmdFields = cmd.getParamFields(); + + for (final Field field : cmdFields) { + final Parameter parameterAnnotation = field.getAnnotation(Parameter.class); + final Object paramObj = params.get(parameterAnnotation.name()); + if (paramObj == null) { + if (parameterAnnotation.required()) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + + " due to missing parameter " + parameterAnnotation.name()); + } + continue; + } + + // marshall the parameter into the correct type and set the field value + try { + setFieldValue(field, cmd, paramObj, parameterAnnotation); + } catch (final IllegalArgumentException argEx) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to execute API command " + cmd.getCommandName() + " due to invalid value " + paramObj + " for parameter " + + parameterAnnotation.name()); + } + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value " + paramObj + " for parameter " + + parameterAnnotation.name()); + } catch (final ParseException parseEx) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Invalid date parameter " + paramObj + " passed to command " + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)); + } + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to parse date " + paramObj + " for command " + + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + ", please pass dates in the format mentioned in the api documentation"); + } catch (final InvalidParameterValueException invEx) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Unable to execute API command " + + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8) + " due to invalid value. " + invEx.getMessage()); + } catch (final CloudRuntimeException cloudEx) { + s_logger.error("CloudRuntimeException", cloudEx); + // FIXME: Better error message? This only happens if the API command is not executable, which typically + //means + // there was + // and IllegalAccessException setting one of the parameters. + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Internal error executing API command " + + cmd.getCommandName().substring(0, cmd.getCommandName().length() - 8)); + } + + //check access on the resource this field points to + try { + final ACL checkAccess = field.getAnnotation(ACL.class); + final CommandType fieldType = parameterAnnotation.type(); + + if (checkAccess != null) { + // Verify that caller can perform actions in behalf of vm owner + //acumulate all Controlled Entities together. + + //parse the array of resource types and in case of map check access on key or value or both as specified in @acl + //implement external dao for classes that need findByName + //for maps, specify access to be checkd on key or value. + + // find the controlled entity DBid by uuid + if (parameterAnnotation.entityType() != null) { + final Class[] entityList = parameterAnnotation.entityType()[0].getAnnotation(EntityReference.class).value(); + + for (final Class entity : entityList) { + // Check if the parameter type is a single + // Id or list of id's/name's + switch (fieldType) { + case LIST: + final CommandType listType = parameterAnnotation.collectionType(); + switch (listType) { + case LONG: + case UUID: + final List listParam = (List)field.get(cmd); + for (final Long entityId : listParam) { + final Object entityObj = _entityMgr.findById(entity, entityId); + entitiesToAccess.put(entityObj, checkAccess.accessType()); + } + break; + /* + * case STRING: List listParam = + * new ArrayList(); listParam = + * (List)field.get(cmd); for(String + * entityName: listParam){ + * ControlledEntity entityObj = + * (ControlledEntity + * )daoClassInstance(entityId); + * entitiesToAccess.add(entityObj); } + * break; + */ + default: + break; + } + break; + case LONG: + case UUID: + final Object entityObj = _entityMgr.findById(entity, (Long)field.get(cmd)); + entitiesToAccess.put(entityObj, checkAccess.accessType()); + break; + default: + break; + } + + if (ControlledEntity.class.isAssignableFrom(entity)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("ControlledEntity name is:" + entity.getName()); + } + } + + if (InfrastructureEntity.class.isAssignableFrom(entity)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("InfrastructureEntity name is:" + entity.getName()); + } + } + } + + } + + } + + } catch (final IllegalArgumentException e) { + s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + + " is not accessible]"); + } catch (final IllegalAccessException e) { + s_logger.error("Error initializing command " + cmd.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error initializing parameters for command " + cmd.getCommandName() + " [field " + field.getName() + + " is not accessible]"); + } + + } + + doAccessChecks(cmd, entitiesToAccess); + } + + + private void doAccessChecks(final BaseCmd cmd, final Map entitiesToAccess) { + final Account caller = CallContext.current().getCallingAccount(); + final Account owner = _accountMgr.getActiveAccountById(cmd.getEntityOwnerId()); + + if (cmd instanceof BaseAsyncCreateCmd) { + //check that caller can access the owner account. + _accountMgr.checkAccess(caller, null, true, owner); + } + + if (!entitiesToAccess.isEmpty()) { + //check that caller can access the owner account. + _accountMgr.checkAccess(caller, null, true, owner); + for (final Object entity : entitiesToAccess.keySet()) { + if (entity instanceof ControlledEntity) { + _accountMgr.checkAccess(caller, entitiesToAccess.get(entity), true, (ControlledEntity)entity); + } else if (entity instanceof InfrastructureEntity) { + //FIXME: Move this code in adapter, remove code from Account manager + } + } + } + } + + + @SuppressWarnings({"unchecked", "rawtypes"}) + private void setFieldValue(final Field field, final BaseCmd cmdObj, final Object paramObj, final Parameter annotation) throws IllegalArgumentException, ParseException { + try { + field.setAccessible(true); + final CommandType fieldType = annotation.type(); + switch (fieldType) { + case BOOLEAN: + field.set(cmdObj, Boolean.valueOf(paramObj.toString())); + break; + case DATE: + // This piece of code is for maintaining backward compatibility + // and support both the date formats(Bug 9724) + // Do the date messaging for ListEventsCmd only + if (cmdObj instanceof ListEventsCmd || cmdObj instanceof DeleteEventsCmd || cmdObj instanceof ArchiveEventsCmd || + cmdObj instanceof ArchiveAlertsCmd || cmdObj instanceof DeleteAlertsCmd) { + final boolean isObjInNewDateFormat = isObjInNewDateFormat(paramObj.toString()); + if (isObjInNewDateFormat) { + final DateFormat newFormat = BaseCmd.NEW_INPUT_FORMAT; + synchronized (newFormat) { + field.set(cmdObj, newFormat.parse(paramObj.toString())); + } + } else { + final DateFormat format = BaseCmd.INPUT_FORMAT; + synchronized (format) { + Date date = format.parse(paramObj.toString()); + if (field.getName().equals("startDate")) { + date = messageDate(date, 0, 0, 0); + } else if (field.getName().equals("endDate")) { + date = messageDate(date, 23, 59, 59); + } + field.set(cmdObj, date); + } + } + } else { + final DateFormat format = BaseCmd.INPUT_FORMAT; + synchronized (format) { + format.setLenient(false); + field.set(cmdObj, format.parse(paramObj.toString())); + } + } + break; + case FLOAT: + // Assuming that the parameters have been checked for required before now, + // we ignore blank or null values and defer to the command to set a default + // value for optional parameters ... + if (paramObj != null && isNotBlank(paramObj.toString())) { + field.set(cmdObj, Float.valueOf(paramObj.toString())); + } + break; + case INTEGER: + // Assuming that the parameters have been checked for required before now, + // we ignore blank or null values and defer to the command to set a default + // value for optional parameters ... + if (paramObj != null && isNotBlank(paramObj.toString())) { + field.set(cmdObj, Integer.valueOf(paramObj.toString())); + } + break; + case LIST: + final List listParam = new ArrayList(); + final StringTokenizer st = new StringTokenizer(paramObj.toString(), ","); + while (st.hasMoreTokens()) { + final String token = st.nextToken(); + final CommandType listType = annotation.collectionType(); + switch (listType) { + case INTEGER: + listParam.add(Integer.valueOf(token)); + break; + case UUID: + if (token.isEmpty()) + break; + final Long internalId = translateUuidToInternalId(token, annotation); + listParam.add(internalId); + break; + case LONG: { + listParam.add(Long.valueOf(token)); + } + break; + case SHORT: + listParam.add(Short.valueOf(token)); + case STRING: + listParam.add(token); + break; + } + } + field.set(cmdObj, listParam); + break; + case UUID: + if (paramObj.toString().isEmpty()) + break; + final Long internalId = translateUuidToInternalId(paramObj.toString(), annotation); + field.set(cmdObj, internalId); + break; + case LONG: + field.set(cmdObj, Long.valueOf(paramObj.toString())); + break; + case SHORT: + field.set(cmdObj, Short.valueOf(paramObj.toString())); + break; + case STRING: + if ((paramObj != null) && paramObj.toString().length() > annotation.length()) { + s_logger.error("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName()); + throw new InvalidParameterValueException("Value greater than max allowed length " + annotation.length() + " for param: " + field.getName()); + } + field.set(cmdObj, paramObj.toString()); + break; + case TZDATE: + field.set(cmdObj, DateUtil.parseTZDateString(paramObj.toString())); + break; + case MAP: + default: + field.set(cmdObj, paramObj); + break; + } + } catch (final IllegalAccessException ex) { + s_logger.error("Error initializing command " + cmdObj.getCommandName() + ", field " + field.getName() + " is not accessible."); + throw new CloudRuntimeException("Internal error initializing parameters for command " + cmdObj.getCommandName() + " [field " + field.getName() + + " is not accessible]"); + } + } + + private boolean isObjInNewDateFormat(final String string) { + final Matcher matcher = BaseCmd.newInputDateFormat.matcher(string); + return matcher.matches(); + } + + private Date messageDate(final Date date, final int hourOfDay, final int minute, final int second) { + final Calendar cal = Calendar.getInstance(); + cal.setTime(date); + cal.set(Calendar.HOUR_OF_DAY, hourOfDay); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + return cal.getTime(); + } + + private Long translateUuidToInternalId(final String uuid, final Parameter annotation) { + if (uuid.equals("-1")) { + // FIXME: This is to handle a lot of hardcoded special cases where -1 is sent + // APITODO: Find and get rid of all hardcoded params in API Cmds and service layer + return -1L; + } + Long internalId = null; + // If annotation's empty, the cmd existed before 3.x try conversion to long + final boolean isPre3x = annotation.since().isEmpty(); + // Match against Java's UUID regex to check if input is uuid string + final boolean isUuid = uuid.matches("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"); + // Enforce that it's uuid for newly added apis from version 3.x + if (!isPre3x && !isUuid) + return null; + // Allow both uuid and internal id for pre3x apis + if (isPre3x && !isUuid) { + try { + internalId = Long.parseLong(uuid); + } catch (final NumberFormatException e) { + internalId = null; + } + if (internalId != null) + return internalId; + } + // There may be multiple entities defined on the @EntityReference of a Response.class + // UUID CommandType would expect only one entityType, so use the first entityType + final Class[] entities = annotation.entityType()[0].getAnnotation(EntityReference.class).value(); + // Go through each entity which is an interface to a VO class and get a VO object + // Try to getId() for the object using reflection, break on first non-null value + for (final Class entity : entities) { + // For backward compatibility, we search within removed entities and let service layer deal + // with removed ones, return empty response or error + final Object objVO = _entityMgr.findByUuidIncludingRemoved(entity, uuid); + if (objVO == null) { + continue; + } + // Invoke the getId method, get the internal long ID + // If that fails hide exceptions as the uuid may not exist + try { + internalId = ((InternalIdentity)objVO).getId(); + } catch (final IllegalArgumentException e) { + } catch (final NullPointerException e) { + } + // Return on first non-null Id for the uuid entity + if (internalId != null) + break; + } + if (internalId == null) { + if (s_logger.isDebugEnabled()) + s_logger.debug("Object entity uuid = " + uuid + " does not exist in the database."); + throw new InvalidParameterValueException("Invalid parameter " + annotation.name() + " value=" + uuid + + " due to incorrect long value format, or entity does not exist or due to incorrect parameter annotation for the field in api cmd class."); + } + return internalId; + } +} diff --git a/server/src/com/cloud/api/dispatch/ParamUnpackWorker.java b/server/src/com/cloud/api/dispatch/ParamUnpackWorker.java new file mode 100644 index 00000000000..12e12265d4d --- /dev/null +++ b/server/src/com/cloud/api/dispatch/ParamUnpackWorker.java @@ -0,0 +1,114 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; + +public class ParamUnpackWorker implements DispatchWorker { + + private static final Logger s_logger = Logger.getLogger(ParamUnpackWorker.class); + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public void handle(final DispatchTask task) throws ServerApiException { + final Map lowercaseParams = new HashMap(); + final Map params = task.getParams(); + for (final String key : params.keySet()) { + final int arrayStartIndex = key.indexOf('['); + final int arrayStartLastIndex = key.lastIndexOf('['); + if (arrayStartIndex != arrayStartLastIndex) { + throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + + "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); + } + + if (arrayStartIndex > 0) { + final int arrayEndIndex = key.indexOf(']'); + final int arrayEndLastIndex = key.lastIndexOf(']'); + if ((arrayEndIndex < arrayStartIndex) || (arrayEndIndex != arrayEndLastIndex)) { + // malformed parameter + throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + + "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); + } + + // Now that we have an array object, check for a field name in the case of a complex object + final int fieldIndex = key.indexOf('.'); + String fieldName = null; + if (fieldIndex < arrayEndIndex) { + throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + + "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); + } else { + fieldName = key.substring(fieldIndex + 1); + } + + // parse the parameter name as the text before the first '[' character + String paramName = key.substring(0, arrayStartIndex); + paramName = paramName.toLowerCase(); + + Map mapArray = null; + Map mapValue = null; + final String indexStr = key.substring(arrayStartIndex + 1, arrayEndIndex); + int index = 0; + boolean parsedIndex = false; + try { + if (indexStr != null) { + index = Integer.parseInt(indexStr); + parsedIndex = true; + } + } catch (final NumberFormatException nfe) { + s_logger.warn("Invalid parameter " + key + " received, unable to parse object array, returning an error."); + } + + if (!parsedIndex) { + throw new ServerApiException(ApiErrorCode.MALFORMED_PARAMETER_ERROR, "Unable to decode parameter " + key + + "; if specifying an object array, please use parameter[index].field=XXX, e.g. userGroupList[0].group=httpGroup"); + } + + final Object value = lowercaseParams.get(paramName); + if (value == null) { + // for now, assume object array with sub fields + mapArray = new HashMap(); + mapValue = new HashMap(); + mapArray.put(Integer.valueOf(index), mapValue); + } else if (value instanceof Map) { + mapArray = (HashMap)value; + mapValue = mapArray.get(Integer.valueOf(index)); + if (mapValue == null) { + mapValue = new HashMap(); + mapArray.put(Integer.valueOf(index), mapValue); + } + } + + // we are ready to store the value for a particular field into the map for this object + mapValue.put(fieldName, params.get(key)); + + lowercaseParams.put(paramName, mapArray); + } else { + lowercaseParams.put(key.toLowerCase(), params.get(key)); + } + } + + // The chain continues processing the unpacked parameters + task.setParams(lowercaseParams); + } + +} diff --git a/server/src/com/cloud/api/dispatch/SpecificCmdValidationWorker.java b/server/src/com/cloud/api/dispatch/SpecificCmdValidationWorker.java new file mode 100644 index 00000000000..3566a1ad411 --- /dev/null +++ b/server/src/com/cloud/api/dispatch/SpecificCmdValidationWorker.java @@ -0,0 +1,34 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +/** + * This worker validates parameters in a semantic way, that is of + * course specific for each {@link BaseCmd}, so actually it delegates + * the validation on the {@link BaseCmd} itself + * + */ +public class SpecificCmdValidationWorker implements DispatchWorker { + + @SuppressWarnings("unchecked") + @Override + public void handle(final DispatchTask task) { + task.getCmd().validateSpecificParameters(task.getParams()); + } + +} diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 96647f82113..0554e3a6e75 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -511,7 +511,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sb.and("state", sb.entity().getState(), SearchCriteria.Op.NEQ); sb.and("startId", sb.entity().getStartId(), SearchCriteria.Op.EQ); sb.and("createDate", sb.entity().getCreateDate(), SearchCriteria.Op.BETWEEN); - sb.and("displayEvent", sb.entity().getDisplayEvent(), SearchCriteria.Op.EQ); + sb.and("displayEvent", sb.entity().getDisplay(), SearchCriteria.Op.EQ); sb.and("archived", sb.entity().getArchived(), SearchCriteria.Op.EQ); SearchCriteria sc = sb.create(); @@ -754,6 +754,17 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { Filter searchFilter = new Filter(UserVmJoinVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); + List ids = null; + if (cmd.getId() != null) { + if (cmd.getIds() != null && !cmd.getIds().isEmpty()) { + throw new InvalidParameterValueException("Specify either id or ids but not both parameters"); + } + ids = new ArrayList(); + ids.add(cmd.getId()); + } else { + ids = cmd.getIds(); + } + // first search distinct vm id by using query criteria and pagination SearchBuilder sb = _userVmJoinDao.createSearchBuilder(); sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct ids @@ -794,7 +805,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { } sb.and("displayName", sb.entity().getDisplayName(), SearchCriteria.Op.LIKE); - sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); + sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN); sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE); sb.and("stateEQ", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); @@ -877,8 +888,11 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.setParameters("display", display); } - if (id != null) { - sc.setParameters("id", id); + if (ids != null) { + List idList = (ids instanceof List ? (List)ids : null); + if (idList != null && !idList.isEmpty()) { + sc.setParameters("idIN", idList.toArray()); + } } if (templateId != null) { diff --git a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java index d15371215c5..bf28c03e46f 100644 --- a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java @@ -71,6 +71,7 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase implements hostResponse.setVersion(host.getVersion()); hostResponse.setCreated(host.getCreated()); + List gpuGroups = ApiDBUtils.getGpuGroups(host.getId()); + if (gpuGroups != null && !gpuGroups.isEmpty()) { + List gpus = new ArrayList(); + for (HostGpuGroupsVO entry : gpuGroups) { + GpuResponse gpuResponse = new GpuResponse(); + gpuResponse.setGpuGroupName(entry.getGroupName()); + List vgpuTypes = ApiDBUtils.getVgpus(entry.getId()); + if (vgpuTypes != null && !vgpuTypes.isEmpty()) { + List vgpus = new ArrayList(); + for (VGPUTypesVO vgpuType : vgpuTypes) { + VgpuResponse vgpuResponse = new VgpuResponse(); + vgpuResponse.setName(vgpuType.getVgpuType()); + vgpuResponse.setCapacity(vgpuType.getRemainingCapacity()); + vgpus.add(vgpuResponse); + } + gpuResponse.setVgpu(vgpus); + } + gpus.add(gpuResponse); + } + hostResponse.setGpuGroups(gpus); + } if (details.contains(HostDetails.all) || details.contains(HostDetails.capacity) || details.contains(HostDetails.stats) || details.contains(HostDetails.events)) { hostResponse.setOsCategoryId(host.getOsCategoryUuid()); diff --git a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 7e113e4baed..7fcc5c67236 100644 --- a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -73,6 +73,10 @@ public class ServiceOfferingJoinDaoImpl extends GenericDaoBase im @Inject private ConfigurationDao _configDao; @Inject - public AccountService _accountService; + private AccountService _accountService; private final SearchBuilder tmpltIdPairSearch; @@ -98,7 +98,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im _count = "select count(distinct temp_zone_pair) from template_view WHERE "; } - private String getTemplateStatus(TemplateJoinVO template) { String templateStatus = null; if (template.getDownloadState() != Status.DOWNLOADED) { diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index a5dfb16224e..779ddb4c35f 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -42,7 +42,9 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.ResourceTagJoinVO; import com.cloud.api.query.vo.UserVmJoinVO; +import com.cloud.gpu.GPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.uservm.UserVm; @@ -162,6 +164,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setCpuNumber(userVm.getCpu()); userVmResponse.setCpuSpeed(userVm.getSpeed()); userVmResponse.setMemory(userVm.getRamSize()); + ServiceOfferingDetailsVO serviceOfferingDetail = ApiDBUtils.findServiceOfferingDetail(userVm.getServiceOfferingId(), GPU.Keys.vgpuType.toString()); + if (serviceOfferingDetail != null) { + userVmResponse.setVgpu(serviceOfferingDetail.getValue()); + } } userVmResponse.setGuestOsId(userVm.getGuestOsUuid()); if (details.contains(VMDetails.all) || details.contains(VMDetails.volume)) { diff --git a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java index 9d81bc45b6b..e3e0a9e2186 100644 --- a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java +++ b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java @@ -70,6 +70,9 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "max_iops") private Long maxIops; + @Column(name = "hv_ss_reserve") + private Integer hypervisorSnapshotReserve; + @Column(name = "sort_key") int sortKey; @@ -165,8 +168,8 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, return maxIops; } - public void setMaxIops(Long maxIops) { - this.maxIops = maxIops; + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; } public String getCacheMode() { diff --git a/server/src/com/cloud/api/query/vo/EventJoinVO.java b/server/src/com/cloud/api/query/vo/EventJoinVO.java index 91e2e6a2f60..b6b023ef6e1 100644 --- a/server/src/com/cloud/api/query/vo/EventJoinVO.java +++ b/server/src/com/cloud/api/query/vo/EventJoinVO.java @@ -108,8 +108,8 @@ public class EventJoinVO extends BaseViewVO implements ControlledViewEntity { @Column(name = "archived") private boolean archived; - @Column(name = "display_event") - protected boolean displayEvent = true; + @Column(name = "display") + protected boolean display = true; public EventJoinVO() { } @@ -222,8 +222,8 @@ public class EventJoinVO extends BaseViewVO implements ControlledViewEntity { return archived; } - public boolean getDisplayEvent() { - return displayEvent; + public boolean getDisplay() { + return display; } @Override diff --git a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java index 06b9e6b4231..dbeb53062f3 100644 --- a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java +++ b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java @@ -87,6 +87,18 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit @Column(name = "vm_type") private String vmType; + @Column(name = "customized_iops") + private Boolean customizedIops; + + @Column(name = "min_iops") + private Long minIops; + + @Column(name = "max_iops") + private Long maxIops; + + @Column(name = "hv_ss_reserve") + private Integer hypervisorSnapshotReserve; + @Column(name = "sort_key") int sortKey; @@ -180,6 +192,22 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit return domainPath; } + public Boolean isCustomizedIops() { + return customizedIops; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + public int getSortKey() { return sortKey; } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 98e5d3424a2..f2fe68a78a9 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -439,7 +439,7 @@ public enum Config { "Console proxy command port that is used to communicate with management server", null), ConsoleProxyRestart("Console Proxy", AgentManager.class, Boolean.class, "consoleproxy.restart", "true", "Console proxy restart flag, defaulted to true", null), - ConsoleProxyUrlDomain("Console Proxy", AgentManager.class, String.class, "consoleproxy.url.domain", "realhostip.com", "Console proxy url domain", null), + ConsoleProxyUrlDomain("Console Proxy", AgentManager.class, String.class, "consoleproxy.url.domain", "", "Console proxy url domain", null), ConsoleProxyLoadscanInterval( "Console Proxy", AgentManager.class, @@ -782,7 +782,7 @@ public enum Config { ManagementServer.class, String.class, "secstorage.ssl.cert.domain", - "realhostip.com", + "", "SSL certificate used to encrypt copy traffic between zones", null), SecStorageCapacityStandby( diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index b739d30cdc7..9b9bd13d261 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -36,6 +36,8 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -83,7 +85,6 @@ import org.apache.cloudstack.region.dao.RegionDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.log4j.Logger; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -130,6 +131,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.gpu.GPU; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; @@ -2017,15 +2019,54 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), - cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate()); + cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), + cmd.getHypervisorSnapshotReserve()); } protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed, String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, - Integer networkRate, String deploymentPlanner, Map details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) { + Integer networkRate, String deploymentPlanner, Map details, Boolean isCustomizedIops, Long minIops, Long maxIops, + Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) { tags = StringUtils.cleanupTags(tags); ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner); + + if (isCustomizedIops != null) { + bytesReadRate = null; + bytesWriteRate = null; + iopsReadRate = null; + iopsWriteRate = null; + + if (isCustomizedIops) { + minIops = null; + maxIops = null; + } else { + if (minIops == null && maxIops == null) { + minIops = 0L; + maxIops = 0L; + } else { + if (minIops == null || minIops <= 0) { + throw new InvalidParameterValueException("The min IOPS must be greater than 0."); + } + + if (maxIops == null) { + maxIops = 0L; + } + + if (minIops > maxIops) { + throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); + } + } + } + } else { + minIops = null; + maxIops = null; + } + + offering.setCustomizedIops(isCustomizedIops); + offering.setMinIops(minIops); + offering.setMaxIops(maxIops); + if ((bytesReadRate != null) && (bytesReadRate > 0)) offering.setBytesReadRate(bytesReadRate); if ((bytesWriteRate != null) && (bytesWriteRate > 0)) @@ -2035,13 +2076,62 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if ((iopsWriteRate != null) && (iopsWriteRate > 0)) offering.setIopsWriteRate(iopsWriteRate); - if ((offering = _serviceOfferingDao.persist(offering)) != null) { + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { + throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); + } + + offering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + + List detailsVO = null; if (details != null) { - List detailsVO = new ArrayList(); + // Check if the user has passed the gpu-type before passing the VGPU type + if (!details.containsKey(GPU.Keys.pciDevice.toString()) && details.containsKey(GPU.Keys.vgpuType.toString())) { + throw new InvalidParameterValueException("Please specify the gpu type"); + } + detailsVO = new ArrayList(); for (Entry detailEntry : details.entrySet()) { + String value = null; + if (detailEntry.getKey().equals(GPU.Keys.pciDevice.toString())) { + for (GPU.Type type : GPU.Type.values()) { + if (detailEntry.getValue().equals(type.toString())) { + value = detailEntry.getValue(); + } + } + if (value == null) { + throw new InvalidParameterValueException("Please specify valid gpu type"); + } + } + if (detailEntry.getKey().equals(GPU.Keys.vgpuType.toString())) { + if (details.get(GPU.Keys.pciDevice.toString()).equals(GPU.Type.GPU_Passthrough.toString())) { + throw new InvalidParameterValueException("vgpuTypes are supported only with vGPU pciDevice"); + } + if (detailEntry.getValue() == null) { + throw new InvalidParameterValueException("With vGPU as pciDevice, vGPUType value cannot be null"); + } + for (GPU.vGPUType entry : GPU.vGPUType.values()) { + if (detailEntry.getValue().equals(entry.getType())) { + value = entry.getType(); + } + } + if (value == null || detailEntry.getValue().equals(GPU.vGPUType.passthrough.getType())) { + throw new InvalidParameterValueException("Please specify valid vGPU type"); + } + } detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), detailEntry.getKey(), detailEntry.getValue(), true)); } + // If pciDevice type is passed, put the default VGPU type as 'passthrough' + if (details.containsKey(GPU.Keys.pciDevice.toString()) + && !details.containsKey(GPU.Keys.vgpuType.toString())) { + detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), + GPU.Keys.vgpuType.toString(), GPU.vGPUType.passthrough.getType(), true)); + } + } + if ((offering = _serviceOfferingDao.persist(offering)) != null) { + if (detailsVO != null && !detailsVO.isEmpty()) { + for (int index = 0; index < detailsVO.size(); index++) { + detailsVO.get(index).setResourceId(offering.getId()); + } _serviceOfferingDetailsDao.saveDetails(detailsVO); } CallContext.current().setEventDetails("Service offering id=" + offering.getId()); @@ -2536,7 +2626,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati networkId = network.getId(); zoneId = network.getDataCenterId(); } - } else if (network.getGuestType() == null || network.getGuestType() == Network.GuestType.Isolated) { + } else if (network.getGuestType() == null || + (network.getGuestType() == Network.GuestType.Isolated + && _ntwkOffServiceMapDao.areServicesSupportedByNetworkOffering(network.getNetworkOfferingId(), Service.SourceNat))) { throw new InvalidParameterValueException("Can't create direct vlan for network id=" + networkId + " with type: " + network.getGuestType()); } } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 3e4c57e6852..05120963961 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -233,6 +233,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy private int _proxySessionTimeoutValue = DEFAULT_PROXY_SESSION_TIMEOUT; private boolean _sslEnabled = true; + private String _consoleProxyUrlDomain; // global load picture at zone basis private SystemVmLoadScanner _loadScanner; @@ -384,9 +385,9 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy assert (ksVo != null); if (_staticPublicIp == null) { - return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(), ksVo.getDomainSuffix()); + return new ConsoleProxyInfo(proxy.isSslEnabled(), proxy.getPublicIpAddress(), _consoleProxyPort, proxy.getPort(), _consoleProxyUrlDomain); } else { - return new ConsoleProxyInfo(proxy.isSslEnabled(), _staticPublicIp, _consoleProxyPort, _staticPort, ksVo.getDomainSuffix()); + return new ConsoleProxyInfo(proxy.isSslEnabled(), _staticPublicIp, _consoleProxyPort, _staticPort, _consoleProxyUrlDomain); } } @@ -1191,6 +1192,12 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy _sslEnabled = true; } + _consoleProxyUrlDomain = configs.get(Config.ConsoleProxyUrlDomain.key()); + if( _sslEnabled && (_consoleProxyUrlDomain == null || _consoleProxyUrlDomain.isEmpty())) { + s_logger.warn("Empty console proxy domain, explicitly disabling SSL"); + _sslEnabled = false; + } + value = configs.get(Config.ConsoleProxyCapacityScanInterval.key()); _capacityScanInterval = NumbersUtil.parseLong(value, DEFAULT_CAPACITY_SCAN_INTERVAL); diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index ee8cc4dcf2a..74c141edbd1 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -81,6 +81,7 @@ import com.cloud.deploy.dao.PlannerHostReservationDao; import com.cloud.exception.AffinityConflictException; import com.cloud.exception.ConnectionException; import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.gpu.GPU; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; @@ -89,7 +90,10 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; import com.cloud.org.Grouping; +import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceState; +import com.cloud.service.ServiceOfferingDetailsVO; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; @@ -213,6 +217,10 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy DataStoreManager dataStoreMgr; @Inject protected ClusterDetailsDao _clusterDetailsDao; + @Inject + protected ResourceManager _resourceMgr; + @Inject + protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao; protected List _planners; @@ -353,6 +361,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy s_logger.debug("This VM has last host_id specified, trying to choose the same host: " + vm.getLastHostId()); HostVO host = _hostDao.findById(vm.getLastHostId()); + ServiceOfferingDetailsVO offeringDetails = null; if (host == null) { s_logger.debug("The last host of this VM cannot be found"); } else if (avoids.shouldAvoid(host)) { @@ -360,6 +369,9 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } else if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) { s_logger.debug("The last Host, hostId: " + host.getId() + " already has max Running VMs(count includes system VMs), skipping this and trying other available hosts"); + } else if ((offeringDetails = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString())) != null + && !_resourceMgr.isGPUDeviceAvailable(host.getId(), offeringDetails.getValue())){ + s_logger.debug("The last host of this VM does not have required GPU devices available"); } else { if (host.getStatus() == Status.Up && host.getResourceState() == ResourceState.Enabled) { boolean hostTagsMatch = true; diff --git a/server/src/com/cloud/event/ActionEventUtils.java b/server/src/com/cloud/event/ActionEventUtils.java index a9999dc520e..28e5680b630 100755 --- a/server/src/com/cloud/event/ActionEventUtils.java +++ b/server/src/com/cloud/event/ActionEventUtils.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.utils.ReflectUtil; import com.cloud.vm.VirtualMachine; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -159,7 +160,7 @@ public class ActionEventUtils { event.setType(type); event.setState(state); event.setDescription(description); - event.setDisplayEventEnabled(eventDisplayEnabled); + event.setDisplay(eventDisplayEnabled); if (domainId != null) { event.setDomainId(domainId); @@ -186,13 +187,21 @@ public class ActionEventUtils { // get the entity details for which ActionEvent is generated String entityType = null; String entityUuid = null; + CallContext context = CallContext.current(); + String vmEntityName = ReflectUtil.getEntityName(VirtualMachine.class); + String vmuuid = (String) context.getContextParameter(vmEntityName); Class entityKey = getEntityKey(eventType); if (entityKey != null) { - CallContext context = CallContext.current(); + //FIXME - Remove this since it should be covered by the else if condition below. entityUuid = (String)context.getContextParameter(entityKey); if (entityUuid != null) entityType = entityKey.getName(); + }else if (EventTypes.getEntityForEvent(eventType) != null){ + entityType = EventTypes.getEntityForEvent(eventType); + if (entityType != null){ + entityUuid = (String)context.getContextParameter(entityType); + } } org.apache.cloudstack.framework.events.Event event = @@ -215,6 +224,8 @@ public class ActionEventUtils { eventDescription.put("status", state.toString()); eventDescription.put("entity", entityType); eventDescription.put("entityuuid", entityUuid); + //Put all the first class entities that are touched during the action. For now atleast put in the vmid. + eventDescription.put(vmEntityName, vmuuid); eventDescription.put("description", description); String eventDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z").format(new Date()); @@ -240,6 +251,7 @@ public class ActionEventUtils { private static Class getEntityKey(String eventType) { + // FIXME - Remove this if (eventType.startsWith("DOMAIN.")) { return Domain.class; @@ -251,8 +263,6 @@ public class ActionEventUtils { else if (eventType.startsWith("USER.")) { return User.class; - }else if (eventType.startsWith("VM.")){ - return VirtualMachine.class; } return null; diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index 7fb79fa1578..4af20c6f00d 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -25,8 +25,12 @@ import com.cloud.agent.api.Command; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.gpu.GPU; import com.cloud.offering.ServiceOffering; +import com.cloud.resource.ResourceManager; import com.cloud.server.ConfigurationServer; +import com.cloud.service.ServiceOfferingDetailsVO; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.dao.VMTemplateDetailsDao; import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; @@ -55,6 +59,10 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis NicSecondaryIpDao _nicSecIpDao; @Inject ConfigurationServer _configServer; + @Inject + ResourceManager _resourceMgr; + @Inject + ServiceOfferingDetailsDao _serviceOfferingDetailsDao; protected HypervisorGuruBase() { super(); @@ -125,6 +133,13 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis if (detailsInVm != null) { to.setDetails(detailsInVm); } + + // Set GPU details + ServiceOfferingDetailsVO offeringDetail = null; + if ((offeringDetail = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString())) != null) { + to.setGpuDevice(_resourceMgr.getGPUDevice(vm.getHostId(), offeringDetail.getValue())); + } + // Workaround to make sure the TO has the UUID we need for Niciri integration VMInstanceVO vmInstance = _virtualMachineDao.findById(to.getId()); // check if XStools/VMWare tools are present in the VM and dynamic scaling feature is enabled (per zone/global) diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 5905fd3de43..4b7d4b96ab9 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -693,7 +693,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage errorMessage.append(", vlanId id=" + Arrays.toString(dedicatedVlanDbIds.toArray())); } else if (nonDedicatedVlanDbIds != null && !nonDedicatedVlanDbIds.isEmpty()) { sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray()); - errorMessage.append(", vlanId id=" + nonDedicatedVlanDbIds.toArray()); + errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray())); } else { if (podId != null) { InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Insufficient address capacity", Pod.class, podId); @@ -733,7 +733,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage if (useSystemIps && nonDedicatedVlanDbIds != null && !nonDedicatedVlanDbIds.isEmpty()) { fetchFromDedicatedRange = false; sc.setParameters("vlanId", nonDedicatedVlanDbIds.toArray()); - errorMessage.append(", vlanId id=" + nonDedicatedVlanDbIds.toArray()); + errorMessage.append(", vlanId id=" + Arrays.toString(nonDedicatedVlanDbIds.toArray())); addrs = _ipAddressDao.lockRows(sc, filter, true); } } diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index 9212a89b838..7b4b2bebdb2 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -1533,28 +1533,34 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { @Override public void checkNetworkPermissions(Account owner, Network network) { + // dahn 20140310: I was thinking of making this an assert but + // as we hardly ever test with asserts I think + // we better make sure at runtime. + if (network == null) { + throw new CloudRuntimeException("cannot check permissions on (Network) "); + } // Perform account permission check if (network.getGuestType() != Network.GuestType.Shared || (network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Account)) { AccountVO networkOwner = _accountDao.findById(network.getAccountId()); if (networkOwner == null) - throw new PermissionDeniedException("Unable to use network with id= " + ((network != null) ? ((NetworkVO)network).getUuid() : "") + + throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", network does not have an owner"); if (owner.getType() != Account.ACCOUNT_TYPE_PROJECT && networkOwner.getType() == Account.ACCOUNT_TYPE_PROJECT) { if (!_projectAccountDao.canAccessProjectAccount(owner.getAccountId(), network.getAccountId())) { - throw new PermissionDeniedException("Unable to use network with id= " + ((network != null) ? ((NetworkVO)network).getUuid() : "") + + throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", permission denied"); } } else { List networkMap = _networksDao.listBy(owner.getId(), network.getId()); if (networkMap == null || networkMap.isEmpty()) { - throw new PermissionDeniedException("Unable to use network with id= " + ((network != null) ? ((NetworkVO)network).getUuid() : "") + + throw new PermissionDeniedException("Unable to use network with id= " + ((NetworkVO)network).getUuid() + ", permission denied"); } } } else { if (!isNetworkAvailableInDomain(network.getId(), owner.getDomainId())) { - throw new PermissionDeniedException("Shared network id=" + ((network != null) ? ((NetworkVO)network).getUuid() : "") + " is not available in domain id=" + + throw new PermissionDeniedException("Shared network id=" + ((NetworkVO)network).getUuid() + " is not available in domain id=" + owner.getDomainId()); } } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 828e6bb4567..be95a360bd4 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -1238,7 +1238,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // Vlan is created in 1 cases - works in Advance zone only: // 1) GuestType is Shared - boolean createVlan = (startIP != null && endIP != null && zone.getNetworkType() == NetworkType.Advanced && (ntwkOff.getGuestType() == Network.GuestType.Shared)); + boolean createVlan = (startIP != null && endIP != null && zone.getNetworkType() == NetworkType.Advanced + && ((ntwkOff.getGuestType() == Network.GuestType.Shared) + || (ntwkOff.getGuestType() == GuestType.Isolated && + !areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)))); if (!createVlan) { // Only support advance shared network in IPv6, which means createVlan is a must diff --git a/server/src/com/cloud/network/NetworkUsageManagerImpl.java b/server/src/com/cloud/network/NetworkUsageManagerImpl.java index e9b039369aa..13eb2107e8b 100755 --- a/server/src/com/cloud/network/NetworkUsageManagerImpl.java +++ b/server/src/com/cloud/network/NetworkUsageManagerImpl.java @@ -57,6 +57,7 @@ import com.cloud.event.UsageEventVO; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.host.DetailVO; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -116,6 +117,8 @@ public class NetworkUsageManagerImpl extends ManagerBase implements NetworkUsage @Inject HostDetailsDao _detailsDao; @Inject + HostGpuGroupsDao _hostGpuGroupsDao; + @Inject AccountManager _accountMgr; @Inject NetworkDao _networksDao = null; @@ -537,6 +540,7 @@ public class NetworkUsageManagerImpl extends ManagerBase implements NetworkUsage long hostId = host.getId(); _agentMgr.disconnectWithoutInvestigation(hostId, Status.Event.Remove); _detailsDao.deleteDetails(hostId); + _hostGpuGroupsDao.deleteGpuEntries(hostId); host.setGuid(null); _hostDao.update(hostId, host); _hostDao.remove(hostId); diff --git a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java index e07e5023551..755fc545fc4 100644 --- a/server/src/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/com/cloud/network/as/AutoScaleManagerImpl.java @@ -60,7 +60,8 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import com.cloud.api.ApiDBUtils; -import com.cloud.api.ApiDispatcher; +import com.cloud.api.dispatch.DispatchChainFactory; +import com.cloud.api.dispatch.DispatchTask; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -128,6 +129,8 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScale private static final Logger s_logger = Logger.getLogger(AutoScaleManagerImpl.class); private ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1); + @Inject + protected DispatchChainFactory dispatchChainFactory = null; @Inject EntityManager _entityMgr; @Inject @@ -367,7 +370,7 @@ public class AutoScaleManagerImpl extends ManagerBase implements AutoScale * For ex. if projectId is given as a string instead of an long value, this * will be throwing an error. */ - ApiDispatcher.processParameters(new DeployVMCmd(), deployParams); + dispatchChainFactory.getStandardDispatchChain().dispatch(new DispatchTask(new DeployVMCmd(), deployParams)); AutoScaleVmProfileVO profileVO = new AutoScaleVmProfileVO(cmd.getZoneId(), cmd.getDomainId(), cmd.getAccountId(), cmd.getServiceOfferingId(), cmd.getTemplateId(), cmd.getOtherDeployParams(), diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 455e75ff91b..3f4ba5b5d3f 100755 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -16,26 +16,6 @@ // under the License. package com.cloud.network.element; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.ejb.Local; -import javax.inject.Inject; - -import org.apache.log4j.Logger; - -import com.google.gson.Gson; - -import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd; -import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd; -import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd; -import org.apache.cloudstack.api.command.admin.router.ListOvsElementsCmd; -import org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; - import com.cloud.agent.api.to.LoadBalancerTO; import com.cloud.configuration.ConfigurationManager; import com.cloud.dc.DataCenter; @@ -101,6 +81,22 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; +import com.google.gson.Gson; +import org.apache.cloudstack.api.command.admin.router.ConfigureOvsElementCmd; +import org.apache.cloudstack.api.command.admin.router.ConfigureVirtualRouterElementCmd; +import org.apache.cloudstack.api.command.admin.router.CreateVirtualRouterElementCmd; +import org.apache.cloudstack.api.command.admin.router.ListOvsElementsCmd; +import org.apache.cloudstack.api.command.admin.router.ListVirtualRouterElementsCmd; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.log4j.Logger; + +import javax.ejb.Local; +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; @Local(value = {NetworkElement.class, FirewallServiceProvider.class, DhcpServiceProvider.class, UserDataServiceProvider.class, @@ -110,7 +106,7 @@ import com.cloud.vm.dao.UserVmDao; public class VirtualRouterElement extends AdapterBase implements VirtualRouterElementService, DhcpServiceProvider, UserDataServiceProvider, SourceNatServiceProvider, StaticNatServiceProvider, FirewallServiceProvider, LoadBalancingServiceProvider, PortForwardingServiceProvider, RemoteAccessVPNServiceProvider, IpDeployer, - NetworkMigrationResponder { + NetworkMigrationResponder, AggregatedCommandExecutor { private static final Logger s_logger = Logger.getLogger(VirtualRouterElement.class); public static final AutoScaleCounterType AutoScaleCounterCpu = new AutoScaleCounterType("cpu"); public static final AutoScaleCounterType AutoScaleCounterMemory = new AutoScaleCounterType("memory"); @@ -1091,4 +1087,37 @@ public class VirtualRouterElement extends AdapterBase implements VirtualRouterEl _userVmMgr.setupVmForPvlan(true, userVm.getHostId(), nic); } } + + @Override + public boolean prepareAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException { + List routers = getRouters(network, dest); + + if ((routers == null) || (routers.size() == 0)) { + throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId()); + } + + return _routerMgr.prepareAggregatedExecution(network, routers); + } + + @Override + public boolean completeAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException { + List routers = getRouters(network, dest); + + if ((routers == null) || (routers.size() == 0)) { + throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId()); + } + + return _routerMgr.completeAggregatedExecution(network, routers); + } + + @Override + public boolean cleanupAggregatedExecution(Network network, DeployDestination dest) throws ResourceUnavailableException { + List routers = getRouters(network, dest); + + if ((routers == null) || (routers.size() == 0)) { + throw new ResourceUnavailableException("Can't find at least one router!", DataCenter.class, network.getDataCenterId()); + } + + return _routerMgr.cleanupAggregatedExecution(network, routers); + } } diff --git a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java index 5695a2c9fcd..b433fe4532f 100644 --- a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java @@ -36,6 +36,7 @@ import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; +import com.cloud.network.Network.State; import com.cloud.network.NetworkModel; import com.cloud.network.PublicIpAddress; import com.cloud.network.RemoteAccessVpn; @@ -179,7 +180,11 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc DomainRouterVO router = routers.get(0); //Add router to guest network if needed if (!_networkMgr.isVmPartOfNetwork(router.getId(), network.getId())) { - if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, false)) { + Map paramsForRouter = new HashMap(1); + if (network.getState() == State.Setup) { + paramsForRouter.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true); + } + if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, false, paramsForRouter)) { throw new CloudRuntimeException("Failed to add VPC router " + router + " to guest network " + network); } else { s_logger.debug("Successfully added VPC router " + router + " to guest network " + network); @@ -220,7 +225,12 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc DomainRouterVO router = routers.get(0); //Add router to guest network if needed if (!_networkMgr.isVmPartOfNetwork(router.getId(), network.getId())) { - if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, false)) { + Map paramsForRouter = new HashMap(1); + // need to reprogram guest network if it comes in a setup state + if (network.getState() == State.Setup) { + paramsForRouter.put(VirtualMachineProfile.Param.ReProgramGuestNetworks, true); + } + if (!_vpcRouterMgr.addVpcRouterToGuestNetwork(router, network, false, paramsForRouter)) { throw new CloudRuntimeException("Failed to add VPC router " + router + " to guest network " + network); } else { s_logger.debug("Successfully added VPC router " + router + " to guest network " + network); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index 9097b873caf..e3597acc313 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -16,12 +16,8 @@ // under the License. package com.cloud.network.router; -import java.util.List; -import java.util.Map; - -import org.apache.cloudstack.framework.config.ConfigKey; - import com.cloud.deploy.DeployDestination; +import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; @@ -39,6 +35,10 @@ import com.cloud.utils.component.Manager; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.framework.config.ConfigKey; + +import java.util.List; +import java.util.Map; /** * NetworkManager manages the network for the different end users. @@ -129,4 +129,10 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA boolean removeDhcpSupportForSubnet(Network network, List routers) throws ResourceUnavailableException; boolean setupDhcpForPvlan(boolean add, DomainRouterVO router, Long hostId, NicProfile nic); + + public boolean prepareAggregatedExecution(Network network, List routers) throws AgentUnavailableException; + + public boolean completeAggregatedExecution(Network network, List routers) throws AgentUnavailableException; + + public boolean cleanupAggregatedExecution(Network network, List routers) throws AgentUnavailableException; } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 37171f540a1..74cfd74a8e6 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -17,47 +17,6 @@ package com.cloud.network.router; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.api.command.admin.router.RebootRouterCmd; -import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; -import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; -import org.apache.cloudstack.config.ApiServiceConfiguration; -import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; -import org.apache.cloudstack.framework.config.ConfigDepot; -import org.apache.cloudstack.framework.config.ConfigKey; -import org.apache.cloudstack.framework.config.Configurable; -import org.apache.cloudstack.framework.config.dao.ConfigurationDao; -import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; -import org.apache.cloudstack.managed.context.ManagedContextRunnable; -import org.apache.cloudstack.utils.identity.ManagementServerNode; -import org.apache.log4j.Logger; - import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -78,6 +37,8 @@ import com.cloud.agent.api.PvlanSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; +import com.cloud.agent.api.routing.AggregationControlCommand; +import com.cloud.agent.api.routing.AggregationControlCommand.Action; import com.cloud.agent.api.routing.CreateIpAliasCommand; import com.cloud.agent.api.routing.DeleteIpAliasCommand; import com.cloud.agent.api.routing.DhcpEntryCommand; @@ -105,7 +66,6 @@ import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.api.ApiAsyncJobDispatcher; -import com.cloud.api.ApiDispatcher; import com.cloud.api.ApiGsonHelper; import com.cloud.cluster.ManagementServerHostVO; import com.cloud.cluster.dao.ManagementServerHostDao; @@ -278,6 +238,45 @@ import com.cloud.vm.dao.NicIpAliasVO; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.api.command.admin.router.RebootRouterCmd; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; +import org.apache.cloudstack.config.ApiServiceConfiguration; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.framework.config.ConfigDepot; +import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.framework.config.Configurable; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.log4j.Logger; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; /** * VirtualNetworkApplianceManagerImpl manages the different types of virtual network appliances available in the Cloud Stack. @@ -2321,12 +2320,20 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V final List routerGuestNtwkIds = _routerDao.getRouterNetworks(router.getId()); for (final Long guestNetworkId : routerGuestNtwkIds) { + AggregationControlCommand startCmd = new AggregationControlCommand(Action.Start, router.getInstanceName(), controlNic.getIp4Address(), + getRouterIpInNetwork(guestNetworkId, router.getId())); + cmds.addCommand(startCmd); + if (reprogramGuestNtwks) { finalizeIpAssocForNetwork(cmds, router, provider, guestNetworkId, null); finalizeNetworkRulesForNetwork(cmds, router, provider, guestNetworkId); } finalizeUserDataAndDhcpOnStart(cmds, router, provider, guestNetworkId); + + AggregationControlCommand finishCmd = new AggregationControlCommand(Action.Finish, router.getInstanceName(), controlNic.getIp4Address(), + getRouterIpInNetwork(guestNetworkId, router.getId())); + cmds.addCommand(finishCmd); } @@ -2339,8 +2346,6 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V finalizeMonitorServiceOnStrat(cmds, profile, router, provider, routerGuestNtwkIds.get(0), false); } - - return true; } @@ -2736,12 +2741,10 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V final Commands cmds = new Commands(Command.OnError.Stop); createApplyVpnCommands(true, vpn, router, cmds); - try { - _agentMgr.send(router.getHostId(), cmds); - } catch (final OperationTimedoutException e) { - s_logger.debug("Failed to start remote access VPN: ", e); - throw new AgentUnavailableException("Unable to send commands to virtual router ", router.getHostId(), e); + if (!sendCommandsToRouter(router, cmds)) { + throw new AgentUnavailableException("Unable to send commands to virtual router ", router.getHostId()); } + Answer answer = cmds.getAnswer("users"); if (!answer.getResult()) { s_logger.error("Unable to start vpn: unable add users to vpn in zone " + router.getDataCenterId() + " for account " + vpn.getAccountId() + " on domR: " + @@ -4197,7 +4200,6 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V for(DomainRouterVO router: routers){ if(!checkRouterVersion(router)){ s_logger.debug("Upgrading template for router: "+router.getId()); - ApiDispatcher.getInstance(); Map params = new HashMap(); params.put("ctxUserId", "1"); params.put("ctxAccountId", "" + router.getAccountId()); @@ -4263,4 +4265,31 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V } } } + + protected boolean aggregationExecution(AggregationControlCommand.Action action, Network network, List routers) throws AgentUnavailableException { + for (DomainRouterVO router : routers) { + AggregationControlCommand cmd = new AggregationControlCommand(action, router.getInstanceName(), getRouterControlIp(router.getId()), + getRouterIpInNetwork(network.getId(), router.getId())); + Commands cmds = new Commands(cmd); + if (!sendCommandsToRouter(router, cmds)) { + return false; + } + } + return true; + } + + @Override + public boolean prepareAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return aggregationExecution(Action.Start, network, routers); + } + + @Override + public boolean completeAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return aggregationExecution(Action.Finish, network, routers); + } + + @Override + public boolean cleanupAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return aggregationExecution(Action.Cleanup, network, routers); + } } diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index f6bd9e1ed42..82964602d4a 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -17,28 +17,14 @@ package com.cloud.network.router; -import java.net.URI; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.Command.OnError; import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PlugNicCommand; import com.cloud.agent.api.SetupGuestNetworkCommand; +import com.cloud.agent.api.routing.AggregationControlCommand; +import com.cloud.agent.api.routing.AggregationControlCommand.Action; import com.cloud.agent.api.routing.IpAssocVpcCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.SetNetworkACLCommand; @@ -127,6 +113,21 @@ import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile.Param; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; @Component @Local(value = {VpcVirtualNetworkApplianceManager.class, VpcVirtualNetworkApplianceService.class}) @@ -246,9 +247,8 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian } @Override - public boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { - + public boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant, Map params) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { if (network.getTrafficType() != TrafficType.Guest) { s_logger.warn("Network " + network + " is not of type " + TrafficType.Guest); return false; @@ -257,16 +257,23 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian //Add router to the Guest network boolean result = true; try { + + // 1) add nic to the router _routerDao.addRouterToGuestNetwork(router, network); NicProfile guestNic = _itMgr.addVmToNetwork(router, network, null); - //setup guest network + //2) setup guest network if (guestNic != null) { result = setupVpcGuestNetwork(network, router, true, guestNic); } else { s_logger.warn("Failed to add router " + router + " to guest network " + network); result = false; } + //3) apply networking rules + if (result && params.get(Param.ReProgramGuestNetworks) != null + && (Boolean) params.get(Param.ReProgramGuestNetworks) == true) { + sendNetworkRulesToRouter(router.getId(), network.getId()); + } } catch (Exception ex) { s_logger.warn("Failed to add router " + router + " to network " + network + " due to ", ex); result = false; @@ -854,12 +861,18 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian for (Pair nicNtwk : guestNics) { Nic guestNic = nicNtwk.first(); + AggregationControlCommand startCmd = new AggregationControlCommand(Action.Start, router.getInstanceName(), controlNic.getIp4Address(), + getRouterIpInNetwork(guestNic.getNetworkId(), router.getId())); + cmds.addCommand(startCmd); if (reprogramGuestNtwks) { finalizeIpAssocForNetwork(cmds, router, provider, guestNic.getNetworkId(), vlanMacAddress); finalizeNetworkRulesForNetwork(cmds, router, provider, guestNic.getNetworkId()); } finalizeUserDataAndDhcpOnStart(cmds, router, provider, guestNic.getNetworkId()); + AggregationControlCommand finishCmd = new AggregationControlCommand(Action.Finish, router.getInstanceName(), controlNic.getIp4Address(), + getRouterIpInNetwork(guestNic.getNetworkId(), router.getId())); + cmds.addCommand(finishCmd); } //Add network usage commands @@ -885,6 +898,25 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian } } + + protected boolean sendNetworkRulesToRouter(long routerId, long networkId) + throws ResourceUnavailableException { + DomainRouterVO router = _routerDao.findById(routerId); + Commands cmds = new Commands(OnError.Continue); + + VirtualRouterProvider vrProvider = _vrProviderDao.findById(router.getElementId()); + if (vrProvider == null) { + throw new CloudRuntimeException("Cannot find related virtual router provider of router: " + router.getHostName()); + } + Provider provider = Network.Provider.getProvider(vrProvider.getType().toString()); + if (provider == null) { + throw new CloudRuntimeException("Cannot find related provider of virtual router provider: " + vrProvider.getType().toString()); + } + + finalizeNetworkRulesForNetwork(cmds, router, provider, networkId); + return sendCommandsToRouter(router, cmds); + } + @Override public boolean setupPrivateGateway(PrivateGateway gateway, VirtualRouter router) throws ConcurrentOperationException, ResourceUnavailableException { boolean result = true; @@ -1178,7 +1210,7 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian //3) allocate nic for guest gateway if needed List guestNetworks = _vpcMgr.getVpcNetworks(vpcId); for (Network guestNetwork : guestNetworks) { - if (guestNetwork.getState() == Network.State.Implemented) { + if (guestNetwork.getState() == Network.State.Implemented || guestNetwork.getState() == Network.State.Setup) { NicProfile guestNic = createGuestNicProfileForVpcRouter(guestNetwork); networks.put(guestNetwork, new ArrayList(Arrays.asList(guestNic))); } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 08381e273e3..99dbfdee127 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -424,14 +424,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis List map = _vpcOffSvcMapDao.listByVpcOffId(vpcOffId); for (VpcOfferingServiceMapVO instance : map) { - String service = instance.getService(); + Service service = Service.getService(instance.getService()); Set providers; providers = serviceProviderMap.get(service); if (providers == null) { providers = new HashSet(); } providers.add(Provider.getProvider(instance.getProvider())); - serviceProviderMap.put(Service.getService(service), providers); + serviceProviderMap.put(service, providers); } return serviceProviderMap; @@ -2186,6 +2186,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis hTypes.add(HypervisorType.VMware); hTypes.add(HypervisorType.KVM); hTypes.add(HypervisorType.Simulator); + hTypes.add(HypervisorType.LXC); return hTypes; } diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index 689a0d75849..f9a59ba4eb8 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -30,11 +30,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import com.google.gson.Gson; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd; import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; @@ -51,10 +46,14 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.commons.lang.ObjectUtils; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.GetGPUStatsAnswer; +import com.cloud.agent.api.GetGPUStatsCommand; import com.cloud.agent.api.GetHostStatsAnswer; import com.cloud.agent.api.GetHostStatsCommand; import com.cloud.agent.api.MaintainAnswer; @@ -64,6 +63,7 @@ import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.UnsupportedAnswer; import com.cloud.agent.api.UpdateHostPasswordCommand; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.agent.transport.Request; import com.cloud.api.ApiDBUtils; import com.cloud.capacity.Capacity; @@ -97,6 +97,11 @@ import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceInUseException; +import com.cloud.gpu.GPU.vGPUType; +import com.cloud.gpu.HostGpuGroupsVO; +import com.cloud.gpu.VGPUTypesVO; +import com.cloud.gpu.dao.HostGpuGroupsDao; +import com.cloud.gpu.dao.VGPUTypesDao; import com.cloud.ha.HighAvailabilityManager; import com.cloud.ha.HighAvailabilityManager.WorkType; import com.cloud.host.DetailVO; @@ -137,11 +142,14 @@ import com.cloud.utils.UriUtils; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.GlobalLock; +import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.Transaction; @@ -158,6 +166,7 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.dao.VMInstanceDao; +import com.google.gson.Gson; @Component @Local({ResourceManager.class, ResourceService.class}) @@ -193,6 +202,10 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, @Inject private GuestOSCategoryDao _guestOSCategoryDao; @Inject + protected HostGpuGroupsDao _hostGpuGroupsDao; + @Inject + protected VGPUTypesDao _vgpuTypesDao; + @Inject private PrimaryDataStoreDao _storagePoolDao; @Inject private DataCenterIpAddressDao _privateIPAddressDao; @@ -244,6 +257,8 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, private GenericSearchBuilder _hypervisorsInDC; + private SearchBuilder _gpuAvailability; + private void insertListener(Integer event, ResourceListener listener) { List lst = _lifeCycleListeners.get(event); if (lst == null) { @@ -827,6 +842,9 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, // delete host details _hostDetailsDao.deleteDetails(hostId); + // if host is GPU enabled, delete GPU entries + _hostGpuGroupsDao.deleteGpuEntries(hostId); + host.setGuid(null); Long clusterId = host.getClusterId(); host.setClusterId(null); @@ -1329,6 +1347,14 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, _hypervisorsInDC.and("type", _hypervisorsInDC.entity().getType(), SearchCriteria.Op.EQ); _hypervisorsInDC.done(); + _gpuAvailability = _hostGpuGroupsDao.createSearchBuilder(); + _gpuAvailability.and("hostId", _gpuAvailability.entity().getHostId(), Op.EQ); + SearchBuilder join1 = _vgpuTypesDao.createSearchBuilder(); + join1.and("vgpuType", join1.entity().getVgpuType(), Op.EQ); + join1.and("remainingCapacity", join1.entity().getRemainingCapacity(), Op.GT); + _gpuAvailability.join("groupId", join1, _gpuAvailability.entity().getId(), join1.entity().getGpuGroupId(), JoinBuilder.JoinType.INNER); + _gpuAvailability.done(); + return true; } @@ -1958,6 +1984,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, host.setSpeed(ssCmd.getSpeed()); host.setHypervisorType(hyType); host.setHypervisorVersion(ssCmd.getHypervisorVersion()); + host.setGpuGroups(ssCmd.getGpuGroupDetails()); return host; } @@ -2473,6 +2500,66 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, return sc.list(); } + @Override + public List listAvailableGPUDevice(long hostId, String vgpuType) { + if (vgpuType == null) { + vgpuType = vGPUType.passthrough.getType(); + } + Filter searchFilter = new Filter(VGPUTypesVO.class, "remainingCapacity", false, null, null); + SearchCriteria sc = _gpuAvailability.create(); + sc.setParameters("hostId", hostId); + sc.setJoinParameters("groupId", "vgpuType", vgpuType); + sc.setJoinParameters("groupId", "remainingCapacity", 0); + return _hostGpuGroupsDao.customSearch(sc, searchFilter); + } + + @Override + public boolean isGPUDeviceAvailable(long hostId, String vgpuType) { + if(!listAvailableGPUDevice(hostId, vgpuType).isEmpty()) { + return true; + } else { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Host ID: "+ hostId +" does not have GPU device available"); + } + return false; + } + } + + @Override + public GPUDeviceTO getGPUDevice(long hostId, String vgpuType) { + HostGpuGroupsVO gpuDevice = listAvailableGPUDevice(hostId, vgpuType).get(0); + return new GPUDeviceTO(gpuDevice.getGroupName(), vgpuType, null); + } + + @Override + public void updateGPUDetails(long hostId, HashMap> groupDetails) { + // Update GPU group capacity + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + _hostGpuGroupsDao.persist(hostId, new ArrayList(groupDetails.keySet())); + _vgpuTypesDao.persist(hostId, groupDetails); + txn.commit(); + } + + @Override + public HashMap> getGPUStatistics(HostVO host) { + Answer answer = _agentMgr.easySend(host.getId(), new GetGPUStatsCommand(host.getGuid(), host.getName())); + if (answer != null && (answer instanceof UnsupportedAnswer)) { + return null; + } + if (answer == null || !answer.getResult()) { + String msg = "Unable to obtain GPU stats for host " + host.getName(); + s_logger.warn(msg); + return null; + } else { + // now construct the result object + if (answer instanceof GetGPUStatsAnswer) { + return ((GetGPUStatsAnswer)answer).getGroupDetails(); + } + } + return null; + } + @Override @DB @ActionEvent(eventType = EventTypes.EVENT_HOST_RESERVATION_RELEASE, eventDescription = "releasing host reservation", async = true) diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index 3b5f5ff206a..4e58a4f5576 100755 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -18,6 +18,7 @@ package com.cloud.server; import com.cloud.host.DetailVO; import com.cloud.host.HostVO; +import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; import com.cloud.utils.component.PluggableService; @@ -54,6 +55,8 @@ public interface ManagementServer extends ManagementService, PluggableService { GuestOSVO getGuestOs(Long guestOsId); + GuestOSHypervisorVO getGuestOsHypervisor(Long guestOsHypervisorId); + /** * Returns the vnc port of the vm. * diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index c6d4e02f33c..09823d78cf6 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -72,6 +72,13 @@ import org.apache.cloudstack.api.command.admin.domain.DeleteDomainCmd; import org.apache.cloudstack.api.command.admin.domain.ListDomainChildrenCmd; import org.apache.cloudstack.api.command.admin.domain.ListDomainsCmd; import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.AddGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.ListGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.RemoveGuestOsMappingCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsCmd; +import org.apache.cloudstack.api.command.admin.guest.UpdateGuestOsMappingCmd; import org.apache.cloudstack.api.command.admin.host.AddHostCmd; import org.apache.cloudstack.api.command.admin.host.AddSecondaryStorageCmd; import org.apache.cloudstack.api.command.admin.host.CancelMaintenanceCmd; @@ -510,6 +517,7 @@ import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.gpu.GPU; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.DetailVO; import com.cloud.host.Host; @@ -541,9 +549,12 @@ import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.auth.UserAuthenticator; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSCategoryVO; +import com.cloud.storage.GuestOSHypervisor; +import com.cloud.storage.GuestOSHypervisorVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.GuestOsCategory; import com.cloud.storage.Storage.ImageFormat; @@ -556,6 +567,7 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.GuestOSHypervisorDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.secondary.SecondaryStorageVmManager; @@ -671,6 +683,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private GuestOSCategoryDao _guestOSCategoryDao; @Inject + private GuestOSHypervisorDao _guestOSHypervisorDao; + @Inject private PrimaryDataStoreDao _poolDao; @Inject private NetworkDao _networkDao; @@ -702,6 +716,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe private ResourceTagDao _resourceTagDao; @Inject private ImageStoreDao _imgStoreDao; + @Inject + private ServiceOfferingDetailsDao _serviceOfferingDetailsDao; + @Inject private ProjectManager _projectMgr; @@ -1061,6 +1078,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw ex; } + if(_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { + s_logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName()+ " is not supported"); + // Return empty list. + return new Ternary, Integer>, List, Map>(new Pair, + Integer>(new ArrayList(), new Integer(0)), new ArrayList(), new HashMap()); + } + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv)) { if (s_logger.isDebugEnabled()) { @@ -2102,6 +2126,216 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return new Pair, Integer>(result.first(), result.second()); } + @Override + public Pair, Integer> listGuestOSMappingByCriteria(ListGuestOsMappingCmd cmd) { + Filter searchFilter = new Filter(GuestOSHypervisorVO.class, "hypervisorType", true, cmd.getStartIndex(), cmd.getPageSizeVal()); + Long id = cmd.getId(); + Long osTypeId = cmd.getOsTypeId(); + String hypervisor = cmd.getHypervisor(); + String hypervisorVersion = cmd.getHypervisorVersion(); + + //throw exception if hypervisor name is not passed, but version is + if (hypervisorVersion != null && (hypervisor == null || hypervisor.isEmpty())) { + throw new InvalidParameterValueException("Hypervisor version parameter cannot be used without specifying a hypervisor : XenServer, KVM or VMware"); + } + + SearchCriteria sc = _guestOSHypervisorDao.createSearchCriteria(); + + if (id != null) { + sc.addAnd("id", SearchCriteria.Op.EQ, id); + } + + if (osTypeId != null) { + sc.addAnd("guestOsId", SearchCriteria.Op.EQ, osTypeId); + } + + if (hypervisor != null) { + sc.addAnd("hypervisorType", SearchCriteria.Op.EQ, hypervisor); + } + + if (hypervisorVersion != null) { + sc.addAnd("hypervisorVersion", SearchCriteria.Op.EQ, hypervisorVersion); + } + + Pair, Integer> result = _guestOSHypervisorDao.searchAndCount(sc, searchFilter); + return new Pair, Integer>(result.first(), result.second()); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_MAPPING_ADD, eventDescription = "Adding new guest OS to hypervisor name mapping", create = true) + public GuestOSHypervisor addGuestOsMapping(AddGuestOsMappingCmd cmd) { + Long osTypeId = cmd.getOsTypeId(); + String osStdName = cmd.getOsStdName(); + String hypervisor = cmd.getHypervisor(); + String hypervisorVersion = cmd.getHypervisorVersion(); + String osNameForHypervisor = cmd.getOsNameForHypervisor(); + GuestOS guestOs = null; + + if ((osTypeId == null) && (osStdName == null || osStdName.isEmpty())) { + throw new InvalidParameterValueException("Please specify either a guest OS name or UUID"); + } + + HypervisorType hypervisorType = HypervisorType.getType(hypervisor); + + if (!(hypervisorType == HypervisorType.KVM || hypervisorType == HypervisorType.XenServer || hypervisorType == HypervisorType.VMware)) { + throw new InvalidParameterValueException("Please specify a valid hypervisor : XenServer, KVM or VMware"); + } + + HypervisorCapabilitiesVO hypervisorCapabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(hypervisorType, hypervisorVersion); + if (hypervisorCapabilities == null) { + throw new InvalidParameterValueException("Please specify a valid hypervisor and supported version"); + } + + //by this point either osTypeId or osStdType is non-empty. Find by either of them. ID takes preference if both are specified + if (osTypeId != null) { + guestOs = ApiDBUtils.findGuestOSById(osTypeId); + } + else if (osStdName != null) { + guestOs = ApiDBUtils.findGuestOSByDisplayName(osStdName); + } + + if (guestOs == null) { + throw new InvalidParameterValueException("Unable to find the guest OS by name or UUID"); + } + //check for duplicates + GuestOSHypervisorVO duplicate = _guestOSHypervisorDao.findByOsIdAndHypervisor(osTypeId.longValue(), hypervisorType.toString(), hypervisorVersion); + + if (duplicate != null) { + throw new InvalidParameterValueException("Mapping from hypervisor : " + hypervisorType.toString() + ", version : " + hypervisorVersion + " and guest OS : " + + guestOs.getDisplayName() + " already exists!"); + } + GuestOSHypervisorVO guestOsMapping = new GuestOSHypervisorVO(); + guestOsMapping.setGuestOsId(guestOs.getId()); + guestOsMapping.setGuestOsName(osNameForHypervisor); + guestOsMapping.setHypervisorType(hypervisorType.toString()); + guestOsMapping.setHypervisorVersion(hypervisorVersion); + return _guestOSHypervisorDao.persist(guestOsMapping); + + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_MAPPING_ADD, eventDescription = "Adding a new guest OS to hypervisor name mapping", async = true) + public GuestOSHypervisor getAddedGuestOsMapping(Long guestOsMappingId) { + return getGuestOsHypervisor(guestOsMappingId); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_ADD, eventDescription = "Adding new guest OS type", create = true) + public GuestOS addGuestOs(AddGuestOsCmd cmd) { + Long categoryId = cmd.getOsCategoryId(); + String displayName = cmd.getOsDisplayName(); + String name = cmd.getOsName(); + + GuestOSCategoryVO guestOsCategory = ApiDBUtils.findGuestOsCategoryById(categoryId); + if (guestOsCategory == null) { + throw new InvalidParameterValueException("Guest OS category not found. Please specify a valid Guest OS category"); + } + + GuestOS guestOs = ApiDBUtils.findGuestOSByDisplayName(displayName); + if (guestOs != null) { + throw new InvalidParameterValueException("The specified Guest OS name : " + displayName + " already exists. Please specify a unique name"); + } + + GuestOSVO guestOsVo = new GuestOSVO(); + guestOsVo.setCategoryId(categoryId.longValue()); + guestOsVo.setDisplayName(displayName); + guestOsVo.setName(name); + return _guestOSDao.persist(guestOsVo); + } + + @Override + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_ADD, eventDescription = "Adding a new guest OS type", async = true) + public GuestOS getAddedGuestOs(Long guestOsId) { + return getGuestOs(guestOsId); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_UPDATE, eventDescription = "updating guest OS type", async = true) + public GuestOS updateGuestOs(UpdateGuestOsCmd cmd) { + Long id = cmd.getId(); + String displayName = cmd.getOsDisplayName(); + + //check if guest OS exists + GuestOS guestOsHandle = ApiDBUtils.findGuestOSById(id); + if (guestOsHandle == null) { + throw new InvalidParameterValueException("Guest OS not found. Please specify a valid ID for the Guest OS"); + } + + //Check if update is needed + if (displayName.equals(guestOsHandle.getDisplayName())) { + return guestOsHandle; + } + + //Check if another Guest OS by same name exists + GuestOS duplicate = ApiDBUtils.findGuestOSByDisplayName(displayName); + if(duplicate != null) { + throw new InvalidParameterValueException("The specified Guest OS name : " + displayName + " already exists. Please specify a unique guest OS name"); + } + GuestOSVO guestOs = _guestOSDao.createForUpdate(id); + guestOs.setDisplayName(displayName); + if (_guestOSDao.update(id, guestOs)) { + return _guestOSDao.findById(id); + } else { + return null; + } + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_REMOVE, eventDescription = "removing guest OS type", async = true) + public boolean removeGuestOs(RemoveGuestOsCmd cmd) { + Long id = cmd.getId(); + + //check if guest OS exists + GuestOS guestOs = ApiDBUtils.findGuestOSById(id); + if (guestOs == null) { + throw new InvalidParameterValueException("Guest OS not found. Please specify a valid ID for the Guest OS"); + } + + return _guestOSDao.remove(id); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_MAPPING_UPDATE, eventDescription = "updating guest OS mapping", async = true) + public GuestOSHypervisor updateGuestOsMapping(UpdateGuestOsMappingCmd cmd) { + Long id = cmd.getId(); + String osNameForHypervisor = cmd.getOsNameForHypervisor(); + + //check if mapping exists + GuestOSHypervisor guestOsHypervisorHandle = _guestOSHypervisorDao.findById(id); + if (guestOsHypervisorHandle == null) { + throw new InvalidParameterValueException("Guest OS Mapping not found. Please specify a valid ID for the Guest OS Mapping"); + } + + GuestOSHypervisorVO guestOsHypervisor = _guestOSHypervisorDao.createForUpdate(id); + guestOsHypervisor.setGuestOsName(osNameForHypervisor); + if (_guestOSHypervisorDao.update(id, guestOsHypervisor)) { + return _guestOSHypervisorDao.findById(id); + } else { + return null; + } + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_GUEST_OS_MAPPING_REMOVE, eventDescription = "removing guest OS mapping", async = true) + public boolean removeGuestOsMapping(RemoveGuestOsMappingCmd cmd) { + Long id = cmd.getId(); + + //check if mapping exists + GuestOSHypervisor guestOsHypervisorHandle = _guestOSHypervisorDao.findById(id); + if (guestOsHypervisorHandle == null) { + throw new InvalidParameterValueException("Guest OS Mapping not found. Please specify a valid ID for the Guest OS Mapping"); + } + + return _guestOSHypervisorDao.removeGuestOsMapping(id); + + } + protected ConsoleProxyInfo getConsoleProxyForVm(long dataCenterId, long userVmId) { return _consoleProxyMgr.assignProxy(dataCenterId, userVmId); } @@ -2652,6 +2886,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(UpdatePortForwardingRuleCmd.class); cmdList.add(ListGuestOsCategoriesCmd.class); cmdList.add(ListGuestOsCmd.class); + cmdList.add(ListGuestOsMappingCmd.class); + cmdList.add(AddGuestOsCmd.class); + cmdList.add(AddGuestOsMappingCmd.class); + cmdList.add(UpdateGuestOsCmd.class); + cmdList.add(UpdateGuestOsMappingCmd.class); + cmdList.add(RemoveGuestOsCmd.class); + cmdList.add(RemoveGuestOsMappingCmd.class); cmdList.add(AttachIsoCmd.class); cmdList.add(CopyIsoCmd.class); cmdList.add(DeleteIsoCmd.class); @@ -3261,6 +3502,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return _guestOSDao.findById(guestOsId); } + @Override + public GuestOSHypervisorVO getGuestOsHypervisor(Long guestOsHypervisorId) { + return _guestOSHypervisorDao.findById(guestOsHypervisorId); + } + @Override public InstanceGroupVO updateVmGroup(UpdateVMGroupCmd cmd) { Account caller = CallContext.current().getCallingAccount(); @@ -3302,16 +3548,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return "unknown"; } - @Override - public Long saveStartedEvent(Long userId, Long accountId, String type, String description, boolean displayResourceEnabled, Long startEventId) { - return ActionEventUtils.onStartedActionEvent(userId, accountId, type, description, displayResourceEnabled, startEventId); - } - - @Override - public Long saveCompletedEvent(Long userId, Long accountId, String level, String type, String description, boolean displayResourceEnabled, long startEventId) { - return ActionEventUtils.onCompletedActionEvent(userId, accountId, level, type, displayResourceEnabled, description, startEventId); - } - @Override @DB public String uploadCertificate(UploadCustomCertificateCmd cmd) { @@ -3791,4 +4027,5 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public void setLockMasterListener(LockMasterListener lockMasterListener) { _lockMasterListener = lockMasterListener; } + } diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java index 548587cfa8e..067ed003f39 100755 --- a/server/src/com/cloud/server/StatsCollector.java +++ b/server/src/com/cloud/server/StatsCollector.java @@ -54,6 +54,7 @@ import com.cloud.agent.api.VmStatsEntry; import com.cloud.cluster.ManagementServerHostVO; import com.cloud.cluster.dao.ManagementServerHostDao; import com.cloud.exception.StorageUnavailableException; +import com.cloud.gpu.dao.HostGpuGroupsDao; import com.cloud.host.Host; import com.cloud.host.HostStats; import com.cloud.host.HostVO; @@ -175,6 +176,8 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc private AutoScaleVmProfileDao _asProfileDao; @Inject private ServiceOfferingDao _serviceOfferingDao; + @Inject + private HostGpuGroupsDao _hostGpuGroupsDao; private ConcurrentHashMap _hostStats = new ConcurrentHashMap(); private final ConcurrentHashMap _VmStats = new ConcurrentHashMap(); @@ -188,6 +191,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc long volumeStatsInterval = -1L; long autoScaleStatsInterval = -1L; int vmDiskStatsInterval = 0; + List hostIds = null; private ScheduledExecutorService _diskStatsUpdateExecutor; private int _usageAggregationRange = 1440; @@ -325,6 +329,23 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc } } _hostStats = hostStats; + // Get a subset of hosts with GPU support from the list of "hosts" + List gpuEnabledHosts = new ArrayList(); + if (hostIds != null) { + for (HostVO host : hosts) { + if (hostIds.contains(host.getId())) { + gpuEnabledHosts.add(host); + } + } + } else { + // Check for all the hosts managed by CloudStack. + gpuEnabledHosts = hosts; + } + for (HostVO host : gpuEnabledHosts) { + HashMap> groupDetails = _resourceMgr.getGPUStatistics(host); + _resourceMgr.updateGPUDetails(host.getId(), groupDetails); + } + hostIds = _hostGpuGroupsDao.listHostIds(); } catch (Throwable t) { s_logger.error("Error trying to retrieve host stats", t); } diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 3ef8e135d84..5ce07f0bb3b 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -92,12 +92,14 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.StorageUnavailableException; +import com.cloud.gpu.GPU; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.HypervisorCapabilitiesVO; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.org.Grouping; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; @@ -171,6 +173,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject private SnapshotDao _snapshotDao; @Inject + protected ServiceOfferingDetailsDao _serviceOfferingDetailsDao; + @Inject StoragePoolDetailsDao storagePoolDetailsDao; @Inject private UserVmDao _userVmDao; @@ -1466,6 +1470,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } if (vm != null && vm.getState() == State.Running) { + // Check if the VM is GPU enabled. + if(_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { + throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + } // Check if the underlying hypervisor supports storage motion. Long hostId = vm.getHostId(); if (hostId != null) { @@ -1820,6 +1828,15 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return extractUrl; } + @Override + public boolean isDisplayResourceEnabled(Long id) { + Volume volume = _volumeDao.findById(id); + if (volume == null) { + return true; // bad id given, default to true + } + return volume.isDisplayVolume(); + } + private String getFormatForPool(StoragePool pool) { ClusterVO cluster = ApiDBUtils.findClusterById(pool.getClusterId()); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java index a85c0526e1f..447697f1005 100644 --- a/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotSchedulerImpl.java @@ -82,6 +82,8 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu protected VolumeDao _volsDao; @Inject protected ConfigurationDao _configDao; + @Inject + protected ApiDispatcher _dispatcher; protected AsyncJobDispatcher _asyncDispatcher; @@ -95,21 +97,21 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu return _asyncDispatcher; } - public void setAsyncJobDispatcher(AsyncJobDispatcher dispatcher) { + public void setAsyncJobDispatcher(final AsyncJobDispatcher dispatcher) { _asyncDispatcher = dispatcher; } - private Date getNextScheduledTime(long policyId, Date currentTimestamp) { - SnapshotPolicyVO policy = _snapshotPolicyDao.findById(policyId); + private Date getNextScheduledTime(final long policyId, final Date currentTimestamp) { + final SnapshotPolicyVO policy = _snapshotPolicyDao.findById(policyId); Date nextTimestamp = null; if (policy != null) { - short intervalType = policy.getInterval(); - IntervalType type = DateUtil.getIntervalType(intervalType); - String schedule = policy.getSchedule(); - String timezone = policy.getTimezone(); + final short intervalType = policy.getInterval(); + final IntervalType type = DateUtil.getIntervalType(intervalType); + final String schedule = policy.getSchedule(); + final String timezone = policy.getTimezone(); nextTimestamp = DateUtil.getNextRunTime(type, schedule, timezone, currentTimestamp); - String currentTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp); - String nextScheduledTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, nextTimestamp); + final String currentTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, currentTimestamp); + final String nextScheduledTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, nextTimestamp); s_logger.debug("Current time is " + currentTime + ". NextScheduledTime of policyId " + policyId + " is " + nextScheduledTime); } return nextTimestamp; @@ -119,7 +121,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu * {@inheritDoc} */ @Override - public void poll(Date currentTimestamp) { + public void poll(final Date currentTimestamp) { // We don't maintain the time. The timer task does. _currentTimestamp = currentTimestamp; @@ -151,12 +153,12 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu } private void checkStatusOfCurrentlyExecutingSnapshots() { - SearchCriteria sc = _snapshotScheduleDao.createSearchCriteria(); + final SearchCriteria sc = _snapshotScheduleDao.createSearchCriteria(); sc.addAnd("asyncJobId", SearchCriteria.Op.NNULL); - List snapshotSchedules = _snapshotScheduleDao.search(sc, null); - for (SnapshotScheduleVO snapshotSchedule : snapshotSchedules) { - Long asyncJobId = snapshotSchedule.getAsyncJobId(); - AsyncJobVO asyncJob = _asyncJobDao.findById(asyncJobId); + final List snapshotSchedules = _snapshotScheduleDao.search(sc, null); + for (final SnapshotScheduleVO snapshotSchedule : snapshotSchedules) { + final Long asyncJobId = snapshotSchedule.getAsyncJobId(); + final AsyncJobVO asyncJob = _asyncJobDao.findById(asyncJobId); switch (asyncJob.getStatus()) { case SUCCEEDED: // The snapshot has been successfully backed up. @@ -167,7 +169,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu break; case FAILED: // Check the snapshot status. - Long snapshotId = snapshotSchedule.getSnapshotId(); + final Long snapshotId = snapshotSchedule.getSnapshotId(); if (snapshotId == null) { // createSnapshotAsync exited, successfully or unsuccessfully, // even before creating a snapshot record @@ -175,7 +177,7 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu // Schedule the next snapshot. scheduleNextSnapshotJob(snapshotSchedule); } else { - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + final SnapshotVO snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null || snapshot.getRemoved() != null) { // This snapshot has been deleted successfully from the primary storage // Again no cleanup needs to be done. @@ -219,16 +221,16 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu String displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, _currentTimestamp); s_logger.debug("Snapshot scheduler.poll is being called at " + displayTime); - List snapshotsToBeExecuted = _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp); + final List snapshotsToBeExecuted = _snapshotScheduleDao.getSchedulesToExecute(_currentTimestamp); s_logger.debug("Got " + snapshotsToBeExecuted.size() + " snapshots to be executed at " + displayTime); - for (SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) { + for (final SnapshotScheduleVO snapshotToBeExecuted : snapshotsToBeExecuted) { SnapshotScheduleVO tmpSnapshotScheduleVO = null; - long snapshotScheId = snapshotToBeExecuted.getId(); - long policyId = snapshotToBeExecuted.getPolicyId(); - long volumeId = snapshotToBeExecuted.getVolumeId(); + final long snapshotScheId = snapshotToBeExecuted.getId(); + final long policyId = snapshotToBeExecuted.getPolicyId(); + final long volumeId = snapshotToBeExecuted.getVolumeId(); try { - VolumeVO volume = _volsDao.findById(volumeId); + final VolumeVO volume = _volsDao.findById(volumeId); if (volume.getPoolId() == null) { // this volume is not attached continue; @@ -237,26 +239,26 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu _snapshotScheduleDao.remove(snapshotToBeExecuted.getId()); } if (s_logger.isDebugEnabled()) { - Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp(); + final Date scheduledTimestamp = snapshotToBeExecuted.getScheduledTimestamp(); displayTime = DateUtil.displayDateInTimezone(DateUtil.GMT_TIMEZONE, scheduledTimestamp); s_logger.debug("Scheduling 1 snapshot for volume " + volumeId + " for schedule id: " + snapshotToBeExecuted.getId() + " at " + displayTime); } tmpSnapshotScheduleVO = _snapshotScheduleDao.acquireInLockTable(snapshotScheId); - Long eventId = + final Long eventId = ActionEventUtils.onScheduledActionEvent(User.UID_SYSTEM, volume.getAccountId(), EventTypes.EVENT_SNAPSHOT_CREATE, "creating snapshot for volume Id:" + volumeId, true, 0); - Map params = new HashMap(); + final Map params = new HashMap(); params.put(ApiConstants.VOLUME_ID, "" + volumeId); params.put(ApiConstants.POLICY_ID, "" + policyId); params.put("ctxUserId", "1"); params.put("ctxAccountId", "" + volume.getAccountId()); params.put("ctxStartEventId", String.valueOf(eventId)); - CreateSnapshotCmd cmd = new CreateSnapshotCmd(); + final CreateSnapshotCmd cmd = new CreateSnapshotCmd(); ComponentContext.inject(cmd); - ApiDispatcher.getInstance().dispatchCreateCmd(cmd, params); + _dispatcher.dispatchCreateCmd(cmd, params); params.put("id", "" + cmd.getEntityId()); params.put("ctxStartEventId", "1"); @@ -265,11 +267,12 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu cmd.getInstanceType() != null ? cmd.getInstanceType().toString() : null); job.setDispatcher(_asyncDispatcher.getName()); - long jobId = _asyncMgr.submitAsyncJob(job); + final long jobId = _asyncMgr.submitAsyncJob(job); tmpSnapshotScheduleVO.setAsyncJobId(jobId); _snapshotScheduleDao.update(snapshotScheId, tmpSnapshotScheduleVO); - } catch (Exception e) { + } catch (final Exception e) { + // TODO Logging this exception is enough? s_logger.warn("Scheduling snapshot failed due to " + e.toString()); } finally { if (tmpSnapshotScheduleVO != null) { @@ -279,16 +282,16 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu } } - private Date scheduleNextSnapshotJob(SnapshotScheduleVO snapshotSchedule) { + private Date scheduleNextSnapshotJob(final SnapshotScheduleVO snapshotSchedule) { if (snapshotSchedule == null) { return null; } - Long policyId = snapshotSchedule.getPolicyId(); + final Long policyId = snapshotSchedule.getPolicyId(); if (policyId.longValue() == Snapshot.MANUAL_POLICY_ID) { // Don't need to schedule the next job for this. return null; } - SnapshotPolicyVO snapshotPolicy = _snapshotPolicyDao.findById(policyId); + final SnapshotPolicyVO snapshotPolicy = _snapshotPolicyDao.findById(policyId); if (snapshotPolicy == null) { _snapshotScheduleDao.expunge(snapshotSchedule.getId()); } @@ -297,15 +300,15 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu @Override @DB - public Date scheduleNextSnapshotJob(SnapshotPolicyVO policy) { + public Date scheduleNextSnapshotJob(final SnapshotPolicyVO policy) { if (policy == null) { return null; } - long policyId = policy.getId(); + final long policyId = policy.getId(); if (policyId == Snapshot.MANUAL_POLICY_ID) { return null; } - Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp); + final Date nextSnapshotTimestamp = getNextScheduledTime(policyId, _currentTimestamp); SnapshotScheduleVO spstSchedVO = _snapshotScheduleDao.findOneByVolumePolicy(policy.getVolumeId(), policy.getId()); if (spstSchedVO == null) { spstSchedVO = new SnapshotScheduleVO(policy.getVolumeId(), policyId, nextSnapshotTimestamp); @@ -329,9 +332,9 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu @Override @DB - public boolean removeSchedule(Long volumeId, Long policyId) { + public boolean removeSchedule(final Long volumeId, final Long policyId) { // We can only remove schedules which are in the future. Not which are already executed in the past. - SnapshotScheduleVO schedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, false); + final SnapshotScheduleVO schedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, false); boolean success = true; if (schedule != null) { success = _snapshotScheduleDao.remove(schedule.getId()); @@ -343,18 +346,18 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu } @Override - public boolean configure(String name, Map params) throws ConfigurationException { + public boolean configure(final String name, final Map params) throws ConfigurationException { _snapshotPollInterval = NumbersUtil.parseInt(_configDao.getValue("snapshot.poll.interval"), 300); - boolean snapshotsRecurringTest = Boolean.parseBoolean(_configDao.getValue("snapshot.recurring.test")); + final boolean snapshotsRecurringTest = Boolean.parseBoolean(_configDao.getValue("snapshot.recurring.test")); if (snapshotsRecurringTest) { // look for some test values in the configuration table so that snapshots can be taken more frequently (QA test code) - int minutesPerHour = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.minutes.per.hour"), 60); - int hoursPerDay = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.hours.per.day"), 24); - int daysPerWeek = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.week"), 7); - int daysPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.month"), 30); - int weeksPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.weeks.per.month"), 4); - int monthsPerYear = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.months.per.year"), 12); + final int minutesPerHour = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.minutes.per.hour"), 60); + final int hoursPerDay = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.hours.per.day"), 24); + final int daysPerWeek = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.week"), 7); + final int daysPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.days.per.month"), 30); + final int weeksPerMonth = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.weeks.per.month"), 4); + final int monthsPerYear = NumbersUtil.parseInt(_configDao.getValue("snapshot.test.months.per.year"), 12); _testTimerTask = new TestClock(this, minutesPerHour, hoursPerDay, daysPerWeek, daysPerMonth, weeksPerMonth, monthsPerYear); } @@ -369,8 +372,8 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu @DB public boolean start() { // reschedule all policies after management restart - List policyInstances = _snapshotPolicyDao.listAll(); - for (SnapshotPolicyVO policyInstance : policyInstances) { + final List policyInstances = _snapshotPolicyDao.listAll(); + for (final SnapshotPolicyVO policyInstance : policyInstances) { if (policyInstance.getId() != Snapshot.MANUAL_POLICY_ID) { scheduleNextSnapshotJob(policyInstance); } @@ -381,13 +384,13 @@ public class SnapshotSchedulerImpl extends ManagerBase implements SnapshotSchedu // Else it becomes too confusing. _testClockTimer.schedule(_testTimerTask, 100 * 1000L, 60 * 1000L); } else { - TimerTask timerTask = new ManagedContextTimerTask() { + final TimerTask timerTask = new ManagedContextTimerTask() { @Override protected void runInContext() { try { - Date currentTimestamp = new Date(); + final Date currentTimestamp = new Date(); poll(currentTimestamp); - } catch (Throwable t) { + } catch (final Throwable t) { s_logger.warn("Catch throwable in snapshot scheduler ", t); } } diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index d5e4afc7a34..04d3e23b911 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -2480,4 +2480,50 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M return qs.listAclGroupsByAccount(accountId); } + @Override + public Long finalyzeAccountId(final String accountName, final Long domainId, final Long projectId, final boolean enabledOnly) { + if (accountName != null) { + if (domainId == null) { + throw new InvalidParameterValueException("Account must be specified with domainId parameter"); + } + + final Domain domain = _domainMgr.getDomain(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Unable to find domain by id"); + } + + final Account account = getActiveAccountByName(accountName, domainId); + if (account != null && account.getType() != Account.ACCOUNT_TYPE_PROJECT) { + if (!enabledOnly || account.getState() == Account.State.enabled) { + return account.getId(); + } else { + throw new PermissionDeniedException("Can't add resources to the account id=" + account.getId() + " in state=" + account.getState() + + " as it's no longer active"); + } + } else { + // idList is not used anywhere, so removed it now + // List idList = new ArrayList(); + // idList.add(new IdentityProxy("domain", domainId, "domainId")); + throw new InvalidParameterValueException("Unable to find account by name " + accountName + " in domain with specified id"); + } + } + + if (projectId != null) { + final Project project = _projectMgr.getProject(projectId); + if (project != null) { + if (!enabledOnly || project.getState() == Project.State.Active) { + return project.getProjectAccountId(); + } else { + final PermissionDeniedException ex = + new PermissionDeniedException("Can't add resources to the project with specified projectId in state=" + project.getState() + + " as it's no longer active"); + ex.addProxyObject(project.getUuid(), "projectId"); + throw ex; + } + } else { + throw new InvalidParameterValueException("Unable to find project by id"); + } + } + return null; + } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index ca734c237d5..7daa335625a 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -137,6 +137,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.gpu.GPU; import com.cloud.ha.HighAvailabilityManager; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -2980,7 +2981,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir long vmId = cmd.getEntityId(); Long hostId = cmd.getHostId(); UserVmVO vm = _vmDao.findById(vmId); - CallContext.current().putContextParameter(VirtualMachine.class, vm.getUuid()); Pair> vmParamPair = null; try { @@ -3676,6 +3676,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } + + if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { + throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + } + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv) && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { @@ -3987,6 +3992,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw ex; } + if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { + throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + } + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" + " XenServer/VMware/KVM only"); @@ -4634,4 +4643,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, vm); return vm.getUserData(); } + + @Override + public boolean isDisplayResourceEnabled(Long vmId) { + UserVm vm = _vmDao.findById(vmId); + if (vm != null) { + return vm.isDisplayVm(); + } + + return true; // no info then default to true + } } diff --git a/server/test/com/cloud/api/dispatch/CommandCreationWorkerTest.java b/server/test/com/cloud/api/dispatch/CommandCreationWorkerTest.java new file mode 100644 index 00000000000..34d64fc3c1b --- /dev/null +++ b/server/test/com/cloud/api/dispatch/CommandCreationWorkerTest.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.apache.cloudstack.api.BaseAsyncCreateCmd; + +import com.cloud.exception.ResourceAllocationException; + +public class CommandCreationWorkerTest { + + @Test + public void testHandle() throws ResourceAllocationException { + // Prepare + final BaseAsyncCreateCmd asyncCreateCmd = mock(BaseAsyncCreateCmd.class); + final Map params = new HashMap(); + + // Execute + final CommandCreationWorker creationWorker = new CommandCreationWorker(); + + creationWorker.handle(new DispatchTask(asyncCreateCmd, params)); + + // Assert + verify(asyncCreateCmd, times(1)).create(); + } +} diff --git a/server/test/com/cloud/api/dispatch/DispatchChainFactoryTest.java b/server/test/com/cloud/api/dispatch/DispatchChainFactoryTest.java new file mode 100644 index 00000000000..f54caae5141 --- /dev/null +++ b/server/test/com/cloud/api/dispatch/DispatchChainFactoryTest.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +public class DispatchChainFactoryTest { + + protected static final String STANDARD_CHAIN_ERROR = "Expecting worker of class %s at index %s of StandardChain"; + protected static final String ASYNC_CHAIN_ERROR = "Expecting worker of class %s at index %s of StandardChain"; + + @Test + public void testAllChainCreation() { + // Prepare + final DispatchChainFactory dispatchChainFactory = new DispatchChainFactory(); + dispatchChainFactory.paramGenericValidationWorker = new ParamGenericValidationWorker(); + dispatchChainFactory.specificCmdValidationWorker = new SpecificCmdValidationWorker(); + dispatchChainFactory.paramProcessWorker = new ParamProcessWorker(); + dispatchChainFactory.commandCreationWorker = new CommandCreationWorker(); + dispatchChainFactory.paramUnpackWorker = new ParamUnpackWorker(); + + final Class[] standardClasses = {ParamUnpackWorker.class, ParamProcessWorker.class, + ParamGenericValidationWorker.class, SpecificCmdValidationWorker.class}; + final Class[] asyncClasses = {ParamUnpackWorker.class, ParamProcessWorker.class, + ParamGenericValidationWorker.class, SpecificCmdValidationWorker.class, CommandCreationWorker.class}; + + // Execute + dispatchChainFactory.setup(); + final DispatchChain standardChain = dispatchChainFactory.getStandardDispatchChain(); + final DispatchChain asyncChain = dispatchChainFactory.getAsyncCreationDispatchChain(); + for (int i = 0; i < standardClasses.length; i++) { + assertEquals(String.format(STANDARD_CHAIN_ERROR, standardClasses[i], i), + standardClasses[i], standardChain.workers.get(i).getClass()); + } + for (int i = 0; i < asyncClasses.length; i++) { + assertEquals(String.format(ASYNC_CHAIN_ERROR, asyncClasses[i], i), + asyncClasses[i], asyncChain.workers.get(i).getClass()); + } + } +} diff --git a/server/test/com/cloud/api/dispatch/ParamGenericValidationWorkerTest.java b/server/test/com/cloud/api/dispatch/ParamGenericValidationWorkerTest.java new file mode 100644 index 00000000000..0803a2b0dbc --- /dev/null +++ b/server/test/com/cloud/api/dispatch/ParamGenericValidationWorkerTest.java @@ -0,0 +1,195 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.junit.Test; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.AccountVO; +import com.cloud.user.User; +import com.cloud.user.UserVO; + +public class ParamGenericValidationWorkerTest { + + protected static final String FAKE_CMD_NAME = "fakecmdname"; + + protected static final String FAKE_CMD_ROLE_NAME = "fakecmdrolename"; + + protected String loggerOutput; + + protected void driveTest(final BaseCmd cmd, final Map params) { + final ParamGenericValidationWorker genValidationWorker = new ParamGenericValidationWorker(); + + // We create a mock logger to verify the result + ParamGenericValidationWorker.s_logger = new Logger("") { + @Override + public void warn(final Object msg) { + loggerOutput = msg.toString(); + } + }; + + // Execute + genValidationWorker.handle(new DispatchTask(cmd, params)); + } + + @Test + public void testHandle() throws ResourceAllocationException { + // Prepare + final BaseCmd cmd = new FakeCmd(); + final Map params = new HashMap(); + params.put(ApiConstants.COMMAND, ""); + params.put("addedParam", ""); + + Account account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); + CallContext.register(user, account); + // Execute + try { + driveTest(cmd, params); + } finally { + CallContext.unregister(); + } + + // Assert + assertEquals("There should be no errors since there are no unknown parameters for this command class", null, loggerOutput); + } + + @Test + public void testHandleWithUnknownParams() throws ResourceAllocationException { + // Prepare + final String unknownParamKey = "unknownParam"; + final BaseCmd cmd = new FakeCmd(); + final Map params = new HashMap(); + params.put(ApiConstants.COMMAND, ""); + params.put("addedParam", ""); + params.put(unknownParamKey, ""); + + Account account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid"); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); + CallContext.register(user, account); + + // Execute + try { + driveTest(cmd, params); + } finally { + CallContext.unregister(); + } + + // Assert + assertTrue("There should be error msg, since there is one unknown parameter", loggerOutput.contains(unknownParamKey)); + assertTrue("There should be error msg containing the correct command name", loggerOutput.contains(FAKE_CMD_NAME)); + } + + @Test + public void testHandleWithoutAuthorization() throws ResourceAllocationException { + final short type = Account.ACCOUNT_TYPE_NORMAL; + + driveAuthTest(type); + + // Assert + assertTrue("There should be error msg, since there is one unauthorized parameter", loggerOutput.contains("paramWithRole")); + assertTrue("There should be error msg containing the correct command name", loggerOutput.contains(FAKE_CMD_ROLE_NAME)); + } + + @Test + public void testHandleWithAuthorization() throws ResourceAllocationException { + final short type = Account.ACCOUNT_TYPE_ADMIN; + + driveAuthTest(type); + // Assert + assertEquals("There should be no errors since parameters have authorization", null, loggerOutput); + } + + protected void driveAuthTest(final short type) { + + // Prepare + final BaseCmd cmd = new FakeCmdWithRoleAdmin(); + final Account account = mock(Account.class); + ((FakeCmdWithRoleAdmin)cmd).account = account; + when(account.getType()).thenReturn(type); + User user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); + CallContext.register(user, account); + + final Map params = new HashMap(); + params.put(ApiConstants.COMMAND, ""); + params.put("addedParam", ""); + params.put("paramWithRole", ""); + + // Execute + try { + driveTest(cmd, params); + } finally { + CallContext.unregister(); + } + } +} + + +@APICommand(name=ParamGenericValidationWorkerTest.FAKE_CMD_NAME, responseObject=BaseResponse.class) +class FakeCmd extends BaseCmd { + + @Parameter(name = "addedParam") + private String addedParam; + + public Account account; + + // + // Dummy methods for mere correct compilation + // + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + } + @Override + public String getCommandName() { + return null; + } + @Override + public long getEntityOwnerId() { + return 0; + } +} + +@APICommand(name=ParamGenericValidationWorkerTest.FAKE_CMD_ROLE_NAME, responseObject=BaseResponse.class) +class FakeCmdWithRoleAdmin extends FakeCmd { + + @Parameter(name = "paramWithRole", authorized = {RoleType.Admin}) + private String paramWithRole; +} diff --git a/server/test/com/cloud/api/ApiDispatcherTest.java b/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java similarity index 86% rename from server/test/com/cloud/api/ApiDispatcherTest.java rename to server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java index 7314a57b468..12051a6f743 100644 --- a/server/test/com/cloud/api/ApiDispatcherTest.java +++ b/server/test/com/cloud/api/dispatch/ParamProcessWorkerTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package com.cloud.api; +package com.cloud.api.dispatch; import java.util.HashMap; @@ -44,10 +44,12 @@ import com.cloud.user.AccountManager; import com.cloud.user.User; @RunWith(MockitoJUnitRunner.class) -public class ApiDispatcherTest { +public class ParamProcessWorkerTest { @Mock - AccountManager accountManager; + protected AccountManager accountManager; + + protected ParamProcessWorker paramProcessWorker; public static class TestCmd extends BaseCmd { @@ -81,8 +83,8 @@ public class ApiDispatcherTest { @Before public void setup() { CallContext.register(Mockito.mock(User.class), Mockito.mock(Account.class)); - new ApiDispatcher().init(); - ApiDispatcher.getInstance()._accountMgr = accountManager; + paramProcessWorker = new ParamProcessWorker(); + paramProcessWorker._accountMgr = accountManager; } @After @@ -92,13 +94,12 @@ public class ApiDispatcherTest { @Test public void processParameters() { - HashMap params = new HashMap(); + final HashMap params = new HashMap(); params.put("strparam1", "foo"); params.put("intparam1", "100"); params.put("boolparam1", "true"); - TestCmd cmd = new TestCmd(); - //how lucky that field is not protected, this test would be impossible - ApiDispatcher.processParameters(cmd, params); + final TestCmd cmd = new TestCmd(); + paramProcessWorker.processParameters(cmd, params); Assert.assertEquals("foo", cmd.strparam1); Assert.assertEquals(100, cmd.intparam1); } diff --git a/server/test/com/cloud/api/dispatch/SpecificCmdValidationWorkerTest.java b/server/test/com/cloud/api/dispatch/SpecificCmdValidationWorkerTest.java new file mode 100644 index 00000000000..4ae720060f0 --- /dev/null +++ b/server/test/com/cloud/api/dispatch/SpecificCmdValidationWorkerTest.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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 com.cloud.api.dispatch; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.apache.cloudstack.api.BaseCmd; + +import com.cloud.exception.ResourceAllocationException; + +public class SpecificCmdValidationWorkerTest { + + @Test + public void testHandle() throws ResourceAllocationException { + // Prepare + final BaseCmd cmd = mock(BaseCmd.class); + final Map params = new HashMap(); + + // Execute + final SpecificCmdValidationWorker worker = new SpecificCmdValidationWorker(); + + worker.handle(new DispatchTask(cmd, params)); + + // Assert + verify(cmd, times(1)).validateSpecificParameters(params); + } +} diff --git a/server/test/com/cloud/network/vpc/VpcManagerImplTest.java b/server/test/com/cloud/network/vpc/VpcManagerImplTest.java new file mode 100644 index 00000000000..3918a374034 --- /dev/null +++ b/server/test/com/cloud/network/vpc/VpcManagerImplTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 com.cloud.network.vpc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; + +public class VpcManagerImplTest { + + @Mock + VpcOfferingServiceMapDao vpcOffSvcMapDao; + VpcManagerImpl manager; + + @Before + public void setup() + { + MockitoAnnotations.initMocks(this); + manager = new VpcManagerImpl(); + manager._vpcOffSvcMapDao = vpcOffSvcMapDao; + } + @Test + public void getVpcOffSvcProvidersMapForEmptyServiceTest() { + long vpcOffId = 1L; + List list = new ArrayList(); + list.add(Mockito.mock(VpcOfferingServiceMapVO.class)); + when(manager._vpcOffSvcMapDao.listByVpcOffId(vpcOffId)).thenReturn(list); + + Map> map = manager.getVpcOffSvcProvidersMap(vpcOffId); + + assertNotNull(map); + assertEquals(map.size(),1); + } + +} diff --git a/server/test/com/cloud/resource/MockResourceManagerImpl.java b/server/test/com/cloud/resource/MockResourceManagerImpl.java index 5599e8c42b9..e6bf9a26b2c 100644 --- a/server/test/com/cloud/resource/MockResourceManagerImpl.java +++ b/server/test/com/cloud/resource/MockResourceManagerImpl.java @@ -17,6 +17,7 @@ package com.cloud.resource; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,6 +36,7 @@ import org.apache.cloudstack.api.command.admin.host.UpdateHostPasswordCmd; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.PodCluster; @@ -42,6 +44,7 @@ import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.DiscoveryException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceInUseException; +import com.cloud.gpu.HostGpuGroupsVO; import com.cloud.host.Host; import com.cloud.host.Host.Type; import com.cloud.host.HostStats; @@ -554,4 +557,32 @@ public class MockResourceManagerImpl extends ManagerBase implements ResourceMana return false; } + @Override + public boolean isGPUDeviceAvailable(long hostId, String vgpuType) { + // TODO Auto-generated method stub + return false; + } + + @Override + public GPUDeviceTO getGPUDevice(long hostId, String vgpuType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List listAvailableGPUDevice(long hostId, String vgpuType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void updateGPUDetails(long hostId, HashMap> deviceDetails) { + // TODO Auto-generated method stub + } + + @Override + public HashMap> getGPUStatistics(HostVO host) { + // TODO Auto-generated method stub + return null; + } } diff --git a/server/test/com/cloud/user/MockAccountManagerImpl.java b/server/test/com/cloud/user/MockAccountManagerImpl.java index fa786bf48fd..b411b18f020 100644 --- a/server/test/com/cloud/user/MockAccountManagerImpl.java +++ b/server/test/com/cloud/user/MockAccountManagerImpl.java @@ -348,7 +348,12 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco public void checkAccess(Account account, AccessType accessType, boolean sameOwner, String apiName, ControlledEntity... entities) throws PermissionDeniedException { // TODO Auto-generated method stub + } + @Override + public Long finalyzeAccountId(String accountName, Long domainId, Long projectId, boolean enabledOnly) { + // TODO Auto-generated method stub + return null; } } diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java index 751a3bdd43f..fb63766e378 100644 --- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -78,7 +78,9 @@ import com.cloud.exception.AffinityConflictException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.resource.ResourceManager; import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.StorageManager; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSCategoryDao; @@ -238,6 +240,16 @@ public class DeploymentPlanningManagerImplTest { return Mockito.mock(ClusterDetailsDao.class); } + @Bean + public ResourceManager resourceManager() { + return Mockito.mock(ResourceManager.class); + } + + @Bean + public ServiceOfferingDetailsDao serviceOfferingDetailsDao() { + return Mockito.mock(ServiceOfferingDetailsDao.class); + } + @Bean public DataStoreManager cataStoreManager() { return Mockito.mock(DataStoreManager.class); diff --git a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java index 4802cfff93b..cbed4cab41a 100644 --- a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java +++ b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java @@ -17,18 +17,8 @@ package com.cloud.vpc; -import java.util.List; -import java.util.Map; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; - -import org.springframework.stereotype.Component; - -import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; -import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; - import com.cloud.deploy.DeployDestination; +import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; @@ -54,6 +44,14 @@ import com.cloud.vm.DomainRouterVO; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile.Param; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import java.util.List; +import java.util.Map; @Component @Local(value = {VpcVirtualNetworkApplianceManager.class, VpcVirtualNetworkApplianceService.class}) @@ -313,8 +311,8 @@ public class MockVpcVirtualNetworkApplianceManager extends ManagerBase implement * @see com.cloud.network.VpcVirtualNetworkApplianceService#addVpcRouterToGuestNetwork(com.cloud.network.router.VirtualRouter, com.cloud.network.Network, boolean) */ @Override - public boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { + public boolean addVpcRouterToGuestNetwork(VirtualRouter router, Network network, boolean isRedundant, Map params) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // TODO Auto-generated method stub return false; } @@ -420,6 +418,21 @@ public class MockVpcVirtualNetworkApplianceManager extends ManagerBase implement return false; } + @Override + public boolean prepareAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return true; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean completeAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return true; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean cleanupAggregatedExecution(Network network, List routers) throws AgentUnavailableException { + return true; //To change body of implemented methods use File | Settings | File Templates. + } + @Override public boolean startRemoteAccessVpn(RemoteAccessVpn vpn, VirtualRouter router) throws ResourceUnavailableException { // TODO Auto-generated method stub diff --git a/server/test/resources/createNetworkOffering.xml b/server/test/resources/createNetworkOffering.xml index c6228dab707..6ae1978f40f 100644 --- a/server/test/resources/createNetworkOffering.xml +++ b/server/test/resources/createNetworkOffering.xml @@ -43,7 +43,9 @@ - + + + diff --git a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index dd0c26774d2..f8edefa39a1 100755 --- a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -807,6 +807,13 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar _useSSlCopy = true; } + //default to HTTP in case of missing domain + String ssvmUrlDomain = _configDao.getValue("secstorage.ssl.cert.domain"); + if(_useSSlCopy && (ssvmUrlDomain == null || ssvmUrlDomain.isEmpty())){ + s_logger.warn("Empty secondary storage url domain, explicitly disabling SSL"); + _useSSlCopy = false; + } + _allowedInternalSites = _configDao.getValue("secstorage.allowed.internal.sites"); String value = configs.get("secstorage.capacityscan.interval"); diff --git a/setup/db/db/schema-421to430.sql b/setup/db/db/schema-421to430.sql index b49fd965fb7..3f2ad023d26 100644 --- a/setup/db/db/schema-421to430.sql +++ b/setup/db/db/schema-421to430.sql @@ -110,6 +110,10 @@ CREATE TABLE `cloud`.`async_job_join_map` ( INDEX `i_async_job_join_map__expiration`(`expiration`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +#realhostip changes, before changing table and adding default value +UPDATE `cloud`.`configuration` SET value = CONCAT("*.",(SELECT `temptable`.`value` FROM (SELECT * FROM `cloud`.`configuration` WHERE `name`="consoleproxy.url.domain") AS `temptable` WHERE `temptable`.`name`="consoleproxy.url.domain")) WHERE `name`="consoleproxy.url.domain"; +UPDATE `cloud`.`configuration` SET `value` = CONCAT("*.",(SELECT `temptable`.`value` FROM (SELECT * FROM `cloud`.`configuration` WHERE `name`="secstorage.ssl.cert.domain") AS `temptable` WHERE `temptable`.`name`="secstorage.ssl.cert.domain")) WHERE `name`="secstorage.ssl.cert.domain"; + ALTER TABLE `cloud`.`configuration` ADD COLUMN `default_value` VARCHAR(4095) COMMENT 'Default value for a configuration parameter'; ALTER TABLE `cloud`.`configuration` ADD COLUMN `updated` datetime COMMENT 'Time this was updated by the server. null means this row is obsolete.'; ALTER TABLE `cloud`.`configuration` ADD COLUMN `scope` VARCHAR(255) DEFAULT NULL COMMENT 'Can this parameter be scoped'; diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql index 52f6c67c8f8..d321bed4c66 100644 --- a/setup/db/db/schema-430to440.sql +++ b/setup/db/db/schema-430to440.sql @@ -44,6 +44,7 @@ CREATE VIEW `cloud`.`disk_offering_view` AS disk_offering.removed, disk_offering.use_local_storage, disk_offering.system_use, + disk_offering.hv_ss_reserve, disk_offering.bytes_read_rate, disk_offering.bytes_write_rate, disk_offering.iops_read_rate, @@ -75,6 +76,10 @@ CREATE VIEW `cloud`.`service_offering_view` AS disk_offering.removed, disk_offering.use_local_storage, disk_offering.system_use, + disk_offering.customized_iops, + disk_offering.min_iops, + disk_offering.max_iops, + disk_offering.hv_ss_reserve, disk_offering.bytes_read_rate, disk_offering.bytes_write_rate, disk_offering.iops_read_rate, @@ -652,7 +657,7 @@ ALTER TABLE `cloud`.`s2s_vpn_gateway` ADD COLUMN `display` tinyint(1) NOT NULL D INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (225, UUID(), 9, 'FreeBSD 10 (32-bit)'); INSERT IGNORE INTO `cloud`.`guest_os` (id, uuid, category_id, display_name) VALUES (226, UUID(), 9, 'FreeBSD 10 (64-bit)'); -ALTER TABLE `cloud`.`event` ADD COLUMN `display_event` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user'; +ALTER TABLE `cloud`.`event` ADD COLUMN `display` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'True if the detail can be displayed to the end user'; DROP VIEW IF EXISTS `cloud`.`event_view`; CREATE VIEW `cloud`.`event_view` AS @@ -669,7 +674,7 @@ CREATE VIEW `cloud`.`event_view` AS eve.uuid start_uuid, event.user_id, event.archived, - event.display_event, + event.display, user.username user_name, account.id account_id, account.uuid account_uuid, @@ -696,4 +701,32 @@ CREATE VIEW `cloud`.`event_view` AS `cloud`.`event` eve ON event.start_id = eve.id; +DROP TABLE IF EXISTS `cloud`.`host_gpu_groups`; +CREATE TABLE `cloud`.`host_gpu_groups` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `group_name` varchar(255) NOT NULL, + `host_id` bigint(20) unsigned NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_host_gpu_groups__host_id` FOREIGN KEY (`host_id`) REFERENCES `host` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB CHARSET=utf8; +DROP TABLE IF EXISTS `cloud`.`vgpu_types`; +CREATE TABLE `cloud`.`vgpu_types` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `gpu_group_id` bigint(20) unsigned NOT NULL, + `vgpu_type` varchar(40) NOT NULL COMMENT 'vgpu type supported by this gpu group', + `remaining_vm_capacity` bigint(20) unsigned DEFAULT NULL COMMENT 'remaining vgpu can be created with this vgpu_type on the given gpu group', + PRIMARY KEY (`id`), + CONSTRAINT `fk_vgpu_types__gpu_group_id` FOREIGN KEY (`gpu_group_id`) REFERENCES `host_gpu_groups` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB CHARSET=utf8; + +ALTER TABLE `cloud`.`guest_os_hypervisor` ADD COLUMN `hypervisor_version` varchar(32) NOT NULL DEFAULT 'default' COMMENT 'Hypervisor version for this mapping'; +ALTER TABLE `cloud`.`guest_os_hypervisor` ADD COLUMN `uuid` varchar(40) COMMENT 'UUID of the mapping'; +ALTER TABLE `cloud`.`guest_os_hypervisor` ADD CONSTRAINT `uc_guest_os_hypervisor__uuid` UNIQUE (`uuid`); +ALTER TABLE `cloud`.`guest_os_hypervisor` ADD COLUMN `created` datetime COMMENT 'Time when mapping was created'; +ALTER TABLE `cloud`.`guest_os_hypervisor` ADD COLUMN `removed` datetime COMMENT 'Time when mapping was removed if deleted, else NULL'; +UPDATE `cloud`.`guest_os_hypervisor` SET `uuid` = UUID(); +UPDATE `cloud`.`guest_os_hypervisor` SET `created` = now(); +ALTER TABLE `cloud`.`guest_os` ADD COLUMN `created` datetime COMMENT 'Time when Guest OS was created in system'; +ALTER TABLE `cloud`.`guest_os` ADD COLUMN `removed` datetime COMMENT 'Time when Guest OS was removed if deleted, else NULL'; +UPDATE `cloud`.`guest_os` SET `created` = now(); diff --git a/systemvm/conf/consoleproxy.properties b/systemvm/conf/consoleproxy.properties index bb452f5823c..a3cddbcab96 100644 --- a/systemvm/conf/consoleproxy.properties +++ b/systemvm/conf/consoleproxy.properties @@ -16,7 +16,7 @@ # under the License. consoleproxy.tcpListenPort=0 -consoleproxy.httpListenPort=8088 +consoleproxy.httpListenPort=80 consoleproxy.httpCmdListenPort=8001 consoleproxy.jarDir=./applet/ consoleproxy.viewerLinger=180 diff --git a/systemvm/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/systemvm/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in index 76ee681dbe9..3ec4d0655cd 100644 --- a/systemvm/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ b/systemvm/distro/centos/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@CPLOG@ diff --git a/systemvm/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/systemvm/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in index 76ee681dbe9..3ec4d0655cd 100644 --- a/systemvm/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ b/systemvm/distro/fedora/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@CPLOG@ diff --git a/systemvm/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in b/systemvm/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in index 76ee681dbe9..3ec4d0655cd 100644 --- a/systemvm/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in +++ b/systemvm/distro/rhel/SYSCONFDIR/rc.d/init.d/cloud-console-proxy.in @@ -26,7 +26,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@CPLOG@ diff --git a/systemvm/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in b/systemvm/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in index c83f00aa76d..0c7be737353 100755 --- a/systemvm/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in +++ b/systemvm/distro/ubuntu/SYSCONFDIR/init.d/cloud-console-proxy.in @@ -27,7 +27,7 @@ # set environment variables -SHORTNAME="$0" +SHORTNAME=`basename $0` PIDFILE=@PIDDIR@/"$SHORTNAME".pid LOCKFILE=@LOCKDIR@/"$SHORTNAME" LOGFILE=@CPLOG@ diff --git a/systemvm/patches/debian/config/etc/init.d/cloud-early-config b/systemvm/patches/debian/config/etc/init.d/cloud-early-config index fa95fdadb16..5751f09ac34 100755 --- a/systemvm/patches/debian/config/etc/init.d/cloud-early-config +++ b/systemvm/patches/debian/config/etc/init.d/cloud-early-config @@ -1042,6 +1042,16 @@ EOF sed -r -i s/^[#]?domain=.*$/domain=$DOMAIN/ /etc/dnsmasq.conf #answer all local domain queries sed -i -e "s/^[#]*local=.*$/local=\/$DOMAIN\//" /etc/dnsmasq.conf + + command -v dhcp_release > /dev/null 2>&1 + no_dhcp_release=$? + if [ $no_dhcp_release -eq 0 ] + then + echo 1 > /var/cache/cloud/dnsmasq_managed_lease + sed -i -e "/^leasefile-ro/d" /etc/dnsmasq.conf + else + echo 0 > /var/cache/cloud/dnsmasq_managed_lease + fi } @@ -1221,10 +1231,6 @@ start() { patch_log4j parse_cmd_line change_password - if [ "$hyp" == "hyperv" ]; then - # eject the systemvm.iso - eject - fi case $TYPE in router) [ "$NAME" == "" ] && NAME=router @@ -1259,6 +1265,10 @@ start() { setup_default; ;; esac + if [ "$hyp" == "hyperv" ]; then + # eject the systemvm.iso + eject + fi return 0 } diff --git a/systemvm/patches/debian/config/opt/cloud/bin/vr_cfg.sh b/systemvm/patches/debian/config/opt/cloud/bin/vr_cfg.sh new file mode 100755 index 00000000000..dffa45a3057 --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/vr_cfg.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +#set -x + +cfg= +version= +log=/var/log/cloud.log + +log_it() { + logger -t cloud "$*" + echo "$(date) : $*" >> $log +} + +while getopts 'c:' OPTION +do + case $OPTION in + c) cfg="$OPTARG" + ;; + esac +done + +while read line +do + #comment + if [[ $line == \#* ]] + then + continue + fi + + if [ "$line" == "" ] + then + read line + version=$line + log_it "VR config: configuation format version $version" + #skip + read line + continue + fi + + if [ "$line" == " + read line + log_it "VR config: execution success " + continue + fi + + if [ "$line" == "" ] + then + read line + file=$line + log_it "VR config: creating file: $file" + rm -f $file + while read line + do + if [ "$line" == "" ] + then + break + fi + echo $line >> $file + done + log_it "VR config: create file success" + continue + fi +done < $cfg + +#remove the configuration file, log file should have all the records as well +rm -f $cfg + +exit 0 diff --git a/test/integration/component/test_cpu_domain_limits.py b/test/integration/component/test_cpu_domain_limits.py index c427e4fcf41..1247a799c89 100644 --- a/test/integration/component/test_cpu_domain_limits.py +++ b/test/integration/component/test_cpu_domain_limits.py @@ -30,10 +30,11 @@ from marvin.integration.lib.base import ( from marvin.integration.lib.common import (get_domain, get_zone, get_template, - find_suitable_host, + findSuitableHostForMigration, get_resource_type ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: """Test resource limit services @@ -329,7 +330,9 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Initial resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, vm) + host = findSuitableHostForMigration(self.apiclient, vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm.name, host.name)) try: @@ -477,7 +480,9 @@ class TestDomainCPULimitsUpdateResources(cloudstackTestCase): self.assertEqual(resource_count_after_delete, expected_resource_count, "Resource count should match with the expected count") - host = find_suitable_host(self.apiclient, vm_2) + host = findSuitableHostForMigration(self.apiclient, vm_2.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm_2.name, host.name)) try: diff --git a/test/integration/component/test_cpu_limits.py b/test/integration/component/test_cpu_limits.py index bdf2869c7c1..f043773e7b1 100644 --- a/test/integration/component/test_cpu_limits.py +++ b/test/integration/component/test_cpu_limits.py @@ -30,10 +30,11 @@ from marvin.integration.lib.base import ( from marvin.integration.lib.common import (get_domain, get_zone, get_template, - find_suitable_host, + findSuitableHostForMigration, get_resource_type ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: @@ -251,7 +252,9 @@ class TestCPULimits(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, self.vm) + host = findSuitableHostForMigration(self.apiclient, self.vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) try: self.vm.migrate(self.apiclient, host.id) @@ -570,7 +573,9 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Initial resource count should with the expected resource count") - host = find_suitable_host(self.apiclient, vm) + host = findSuitableHostForMigration(self.apiclient, vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm.name, host.name)) try: @@ -725,7 +730,9 @@ class TestDomainCPULimitsConfiguration(cloudstackTestCase): self.assertEqual(resource_count_after_delete, expected_resource_count, "Resource count should be less than before after deleting the instance") - host = find_suitable_host(self.apiclient, vm_2) + host = findSuitableHostForMigration(self.apiclient, vm_2.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm_2.name, host.name)) try: diff --git a/test/integration/component/test_cpu_project_limits.py b/test/integration/component/test_cpu_project_limits.py index a8a1b3c3b24..ed7cd88248a 100644 --- a/test/integration/component/test_cpu_project_limits.py +++ b/test/integration/component/test_cpu_project_limits.py @@ -30,10 +30,11 @@ from marvin.integration.lib.base import ( from marvin.integration.lib.common import (get_domain, get_zone, get_template, - find_suitable_host, + findSuitableHostForMigration, get_resource_type ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: """Test resource limit services @@ -291,7 +292,9 @@ class TestProjectsCPULimits(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, self.vm) + host = findSuitableHostForMigration(self.apiclient, self.vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) try: diff --git a/test/integration/component/test_dynamic_compute_offering.py b/test/integration/component/test_dynamic_compute_offering.py new file mode 100644 index 00000000000..75cf0d658f4 --- /dev/null +++ b/test/integration/component/test_dynamic_compute_offering.py @@ -0,0 +1,409 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +""" Tests for Dynamic Compute Offering Feature + + Test Plan: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Dynamic+ComputeOffering + + Issue Link: https://issues.apache.org/jira/browse/CLOUDSTACK-6147 + + Feature Specifications: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Dynamic+Compute+Offering+FS +""" +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.integration.lib.utils import (cleanup_resources, + validateList) +from marvin.integration.lib.base import (ServiceOffering, + VirtualMachine, + Account) +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + verifyComputeOfferingCreation) + +from nose.plugins.attrib import attr +from marvin.codes import PASS, ADMIN_ACCOUNT, USER_ACCOUNT +from ddt import ddt, data + +@ddt +class TestDynamicServiceOffering(cloudstackTestCase): + """Test Dynamic Service Offerings + """ + + @classmethod + def setUpClass(cls): + cloudstackTestClient = super(TestDynamicServiceOffering,cls).getClsTestClient() + cls.api_client = cloudstackTestClient.getApiClient() + + # Fill services from the external config file + cls.services = cloudstackTestClient.getConfigParser().parsedDict + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.mode = str(cls.zone.networktype).lower() + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + # Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup_co = [] + self.cleanup = [] + return + + def tearDown(self): + try: + # Clean up compute offerings + cleanup_resources(self.apiclient, self.cleanup) + + # Clean up compute offerings + cleanup_resources(self.apiclient, self.cleanup_co) + + self.cleanup_co[:] = [] + self.cleanup[:] = [] + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["basic","advanced"]) + def test_create_normal_compute_offering(self): + """ Create normal compute offering with non zero values for cpu, + cpu number and memory""" + + # Steps: + # 1. Create normal compute offering with non zero values for cpu number, + # cpu speed, memory + + # Validations: + # 1. Compute offering should be created + + self.services["service_offering"]["cpunumber"] = 2 + self.services["service_offering"]["cpuspeed"] = 256 + self.services["service_offering"]["memory"] = 128 + + serviceOffering = ServiceOffering.create(self.api_client, + self.services["service_offering"] + ) + self.assertEqual(verifyComputeOfferingCreation(self.apiclient, serviceOffering.id), + PASS, "Compute Offering verification failed") + + self.cleanup_co.append(serviceOffering) + return + + @attr(tags=["basic","advanced"]) + def test_create_dynamic_compute_offering(self): + """ Create dynamic compute offering with cpunumber, cpuspeed and memory + not specified""" + + # Steps: + # 1. Create dynamic compute offering with values for cpu number, + # cpu speed, memory not specified + + # Validations: + # 1. Compute offering should be created + + self.services["service_offering"]["cpunumber"] = "" + self.services["service_offering"]["cpuspeed"] = "" + self.services["service_offering"]["memory"] = "" + + serviceOffering = ServiceOffering.create(self.api_client, + self.services["service_offering"] + ) + self.assertEqual(verifyComputeOfferingCreation(self.apiclient, serviceOffering.id), + PASS, "Compute Offering verification failed") + + self.cleanup_co.append(serviceOffering) + return + + @attr(tags=["basic","advanced"]) + def test_create_dynamic_compute_offering_no_cpunumber(self): + """ Create dynamic compute offering with only cpunumber unspecified""" + + # Validations: + # 1. Compute Offering creation should fail + + self.services["service_offering"]["cpunumber"] = "" + self.services["service_offering"]["cpuspeed"] = 256 + self.services["service_offering"]["memory"] = 128 + + try: + serviceOffering = ServiceOffering.create(self.api_client, + self.services["service_offering"] + ) + self.cleanup_co.append(serviceOffering) + self.fail("Compute Offering creation succeded, it should have failed") + except Exception: + self.debug("Compute Offering Creation failed as expected") + return + + @attr(tags=["basic","advanced"]) + def test_create_dynamic_compute_offering_no_cpuspeed(self): + """ Create dynamic compute offering with only cpuspeed unspecified""" + + # Validations: + # 1. Compute offering creation should fail + + self.services["service_offering"]["cpunumber"] = 2 + self.services["service_offering"]["cpuspeed"] = "" + self.services["service_offering"]["memory"] = 128 + + try: + serviceOffering = ServiceOffering.create(self.api_client, + self.services["service_offering"] + ) + self.cleanup_co.append(serviceOffering) + self.fail("Compute Offering creation succeded, it should have failed") + except Exception: + self.debug("Compute Offering Creation failed as expected") + return + + @attr(tags=["basic","advanced"]) + def test_create_dynamic_compute_offering_no_memory(self): + """ Create dynamic compute offering with only memory unspecified""" + + # Validations: + # 1. Compute offering creation should fail + + self.services["service_offering"]["cpunumber"] = 2 + self.services["service_offering"]["cpuspeed"] = 256 + self.services["service_offering"]["memory"] = "" + + try: + serviceOffering = ServiceOffering.create(self.api_client, + self.services["service_offering"] + ) + self.cleanup_co.append(serviceOffering) + self.fail("Compute Offering creation succeded, it should have failed") + except Exception: + self.debug("Compute Offering Creation failed as expected") + return + + @data(ADMIN_ACCOUNT, USER_ACCOUNT) + @attr(tags=["basic","advanced"]) + def test_deploy_virtual_machines_static_offering(self, value): + """Test deploy VM with static offering""" + + # Steps: + # 1. Create admin/user account and create its user api client + # 2. Create a static compute offering + # 3. Deploy a VM with account api client and static service offering + # 4. Repeat step 3 but also pass custom values for cpu number, cpu speed and memory + # while deploying VM + + # Validations: + # 1. Step 3 should succeed + # 2. Step 4 should fail + + isadmin=True + if value == USER_ACCOUNT: + isadmin=False + + # Create Account + self.account = Account.create(self.apiclient,self.services["account"],domainid=self.domain.id, admin=isadmin) + apiclient = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.cleanup.append(self.account) + + # Create service offering + self.services["service_offering"]["cpunumber"] = 2 + self.services["service_offering"]["cpuspeed"] = 256 + self.services["service_offering"]["memory"] = 128 + + serviceOffering = ServiceOffering.create(self.apiclient, + self.services["service_offering"]) + + self.cleanup_co.append(serviceOffering) + + # Deploy VM with static service offering + try: + VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + accountid=self.account.name,domainid=self.account.domainid) + except Exception as e: + self.fail("vm creation failed: %s" % e) + + # Deploy VM with static service offering, also with custom values + try: + VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + customcpunumber=4, + customcpuspeed=512, + custommemory=256, + accountid=self.account.name,domainid=self.account.domainid) + self.fail("VM creation should have failed, it succeeded") + except Exception as e: + self.debug("vm creation failed as expected: %s" % e) + return + + @data(ADMIN_ACCOUNT, USER_ACCOUNT) + @attr(tags=["basic","advanced"]) + def test_deploy_virtual_machines_dynamic_offering(self, value): + """Test deploy VM with dynamic compute offering""" + + # Steps: + # 1. Create admin/user account and create its user api client + # 2. Create a dynamic service offering + # 3. Deploy a VM with account api client and dynamic service offering + # without providing custom values for cpu number, cpu speed and memory + # 4. Deploy a VM with account api client and dynamic service offering providing + # custom values for cpu number, cpu speed and memory + # 5. Deploy a VM with account api client and dynamic service offering providing + # custom values only for cpu number + + # Validations: + # 1. Step 3 should fail + # 2. Step 4 should succeed + # 3. Step 5 should fail + + isadmin=True + if value == USER_ACCOUNT: + isadmin=False + + # Create Account and its api client + self.account = Account.create(self.apiclient,self.services["account"],domainid=self.domain.id, admin=isadmin) + apiclient = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.cleanup.append(self.account) + + # Create dynamic service offering + self.services["service_offering"]["cpunumber"] = "" + self.services["service_offering"]["cpuspeed"] = "" + self.services["service_offering"]["memory"] = "" + + serviceOffering = ServiceOffering.create(self.apiclient, + self.services["service_offering"]) + + self.cleanup_co.append(serviceOffering) + + # Deploy VM with dynamic compute offering without providing custom values for + # cpu number, cpu speed and memory + try: + VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + accountid=self.account.name,domainid=self.account.domainid) + self.fail("VM creation succeded, it should have failed") + except Exception as e: + self.debug("vm creation failed as expected with error: %s" % e) + + # Deploy VM with dynamic compute offering providing custom values for + # cpu number, cpu speed and memory + try: + VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + customcpunumber=2, + customcpuspeed=256, + custommemory=128, + accountid=self.account.name,domainid=self.account.domainid) + except Exception as e: + self.fail("vm creation failed: %s" % e) + + # Deploy VM with dynamic compute offering providing custom values for only + # cpu number + try: + VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + customcpunumber=2, + accountid=self.account.name,domainid=self.account.domainid) + self.fail("VM deployment should have failed, it succeded") + except Exception as e: + self.debug("vm creation failed as expected: %s" % e) + return + + @data(ADMIN_ACCOUNT, USER_ACCOUNT) + @attr(tags=["basic","advanced"]) + def test_check_vm_stats(self, value): + """Deploy VM with dynamic service offering and check VM stats""" + + # Steps: + # 1. Create admin/user account and create its user api client + # 2. Create a dynamic service offering + # 3. Deploy a VM with account api client and dynamic service offering + # providing custom values for cpu number, cpu speed and memory + # 4. List the VM and verify the dynamic parameters are same as passed + + isadmin=True + if value == USER_ACCOUNT: + isadmin=False + + # Create Account and api client + self.account = Account.create(self.apiclient,self.services["account"],domainid=self.domain.id, admin=isadmin) + apiclient = self.testClient.createUserApiClient( + UserName=self.account.name, + DomainName=self.account.domain) + self.cleanup.append(self.account) + + # Create dynamic compute offering + self.services["service_offering"]["cpunumber"] = "" + self.services["service_offering"]["cpuspeed"] = "" + self.services["service_offering"]["memory"] = "" + + serviceOffering = ServiceOffering.create(self.apiclient, + self.services["service_offering"]) + + self.cleanup_co.append(serviceOffering) + + # Custom values + customcpunumber = 2 + customcpuspeed = 256 + custommemory = 128 + + # Deploy VM with dynamic service offering and the custom values + try: + virtualMachine = VirtualMachine.create(apiclient,self.services["virtual_machine"], + serviceofferingid=serviceOffering.id, + customcpunumber=customcpunumber, + customcpuspeed=customcpuspeed, + custommemory=custommemory, + accountid=self.account.name,domainid=self.account.domainid) + except Exception as e: + self.fail("vm creation failed: %s" % e) + + vmlist = VirtualMachine.list(self.apiclient, id=virtualMachine.id) + self.assertEqual(validateList(vmlist)[0], PASS, "vm list validation failed") + vm = vmlist[0] + + # Verify the custom values + self.assertEqual(str(vm.cpunumber), str(customcpunumber), "vm cpu number %s\ + not matching with provided custom cpu number %s" % \ + (vm.cpunumber, customcpunumber)) + + self.assertEqual(str(vm.cpuspeed), str(customcpuspeed), "vm cpu speed %s\ + not matching with provided custom cpu speed %s" % \ + (vm.cpuspeed, customcpuspeed)) + + self.assertEqual(str(vm.memory), str(custommemory), "vm memory %s\ + not matching with provided custom memory %s" % \ + (vm.memory, custommemory)) + return diff --git a/test/integration/component/test_memory_limits.py b/test/integration/component/test_memory_limits.py index 7921e4be9ed..231307f693e 100644 --- a/test/integration/component/test_memory_limits.py +++ b/test/integration/component/test_memory_limits.py @@ -30,10 +30,11 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, wait_for_cleanup, - find_suitable_host, + findSuitableHostForMigration, get_resource_type ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: """Test memory resource limit services @@ -248,7 +249,9 @@ class TestMemoryLimits(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, self.vm) + host = findSuitableHostForMigration(self.apiclient, self.vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) try: self.vm.migrate(self.apiclient, host.id) @@ -587,7 +590,9 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Initial resource count should with the expected resource count") - host = find_suitable_host(self.apiclient, vm) + host = findSuitableHostForMigration(self.apiclient, vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm.name, host.name)) try: @@ -743,7 +748,9 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): self.assertEqual(resource_count_after_delete, expected_resource_count, "Resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, vm_2) + host = findSuitableHostForMigration(self.apiclient, vm_2.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm_2.name, host.name)) try: diff --git a/test/integration/component/test_mm_domain_limits.py b/test/integration/component/test_mm_domain_limits.py index 68660c13cf5..a2b7395cd13 100644 --- a/test/integration/component/test_mm_domain_limits.py +++ b/test/integration/component/test_mm_domain_limits.py @@ -30,11 +30,12 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, wait_for_cleanup, - find_suitable_host, + findSuitableHostForMigration, get_resource_type, update_resource_count ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: """Test memory resource limit services @@ -388,7 +389,9 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.assertEqual(resource_count, expected_resource_count, "Resource count should match with the expected resource count") - host = find_suitable_host(self.apiclient, vm) + host = findSuitableHostForMigration(self.apiclient, vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (vm.name, host.name)) try: diff --git a/test/integration/component/test_mm_project_limits.py b/test/integration/component/test_mm_project_limits.py index c314011090c..29a3b549729 100644 --- a/test/integration/component/test_mm_project_limits.py +++ b/test/integration/component/test_mm_project_limits.py @@ -30,10 +30,11 @@ from marvin.integration.lib.common import (get_domain, get_zone, get_template, wait_for_cleanup, - find_suitable_host, + findSuitableHostForMigration, get_resource_type ) from marvin.integration.lib.utils import cleanup_resources +from marvin.codes import ERROR_NO_HOST_FOR_MIGRATION class Services: """Test memory resource limit services @@ -291,7 +292,9 @@ class TestProjectsMemoryLimits(cloudstackTestCase): resource_count = project_list[0].memorytotal self.debug(resource_count) - host = find_suitable_host(self.apiclient, self.vm) + host = findSuitableHostForMigration(self.apiclient, self.vm.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) self.debug("Migrating instance: %s to host: %s" % (self.vm.name, host.name)) try: diff --git a/test/integration/component/test_routers.py b/test/integration/component/test_routers.py index f8359f0e2c5..ced3f525ca2 100644 --- a/test/integration/component/test_routers.py +++ b/test/integration/component/test_routers.py @@ -1254,21 +1254,10 @@ class TestRouterStopCreateFW(cloudstackTestCase): str(self.services["fw_rule"]["endport"]), "Check end port of firewall rule" ) - hosts = list_hosts( - self.apiclient, - id=router.hostid, - ) - self.assertEqual( - isinstance(hosts, list), - True, - "Check for list hosts response return valid data" - ) - host = hosts[0] - host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) - # For DNS and DHCP check 'dnsmasq' process status - if self.apiclient.hypervisor.lower() == 'vmware': - result = get_process_status( + if (self.apiclient.hypervisor.lower() == 'vmware' + or self.apiclient.hypervisor.lower() == 'hyperv'): + result = get_process_status( self.apiclient.connection.mgtSvr, 22, self.apiclient.connection.user, @@ -1278,6 +1267,17 @@ class TestRouterStopCreateFW(cloudstackTestCase): hypervisor=self.apiclient.hypervisor ) else: + hosts = list_hosts( + self.apiclient, + id=router.hostid, + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check for list hosts response return valid data" + ) + host = hosts[0] + host.user, host.passwd = get_host_credentials(self.config, host.ipaddress) try: result = get_process_status( host.ipaddress, diff --git a/test/integration/component/test_vpc_vm_life_cycle.py b/test/integration/component/test_vpc_vm_life_cycle.py index 01373ac5a73..e40067ea86d 100644 --- a/test/integration/component/test_vpc_vm_life_cycle.py +++ b/test/integration/component/test_vpc_vm_life_cycle.py @@ -41,9 +41,10 @@ from marvin.integration.lib.common import (get_domain, get_free_vlan, wait_for_cleanup, list_virtual_machines, - list_hosts) + list_hosts, + findSuitableHostForMigration) -from marvin.codes import PASS +from marvin.codes import PASS, ERROR_NO_HOST_FOR_MIGRATION import time @@ -715,35 +716,13 @@ class TestVMLifeCycleVPC(cloudstackTestCase): # works as expected. # 3. Make sure that we are able to access google.com from this user Vm - vm_list = VirtualMachine.list(self.apiclient, id=self.vm_1.id) - self.assertEqual(validateList(vm_list)[0], PASS, "vm list validation failed, vm list is %s" % vm_list) - - vm_hostid = vm_list[0].hostid - - self.debug("Checking if the host is available for migration?") - hosts = Host.list( - self.apiclient, - zoneid=self.zone.id, - type='Routing' - ) - - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should return a valid list" - ) - if len(hosts) < 2: - raise unittest.SkipTest( - "No host available for migration. Test requires atleast 2 hosts") - - # Remove the host of current VM from the hosts list - hosts[:] = [host for host in hosts if host.id != vm_hostid] - - host = hosts[0] - self.debug("Validating if the network rules work properly or not?") self.validate_network_rules() + host = findSuitableHostForMigration(self.apiclient, self.vm_1.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) + self.debug("Migrating VM-ID: %s to Host: %s" % ( self.vm_1.id, host.id @@ -1506,30 +1485,13 @@ class TestVMLifeCycleSharedNwVPC(cloudstackTestCase): # works as expected. # 3. Make sure that we are able to access google.com from this user Vm - self.debug("Checking if the host is available for migration?") - hosts = Host.list( - self.apiclient, - zoneid=self.zone.id, - type='Routing' - ) - - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should return a valid list" - ) - if len(hosts) < 2: - raise unittest.SkipTest( - "No host available for migration. Test requires atleast 2 hosts") - - # Remove the host of current VM from the hosts list - hosts[:] = [host for host in hosts if host.id != self.vm_1.hostid] - - host = hosts[0] - self.debug("Validating if network rules are coonfigured properly?") self.validate_network_rules() + host = findSuitableHostForMigration(self.apiclient, self.vm_1.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) + self.debug("Migrating VM-ID: %s to Host: %s" % ( self.vm_1.id, host.id @@ -2559,30 +2521,13 @@ class TestVMLifeCycleStoppedVPCVR(cloudstackTestCase): # works as expected. # 3. Make sure that we are able to access google.com from this user Vm - self.debug("Checking if the host is available for migration?") - hosts = Host.list( - self.apiclient, - zoneid=self.zone.id, - type='Routing' - ) - - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should return a valid list" - ) - if len(hosts) < 2: - raise unittest.SkipTest( - "No host available for migration. Test requires atleast 2 hosts") - - # Remove the host of current VM from the hosts list - hosts[:] = [host for host in hosts if host.id != self.vm_1.hostid] - - host = hosts[0] - self.debug("Validating if the network rules work properly or not?") self.validate_network_rules() + host = findSuitableHostForMigration(self.apiclient, self.vm_1.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) + self.debug("Migrating VM-ID: %s on Host: %s to Host: %s" % ( self.vm_1.id, self.vm_1.hostid, @@ -3459,28 +3404,13 @@ class TestVMLifeCycleDiffHosts(cloudstackTestCase): # works as expected. # 3. Make sure that we are able to access google.com from this user Vm - self.debug("Checking if the host is available for migration?") - hosts = Host.listForMigration( - self.apiclient, - virtualmachineid=self.vm_1.id, - ) - self.debug("Hosts vm can be migrated to are : %s" %(hosts)) - self.assertEqual( - isinstance(hosts, list), - True, - "List hosts should return a valid list" - ) - # Remove the host of current VM from the hosts list - hosts[:] = [host for host in hosts if host.id != self.vm_1.hostid] - if len(hosts) <= 0: - self.skipTest( - "No host available for migration. Test requires atleast 2 hosts tagged with host1") - - host = hosts[0] - self.debug("Validating if the network rules work properly or not?") self.validate_network_rules() + host = findSuitableHostForMigration(self.apiclient, self.vm_1.id) + if host is None: + self.skipTest(ERROR_NO_HOST_FOR_MIGRATION) + self.debug("Migrating VM-ID: %s to Host: %s" % ( self.vm_1.id, host.id diff --git a/test/integration/smoke/test_deploy_vgpu_enabled_vm.py b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py new file mode 100644 index 00000000000..a09e87e6f2d --- /dev/null +++ b/test/integration/smoke/test_deploy_vgpu_enabled_vm.py @@ -0,0 +1,227 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +#Test from the Marvin - Testing in Python wiki + +#All tests inherit from cloudstackTestCase +from marvin.cloudstackTestCase import cloudstackTestCase + +#Import Integration Libraries + +#base - contains all resources as entities and defines create, delete, list operations on them +from marvin.integration.lib.base import Account, VirtualMachine, ServiceOffering + +#utils - utility classes for common cleanup, external library wrappers etc +from marvin.integration.lib.utils import cleanup_resources + +#common - commonly used methods for all tests are listed here +from marvin.integration.lib.common import get_zone, get_domain, get_template + +from nose.plugins.attrib import attr + +class Services: + """Test VM Life Cycle Services + """ + + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "password", + }, + "vgpu260q": # Create a virtual machine instance with vgpu type as 260q + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "vgpu140q": # Create a virtual machine instance with vgpu type as 140q + { + "displayname": "testserver", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "vgpu260qwin": + { + "name": "Windows Instance with vGPU260Q", + "displaytext": "Windows Instance with vGPU260Q", + "cpunumber": 2, + "cpuspeed": 1600, # in MHz + "memory": 3072, # In MBs + }, + "vgpu140qwin": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "Windows Instance with vGPU140Q", + "displaytext": "Windows Instance with vGPU140Q", + "cpunumber": 2, + "cpuspeed": 1600, + "memory": 3072, + } + }, + "diskdevice": ['/dev/vdc', '/dev/vdb', '/dev/hdb', '/dev/hdc', '/dev/xvdd', '/dev/cdrom', '/dev/sr0', '/dev/cdrom1' ], + # Disk device where ISO is attached to instance + "mount_dir": "/mnt/tmp", + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostype": 'Windows 7 (32-bit)', + # CentOS 5.3 (64-bit) + } + + +class TestDeployvGPUenabledVM(cloudstackTestCase): + """Test deploy a vGPU enabled VM into a user account + """ + + def setUp(self): + self.services = Services().services + self.apiclient = self.testClient.getApiClient() + + # Get Zone, Domain and Default Built-in template + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + self.services["mode"] = self.zone.networktype + # Before running this test, register a windows template with ostype as 'Windows 7 (32-bit)' + self.template = get_template(self.apiclient, self.zone.id, self.services["ostype"], templatetype='USER') + + #create a user account + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.services["vgpu260q"]["zoneid"] = self.zone.id + self.services["vgpu260q"]["template"] = self.template.id + + self.services["vgpu140q"]["zoneid"] = self.zone.id + self.services["vgpu140q"]["template"] = self.template.id + #create a service offering + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["vgpu260qwin"], + serviceofferingdetails={'pciDevice': 'VGPU'} + ) + #build cleanup list + self.cleanup = [ + self.service_offering, + self.account + ] + + @attr(tags = ['advanced', 'simulator', 'basic', 'vgpu']) + def test_deploy_vgpu_enabled_vm(self): + """Test Deploy Virtual Machine + + # Validate the following: + # 1. Virtual Machine is accessible via SSH + # 2. Virtual Machine is vGPU enabled (via SSH) + # 3. listVirtualMachines returns accurate information + """ + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["vgpu260q"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services['mode'] + ) + + list_vms = VirtualMachine.list(self.apiclient, id=self.virtual_machine.id) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s"\ + % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vms, list), + True, + "List VM response was not a valid list" + ) + self.assertNotEqual( + len(list_vms), + 0, + "List VM response was empty" + ) + + vm = list_vms[0] + self.assertEqual( + vm.id, + self.virtual_machine.id, + "Virtual Machine ids do not match" + ) + self.assertEqual( + vm.name, + self.virtual_machine.name, + "Virtual Machine names do not match" + ) + self.assertEqual( + vm.state, + "Running", + msg="VM is not in Running state" + ) + list_hosts = list_hosts( + self.apiclient, + id=vm.hostid + ) + hostip = list_hosts[0].ipaddress + try: + sshClient = SshClient(host=hostip, port=22, user='root',passwd=self.services["host_password"]) + res = sshClient.execute("xe vgpu-list vm-name-label=%s params=type-uuid %s" % ( + vm.instancename + )) + self.debug("SSH result: %s" % res) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (hostip, e) + ) + result = str(res) + self.assertEqual( + result.count("type-uuid"), + 1, + "VM is vGPU enabled." + ) + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) \ No newline at end of file diff --git a/test/integration/smoke/test_deploy_vm.py b/test/integration/smoke/test_deploy_vm.py index 425aeb749d2..fcde2294df9 100644 --- a/test/integration/smoke/test_deploy_vm.py +++ b/test/integration/smoke/test_deploy_vm.py @@ -51,6 +51,11 @@ class TestData(object): "name" : "testvm", "displayname" : "Test VM", }, + #data reqd for virtual machine creation + "virtual_machine2" : { + "name" : "testvm2", + "displayname" : "Test VM2", + }, #small service offering "service_offering": { "small": { @@ -149,8 +154,53 @@ class TestDeployVM(cloudstackTestCase): msg="VM is not in Running state" ) + @attr(tags = ['advanced', 'simulator', 'basic', 'sg']) + def test_deploy_vm_multiple(self): + """Test Multiple Deploy Virtual Machine + + # Validate the following: + # 1. deploy 2 virtual machines + # 2. listVirtualMachines using 'ids' parameter returns accurate information + """ + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + + self.virtual_machine2 = VirtualMachine.create( + self.apiclient, + self.testdata["virtual_machine2"], + accountid=self.account.name, + zoneid=self.zone.id, + domainid=self.account.domainid, + serviceofferingid=self.service_offering.id, + templateid=self.template.id + ) + + list_vms = VirtualMachine.list(self.apiclient, ids=[self.virtual_machine.id, self.virtual_machine2.id], listAll=True) + self.debug( + "Verify listVirtualMachines response for virtual machines: %s, %s" % (self.virtual_machine.id, self.virtual_machine2.id) + ) + + self.assertEqual( + isinstance(list_vms, list), + True, + "List VM response was not a valid list" + ) + self.assertEqual( + len(list_vms), + 2, + "List VM response was empty, expected 2 VMs" + ) + def tearDown(self): try: cleanup_resources(self.apiclient, self.cleanup) except Exception as e: - self.debug("Warning! Exception in tearDown: %s" % e) \ No newline at end of file + self.debug("Warning! Exception in tearDown: %s" % e) + diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py index 3882f0d59b8..e4a0f6a789a 100644 --- a/tools/marvin/marvin/codes.py +++ b/tools/marvin/marvin/codes.py @@ -51,3 +51,4 @@ BASIC_ZONE = "basic" ISOLATED_NETWORK = "ISOLATED" SHARED_NETWORK = "SHARED" VPC_NETWORK = "VPC" +ERROR_NO_HOST_FOR_MIGRATION = "Could not find suitable host for migration, please ensure setup has required no. of hosts" diff --git a/tools/marvin/marvin/configGenerator.py b/tools/marvin/marvin/configGenerator.py index 0d79e8ea6ad..0b4a0a132bc 100644 --- a/tools/marvin/marvin/configGenerator.py +++ b/tools/marvin/marvin/configGenerator.py @@ -51,8 +51,7 @@ class configuration(object): class logger(object): def __init__(self): '''TestCase/TestClient''' - self.name = None - self.file = None + self.logFolderPath = None class cloudstackConfiguration(object): @@ -61,7 +60,7 @@ class cloudstackConfiguration(object): self.mgtSvr = [] self.dbSvr = None self.globalConfig = [] - self.logger = [] + self.logger = None class zone(object): diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 4d968d1ffd8..72a6926b6bb 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -324,7 +324,8 @@ class VirtualMachine: domainid=None, zoneid=None, networkids=None, serviceofferingid=None, securitygroupids=None, projectid=None, startvm=None, diskofferingid=None, affinitygroupnames=None, affinitygroupids=None, group=None, - hostid=None, keypair=None, ipaddress=None, mode='default', method='GET'): + hostid=None, keypair=None, ipaddress=None, mode='default', method='GET', + customcpunumber=None, customcpuspeed=None, custommemory=None): """Create the instance""" cmd = deployVirtualMachine.deployVirtualMachineCmd() @@ -412,6 +413,17 @@ class VirtualMachine: if "userdata" in services: cmd.userdata = base64.urlsafe_b64encode(services["userdata"]) + cmd.details = [{"cpuNumber": "","cpuSpeed":"","memory":""}] + + if customcpunumber: + cmd.details[0]["cpuNumber"] = customcpunumber + + if customcpuspeed: + cmd.details[0]["cpuSpeed"] = customcpuspeed + + if custommemory: + cmd.details[0]["memory"] = custommemory + if group: cmd.group = group @@ -636,6 +648,21 @@ class VirtualMachine: return apiclient.updateVMAffinityGroup(cmd) + def scale(self, apiclient, serviceOfferingId, + customcpunumber=None, customcpuspeed=None, custommemory=None): + """Change service offering of the instance""" + cmd = scaleVirtualMachine.scaleVirtualMachineCmd() + cmd.id = self.id + cmd.serviceofferingid = serviceOfferingId + cmd.details = [{"cpuNumber": "","cpuSpeed":"","memory":""}] + if customcpunumber: + cmd.details[0]["cpuNumber"] = customcpunumber + if customcpuspeed: + cmd.details[0]["cpuSpeed"] = customcpuspeed + if custommemory: + cmd.details[0]["memory"] = custommemory + return apiclient.scaleVirtualMachine(cmd) + class Volume: """Manage Volume Life cycle @@ -1435,6 +1462,9 @@ class ServiceOffering: if "deploymentplanner" in services: cmd.deploymentplanner = services["deploymentplanner"] + if "serviceofferingdetails" in services: + cmd.serviceofferingdetails.append({services['serviceofferingdetails']}) + if "isvolatile" in services: cmd.isvolatile = services["isvolatile"] diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index b2da3ffef24..3b292bf920f 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -712,18 +712,23 @@ def update_resource_count(apiclient, domainid, accountid=None, ) return -def find_suitable_host(apiclient, vm): - """Returns a suitable host for VM migration""" +def findSuitableHostForMigration(apiclient, vmid): + """Returns a suitable host for VM migration""" + suitableHost = None + try: + hosts = Host.listForMigration(apiclient, virtualmachineid=vmid, + ) + except Exception as e: + raise Exception("Exception while getting hosts list suitable for migration: %s" % e) - hosts = Host.list(apiclient, - virtualmachineid=vm.id, - listall=True) + suitablehosts = [] + if isinstance(hosts, list) and len(hosts) > 0: + suitablehosts = [host for host in hosts if (str(host.resourcestate).lower() == "enabled"\ + and str(host.state).lower() == "up")] + if len(suitablehosts)>0: + suitableHost = suitablehosts[0] - if isinstance(hosts, list): - assert len(hosts) > 0, "List host should return valid response" - else: - raise Exception("Exception: List host should return valid response") - return hosts[0] + return suitableHost def get_resource_type(resource_id): """Returns resource type""" @@ -980,3 +985,17 @@ def verifyNetworkState(apiclient, networkid, state): assert validateList(networks)[0] == PASS, "Networks list validation failed, list is %s" % networks assert str(networks[0].state).lower() == state, "network state should be %s, it is %s" % (state, networks[0].state) return + +def verifyComputeOfferingCreation(apiclient, computeofferingid): + """List Compute offerings by ID and verify that the offering exists""" + + cmd = listServiceOfferings.listServiceOfferingsCmd() + cmd.id = computeofferingid + serviceOfferings = None + try: + serviceOfferings = apiclient.listServiceOfferings(cmd) + except Exception as e: + return FAIL + if not (isinstance(serviceOfferings, list) and len(serviceOfferings) > 0): + return FAIL + return PASS diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index e870158cfb5..709fddebe78 100644 --- a/tools/marvin/marvin/integration/lib/utils.py +++ b/tools/marvin/marvin/integration/lib/utils.py @@ -191,7 +191,8 @@ def get_process_status(hostip, port, username, password, linklocalip, process, h #SSH to the machine ssh = SshClient(hostip, port, username, password) - if str(hypervisor).lower() == 'vmware': + if (str(hypervisor).lower() == 'vmware' + or str(hypervisor).lower() == 'hyperv'): ssh_command = "ssh -i /var/cloudstack/management/.ssh/id_rsa -ostricthostkeychecking=no " else: ssh_command = "ssh -i ~/.ssh/id_rsa.cloud -ostricthostkeychecking=no " diff --git a/tools/marvin/marvin/sandbox/advanced/advanced_env.py b/tools/marvin/marvin/sandbox/advanced/advanced_env.py index 1728e61fb19..f3d30643f21 100644 --- a/tools/marvin/marvin/sandbox/advanced/advanced_env.py +++ b/tools/marvin/marvin/sandbox/advanced/advanced_env.py @@ -54,22 +54,22 @@ def describeResources(config): lbprovider = provider() lbprovider.name = 'InternalLbVm' - pn = physical_network() + pn = physicalNetwork() pn.name = "Sandbox-pnet" pn.vlan = config.get('cloudstack', 'pnet.vlan') pn.tags = ["cloud-simulator-public"] - pn.traffictypes = [traffictype("Guest"), - traffictype("Management", {"simulator" : "cloud-simulator-mgmt"}), - traffictype("Public", {"simulator":"cloud-simulator-public"})] + pn.traffictypes = [trafficType("Guest"), + trafficType("Management", {"simulator" : "cloud-simulator-mgmt"}), + trafficType("Public", {"simulator":"cloud-simulator-public"})] pn.isolationmethods = ["VLAN"] pn.providers.append(vpcprovider) pn.providers.append(lbprovider) - pn2 = physical_network() + pn2 = physicalNetwork() pn2.name = "Sandbox-pnet2" pn2.vlan = config.get('cloudstack', 'pnet2.vlan') pn2.tags = ["cloud-simulator-guest"] - pn2.traffictypes = [traffictype('Guest', {'simulator': 'cloud-simulator-guest'})] + pn2.traffictypes = [trafficType('Guest', {'simulator': 'cloud-simulator-guest'})] pn2.isolationmethods = ["VLAN"] pn2.providers.append(vpcprovider) pn2.providers.append(lbprovider) @@ -137,16 +137,11 @@ def describeResources(config): [zs.globalConfig.append(cfg) for cfg in getGlobalSettings(config)] ''''add loggers''' - testClientLogger = logger() - testClientLogger.name = 'TestClient' - testClientLogger.file = 'testclient.log' + testLogger = logger() + testLogger.logFolderPath = '/tmp/' + zs.logger = testLogger - testCaseLogger = logger() - testCaseLogger.name = 'TestCase' - testCaseLogger.file = 'testcase.log' - zs.logger.append(testClientLogger) - zs.logger.append(testCaseLogger) return zs diff --git a/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py b/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py index 9cf4a0aa98f..284ea2eb25b 100644 --- a/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py +++ b/tools/marvin/marvin/sandbox/advancedsg/advancedsg_env.py @@ -51,11 +51,11 @@ def describeResources(config): sgprovider.broadcastdomainrange = 'ZONE' sgprovider.name = 'SecurityGroupProvider' - pn = physical_network() + pn = physicalNetwork() pn.name = "Sandbox-pnet" pn.tags = ["cloud-simulator-pnet"] - pn.traffictypes = [traffictype("Guest"), - traffictype("Management", {"simulator" : "cloud-simulator-mgmt"})] + pn.traffictypes = [trafficType("Guest"), + trafficType("Management", {"simulator" : "cloud-simulator-mgmt"})] pn.isolationmethods = ["VLAN"] pn.providers.append(sgprovider) @@ -121,16 +121,10 @@ def describeResources(config): [zs.globalConfig.append(cfg) for cfg in getGlobalSettings(config)] ''''add loggers''' - testClientLogger = logger() - testClientLogger.name = 'TestClient' - testClientLogger.file = 'testclient.log' + testLogger = logger() + testLogger.logFolderPath = '/tmp/' + zs.logger = testLogger - testCaseLogger = logger() - testCaseLogger.name = 'TestCase' - testCaseLogger.file = 'testcase.log' - - zs.logger.append(testClientLogger) - zs.logger.append(testCaseLogger) return zs diff --git a/tools/marvin/marvin/sandbox/basic/basic_env.py b/tools/marvin/marvin/sandbox/basic/basic_env.py index bf106fcb8de..6198163a866 100644 --- a/tools/marvin/marvin/sandbox/basic/basic_env.py +++ b/tools/marvin/marvin/sandbox/basic/basic_env.py @@ -52,9 +52,9 @@ def describeResources(config): sgprovider.broadcastdomainrange = 'Pod' sgprovider.name = 'SecurityGroupProvider' - pn = physical_network() + pn = physicalNetwork() pn.name = "Sandbox-pnet" - pn.traffictypes = [traffictype("Guest"), traffictype("Management")] + pn.traffictypes = [trafficType("Guest"), trafficType("Management")] pn.isolationmethods = ["L3"] pn.providers.append(sgprovider) @@ -119,16 +119,10 @@ def describeResources(config): [zs.globalConfig.append(cfg) for cfg in getGlobalSettings(config)] ''''add loggers''' - testClientLogger = logger() - testClientLogger.name = 'TestClient' - testClientLogger.file = '/var/log/testclient.log' + testLogger = logger() + testLogger.logFolderPath = '/tmp/' + zs.logger = testLogger - testCaseLogger = logger() - testCaseLogger.name = 'TestCase' - testCaseLogger.file = '/var/log/testcase.log' - - zs.logger.append(testClientLogger) - zs.logger.append(testCaseLogger) return zs diff --git a/tools/marvin/marvin/sandbox/demo/simulator/simulator_setup.py b/tools/marvin/marvin/sandbox/demo/simulator/simulator_setup.py index d45d48243bd..08b20cce67b 100644 --- a/tools/marvin/marvin/sandbox/demo/simulator/simulator_setup.py +++ b/tools/marvin/marvin/sandbox/demo/simulator/simulator_setup.py @@ -46,9 +46,9 @@ def describeResources(config): vpcprovider = provider() vpcprovider.name = 'VpcVirtualRouter' - pn = physical_network() + pn = physicalNetwork() pn.name = "Sandbox-pnet" - pn.traffictypes = [traffictype("Guest"), traffictype("Management"), traffictype("Public")] + pn.traffictypes = [trafficType("Guest"), trafficType("Management"), trafficType("Public")] pn.isolationmethods = ["VLAN"] pn.providers.append(vpcprovider) pn.vlan = config.get('cloudstack', 'zone.vlan') @@ -120,16 +120,10 @@ def describeResources(config): [zs.globalConfig.append(cfg) for cfg in getGlobalSettings(config)] ''''add loggers''' - testClientLogger = logger() - testClientLogger.name = 'TestClient' - testClientLogger.file = '/var/log/testclient.log' + testLogger = logger() + testLogger.logFolderPath = '/tmp/' + zs.logger = testLogger - testCaseLogger = logger() - testCaseLogger.name = 'TestCase' - testCaseLogger.file = '/var/log/testcase.log' - - zs.logger.append(testClientLogger) - zs.logger.append(testCaseLogger) return zs diff --git a/tools/ngui/static/js/app/accounts/accounts.js b/tools/ngui/static/js/app/accounts/accounts.js index eae5070a604..61d825b3b99 100644 --- a/tools/ngui/static/js/app/accounts/accounts.js +++ b/tools/ngui/static/js/app/accounts/accounts.js @@ -43,32 +43,32 @@ angular.module('accounts').controller('AccountsListCtrl', ['$scope', 'accounts', { model: 'username', type: 'input-text', - label: 'username' + label: 'label.username.lower' }, { model: 'password', type: 'input-password', - label: 'password' + label: 'label.password.lower' }, { model: 'email', type: 'input-text', - label: 'email' + label: 'label.email.lower' }, { model: 'firstname', type: 'input-text', - label: 'firstname' + label: 'label.firstname.lower' }, { model: 'lastname', type: 'input-text', - label: 'lastname' + label: 'label.lastname.lower' }, { model: 'domainid', type: 'select', - label: 'domain', + label: 'label.domain.lower', options: Domains.fetch, getName: function(model){ return model.name; @@ -80,12 +80,12 @@ angular.module('accounts').controller('AccountsListCtrl', ['$scope', 'accounts', { model: 'account', type: 'input-text', - label: 'account' + label: 'label.account.lower' }, { model: 'accounttype', type: 'select', - label: 'type', + label: 'label.type.lower', options: function(){ return ['User', 'Admin'] }, diff --git a/tools/ngui/static/js/app/storage/storage.js b/tools/ngui/static/js/app/storage/storage.js index e562a958f1a..af36167e93c 100644 --- a/tools/ngui/static/js/app/storage/storage.js +++ b/tools/ngui/static/js/app/storage/storage.js @@ -47,7 +47,7 @@ angular.module("storage").controller("VolumesListCtrl", ["$scope", "$location", $scope.toDisplay = ['name', 'type', 'hypervisor', 'vmdisplayname']; $scope.addVolumeForm = { - title: 'Add Volume', + title: 'label.add.volume', onSubmit: Volumes.getAll, fields: [ { diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index 162f77b7c31..2b88e033662 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -3080,7 +3080,7 @@ div.toolbar div.button.refresh span { div.toolbar div.button.add span, .detail-group .button.add span.icon { - padding: 0px 0 0px 18px; + padding: 0px 0 3px 18px; background: url(../images/icons.png) no-repeat -626px -209px; /*+placement:shift 0px 0px;*/ position: relative; @@ -6050,6 +6050,13 @@ label.error { border-radius: 4px; } +.multi-wizard.instance-wizard .section.custom-iops { + position: relative; + background: #F4F4F4; + padding: 7px; + border-radius: 4px; +} + .multi-wizard.instance-wizard .section.custom-size input[type=radio] { float: left; } @@ -6060,6 +6067,12 @@ label.error { margin: 6px -1px 0 8px; } +.multi-wizard.instance-wizard .section.custom-iops input[type=text] { + float: left; + width: 28px; + margin: 6px -1px 0 8px; +} + .multi-wizard.instance-wizard .section.custom-size label.error { position: absolute; top: 29px; @@ -6075,29 +6088,56 @@ label.error { height: 235px; } +.instance-wizard .step.service-offering.custom-iops .select-container { + height: 235px; +} + .instance-wizard .step.service-offering .custom-size { display: none; } +.instance-wizard .step.service-offering .custom-iops { + display: none; +} + .instance-wizard .step.service-offering.custom-size .custom-size { display: block; } +.instance-wizard .step.service-offering.custom-iops .custom-iops { + display: block; +} + .instance-wizard .step.service-offering .custom-size .field { width: 30%; float: left; margin-bottom: 13px; } +.instance-wizard .step.service-offering .custom-iops .field { + width: 30%; + float: left; + margin-bottom: 13px; +} + .instance-wizard .step.service-offering .custom-size .field label { text-indent: 20px; } +.instance-wizard .step.service-offering .custom-iops .field label { + text-indent: 20px; +} + .instance-wizard .step.service-offering .custom-size .field input { width: 88%; margin-left: 26px; } +.instance-wizard .step.service-offering .custom-iops .field input { + width: 88%; + margin-left: 26px; +} + /*** Network*/ .multi-wizard.instance-wizard .no-network { background: #FFFFFF; diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index 11dbeb5179c..074db7f061b 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -43,7 +43,6 @@ dictionary = { 'label.hypervisors': '', 'label.home': '', 'label.sockets': '', -'label.add.ucs.manager': '', 'label.root.disk.size': '', 'label.s3.nfs.path': '', 'label.s3.nfs.server': '', @@ -704,9 +703,6 @@ dictionary = { 'label.keyboard.type': '', 'label.key': '', 'label.kvm.traffic.label': '', -'label.ovm.traffic.label': '', -'label.lxc.traffic.label': '', -'label.hyperv.traffic.label': '', 'label.lang.chinese': '', 'label.lang.english': '', 'label.lang.japanese': '', @@ -933,7 +929,6 @@ dictionary = { 'label.projects': '', 'label.project.view': '', 'label.protocol': '', -'label.provider': '', 'label.providers': '', 'label.provider': '', 'label.public': '', @@ -1023,7 +1018,6 @@ dictionary = { 'label.security.group': '', 'label.security.group.name': '', 'label.security.groups.enabled': '', -'label.security.groups': '', 'label.select.a.template': '', 'label.select.a.zone': '', 'label.select': '', @@ -1184,7 +1178,6 @@ dictionary = { 'label.virtual.network': '', 'label.virtual.router': '', 'label.virtual.routers': '', -'label.vlan': '', 'label.vlan.id': '', 'label.vlan.range': '', 'label.vxlan': '', @@ -1247,7 +1240,6 @@ dictionary = { 'label.ldap.group.name': '', 'label.password.reset.confirm': '', 'label.openDaylight': '', -'label.change.affinity': '', 'label.assign.instance.another': '', 'label.network.addVM': '', 'label.set.default.NIC': '', @@ -1260,9 +1252,7 @@ dictionary = { 'label.ipv6.gateway': '', 'label.ipv6.CIDR': '', 'label.VPC.limits': '', -'label.gslb.domain.name': '', 'label.edit.region': '', -'label.gslb.domain.name': '', 'label.add.gslb': '', 'label.gslb.servicetype': '', 'label.gslb.details': '', @@ -1653,6 +1643,214 @@ dictionary = { 'state.Stopping': '', 'state.Suspended': '', 'ui.listView.filters.all': '', -'ui.listView.filters.mine': '' +'ui.listView.filters.mine': '', +'label.security.groups': '', +'label.opendaylight.controller': '', +'label.resource.name': '', +'label.reource.id': '', +'label.vnmc.devices': '', +'label.add.vnmc.provider': '', +'label.enable.vnmc.provider': '', +'label.add.vnmc.device': '', +'label.ciscovnmc.resource.details': '', +'label.delete.ciscovnmc.resource': '', +'label.enable.vnmc.device': '', +'label.disbale.vnmc.device': '', +'label.disable.vnmc.provider': '', +'label.services': '', +'label.secondary.staging.store': '', +'label.release.account': '', +'label.release.account.lowercase': '', +'label.vlan.vni.ranges': '', +'label.dedicated.vlan.vni.ranges': '', +'label.dedicate.vlan.vni.range': '', +'label.vlan.vni.range': '', +'label.vlan.range.details': '', +'label.broadcat.uri': '', +'label.ipv4.cidr': '', +'label.guest.network.details': '', +'label.ipv4.gateway': '', +'label.vlan.ranges': '', +'label.virtual.appliance.details': '', +'label.start.lb.vm': '', +'label.stop.lb.vm': '', +'label.migrate.lb.vm': '', +'label.vpc.virtual.router': '', +'label.ovs': '', +'label.gslb.service': '', +'label.gslb.service.public.ip': '', +'label.gslb.service.private.ip': '', +'label.baremetal.dhcp.provider': '', +'label.add.baremetal.dhcp.device': '', +'label.baremetal.pxe.provider': '', +'label.baremetal.pxe.device': '', +'label.tftp.root.directory': '', +'label.add.vmware.datacenter': '', +'label.remove.vmware.datacenter': '', +'label.dc.name': '', +'label.vcenter': '', +'label.dedicate.zone': '', +'label.zone.dedicated': '', +'label.release.dedicated.zone': '', +'label.ipv6.dns1': '', +'label.ipv6.dns2': '', +'label.vmware.datacenter.name': '', +'label.vmware.datacenter.vcenter': '', +'label.vmware.datacenter.id': '', +'label.system.vm.details': '', +'label.system.vm.scaled.up': '', +'label.console.proxy.vm': '', +'label.settings': '', +'label.requires.upgrade': '', +'label.upgrade.router.newer.template': '', +'label.router.vm.scaled.up': '', +'label.total.virtual.routers': '', +'label.upgrade.required': '', +'label.virtual.routers.group.zone': '', +'label.total.virtual.routers.upgrade': '', +'label.virtual.routers.group.pod': '', +'label.virtual.routers.group.cluster': '', +'label.zone.lower': '', +'label.virtual.routers.group.account': '', +'label.netscaler.details': '', +'label.baremetal.dhcp.devices': '', +'label.baremetal.pxe.devices': '', +'label.addes.new.f5': '', +'label.f5.details': '', +'label.srx.details': '', +'label.palo.alto.details': '', +'label.added.nicira.nvp.controller': '', +'label.nicira.nvp.details': '', +'label.added.new.bigswitch.vns.controller': '', +'label.bigswitch.vns.details': '', +'label.dedicate': '', +'label.dedicate.pod': '', +'label.pod.dedicated': '', +'label.release.dedicated.pod': '', +'label.override.public.traffic': '', +'label.public.traffic.vswitch.type': '', +'label.public.traffic.vswitch.name': '', +'label.override.guest.traffic': '', +'label.guest.traffic.vswitch.type': '', +'label.guest.traffic.vswitch.name': '', +'label.cisco.nexus1000v.ip.address': '', +'label.cisco.nexus1000v.username': '', +'label.cisco.nexus1000v.password': '', +'label.dedicate.cluster': '', +'label.release.dedicated.cluster': '', +'label.dedicate.host': '', +'label.release.dedicated.host': '', +'label.number.of.cpu.sockets': '', +'label.delete.ucs.manager': '', +'label.blades': '', +'label.chassis': '', +'label.blade.id': '', +'label.associated.profile': '', +'label.refresh.blades': '', +'label.instanciate.template.associate.profile.blade': '', +'label.select.template': '', +'label.profile': '', +'label.delete.profile': '', +'label.disassociate.profile.blade': '', +'label.secondary.storage.details': '', +'label.secondary.staging.store.details': '', +'label.add.nfs.secondary.staging.store': '', +'label.delete.secondary.staging.store': '', +'label.ipv4.start.ip': '', +'label.ipv4.end.ip': '', +'label.ipv6.start.ip': '', +'label.ipv6.end.ip': '', +'label.vm.password': '', +'label.group.by.zone': '', +'label.group.by.pod': '', +'label.group.by.cluster': '', +'label.group.by.account': '', +'label.no.grouping': '', +'label.create.nfs.secondary.staging.storage': '', +'label.username.lower': '', +'label.password.lower': '', +'label.email.lower': '', +'label.firstname.lower': '', +'label.lastname.lower': '', +'label.domain.lower': '', +'label.account.lower': '', +'label.type.lower': '', +'label.rule.number': '', +'label.action': '', +'label.name.lower': '', +'label.ucs': '', +'label.change.affinity': '', +'label.persistent': '', +'label.broadcasturi': '', +'label.network.cidr': '', +'label.reserved.ip.range': '', +'label.autoscale': '', +'label.health.check': '', +'label.public.load.balancer.provider': '', +'label.add.isolated.network': '', +'label.vlan': '', +'label.secondary.isolated.vlan.id': '', +'label.ipv4.netmask': '', +'label.custom': '', +'label.disable.network.offering': '', +'label.enable.network.offering': '', +'label.remove.network.offering': '', +'label.system.offering.for.router': '', +'label.mode': '', +'label.associate.public.ip': '', +'label.acl': '', +'label.user.data': '', +'label.virtual.networking': '', +'label.allow': '', +'label.deny': '', +'label.default.egress.policy': '', +'label.xenserver.tools.version.61.plus': '', +'message.confirm.delete.ciscovnmc.resource': '', +'message.confirm.add.vnmc.provider': '', +'message.confirm.enable.vnmc.provider': '', +'message.confirm.disable.vnmc.provider': '', +'message.vnmc.available.list': '', +'message.vnmc.not.available.list': '', +'message.confirm.release.dedicate.vlan.range': '', +'message.confirm.start.lb.vm': '', +'message.confirm.stop.lb.vm': '', +'message.confirm.remove.vmware.datacenter': '', +'message.confirm.dedicate.zone': '', +'message.confirm.release.dedicated.zone': '', +'message.dedicated.zone.released': '', +'message.read.admin.guide.scaling.up': '', +'message.confirm.scale.up.system.vm': '', +'message.confirm.upgrade.router.newer.template': '', +'message.confirm.scale.up.router.vm': '', +'message.confirm.upgrade.routers.newtemplate': '', +'message.confirm.upgrade.routers.pod.newtemplate': '', +'message.confirm.upgrade.routers.cluster.newtemplate': '', +'message.confirm.upgrade.routers.account.newtemplate': '', +'message.confirm.dedicate.pod.domain.account': '', +'message.confirm.release.dedicated.pod': '', +'message.pod.dedication.released': '', +'message.confirm.dedicate.cluster.domain.account': '', +'message.cluster.dedicated': '', +'message.confirm.release.dedicated.cluster': '', +'message.cluster.dedication.released': '', +'message.confirm.dedicate.host.domain.account': '', +'message.host.dedicated': '', +'message.confirm.release.dedicated.host': '', +'message.host.dedication.released': '', +'message.confirm.delete.ucs.manager': '', +'message.confirm.refresh.blades': '', +'message.confirm.delete.secondary.staging.store': '', +'message.select.tier': '', +'message.disallowed.characters': '', +'message.waiting.for.builtin.templates.to.load': '', +'message.systems.vms.ready': '', +'message.your.cloudstack.is.ready': '', +'message.specifiy.tag.key.value': '', +'message.enter.seperated.list.multiple.cidrs': '', +'message.disabling.network.offering': '', +'message.confirm.enable.network.offering': '', +'message.enabling.network.offering': '', +'message.confirm.remove.network.offering': '', +'message.confirm.disable.network.offering': '', }; diff --git a/ui/images/sprites.png b/ui/images/sprites.png index ca7f470f511..ab396700f38 100644 Binary files a/ui/images/sprites.png and b/ui/images/sprites.png differ diff --git a/ui/index.jsp b/ui/index.jsp index 4daa9eb096d..d46c395cd86 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -221,6 +221,17 @@ + +
+
+ + +
+
+ + +
+
diff --git a/ui/modules/vnmcNetworkProvider/vnmcNetworkProvider.js b/ui/modules/vnmcNetworkProvider/vnmcNetworkProvider.js index d618b232d27..c9295a35e89 100644 --- a/ui/modules/vnmcNetworkProvider/vnmcNetworkProvider.js +++ b/ui/modules/vnmcNetworkProvider/vnmcNetworkProvider.js @@ -17,7 +17,7 @@ (function ($, cloudStack) { cloudStack.modules.vnmcNetworkProvider = function (module) { var vnmcDeviceViewAll = window._m = [{ - label: 'VNMC Devices', + label: 'label.vnmc.devices', path: '_zone.vnmcDevices' }]; @@ -25,10 +25,10 @@ id: 'vnmcDevices', fields: { resourcename: { - label: 'Resource Name' + label: 'label.resource.name' }, provider: { - label: 'Provider' + label: 'label.provider' } }, dataProvider: function (args) { @@ -47,16 +47,16 @@ }, actions: { add: { - label: 'Add VNMC device', + label: 'label.add.vnmc.device', messages: { notification: function (args) { - return 'Add VNMC device'; + return 'label.add.vnmc.device'; } }, createForm: { - title: 'Add VNMC device', + title: 'label.add.vnmc.device', fields: { hostname: { label: 'label.host', @@ -203,16 +203,16 @@ }, detailView: { - name: 'CiscoVNMC resource details', + name: 'label.ciscovnmc.resource.details', actions: { remove: { - label: 'delete CiscoVNMC resource', + label: 'label.delete.ciscovnmc.resource', messages: { confirm: function (args) { - return 'Please confirm you want to delete CiscoVNMC resource'; + return 'message.confirm.delete.ciscovnmc.resource'; }, notification: function (args) { - return 'delete CiscoVNMC resource'; + return 'label.delete.ciscovnmc.resource'; } }, action: function (args) { @@ -242,14 +242,14 @@ title: 'label.details', fields: [{ resourcename: { - label: 'Resource Name' + label: 'label.resource.name' } }, { resourceid: { - label: 'Resource ID' + label: 'label.reource.id' }, provider: { - label: 'Provider' + label: 'label.provider' } }], dataProvider: function (args) { @@ -277,14 +277,14 @@ viewAll: vnmcDeviceViewAll, actions: { add: { - label: 'Add VNMC provider', + label: 'label.add.vnmc.provider', messages: { confirm: function (args) { - return 'Please confirm you would like to add the VNMC provider.'; + return 'message.confirm.add.vnmc.provider'; }, notification: function (args) { - return 'Add VNMC device'; + return 'label.add.vnmc.device'; } }, @@ -327,14 +327,14 @@ } }, enable: { - label: 'Enable VNMC provider', + label: 'label.enable.vnmc.provider', messages: { confirm: function (args) { - return 'Please confirm you would like to enable the VNMC provider.'; + return 'message.confirm.enable.vnmc.provider'; }, notification: function (args) { - return 'Enable VNMC device'; + return 'label.enable.vnmc.device'; } }, @@ -352,7 +352,7 @@ enableCiscoVnmcProviderFn(ciscoVnmcProvider); } else { - args.response.error('VNMC is not available from provider list.'); + args.response.error('message.vnmc.available.list'); } } }); @@ -419,14 +419,14 @@ }, disable: { - label: 'Disable VNMC provider', + label: 'label.disable.vnmc.provider', messages: { confirm: function (args) { - return 'Please confirm you would like to disable the VNMC provider.'; + return 'message.confirm.disable.vnmc.provider'; }, notification: function (args) { - return 'Disable VNMC device'; + return 'label.disbale.vnmc.device'; } }, @@ -444,7 +444,7 @@ disableCiscoVnmcProviderFn(ciscoVnmcProvider); } else { - args.response.error('VNMC is not available from provider list.'); + args.response.error('message.vnmc.not.available.list'); } } }); @@ -525,7 +525,7 @@ label: 'label.id' }, servicelist: { - label: 'Services', + label: 'label.services', converter: function (args) { if (args) return args.join(', '); diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 869b876865c..7b2f4e92194 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -96,7 +96,7 @@ } }, isCustomized: { - label: 'Custom', + label: 'label.custom', isBoolean: true, isReverse: true, isChecked: false @@ -136,6 +136,113 @@ number: true } }, + qosType: { + label: 'label.qos.type', + docID: 'helpDiskOfferingQoSType', + select: function(args) { + var items = []; + items.push({ + id: '', + description: '' + }); + items.push({ + id: 'hypervisor', + description: 'hypervisor' + }); + items.push({ + id: 'storage', + description: 'storage' + }); + args.response.success({ + data: items + }); + + args.$select.change(function() { + var $form = $(this).closest('form'); + var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]'); + var $minIops = $form.find('.form-item[rel=minIops]'); + var $maxIops = $form.find('.form-item[rel=maxIops]'); + var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]'); + var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]'); + var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]'); + var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]'); + var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]'); + + var qosId = $(this).val(); + + if (qosId == 'storage') { // Storage QoS + $diskBytesReadRate.hide(); + $diskBytesWriteRate.hide(); + $diskIopsReadRate.hide(); + $diskIopsWriteRate.hide(); + + $isCustomizedIops.css('display', 'inline-block'); + + if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) { + $minIops.hide(); + $maxIops.hide(); + } else { + $minIops.css('display', 'inline-block'); + $maxIops.css('display', 'inline-block'); + } + + $hypervisorSnapshotReserve.css('display', 'inline-block'); + } else if (qosId == 'hypervisor') { // Hypervisor Qos + $isCustomizedIops.hide(); + $minIops.hide(); + $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); + + $diskBytesReadRate.css('display', 'inline-block'); + $diskBytesWriteRate.css('display', 'inline-block'); + $diskIopsReadRate.css('display', 'inline-block'); + $diskIopsWriteRate.css('display', 'inline-block'); + } else { // No Qos + $diskBytesReadRate.hide(); + $diskBytesWriteRate.hide(); + $diskIopsReadRate.hide(); + $diskIopsWriteRate.hide(); + $isCustomizedIops.hide(); + $minIops.hide(); + $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); + } + }); + } + }, + isCustomizedIops: { + label: 'label.custom.disk.iops', + docID: 'helpDiskOfferingCustomDiskIops', + isBoolean: true, + isReverse: true, + isChecked: false + }, + minIops: { + label: 'label.disk.iops.min', + docID: 'helpDiskOfferingDiskIopsMin', + dependsOn: 'isCustomizedIops', + validation: { + required: false, + number: true + } + }, + maxIops: { + label: 'label.disk.iops.max', + docID: 'helpDiskOfferingDiskIopsMax', + dependsOn: 'isCustomizedIops', + validation: { + required: false, + number: true + } + }, + hypervisorSnapshotReserve: { + label: 'label.hypervisor.snapshot.reserve', + docID: 'helpDiskOfferingHypervisorSnapshotReserve', + validation: { + required: false, + number: true + } + }, diskBytesReadRate: { label: 'label.disk.bytes.read.rate', docID: 'helpComputeOfferingDiskBytesReadRate', @@ -254,6 +361,71 @@ } }, + pciDevice: { + label: 'GPU Type', + select: function(args) { + var items = []; + items.push({ + id: '', + description: '' + }); + items.push({ + id: 'GPU_Passthrough', + description: 'GPU-Passthrough' + }); + items.push({ + id: 'VGPU', + description: 'VGPU' + }); + args.response.success({ + data: items + }); + args.$select.change(function() { + var $form = $(this).closest('form'); + var $fields = $form.find('.field'); + if (($(this).val() == "") || $(this).val() == "GPU-Passthrough") { + $form.find('[rel=vgpuType]').hide(); + } else if ($(this).val() == "VGPU") { + $form.find('[rel=vgpuType]').css('display', 'block'); + } + }); + } + }, + + vgpuType: { + label: 'VGPU Type', + select: function(args) { + var items = []; + items.push({ + id: '', + description: '' + }); + items.push({ + id: 'GRID K100', + description: 'GRID K100' + }); + items.push({ + id: 'GRID K140Q', + description: 'GRID K140Q' + }); + items.push({ + id: 'GRID K200', + description: 'GRID K200' + }); + items.push({ + id: 'GRID K240Q', + description: 'GRID K240Q' + }); + items.push({ + id: 'GRID K260Q', + description: 'GRID K260Q' + }); + args.response.success({ + data: items + }); + } + }, + domainId: { label: 'label.domain', docID: 'helpComputeOfferingDomain', @@ -318,7 +490,18 @@ var array1 = []; if (args.data.deploymentPlanner == "ImplicitDedicationPlanner" && args.data.plannerMode != "") { - array1.push("&serviceofferingdetails[0].ImplicitDedicationMode" + "=" + args.data.plannerMode); + array1.push("&serviceofferingdetails[0].key" + "=" + "ImplicitDedicationMode"); + array1.push("&serviceofferingdetails[0].value" + "=" + args.data.plannerMode); + } + + if (args.data.pciDevice != "") { + array1.push("&serviceofferingdetails[1].key" + "=" + "pciDevice"); + array1.push("&serviceofferingdetails[1].value" + "=" + args.data.pciDevice); + } + + if (args.data.pciDevice == "VGPU") { + array1.push("&serviceofferingdetails[2].key" + "=" + "vgpuType"); + array1.push("&serviceofferingdetails[2].value" + "=" + args.data.vgpuType); } if (args.data.networkRate != null && args.data.networkRate.length > 0) { @@ -326,26 +509,59 @@ networkrate: args.data.networkRate }); } - if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) { + + if (args.data.qosType == 'storage') { + var customIops = args.data.isCustomizedIops == "on"; + $.extend(data, { - bytesreadrate: args.data.diskBytesReadRate - }); - } - if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) { - $.extend(data, { - byteswriterate: args.data.diskBytesWriteRate - }); - } - if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) { - $.extend(data, { - iopsreadrate: args.data.diskIopsReadRate - }); - } - if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) { - $.extend(data, { - iopswriterate: args.data.diskIopsWriteRate + customizediops: customIops }); + + if (!customIops) { + if (args.data.minIops != null && args.data.minIops.length > 0) { + $.extend(data, { + miniops: args.data.minIops + }); + } + + if (args.data.maxIops != null && args.data.maxIops.length > 0) { + $.extend(data, { + maxiops: args.data.maxIops + }); + } + } + + if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) { + $.extend(data, { + hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve + }); + } + } else if (args.data.qosType == 'hypervisor') { + if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) { + $.extend(data, { + bytesreadrate: args.data.diskBytesReadRate + }); + } + + if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) { + $.extend(data, { + byteswriterate: args.data.diskBytesWriteRate + }); + } + + if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) { + $.extend(data, { + iopsreadrate: args.data.diskIopsReadRate + }); + } + + if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) { + $.extend(data, { + iopswriterate: args.data.diskIopsWriteRate + }); + } } + $.extend(data, { offerha: (args.data.offerHA == "on") }); @@ -531,6 +747,37 @@ networkrate: { label: 'label.network.rate' }, + iscustomizediops: { + label: 'label.custom.disk.iops', + converter: cloudStack.converters.toBooleanText + }, + miniops: { + label: 'label.disk.iops.min', + converter: function(args) { + if (args > 0) + return args; + else + return "N/A"; + } + }, + maxiops: { + label: 'label.disk.iops.max', + converter: function(args) { + if (args > 0) + return args; + else + return "N/A"; + } + }, + hypervisorsnapshotreserve: { + label: 'label.hypervisor.snapshot.reserve', + converter: function(args) { + if (args > 0) + return args; + else + return "N/A"; + } + }, diskBytesReadRate: { label: 'label.disk.bytes.read.rate' }, @@ -1643,6 +1890,15 @@ return "N/A"; } }, + hypervisorsnapshotreserve: { + label: 'label.hypervisor.snapshot.reserve', + converter: function(args) { + if (args > 0) + return args; + else + return "N/A"; + } + }, diskBytesReadRate: { label: 'label.disk.bytes.write.rate' }, @@ -2180,34 +2436,34 @@ // Sanitize names switch (serviceName) { case 'Vpn': - serviceDisplayName = 'VPN'; + serviceDisplayName = dictionary['label.vpn']; break; case 'Dhcp': serviceDisplayName = dictionary['label.dhcp']; break; case 'Dns': - serviceDisplayName = 'DNS'; + serviceDisplayName = dictionary['label.dns']; break; case 'Lb': - serviceDisplayName = 'Load Balancer'; + serviceDisplayName = dictionary['label.load.balancer']; break; case 'SourceNat': - serviceDisplayName = 'Source NAT'; + serviceDisplayName = dictionary['label.source.nat']; break; case 'StaticNat': - serviceDisplayName = 'Static NAT'; + serviceDisplayName = dictionary['label.static.nat']; break; case 'PortForwarding': - serviceDisplayName = 'Port Forwarding'; + serviceDisplayName = dictionary['label.port.forwarding']; break; case 'SecurityGroup': - serviceDisplayName = 'Security Groups'; + serviceDisplayName = dictionary['label.security.groups']; break; case 'UserData': - serviceDisplayName = 'User Data'; + serviceDisplayName = dictionary['label.user.data']; break; case 'Connectivity': - serviceDisplayName = 'Virtual Networking'; + serviceDisplayName = dictionary['label.virtual.networking']; break; default: serviceDisplayName = serviceName; @@ -2314,7 +2570,7 @@ //show or hide upon checked services and selected providers above (begin) serviceofferingid: { - label: 'System Offering for Router', + label: 'label.system.offering.for.router', isHidden: true, docID: 'helpNetworkOfferingSystemOffering', select: function(args) { @@ -2348,7 +2604,7 @@ }, "service.SourceNat.redundantRouterCapabilityCheckbox": { - label: "label.redundant.router.capability", + label: 'label.redundant.router.capability', isHidden: true, dependsOn: 'service.SourceNat.isEnabled', docID: 'helpNetworkOfferingRedundantRouterCapability', @@ -2394,7 +2650,7 @@ } }, "service.Lb.inlineModeDropdown": { - label: 'Mode', + label: 'label.mode', docID: 'helpNetworkOfferingMode', select: function(args) { var items = []; @@ -2419,7 +2675,7 @@ }, "service.StaticNat.associatePublicIP": { - label: 'Associate Public IP', + label: 'label.associate.public.ip', docID: 'helpNetworkOfferingAssociatePublicIP', isBoolean: true, isHidden: true @@ -2445,23 +2701,23 @@ args.response.success({ data: [{ id: 'Optional', - description: 'Optional' + description: 'label.optional' }, { id: 'Required', - description: 'Required' + description: 'label.required' }] }); } }, egresspolicy: { - label: 'Default egress policy', + label: 'label.default.egress.policy', isHidden: true, select: function(args) { args.response.success({ data: [ - { id: 'ALLOW', description: 'Allow' }, - { id: 'DENY', description: 'Deny' } + { id: 'ALLOW', description: 'label.allow' }, + { id: 'DENY', description: 'label.deny' } ] }); } @@ -2689,13 +2945,13 @@ }, enable: { - label: 'Enable network offering', + label: 'label.enable.network.offering', messages: { confirm: function(args) { - return 'Are you sure you want to enable this network offering?'; + return 'message.confirm.enable.network.offering'; }, notification: function(args) { - return 'Enabling network offering'; + return 'message.enabling.network.offering'; } }, action: function(args) { @@ -2724,13 +2980,13 @@ }, disable: { - label: 'Disable network offering', + label: 'label.disable.network.offering', messages: { confirm: function(args) { - return 'Are you sure you want to disable this network offering?'; + return 'message.confirm.disable.network.offering'; }, notification: function(args) { - return 'Disabling network offering'; + return 'message.disabling.network.offering'; } }, action: function(args) { @@ -2759,7 +3015,7 @@ }, remove: { - label: 'Remove network offering', + label: 'label.remove.network.offering', action: function(args) { $.ajax({ url: createURL('deleteNetworkOffering'), @@ -2779,10 +3035,10 @@ }, messages: { confirm: function() { - return 'Are you sure you want to remove this network offering?'; + return 'message.confirm.remove.network.offering'; }, notification: function() { - return 'Remove network offering'; + return 'label.remove.network.offering'; } }, notification: { @@ -2828,7 +3084,7 @@ }, ispersistent: { - label: 'Persistent ', + label: 'label.persistent ', converter: cloudStack.converters.toBooleanText }, diff --git a/ui/scripts/installWizard.js b/ui/scripts/installWizard.js index 25de4aaa0ad..85eaff00c3f 100644 --- a/ui/scripts/installWizard.js +++ b/ui/scripts/installWizard.js @@ -314,7 +314,7 @@ return vm.state == 'Running'; }).length) { clearInterval(poll); - message('System VMs ready.'); + message('message.systems.vms.ready'); setTimeout(pollBuiltinTemplates, 500); } } @@ -325,7 +325,7 @@ // Wait for builtin template to be present -- otherwise VMs cannot launch var pollBuiltinTemplates = function() { - message('Waiting for builtin templates to load...'); + message('message.waiting.for.builtin.templates.to.load'); var poll = setInterval(function() { $.ajax({ url: createURL('listTemplates'), @@ -341,7 +341,7 @@ if (builtinTemplates.length) { clearInterval(poll); - message('Your CloudStack is ready!'); + message('message.your.cloudstack.is.ready'); setTimeout(success, 1000); } } diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index 3ee9af1b9e4..5c04d0e1455 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -301,6 +301,7 @@ args.response.success({ customFlag: 'iscustomized', //customFlag: 'offerha', //for testing only + customIopsFlag: 'iscustomizediops', data: { serviceOfferings: serviceOfferingObjs } @@ -631,7 +632,20 @@ }); } } - + + if (args.$wizard.find('input[name=disk-min-iops]').parent().parent().css('display') != 'none') { + if (args.$wizard.find('input[name=disk-min-iops]').val().length > 0) { + $.extend(deployVmData, { + 'details[0].minIops' : args.$wizard.find('input[name=disk-min-iops]').val() + }); + } + if (args.$wizard.find('input[name=disk-max-iops]').val().length > 0) { + $.extend(deployVmData, { + 'details[0].maxIops' : args.$wizard.find('input[name=disk-max-iops]').val() + }); + } + } + //step 4: select disk offering if (args.data.diskofferingid != null && args.data.diskofferingid != "0") { $.extend(deployVmData, { diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 53c9e98af98..10d25917f91 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -172,10 +172,10 @@ advSearchFields: { name: { - label: 'Name' + label: 'label.name' }, zoneid: { - label: 'Zone', + label: 'label.zone', select: function(args) { $.ajax({ url: createURL('listZones'), @@ -199,7 +199,7 @@ }, domainid: { - label: 'Domain', + label: 'label.domain', select: function(args) { if (isAdmin() || isDomainAdmin()) { $.ajax({ @@ -241,7 +241,7 @@ } }, account: { - label: 'Account', + label: 'label.account', isHidden: function(args) { if (isAdmin() || isDomainAdmin()) return false; @@ -251,10 +251,10 @@ }, tagKey: { - label: 'Tag Key' + label: 'label.tag.key' }, tagValue: { - label: 'Tag Value' + label: 'label.tag.value' } }, @@ -464,7 +464,7 @@ }, complete: function(args) { if (args.password != null) { - return 'Password of the VM is ' + args.password; + return 'label.vm.password' + ' ' + args.password; } return false; @@ -478,7 +478,7 @@ label: 'label.action.stop.instance', compactLabel: 'label.stop', createForm: { - title: 'Stop instance', + title: 'notification.stop.instance', desc: 'message.action.stop.instance', fields: { forced: { @@ -764,7 +764,7 @@ var networks = json.listnetworksresponse.network; var items = [{ id: -1, - description: 'Please select a tier' + description: 'message.select.tier' }]; $(networks).each(function() { items.push({ @@ -1434,7 +1434,7 @@ var description = ''; var vmObj = args.jsonObj; if (vmObj.state == 'Running' && vmObj.hypervisor == 'VMware') { - description = 'Please read the dynamic scaling section in the admin guide before scaling up.'; + description = 'message.read.admin.guide.scaling.up'; } return description; }, @@ -1804,7 +1804,9 @@ memory: { label: 'label.memory.mb' }, - + vgpu: { + label: 'VGPU' + }, haenable: { label: 'label.ha.enabled', converter: cloudStack.converters.toBooleanText diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 6ce66e8da22..f82a295aee7 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -410,7 +410,7 @@ listView: { actions: { add: { - label: 'Add Isolated Network', + label: 'label.add.isolated.network', preFilter: function(args) { if (advZoneObjs != null && advZoneObjs.length > 0) { @@ -766,13 +766,13 @@ label: 'label.cidr' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.CIDR' } }, advSearchFields: { zoneid: { - label: 'Zone', + label: 'label.zone', select: function(args) { $.ajax({ url: createURL('listZones'), @@ -796,7 +796,7 @@ }, domainid: { - label: 'Domain', + label: 'label.domain', select: function(args) { if (isAdmin() || isDomainAdmin()) { $.ajax({ @@ -839,7 +839,7 @@ }, account: { - label: 'Account', + label: 'label.account', isHidden: function(args) { if (isAdmin() || isDomainAdmin()) return false; @@ -848,10 +848,10 @@ } }, tagKey: { - label: 'Tag Key' + label: 'label.tag.key' }, tagValue: { - label: 'Tag Value' + label: 'label.tag.value' } }, @@ -875,7 +875,7 @@ }, detailView: { - name: 'Guest network details', + name: 'label.guest.network.details', viewAll: { path: 'network.ipAddresses', label: 'label.menu.ipaddresses', @@ -1245,7 +1245,7 @@ }, ispersistent: { - label: 'Persistent ', + label: 'label.persistent', converter: cloudStack.converters.toBooleanText }, @@ -1263,7 +1263,7 @@ }, broadcasturi: { - label: 'broadcasturi' + label: 'label.broadcasturi' }, networkofferingid: { @@ -1323,20 +1323,20 @@ }, networkcidr: { - label: 'Network CIDR' + label: 'label.network.cidr' }, ip6gateway: { - label: 'IPv6 Gateway' + label: 'label.ipv6.gateway' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.CIDR' }, reservediprange: { - label: 'Reserved IP Range' + label: 'label.reserved.ip.range' }, @@ -1688,7 +1688,7 @@ } }, 'autoScale': { - label: 'AutoScale', + label: 'label.autoscale', custom: { requireValidation: true, buttonLabel: 'label.configure', @@ -2785,7 +2785,7 @@ label: 'label.zone' }, vlanname: { - label: 'label.vlan' + label: 'label.vlan.only' } }], @@ -3479,7 +3479,7 @@ }, 'health-check': { - label: 'Health Check', + label: 'label.health.check', custom: { requireValidation: true, buttonLabel: 'Configure', @@ -3489,7 +3489,7 @@ }, 'autoScale': { - label: 'AutoScale', + label: 'label.autoscale', custom: { requireValidation: true, buttonLabel: 'label.configure', @@ -4475,10 +4475,10 @@ advSearchFields: { tagKey: { - label: 'Tag Key' + label: 'label.tag.key' }, tagValue: { - label: 'Tag Value' + label: 'label.tag.value' } }, @@ -5013,10 +5013,10 @@ advSearchFields: { name: { - label: 'Name' + label: 'label.name' }, zoneid: { - label: 'Zone', + label: 'label.zone', select: function(args) { $.ajax({ url: createURL('listZones'), @@ -5040,7 +5040,7 @@ }, domainid: { - label: 'Domain', + label: 'label.domain', select: function(args) { if (isAdmin() || isDomainAdmin()) { $.ajax({ @@ -5083,7 +5083,7 @@ }, account: { - label: 'Account', + label: 'label.account', isHidden: function(args) { if (isAdmin() || isDomainAdmin()) return false; @@ -5092,10 +5092,10 @@ } }, tagKey: { - label: 'Tag Key' + label: 'label.tag.key' }, tagValue: { - label: 'Tag Value' + label: 'label.tag.value' } }, @@ -5186,7 +5186,7 @@ label: 'label.DNS.domain.for.guest.networks' }, publicLoadBalancerProvider: { - label: 'Public Load Balancer Provider', + label: 'label.public.load.balancer.provider', select: function(args) { var items = []; items.push({ @@ -5429,7 +5429,7 @@ label: 'label.state' }, ispersistent: { - label: 'Persistent ', + label: 'label.persistent', converter: cloudStack.converters.toBooleanText }, @@ -5604,8 +5604,8 @@ } }, cidrlist: { - label: 'CIDR list', - desc: 'Please enter a comma separated list of CIDRs if more than one', + label: 'label.CIDR.list', + desc: 'message.enter.seperated.list.multiple.cidrs', validation: { required: true } @@ -5619,7 +5619,7 @@ }, cidrlist: { label: 'label.CIDR.list', - desc: 'Please enter a comma separated list of CIDRs if more than one', + desc: 'message.enter.seperated.list.multiple.cidrs', docID: 'helpVPNGatewayCIDRList', validation: { required: true diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index f0afea508cc..9c24f404890 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -297,7 +297,7 @@ var addGuestNetworkDialog = { docID: 'helpGuestNetworkZoneVLANID' }, isolatedpvlanId: { - label: 'Secondary Isolated VLAN ID' + label: 'label.secondary.isolated.vlan.id' }, scope: { @@ -319,24 +319,24 @@ var addGuestNetworkDialog = { if (selectedZoneObj.networktype == "Advanced" && selectedZoneObj.securitygroupsenabled == true) { array1.push({ id: 'zone-wide', - description: 'All' + description: 'ui.listView.filters.all' }); } else { array1.push({ id: 'zone-wide', - description: 'All' + description: 'ui.listView.filters.all' }); array1.push({ id: 'domain-specific', - description: 'Domain' + description: 'label.domain' }); array1.push({ id: 'account-specific', - description: 'Account' + description: 'label.account' }); array1.push({ id: 'project-specific', - description: 'Project' + description: 'label.project' }); } args.response.success({ @@ -584,37 +584,37 @@ var addGuestNetworkDialog = { //IPv4 (begin) ip4gateway: { - label: 'IPv4 Gateway', + label: 'label.ipv4.gateway', docID: 'helpGuestNetworkZoneGateway' }, ip4Netmask: { - label: 'IPv4 Netmask', + label: 'label.ipv4.netmask', docID: 'helpGuestNetworkZoneNetmask' }, startipv4: { - label: 'IPv4 Start IP', + label: 'label.ipv4.start.ip', docID: 'helpGuestNetworkZoneStartIP' }, endipv4: { - label: 'IPv4 End IP', + label: 'label.ipv4.end.ip', docID: 'helpGuestNetworkZoneEndIP' }, //IPv4 (end) //IPv6 (begin) ip6gateway: { - label: 'IPv6 Gateway', + label: 'label.ipv6.gateway', docID: 'helpGuestNetworkZoneGateway' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.CIDR' }, startipv6: { - label: 'IPv6 Start IP', + label: 'label.ipv6.start.ip', docID: 'helpGuestNetworkZoneStartIP' }, endipv6: { - label: 'IPv6 End IP', + label: 'label.ipv6.end.ip', docID: 'helpGuestNetworkZoneEndIP' }, //IPv6 (end) diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index 1cd8d2e594e..e69a07e115c 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1093,11 +1093,11 @@ isBoolean: true }, isFeatured: { - label: "label.featured", + label: 'label.featured', isBoolean: true }, isdynamicallyscalable: { - label: "Dynamically Scalable", + label: 'label.dynamically.scalable', isBoolean: true } } @@ -1688,7 +1688,7 @@ isBoolean: true }, isdynamicallyscalable: { - label: "Dynamically Scalable", + label: 'label.dynamically.scalable', isBoolean: true } } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index c21e277cf8c..cdd02b29fdc 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -44,11 +44,11 @@ }; var fields = { account: { - label: 'Account', + label: 'label.account', defaultValue: data.account }, domainid: { - label: 'Domain', + label: 'label.domain', defaultValue: data.domainid, select: function (args) { $.ajax({ @@ -753,7 +753,7 @@ }, releaseFromAccount: { - label: 'Release from Account', + label: 'label.release.account', action: function (args) { $.ajax({ url: createURL('releasePublicIpRange'), @@ -763,7 +763,7 @@ success: function (json) { args.response.success({ notification: { - label: 'release from account', + label: 'label.release.account.lowercase', poll: function (args) { args.complete(); } @@ -778,15 +778,15 @@ }, addAccount: { - label: 'Add Account', + label: 'label.add.account', createForm: { - title: 'Add Account', + title: 'label.add.account', fields: { account: { - label: 'Account' + label: 'label.account' }, domainid: { - label: 'Domain', + label: 'label.domain', select: function (args) { $.ajax({ url: createURL('listDomains'), @@ -821,7 +821,7 @@ success: function (json) { args.response.success({ notification: { - label: 'Add Account', + label: 'label.add.account', poll: function (args) { args.complete(); } @@ -902,15 +902,15 @@ isEditable: true }, ovmnetworklabel: { - label: 'OVM traffic label', + label: 'label.ovm.traffic.label', isEditable: true }, lxcnetworklabel: { - label: 'LXC Traffic Label', + label: 'label.lxc.traffic.label', isEditable: true }, hypervnetworklabel: { - label: 'HyperV Traffic Label', + label: 'label.hyperv.traffic.label', isEditable: true } }], @@ -1114,15 +1114,15 @@ isEditable: true }, ovmnetworklabel: { - label: 'OVM traffic label', + label: 'label.ovm.traffic.label', isEditable: true }, lxcnetworklabel: { - label: 'LXC Traffic Label', + label: 'label.lxc.traffic.label', isEditable: true }, hypervnetworklabel: { - label: 'HyperV Traffic Label', + label: 'label.hyperv.traffic.label', isEditable: true } }], @@ -1277,7 +1277,7 @@ label: 'label.state' }, vlan: { - label: 'VLAN/VNI Range(s)', + label: 'label.vlan.vni.ranges', isEditable: true }, tags: { @@ -1303,15 +1303,15 @@ isEditable: true }, ovmnetworklabel: { - label: 'OVM traffic label', + label: 'label.ovm.traffic.label', isEditable: true }, lxcnetworklabel: { - label: 'LXC Traffic Label', + label: 'label.lxc.traffic.label', isEditable: true }, hypervnetworklabel: { - label: 'HyperV Traffic Label', + label: 'label.hyperv.traffic.label', isEditable: true } }], @@ -1524,13 +1524,13 @@ label: 'label.vnet.id' }, broadcasturi: { - label: 'broadcast URI' + label: 'label.broadcat.uri' }, cidr: { - label: 'IPv4 CIDR' + label: 'label.ipv4.cidr' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.cidr' } //scope: { label: 'label.scope' } }, @@ -1593,7 +1593,7 @@ }, detailView: { - name: 'Guest network details', + name: 'label.guest.network.details', noCompact: true, viewAll: { path: '_zone.guestIpRanges', @@ -1850,7 +1850,7 @@ label: 'label.vlan.id' }, broadcasturi: { - label: 'broadcast URI' + label: 'label.broadcat.uri' }, scope: { label: 'label.scope' @@ -1895,18 +1895,18 @@ }, gateway: { - label: 'IPv4 Gateway' + label: 'label.ipv4.gateway' }, //netmask: { label: 'label.netmask' }, cidr: { - label: 'IPv4 CIDR' + label: 'label.ipv4.cidr' }, ip6gateway: { - label: 'IPv6 Gateway' + label: 'label.ipv6.gateway' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.CIDR' }, networkdomaintext: { @@ -1968,13 +1968,13 @@ }, dedicatedGuestVlanRanges: { - title: 'Dedicated VLAN/VNI Ranges', + title: 'label.dedicated.vlan.vni.ranges', listView: { section: 'dedicatedGuestVlanRanges', id: 'dedicatedGuestVlanRanges', fields: { guestvlanrange: { - label: 'VLAN/VNI Range(s)' + label: 'label.vlan.vni.ranges' }, domain: { label: 'label.domain' @@ -1999,17 +1999,17 @@ }, actions: { add: { - label: 'Dedicate VLAN/VNI Range', + label: 'label.dedicate.vlan.vni.range', messages: { notification: function (args) { - return 'Dedicate VLAN/VNI Range'; + return 'label.dedicate.vlan.vni.range'; } }, createForm: { - title: 'Dedicate VLAN/VNI Range', + title: 'label.dedicate.vlan.vni.range', fields: { vlanrange: { - label: 'VLAN/VNI Range', + label: 'label.vlan.vni.range', /* select: function(args) { var items = []; if(args.context.physicalNetworks[0].vlan != null && args.context.physicalNetworks[0].vlan.length > 0) { @@ -2083,16 +2083,16 @@ }, detailView: { - name: 'VLAN Range details', + name: 'label.vlan.range.details', actions: { remove: { - label: 'Release dedicated VLAN range', + label: 'label.release.dedicated.vlan.range', messages: { confirm: function (args) { - return 'Please confirm you want to release dedicated VLAN range'; + return 'message.confirm.release.dedicate.vlan.range'; }, notification: function (args) { - return 'Release dedicated VLAN range'; + return 'label.release.dedicated.vlan.range'; } }, action: function (args) { @@ -2124,7 +2124,7 @@ title: 'label.details', fields:[ { guestvlanrange: { - label: 'VLAN Range(s)' + label: 'label.vlan.ranges' } }, { @@ -2417,7 +2417,7 @@ }); }, detailView: { - name: 'Virtual applicance details', + name: 'label.virtual.appliance.details', actions: { start: { label: 'label.action.start.router', @@ -2974,16 +2974,16 @@ }); }, detailView: { - name: 'Virtual applicance details', + name: 'label.virtual.appliance.details', actions: { start: { - label: 'Start LB VM', + label: 'label.start.lb.vm', messages: { confirm: function (args) { - return 'Please confirm you want to start LB VM'; + return 'message.confirm.start.lb.vm'; }, notification: function (args) { - return 'Start LB VM'; + return 'label.start.lb.vm'; } }, action: function (args) { @@ -3013,10 +3013,10 @@ }, stop: { - label: 'Stop LB VM', + label: 'label.stop.lb.vm', createForm: { - title: 'Please confirm you want to stop LB VM', - desc: 'Stop LB VM', + title: 'message.confirm.stop.lb.vm', + desc: 'label.stop.lb.vm', fields: { forced: { label: 'force.stop', @@ -3027,7 +3027,7 @@ }, messages: { notification: function (args) { - return 'Stop LB VM'; + return 'label.stop.lb.vm'; } }, action: function (args) { @@ -3059,9 +3059,9 @@ }, migrate: { - label: 'Migrate LB VM', + label: 'label.migrate.lb.vm', createForm: { - title: 'Migrate LB VM', + title: 'label.migrate.lb.vm', fields: { hostId: { label: 'label.host', @@ -3097,7 +3097,7 @@ }, messages: { notification: function (args) { - return 'Migrate LB VM'; + return 'label.migrate.lb.vm'; } }, action: function (args) { @@ -3375,7 +3375,7 @@ vpcVirtualRouter: { id: 'vpcVirtualRouterProviders', - label: 'VPC Virtual Router', + label: 'label.vpc.virtual.router', isMaximized: true, type: 'detailView', fields: { @@ -3512,7 +3512,7 @@ }); }, detailView: { - name: 'Virtual applicance details', + name: 'label.virtual.appliance.details', actions: { start: { label: 'label.action.start.router', @@ -3828,7 +3828,7 @@ label: 'label.redundant.state' }, vpcid: { - label: 'VPC ID' + label: 'label.vpc.id' } }], dataProvider: function (args) { @@ -3920,7 +3920,7 @@ Ovs: { id: "Ovs", - label: "Ovs", + label: "label.ovs", isMaximized: true, type: 'detailView', fields: { @@ -4122,15 +4122,15 @@ }, gslbprovider: { - label: 'GSLB service', + label: 'label.gslb.service', isBoolean: true, isChecked: false }, gslbproviderpublicip: { - label: 'GSLB service Public IP' + label: 'label.gslb.service.public.ip' }, gslbproviderprivateip: { - label: 'GSLB service Private IP' + label: 'label.gslb.service.private.ip' }, numretries: { @@ -4310,7 +4310,7 @@ BaremetalDhcpProvider: { type: 'detailView', id: 'BaremetalDhcpProvider', - label: 'Baremetal DHCP Provider', + label: 'label.baremetal.dhcp.provider', viewAll: { label: 'label.devices', path: '_zone.BaremetalDhcpDevices' @@ -4346,9 +4346,9 @@ }, actions: { add: { - label: 'Add Baremetal DHCP Device', + label: 'label.add.baremetal.dhcp.device', createForm: { - title: 'Add Baremetal DHCP Device', + title: 'label.add.baremetal.dhcp.device', fields: { url: { label: 'label.url', @@ -4376,7 +4376,7 @@ }, messages: { notification: function (args) { - return 'Add Baremetal DHCP Device'; + return 'label.add.baremetal.dhcp.device'; } }, notification: { @@ -4482,7 +4482,7 @@ BaremetalPxeProvider: { type: 'detailView', id: 'BaremetalPxeProvider', - label: 'Baremetal PXE Provider', + label: 'label.baremetal.pxe.provider', viewAll: { label: 'label.devices', path: '_zone.BaremetalPxeDevices' @@ -4518,9 +4518,9 @@ }, actions: { add: { - label: 'Add Baremetal PXE Device', + label: 'label.baremetal.pxe.device', createForm: { - title: 'Add Baremetal PXE Device', + title: 'label.baremetal.pxe.device', fields: { url: { label: 'label.url', @@ -4542,7 +4542,7 @@ } }, tftpdir: { - label: 'Tftp root directory', + label: 'label.tftp.root.directory', validation: { required: true } @@ -4554,7 +4554,7 @@ }, messages: { notification: function (args) { - return 'Add Baremetal PXE Device'; + return 'label.baremetal.pxe.device'; } }, notification: { @@ -5474,7 +5474,7 @@ // Security groups detail view securityGroups: { id: 'securityGroup-providers', - label: 'Security Groups', + label: 'label.menu.security.groups', type: 'detailView', viewAll: { label: 'label.rules', @@ -6255,7 +6255,7 @@ }); }, detailView: { - name: 'Virtual applicance details', + name: 'label.virtual.appliance.details', actions: { start: { label: 'label.action.start.router', @@ -6984,7 +6984,7 @@ sections: { physicalResources: { type: 'select', - title: 'Physical Resources', + title: 'label.menu.physical.resources', listView: { zones: { id: 'physicalResources', @@ -7070,24 +7070,24 @@ isMaximized: true, actions: { addVmwareDc: { - label: 'Add VMware datacenter', - textLabel: 'Add VMware datacenter', + label: 'label.add.vmware.datacenter', + textLabel: 'label.add.vmware.datacenter', messages: { notification: function (args) { - return 'Add VMware datacenter'; + return 'label.add.vmware.datacenter'; } }, createForm: { - title: 'Add VMware datacenter', + title: 'label.add.vmware.datacenter', fields: { name: { - label: 'DC Name', + label: 'label.dc.name', validation: { required: true } }, vcenter: { - label: 'vcenter', + label: 'label.vcenter', validation: { required: true } @@ -7143,13 +7143,13 @@ }, removeVmwareDc: { - label: 'Remove VMware datacenter', + label: 'label.remove.vmware.datacenter', messages: { confirm: function (args) { - return 'Please confirm you want to remove VMware datacenter'; + return 'message.confirm.remove.vmware.datacenter'; }, notification: function (args) { - return 'Remove VMware datacenter'; + return 'label.remove.vmware.datacenter'; } }, action: function (args) { @@ -7246,20 +7246,20 @@ }, dedicateZone: { - label: 'Dedicate Zone', + label: 'label.dedicate.zone', messages: { confirm: function (args) { - return 'Do you really want to dedicate this zone to a domain/account? '; + return 'message.confirm.dedicate.zone'; }, notification: function (args) { - return 'Zone Dedicated'; + return 'label.zone.dedicated'; } }, createForm: { - title: 'Dedicate Zone', + title: 'label.dedicate.zone', fields: { domainId: { - label: 'Domain', + label: 'label.domain', validation: { required: true }, @@ -7287,7 +7287,7 @@ } }, accountId: { - label: 'Account', + label: 'label.account', docID: 'helpAccountForDedication', validation: { required: false @@ -7324,13 +7324,13 @@ } }, releaseDedicatedZone: { - label: 'Release Dedicated Zone', + label: 'label.release.dedicated.zone', messages: { confirm: function (args) { - return 'Do you want to release this dedicated zone ?'; + return 'message.confirm.release.dedicated.zone'; }, notification: function (args) { - return 'Zone dedication released'; + return 'message.dedicated.zone.released'; } }, action: function (args) { @@ -7470,11 +7470,11 @@ isEditable: true }, ip6dns1: { - label: 'IPv6 DNS1', + label: 'label.ipv6.dns1', isEditable: true }, ip6dns2: { - label: 'IPv6 DNS2', + label: 'label.ipv6.dns2', isEditable: true }, internaldns1: { @@ -7511,21 +7511,21 @@ }, { isdedicated: { - label: 'Dedicated' + label: 'label.dedicated' }, domainid: { - label: 'Domain ID' + label: 'label.domain.id' } }, { vmwaredcName: { - label: 'VMware datacenter Name' + label: 'label.vmware.datacenter.name' }, vmwaredcVcenter: { - label: 'VMware datacenter vcenter' + label: 'label.vmware.datacenter.vcenter' }, vmwaredcId: { - label: 'VMware datacenter Id' + label: 'label.vmware.datacenter.id' } }], dataProvider: function (args) { @@ -7678,7 +7678,7 @@ detailView: { noCompact: true, - name: 'System VM details', + name: 'label.system.vm.details', actions: { start: { label: 'label.action.start.systemvm', @@ -7914,7 +7914,7 @@ var vmObj = args.jsonObj; //if (vmObj.state == 'Running' && vmObj.hypervisor == 'VMware') { //needs to wait for API fix that will return hypervisor property if (vmObj.state == 'Running') { - description = 'Please read the dynamic scaling section in the admin guide before scaling up.'; + description = 'message.read.admin.guide.scaling.up'; } return description; }, @@ -7977,11 +7977,11 @@ }, messages: { confirm: function (args) { - return 'Do you really want to scale up the system VM ?'; + return 'message.confirm.scale.up.system.vm'; }, notification: function (args) { - return 'System VM Scaled Up'; + return 'label.system.vm.scaled.up'; } }, notification: { @@ -8026,8 +8026,8 @@ label: 'label.type', converter: function (args) { if (args == "consoleproxy") - return "Console Proxy VM"; else if (args == "secondarystoragevm") - return "Secondary Storage VM"; else + return 'label.console.proxy.vm'; else if (args == "secondarystoragevm") + return 'label.secondary.storage.vm'; else return args; } }, @@ -8078,7 +8078,7 @@ // Granular settings for zone settings: { - title: 'Settings', + title: 'label.settings', custom: cloudStack.uiCustom.granularSettings({ dataProvider: function (args) { $.ajax({ @@ -8741,7 +8741,7 @@ routerNoGroup: { id: 'routers', type: 'select', - title: '(no grouping)', + title: 'label.no.grouping', listView: { id: 'routers', label: 'label.virtual.appliances', @@ -8768,7 +8768,7 @@ } }, requiresupgrade: { - label: 'Requires Upgrade', + label: 'label.requires.upgrade', converter: cloudStack.converters.toBooleanText } }, @@ -8830,7 +8830,7 @@ }); }, detailView: { - name: 'Virtual applicance details', + name: 'label.virtual.appliance.details', actions: { start: { label: 'label.action.start.router', @@ -8915,13 +8915,13 @@ }, upgradeRouterToUseNewerTemplate: { - label: 'Upgrade Router to Use Newer Template', + label: 'label.upgrade.router.newer.template', messages: { confirm: function (args) { - return 'Please confirm that you want to upgrade router to use newer template'; + return 'message.confirm.upgrade.router.newer.template'; }, notification: function (args) { - return 'Upgrade Router to Use Newer Template'; + return 'label.upgrade.router.newer.template'; } }, action: function (args) { @@ -9102,7 +9102,7 @@ var vmObj = args.jsonObj; //if (vmObj.state == 'Running' && vmObj.hypervisor == 'VMware') { //needs to wait for API fix that will return hypervisor property if (vmObj.state == 'Running') { - description = 'Please read the dynamic scaling section in the admin guide before scaling up.'; + description = 'message.read.admin.guide.scaling.up'; } return description; }, @@ -9163,11 +9163,11 @@ }, messages: { confirm: function (args) { - return 'Do you really want to scale up the Router VM ?'; + return 'message.confirm.scale.up.router.vm'; }, notification: function (args) { - return 'Router VM Scaled Up'; + return 'label.router.vm.scaled.up'; } }, notification: { @@ -9243,7 +9243,7 @@ label: 'label.version' }, requiresupgrade: { - label: 'Requires Upgrade', + label: 'label.requires.upgrade', converter: cloudStack.converters.toBooleanText }, guestnetworkid: { @@ -9285,7 +9285,7 @@ label: 'label.redundant.state' }, vpcid: { - label: 'VPC ID' + label: 'label.vpc.id' } }], dataProvider: function (args) { @@ -9371,7 +9371,7 @@ routerGroupByZone: { id: 'routerGroupByZone', type: 'select', - title: 'group by zone', + title: 'label.group.by.zone', listView: { id: 'routerGroupByZone', label: 'label.virtual.appliances', @@ -9380,15 +9380,15 @@ label: 'label.zone' }, routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } } @@ -9422,16 +9422,16 @@ }); }, detailView: { - name: 'Virtual Routers group by zone', + name: 'label.virtual.routers.group.zone', actions: { upgradeRouterToUseNewerTemplate: { - label: 'Upgrade Router to Use Newer Template', + label: 'label.upgrade.router.newer.template', messages: { confirm: function (args) { - return 'Please confirm that you want to upgrade all routers in this zone to use newer template'; + return 'message.confirm.upgrade.routers.newtemplate'; }, notification: function (args) { - return 'Upgrade Router to Use Newer Template'; + return 'label.upgrade.router.newer.template'; } }, action: function (args) { @@ -9459,7 +9459,7 @@ }, tabs: { details: { - title: 'Virtual Routers group by zone', + title: 'label.virtual.routers.group.zone', fields:[ { name: { label: 'label.zone' @@ -9467,10 +9467,10 @@ }, { routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { return 'Yes'; @@ -9480,7 +9480,7 @@ } }, numberOfRouterRequiresUpgrade: { - label: 'Total of Virtual Routers that require upgrade' + label: 'label.total.virtual.routers.upgrade' } }], dataProvider: function (args) { @@ -9498,7 +9498,7 @@ routerGroupByPod: { id: 'routerGroupByPod', type: 'select', - title: 'group by pod', + title: 'label.group.by.pod', listView: { id: 'routerGroupByPod', label: 'label.virtual.appliances', @@ -9507,15 +9507,15 @@ label: 'label.pod' }, routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } } @@ -9549,16 +9549,16 @@ }); }, detailView: { - name: 'Virtual Routers group by pod', + name: 'label.virtual.routers.group.pod', actions: { upgradeRouterToUseNewerTemplate: { - label: 'Upgrade Router to Use Newer Template', + label: 'label.upgrade.router.newer.template', messages: { confirm: function (args) { - return 'Please confirm that you want to upgrade all routers in this pod to use newer template'; + return 'message.confirm.upgrade.routers.pod.newtemplate'; }, notification: function (args) { - return 'Upgrade Router to Use Newer Template'; + return 'label.upgrade.router.newer.template'; } }, action: function (args) { @@ -9586,7 +9586,7 @@ }, tabs: { details: { - title: 'Virtual Routers group by pod', + title: 'label.virtual.routers.group.pod', fields:[ { name: { label: 'label.pod' @@ -9594,20 +9594,20 @@ }, { routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } }, numberOfRouterRequiresUpgrade: { - label: 'Total of Virtual Routers that require upgrade' + label: 'label.total.virtual.routers.upgrade' }, zonename: { label: 'label.zone' @@ -9628,7 +9628,7 @@ routerGroupByCluster: { id: 'routerGroupByCluster', type: 'select', - title: 'group by cluster', + title: 'label.group.by.cluster', listView: { id: 'routerGroupByCluster', label: 'label.virtual.appliances', @@ -9637,15 +9637,15 @@ label: 'label.cluster' }, routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } } @@ -9679,16 +9679,16 @@ }); }, detailView: { - name: 'Virtual Routers group by cluster', + name: 'label.virtual.routers.group.cluster', actions: { upgradeRouterToUseNewerTemplate: { - label: 'Upgrade Router to Use Newer Template', + label: 'label.upgrade.router.newer.template', messages: { confirm: function (args) { - return 'Please confirm that you want to upgrade all routers in this cluster to use newer template'; + return 'message.confirm.upgrade.routers.cluster.newtemplate'; }, notification: function (args) { - return 'Upgrade Router to Use Newer Template'; + return 'label.upgrade.router.newer.template'; } }, action: function (args) { @@ -9716,7 +9716,7 @@ }, tabs: { details: { - title: 'Virtual Routers group by cluster', + title: 'label.virtual.routers.group.cluster', fields:[ { name: { label: 'label.cluster' @@ -9724,26 +9724,26 @@ }, { routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } }, numberOfRouterRequiresUpgrade: { - label: 'Total of Virtual Routers that require upgrade' + label: 'label.total.virtual.routers that require upgrade' }, podname: { label: 'label.pod' }, zonename: { - label: 'zone' + label: 'label.zone.lower' } }], dataProvider: function (args) { @@ -9773,15 +9773,15 @@ label: 'label.domain' }, routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } } @@ -9876,16 +9876,16 @@ }); }, detailView: { - name: 'Virtual Routers group by account', + name: 'label.virtual.routers.group.account', actions: { upgradeRouterToUseNewerTemplate: { - label: 'Upgrade Router to Use Newer Template', + label: 'label.upgrade.router.newer.template', messages: { confirm: function (args) { - return 'Please confirm that you want to upgrade all routers in this account to use newer template'; + return 'message.confirm.upgrade.routers.account.newtemplate'; }, notification: function (args) { - return 'Upgrade Router to Use Newer Template'; + return 'label.upgrade.router.newer.template'; } }, action: function (args) { @@ -9914,7 +9914,7 @@ }, tabs: { details: { - title: 'Virtual Routers group by account', + title: 'label.virtual.routers.group.account', fields:[ { name: { label: 'label.account' @@ -9925,20 +9925,20 @@ }, { routerCount: { - label: 'Total of Virtual Routers' + label: 'label.total.virtual.routers' }, routerRequiresUpgrade: { - label: 'Upgrade is required', + label: 'label.upgrade.required', converter: function (args) { if (args > 0) { - return 'Yes'; + return 'label.yes'; } else { - return 'No'; + return 'label.no'; } } }, numberOfRouterRequiresUpgrade: { - label: 'Total of Virtual Routers that require upgrade' + label: 'label.total.virtual.routers that require upgrade' } }], dataProvider: function (args) { @@ -10041,7 +10041,7 @@ label: 'label.zone' }, state: { - label: 'VM state', + label: 'label.vm.state', converter: function (str) { // For localization return str; @@ -10091,7 +10091,7 @@ }, detailView: { - name: 'System VM details', + name: 'label.system.vm.details', actions: { start: { label: 'label.action.start.systemvm', @@ -10327,7 +10327,7 @@ var vmObj = args.jsonObj; //if (vmObj.state == 'Running' && vmObj.hypervisor == 'VMware') { //needs to wait for API fix that will return hypervisor property if (vmObj.state == 'Running') { - description = 'Please read the dynamic scaling section in the admin guide before scaling up.'; + description = 'message.read.admin.guide.scaling.up'; } return description; }, @@ -10390,11 +10390,11 @@ }, messages: { confirm: function (args) { - return 'Do you really want to scale up the system VM ?'; + return 'message.confirm.scale.up.system.vm'; }, notification: function (args) { - return 'System VM Scaled Up'; + return 'label.system.vm.scaled.up'; } }, notification: { @@ -10570,15 +10570,15 @@ }, gslbprovider: { - label: 'GSLB service', + label: 'label.gslb.service', isBoolean: true, isChecked: false }, gslbproviderpublicip: { - label: 'GSLB service Public IP' + label: 'label.gslb.service.public.ip' }, gslbproviderprivateip: { - label: 'GSLB service Private IP' + label: 'label.gslb.service.private.ip' }, numretries: { @@ -10658,7 +10658,7 @@ } }, detailView: { - name: 'NetScaler details', + name: 'label.netscaler.details', actions: { 'remove': { label: 'label.delete.NetScaler', @@ -10722,10 +10722,10 @@ converter: cloudStack.converters.toBooleanText }, gslbproviderpublicip: { - label: 'GSLB service Public IP' + label: 'label.gslb.service.public.ip' }, gslbproviderprivateip: { - label: 'GSLB service Private IP' + label: 'label.gslb.service.private.ip' } }], dataProvider: function (args) { @@ -10750,7 +10750,7 @@ // Baremetal DHCP devices listView BaremetalDhcpDevices: { id: 'BaremetalDhcpDevices', - title: 'Baremetal DHCP Devices', + title: 'label.baremetal.dhcp.devices', listView: { id: 'BaremetalDhcpDevices', fields: { @@ -10760,9 +10760,9 @@ }, actions: { add: { - label: 'Add Baremetal DHCP Device', + label: 'label.add.baremetal.dhcp.device', createForm: { - title: 'Add Baremetal DHCP Device', + title: 'label.add.baremetal.dhcp.device', fields: { url: { label: 'label.url', @@ -10790,7 +10790,7 @@ }, messages: { notification: function (args) { - return 'Add Baremetal DHCP Device'; + return 'label.add.baremetal.dhcp.device'; } }, notification: { @@ -10822,7 +10822,7 @@ // Baremetal PXE devices listView BaremetalPxeDevices: { id: 'BaremetalPxeDevices', - title: 'Baremetal PXE Devices', + title: 'label.baremetal.pxe.devices', listView: { id: 'BaremetalPxeDevices', fields: { @@ -10832,9 +10832,9 @@ }, actions: { add: { - label: 'Add Baremetal PXE Device', + label: 'label.baremetal.pxe.device', createForm: { - title: 'Add Baremetal PXE Device', + title: 'label.baremetal.pxe.device', fields: { url: { label: 'label.url', @@ -10856,7 +10856,7 @@ } }, tftpdir: { - label: 'Tftp root directory', + label: 'label.tftp.root.directory', validation: { required: true } @@ -10868,7 +10868,7 @@ }, messages: { notification: function (args) { - return 'Add Baremetal PXE Device'; + return 'label.baremetal.pxe.device'; } }, notification: { @@ -11022,7 +11022,7 @@ }, messages: { notification: function (args) { - return 'Added new F5'; + return 'label.addes.new.f5'; } }, notification: { @@ -11048,7 +11048,7 @@ }); }, detailView: { - name: 'F5 details', + name: 'label.f5.details', actions: { 'remove': { label: 'label.delete.F5', @@ -11286,7 +11286,7 @@ }); }, detailView: { - name: 'SRX details', + name: 'label.srx.details', actions: { 'remove': { label: 'label.delete.SRX', @@ -11530,7 +11530,7 @@ }); }, detailView: { - name: 'Palo Alto details', + name: 'label.palo.alto.details', actions: { 'remove': { label: 'label.delete.PA', @@ -11695,7 +11695,7 @@ messages: { notification: function (args) { - return 'Added new Nicira Nvp Controller'; + return 'label.added.nicira.nvp.controller'; } }, notification: { @@ -11721,7 +11721,7 @@ }); }, detailView: { - name: 'Nicira Nvp details', + name: 'label.nicira.nvp.details', actions: { 'remove': { label: 'label.delete.NiciaNvp', @@ -11857,7 +11857,7 @@ messages: { notification: function (args) { - return 'Added new BigSwitch Vns Controller'; + return 'label.added.new.bigswitch.vns.controller'; } }, notification: { @@ -11883,7 +11883,7 @@ }); }, detailView: { - name: 'BigSwitch Vns details', + name: 'label.bigswitch.vns.details', actions: { 'remove': { label: 'label.delete.BigSwitchVns', @@ -12070,14 +12070,14 @@ }, isDedicated: { - label: 'Dedicate', + label: 'label.dedicate', isBoolean: true, isChecked: false, docID: 'helpDedicateResource' }, domainId: { - label: 'Domain', + label: 'label.domain', isHidden: true, validation: { required: true @@ -12108,7 +12108,7 @@ }, accountId: { - label: 'Account', + label: 'label.account', isHidden: true, dependsOn: 'isDedicated', docID: 'helpAccountForDedication', @@ -12204,7 +12204,7 @@ detailView: { viewAll: { path: '_zone.clusters', - label: 'Clusters' + label: 'label.clusters' }, tabFilter: function (args) { var hiddenTabs =[]; @@ -12281,20 +12281,20 @@ }, dedicate: { - label: 'Dedicate Pod', + label: 'label.dedicate.pod', messages: { confirm: function (args) { - return 'Do you really want to dedicate this pod to a domain/account? '; + return 'message.confirm.dedicate.pod.domain.account'; }, notification: function (args) { - return 'Pod Dedicated'; + return 'label.pod.dedicated'; } }, createForm: { - title: 'Dedicate Pod', + title: 'label.dedicate.pod', fields: { domainId: { - label: 'Domain', + label: 'label.domain', validation: { required: true }, @@ -12322,7 +12322,7 @@ } }, accountId: { - label: 'Account', + label: 'label.account', docID: 'helpAccountForDedication', validation: { required: false @@ -12362,13 +12362,13 @@ } }, release: { - label: 'Release Dedicated Pod', + label: 'label.release.dedicated.pod', messages: { confirm: function (args) { - return 'Do you want to release this dedicated pod ?'; + return 'message.confirm.release.dedicated.pod'; }, notification: function (args) { - return 'Pod dedication released'; + return 'message.pod.dedication.released'; } }, action: function (args) { @@ -12510,10 +12510,10 @@ }, { isdedicated: { - label: 'Dedicated' + label: 'label.dedicated' }, domainid: { - label: 'Domain ID' + label: 'label.domain.id' } }], @@ -12532,12 +12532,12 @@ var podItem = json.listdedicatedpodsresponse.dedicatedpod[0]; if (podItem.domainid != null) { $.extend(item, podItem, { - isdedicated: 'Yes' + isdedicated: 'label.yes' }); } } else $.extend(item, { - isdedicated: 'No' + isdedicated: 'label.no' }); args.response.success({ @@ -12792,7 +12792,7 @@ }, fields: { zoneid: { - label: 'Zone Name', + label: 'label.zone.name', docID: 'helpClusterZone', validation: { required: true @@ -12847,7 +12847,7 @@ } }, podId: { - label: 'Pod Name', + label: 'label.pod.name', docID: 'helpClusterPod', dependsOn: 'zoneid', select: function (args) { @@ -12885,14 +12885,14 @@ }, isDedicated: { - label: 'Dedicate', + label: 'label.dedicate', isBoolean: true, isChecked: false, docID: 'helpDedicateResource' }, domainId: { - label: 'Domain', + label: 'label.domain', isHidden: true, validation: { required: true @@ -12923,7 +12923,7 @@ }, accountId: { - label: 'Account', + label: 'label.account', isHidden: true, dependsOn: 'isDedicated', docID: 'helpAccountForDedication', @@ -12964,7 +12964,7 @@ }, overridepublictraffic: { - label: 'Override Public-Traffic', + label: 'label.override.public.traffic', isBoolean: true, isHidden: true, isChecked: false, @@ -12973,7 +12973,7 @@ vSwitchPublicType: { - label: 'Public Traffic vSwitch Type', + label: 'label.public.traffic.vswitch.type', select: function (args) { var useNexusDvs = false; var items =[] @@ -13026,12 +13026,12 @@ }, vSwitchPublicName: { - label: 'Public Traffic vSwitch Name', + label: 'label.public.traffic.vswitch.name', isHidden: true }, overrideguesttraffic: { - label: 'Override Guest-Traffic', + label: 'label.override.guest.traffic', isBoolean: true, isHidden: true, isChecked: false, @@ -13039,7 +13039,7 @@ }, vSwitchGuestType: { - label: 'Guest Traffic vSwitch Type', + label: 'label.guest.traffic.vswitch.type', select: function (args) { var items =[] @@ -13094,41 +13094,41 @@ }, vSwitchGuestName: { - label: ' Guest Traffic vSwitch Name', + label: ' label.guest.traffic.vswitch.name', isHidden: true }, vsmipaddress: { - label: 'Nexus 1000v IP Address', + label: 'label.cisco.nexus1000v.ip.address', validation: { required: false }, isHidden: true }, vsmipaddress_req: { - label: 'Nexus 1000v IP Address', + label: 'label.cisco.nexus1000v.ip.address', validation: { required: true }, isHidden: true }, vsmusername: { - label: 'Nexus 1000v Username', + label: 'label.cisco.nexus1000v.username', validation: { required: false }, isHidden: true }, vsmusername_req: { - label: 'Nexus 1000v Username', + label: 'label.cisco.nexus1000v.username', validation: { required: true }, isHidden: true }, vsmpassword: { - label: 'Nexus 1000v Password', + label: 'label.cisco.nexus1000v.password', validation: { required: false }, @@ -13136,7 +13136,7 @@ isHidden: true }, vsmpassword_req: { - label: 'Nexus 1000v Password', + label: 'label.cisco.nexus1000v.password', validation: { required: true }, @@ -13417,20 +13417,20 @@ }, dedicate: { - label: 'Dedicate Cluster', + label: 'label.dedicate.cluster', messages: { confirm: function (args) { - return 'Do you really want to dedicate this cluster to a domain/account? '; + return 'message.confirm.dedicate.cluster.domain.account'; }, notification: function (args) { - return 'Cluster Dedicated'; + return 'message.cluster.dedicated'; } }, createForm: { - title: 'Dedicate Cluster', + title: 'label.dedicate.cluster', fields: { domainId: { - label: 'Domain', + label: 'label.domain', validation: { required: true }, @@ -13458,7 +13458,7 @@ } }, accountId: { - label: 'Account', + label: 'label.account', docID: 'helpAccountForDedication', validation: { required: false @@ -13494,13 +13494,13 @@ } }, release: { - label: 'Release Dedicated Cluster', + label: 'label.release.dedicated.cluster', messages: { confirm: function (args) { - return 'Do you want to release this dedicated cluster ?'; + return 'message.confirm.release.dedicated.cluster'; }, notification: function (args) { - return 'Cluster dedication released'; + return 'message.cluster.dedication.released'; } }, action: function (args) { @@ -13656,10 +13656,10 @@ } }, { isdedicated: { - label: 'Dedicated' + label: 'label.dedicated' }, domainid: { - label: 'Domain ID' + label: 'label.domain.id' } }], dataProvider: function (args) { @@ -13678,12 +13678,12 @@ var clusterItem = json.listdedicatedclustersresponse.dedicatedcluster[0]; if (clusterItem.domainid != null) { $.extend(item, clusterItem, { - isdedicated: 'Yes' + isdedicated: 'label.yes' }); } } else $.extend(item, { - isdedicated: 'No' + isdedicated: 'label.no' }) }, error: function (json) { @@ -13893,7 +13893,7 @@ // Granular settings for cluster settings: { - title: 'Settings', + title: 'label.settings', custom: cloudStack.uiCustom.granularSettings({ dataProvider: function (args) { $.ajax({ @@ -14255,14 +14255,14 @@ }, isDedicated: { - label: 'Dedicate', + label: 'label.dedicate', isBoolean: true, isChecked: false, docID: 'helpDedicateResource' }, domainId: { - label: 'Domain', + label: 'label.domain', isHidden: true, validation: { required: true @@ -14292,7 +14292,7 @@ }, accountId: { - label: 'Account', + label: 'label.account', isHidden: true, dependsOn: 'isDedicated', docID: 'helpAccountForDedication', @@ -14532,20 +14532,20 @@ }, dedicate: { - label: 'Dedicate Host', + label: 'label.dedicate.host', messages: { confirm: function (args) { - return 'Do you really want to dedicate this host to a domain/account? '; + return 'message.confirm.dedicate.host.domain.account'; }, notification: function (args) { - return 'Host Dedicated'; + return 'message.host.dedicated'; } }, createForm: { - title: 'Dedicate Host', + title: 'label.dedicate.host', fields: { domainId: { - label: 'Domain', + label: 'label.domain', validation: { required: true }, @@ -14573,7 +14573,7 @@ } }, accountId: { - label: 'Account', + label: 'label.account', docID: 'helpAccountForDedication', validation: { required: false @@ -14611,13 +14611,13 @@ } }, release: { - label: 'Release Dedicated Host', + label: 'label.release.dedicated.host', messages: { confirm: function (args) { - return 'Do you want to release this dedicated host ?'; + return 'message.confirm.release.dedicated.host'; }, notification: function (args) { - return 'Host dedication released'; + return 'message.host.dedication.released'; } }, action: function (args) { @@ -14762,9 +14762,9 @@ return 'label.action.remove.host'; } }, - preFilter: function (args) { - if (isAdmin()) { - args.$form.find('.form-item[rel=isForced]').css('display', 'inline-block'); + preFilter: function(args) { //bug to fix: preFilter is not picked up from here + if (!isAdmin()) { + args.$form.find('.form-item[rel=isForced]').hide(); } }, createForm: { @@ -14774,25 +14774,33 @@ isForced: { label: 'force.remove', isBoolean: true, - isHidden: true + isHidden: false } } }, action: function (args) { - var array1 =[]; - //if(args.$form.find('.form-item[rel=isForced]').css("display") != "none") //uncomment after Brian fix it to include $form in args - array1.push("&forced=" + (args.data.isForced == "on")); + var data = { + id: args.context.hosts[0].id + }; + if(args.$form.find('.form-item[rel=isForced]').css("display") != "none") { + $.extend(data, { + forced: (args.data.isForced == "on") + }); + } $.ajax({ - url: createURL("deleteHost&id=" + args.context.hosts[0].id + array1.join("")), - dataType: "json", - async: true, + url: createURL("deleteHost"), + data: data, success: function (json) { //{ "deletehostresponse" : { "success" : "true"} } args.response.success({ data: { } }); + + if (args.context.hosts[0].hypervisor == "XenServer"){ + cloudStack.dialog.notice({ message: _s("The host has been deleted. Please eject the host from XenServer Pool") }) + } } }); }, @@ -14897,15 +14905,15 @@ label: 'label.last.disconnected' }, cpusockets: { - label: 'The Number of CPU Sockets' + label: 'label.number.of.cpu.sockets' } }, { isdedicated: { - label: 'Dedicated' + label: 'label.dedicated' }, domainid: { - label: 'Domain ID' + label: 'label.domain.id' } }], @@ -14925,12 +14933,12 @@ var hostItem = json.listdedicatedhostsresponse.dedicatedhost[0]; if (hostItem.domainid != null) { $.extend(item, hostItem, { - isdedicated: 'Yes' + isdedicated: 'label.yes' }); } } else $.extend(item, { - isdedicated: 'No' + isdedicated: 'label.no' }) }, error: function (json) { @@ -15024,7 +15032,7 @@ truncate: true }, scope: { - label: 'Scope' + label: 'label.scope' } }, @@ -15387,7 +15395,7 @@ $form.find('.form-item[rel=path]').css('display', 'inline-block'); var $required = $form.find('.form-item[rel=path]').find(".name").find("label span"); - $form.find('.form-item[rel=path]').find(".name").find("label").text("Path:").prepend($required); + $form.find('.form-item[rel=path]').find(".name").find("label").text('label.path'+":").prepend($required); $form.find('.form-item[rel=smbUsername]').hide(); $form.find('.form-item[rel=smbPassword]').hide(); @@ -15414,7 +15422,7 @@ $form.find('.form-item[rel=path]').css('display', 'inline-block'); var $required = $form.find('.form-item[rel=path]').find(".name").find("label span"); - $form.find('.form-item[rel=path]').find(".name").find("label").text("Path:").prepend($required); + $form.find('.form-item[rel=path]').find(".name").find("label").text('label.path'+":").prepend($required); $form.find('.form-item[rel=smbUsername]').css('display', 'inline-block'); $form.find('.form-item[rel=smbPassword]').css('display', 'inline-block'); @@ -15441,7 +15449,7 @@ $form.find('.form-item[rel=path]').css('display', 'inline-block'); var $required = $form.find('.form-item[rel=path]').find(".name").find("label span"); - $form.find('.form-item[rel=path]').find(".name").find("label").text("Path:").prepend($required); + $form.find('.form-item[rel=path]').find(".name").find("label").text('label.path'+":").prepend($required); $form.find('.form-item[rel=smbUsername]').hide(); $form.find('.form-item[rel=smbPassword]').hide(); @@ -15467,7 +15475,7 @@ $form.find('.form-item[rel=path]').css('display', 'inline-block'); var $required = $form.find('.form-item[rel=path]').find(".name").find("label span"); - $form.find('.form-item[rel=path]').find(".name").find("label").text("SR Name-Label:").prepend($required); + $form.find('.form-item[rel=path]').find(".name").find("label").text('label.SR.name'+":").prepend($required); $form.find('.form-item[rel=smbUsername]').hide(); $form.find('.form-item[rel=smbPassword]').hide(); @@ -15566,7 +15574,7 @@ $form.find('.form-item[rel=path]').css('display', 'inline-block'); var $required = $form.find('.form-item[rel=path]').find(".name").find("label span"); - $form.find('.form-item[rel=path]').find(".name").find("label").text("Path:").prepend($required); + $form.find('.form-item[rel=path]').find(".name").find("label").text('label.path'+":").prepend($required); $form.find('.form-item[rel=smbUsername]').hide(); $form.find('.form-item[rel=smbPassword]').hide(); @@ -16148,7 +16156,7 @@ // Granular settings for storage pool settings: { - title: 'Settings', + title: 'label.settings', custom: cloudStack.uiCustom.granularSettings({ dataProvider: function (args) { @@ -16331,13 +16339,13 @@ noCompact: true, actions: { remove: { - label: 'Delete UCS Manager', + label: 'label.delete.ucs.manager', messages: { confirm: function (args) { - return 'Please confirm that you want to delete UCS Manager'; + return 'message.confirm.delete.ucs.manager'; }, notification: function (args) { - return 'Delete UCS Manager'; + return 'label.delete.ucs.manager'; } }, action: function (args) { @@ -16416,19 +16424,19 @@ }, blades: { - title: 'Blades', + title: 'label.blades', listView: { id: 'blades', hideSearchBar: true, fields: { chassis: { - label: 'Chassis' + label: 'label.chassis' }, bladeid: { - label: 'Blade ID' + label: 'label.blade.id' }, profiledn: { - label: 'Associated Profile' + label: 'label.associated.profile' } }, dataProvider: function (args) { @@ -16486,13 +16494,13 @@ actions: { refreshUcsBlades: { isHeader: true, - label: 'Refresh Blades', + label: 'label.refresh.blades', messages: { confirm: function (args) { - return 'Please confirm that you want to refresh blades.'; + return 'message.confirm.refresh.blades'; }, notification: function (args) { - return 'Refresh Blades'; + return 'label.refresh.blades'; } }, action: function (args) { @@ -16568,18 +16576,18 @@ }, associateTemplateToBlade: { - label: 'Instanciate Template and Associate Profile to Blade', + label: 'label.instanciate.template.associate.profile.blade', addRow: 'false', messages: { notification: function (args) { - return 'Instanciate Template and Associate Profile to Blade'; + return 'label.instanciate.template.associate.profile.blade'; } }, createForm: { - title: 'Instanciate Template and Associate Profile to Blade', + title: 'label.instanciate.template.associate.profile.blade', fields: { templatedn: { - label: 'Select Template', + label: 'label.select.template', select: function (args) { var items =[]; @@ -16626,7 +16634,7 @@ } }, profilename: { - label: 'Profile' + label: 'label.profile' } } }, @@ -16702,18 +16710,18 @@ }, disassociateProfileFromBlade: { - label: 'Disassociate Profile from Blade', + label: 'label.disassociate.profile.blade', addRow: 'false', messages: { notification: function (args) { - return 'Disassociate Profile from Blade'; + return 'label.disassociate.profile.blade'; } }, createForm: { - title: 'Disassociate Profile from Blade', + title: 'label.disassociate.profile.blade', fields: { deleteprofile: { - label: 'Delete Profile', + label: 'label.delete.profile', isBoolean: true, isChecked: true } @@ -16850,7 +16858,7 @@ label: 'label.name' }, provider: { - label: 'Provider', + label: 'label.provider', select: function (args) { /* UI no longer gets providers from "listStorageProviders&type=image" because: @@ -17020,7 +17028,7 @@ //NFS, SMB (begin) zoneid: { - label: 'Zone', + label: 'label.zone', docID: 'helpSecondaryStorageZone', validation: { required: true @@ -17145,7 +17153,7 @@ }, nfsCacheZoneid: { dependsOn: 'createNfsCache', - label: 'Zone', + label: 'label.zone', validation: { required: true }, @@ -17413,7 +17421,7 @@ }, detailView: { - name: 'Secondary storage details', + name: 'label.secondary.storage.details', isMaximized: true, actions: { remove: { @@ -17463,7 +17471,7 @@ label: 'label.protocol' }, providername: { - label: 'Provider' + label: 'label.provider' }, scope: { label: 'label.scope' @@ -17534,7 +17542,7 @@ }, cacheStorage: { type: 'select', - title: 'Secondary Staging Store', + title: 'label.secondary.staging.store', listView: { id: 'secondarystorages', section: 'seconary-storage', @@ -17546,7 +17554,7 @@ label: 'label.url' }, providername: { - label: 'Provider' + label: 'label.provider' } }, @@ -17582,12 +17590,12 @@ actions: { add: { - label: 'Add NFS Secondary Staging Store', + label: 'label.add.nfs.secondary.staging.store', createForm: { - title: 'Add NFS Secondary Staging Store', + title: 'label.add.nfs.secondary.staging.store', fields: { zoneid: { - label: 'Zone', + label: 'label.zone', validation: { required: true }, @@ -17660,24 +17668,24 @@ }, messages: { notification: function (args) { - return 'Add NFS Secondary Staging Store'; + return 'label.add.nfs.secondary.staging.store'; } } } }, detailView: { - name: 'Secondary Staging Store details', + name: 'label.secondary.staging.store.details', isMaximized: true, actions: { remove: { - label: 'Delete Secondary Staging Store', + label: 'label.delete.secondary.staging.store', messages: { confirm: function (args) { - return 'Please confirm you want to delete Secondary Staging Store.'; + return 'message.confirm.delete.secondary.staging.store'; }, notification: function (args) { - return 'Delete Secondary Staging Store'; + return 'label.delete.secondary.staging.store'; } }, action: function (args) { @@ -17796,16 +17804,16 @@ section: 'guest-IP-range', fields: { startip: { - label: 'IPv4 Start IP' + label: 'label.ipv4.start.ip' }, endip: { - label: 'IPv4 End IP' + label: 'label.ipv4.end.ip' }, startipv6: { - label: 'IPv6 Start IP' + label: 'label.ipv6.start.ip' }, endipv6: { - label: 'IPv6 End IP' + label: 'label.ipv6.end.ip' } }, @@ -17836,22 +17844,22 @@ label: 'label.netmask' }, startipv4: { - label: 'IPv4 Start IP' + label: 'label.ipv4.start.ip' }, endipv4: { - label: 'IPv4 End IP' + label: 'label.ipv4.end.ip' }, ip6cidr: { - label: 'IPv6 CIDR' + label: 'label.ipv6.CIDR' }, ip6gateway: { - label: 'IPv6 Gateway' + label: 'label.ipv6.gateway' }, startipv6: { - label: 'IPv6 Start IP' + label: 'label.ipv6.start.ip' }, endipv6: { - label: 'IPv6 End IP' + label: 'label.ipv6.end.ip' } } }, @@ -19260,8 +19268,8 @@ } nspHardcodingArray.push({ - id: "Ovs", - name: "Ovs", + id: 'OVS', + name: 'OVS', state: nspMap.Ovs ? nspMap.Ovs.state : 'Disabled' }); }; diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index 35516130482..7a45463d8a6 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -237,7 +237,7 @@ }, xenserverToolsVersion61plus: { - label: 'XenServer Tools Version 6.1+', + label: 'label.xenserver.tools.version.61.plus', isBoolean: true, isChecked: function (args) { var b = false; @@ -434,7 +434,7 @@ }, isdynamicallyscalable: { - label: "Dynamically Scalable", + label: "label.dynamically.scalable", docID: 'helpRegisterTemplateDynamicallyScalable', isBoolean: true }, @@ -951,7 +951,7 @@ label: 'label.hypervisor' }, xenserverToolsVersion61plus: { - label: 'XenServer Tools Version 6.1+', + label: 'label.xenserver.tools.version.61.plus', isBoolean: true, isEditable: function () { if (isAdmin()) diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index 8884ebe6ec8..f5f7b10cfaf 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -458,6 +458,14 @@ $step.removeClass('custom-size'); } + var customIops = item[args.customIopsFlag]; + + if (customIops) { + $step.addClass('custom-iops'); + } else { + $step.removeClass('custom-iops'); + } + return true; }); diff --git a/ui/scripts/ui-custom/zoneChart.js b/ui/scripts/ui-custom/zoneChart.js index e847a199d8a..bc30d510539 100644 --- a/ui/scripts/ui-custom/zoneChart.js +++ b/ui/scripts/ui-custom/zoneChart.js @@ -24,7 +24,7 @@ */ var viewAllButton = function(args) { var $viewAll = $('
').addClass('button view-all'); - var $label = $('').addClass('view-all-label').html(args.label ? args.label : 'View all'); + var $label = $('').addClass('view-all-label').html(args.label ? args.label : 'label.view.all'); var $browser = args.$browser; var action = args.action; // Launch a list view @@ -111,46 +111,46 @@ // Resource items var computeResources = { zone: { - label: 'Zone' + label: 'label.zone' }, pods: { - label: 'Pods', + label: 'label.pods', viewAll: { action: actions.listView('pods', context) } }, clusters: { - label: 'Clusters', + label: 'label.clusters', viewAll: { action: actions.listView('clusters', context) } }, hosts: { - label: 'Hosts', + label: 'label.hosts', viewAll: { action: actions.listView('hosts', context) } }, primaryStorage: { - label: 'Primary Storage', + label: 'label.primary.storage', viewAll: { action: actions.listView('primary-storage', context) } }, ucs: { - label: 'UCS', + label: 'label.ucs', viewAll: { action: actions.listView('ucs', context) } }, secondaryStorage: { - label: 'Secondary Storage', + label: 'label.secondary.storage', viewAll: { action: actions.listView('secondary-storage', context) } diff --git a/ui/scripts/ui/core.js b/ui/scripts/ui/core.js index 06be12b7b6b..add7d06dedb 100644 --- a/ui/scripts/ui/core.js +++ b/ui/scripts/ui/core.js @@ -363,7 +363,7 @@ function(value, element) { return (value.indexOf("<") == -1 && value.indexOf(">") == -1); }, - jQuery.format("Disallowed characters: <, >") + jQuery.format('message.disallowed.characters') ); // Check for pending project invitations diff --git a/ui/scripts/ui/dialog.js b/ui/scripts/ui/dialog.js index 5f0697ac032..82e7fd428c2 100644 --- a/ui/scripts/ui/dialog.js +++ b/ui/scripts/ui/dialog.js @@ -72,7 +72,7 @@ $(window).trigger('cloudStack.createForm.makeFields', { $form: $form, - fields: fields + fields: args.form.fields }); var ret = function() { diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 873775d7221..713ebdeffd3 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -927,7 +927,7 @@ response: { success: function(args) { $(args.data).each(function() { - $('