diff --git a/api/src/com/cloud/agent/api/storage/CreateVolumeOVAAnswer.java b/api/src/com/cloud/agent/api/storage/CreateVolumeOVAAnswer.java new file mode 100755 index 00000000000..52a57dba7b2 --- /dev/null +++ b/api/src/com/cloud/agent/api/storage/CreateVolumeOVAAnswer.java @@ -0,0 +1,26 @@ +// 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.storage; + +import com.cloud.agent.api.Answer; + +public class CreateVolumeOVAAnswer extends Answer { + public CreateVolumeOVAAnswer(CreateVolumeOVACommand cmd, boolean result, String details) { + super(cmd, result, details); + } + +} diff --git a/api/src/com/cloud/agent/api/storage/CreateVolumeOVACommand.java b/api/src/com/cloud/agent/api/storage/CreateVolumeOVACommand.java new file mode 100755 index 00000000000..224b7c80d43 --- /dev/null +++ b/api/src/com/cloud/agent/api/storage/CreateVolumeOVACommand.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.agent.api.storage; + +import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.storage.StoragePool; + +public class CreateVolumeOVACommand extends Command { + String secUrl; + String volPath; + String volName; + StorageFilerTO pool; + + public CreateVolumeOVACommand() { + } + + public CreateVolumeOVACommand(String secUrl, String volPath, String volName, StoragePool pool, int wait) { + this.secUrl = secUrl; + this.volPath = volPath; + this.volName = volName; + this.pool = new StorageFilerTO(pool); + setWait(wait); + } + + @Override + public boolean executeInSequence() { + return true; + } + + public String getVolPath() { + return this.volPath; + } + + public String getVolName() { + return this.volName; + } + public String getSecondaryStorageUrl() { + return this.secUrl; + } + public StorageFilerTO getPool() { + return pool; + } +} + + diff --git a/api/src/com/cloud/agent/api/storage/PrepareOVAPackingAnswer.java b/api/src/com/cloud/agent/api/storage/PrepareOVAPackingAnswer.java new file mode 100755 index 00000000000..923d952a137 --- /dev/null +++ b/api/src/com/cloud/agent/api/storage/PrepareOVAPackingAnswer.java @@ -0,0 +1,26 @@ +// 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.storage; + +import com.cloud.agent.api.Answer; + +public class PrepareOVAPackingAnswer extends Answer { + public PrepareOVAPackingAnswer(PrepareOVAPackingCommand cmd, boolean result, String details) { + super(cmd, result, details); + } + +} diff --git a/api/src/com/cloud/agent/api/storage/PrepareOVAPackingCommand.java b/api/src/com/cloud/agent/api/storage/PrepareOVAPackingCommand.java new file mode 100755 index 00000000000..29fa26d0bd0 --- /dev/null +++ b/api/src/com/cloud/agent/api/storage/PrepareOVAPackingCommand.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.agent.api.storage; + +import com.cloud.agent.api.Command; + +public class PrepareOVAPackingCommand extends Command { + private String templatePath; + private String secUrl; + + public PrepareOVAPackingCommand() { + } + + public PrepareOVAPackingCommand(String secUrl, String templatePath) { + this.secUrl = secUrl; + this.templatePath = templatePath; + } + + @Override + public boolean executeInSequence() { + return true; + } + + public String getTemplatePath() { + return this.templatePath; + } + + public String getSecondaryStorageUrl() { + return this.secUrl; + } + +} + + \ No newline at end of file diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 6c9bebc1c15..bea92dc2481 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -138,6 +138,7 @@ public interface NetworkService { ResourceAllocationException, ResourceUnavailableException, ConcurrentOperationException; /** + * * @param networkName * @param displayText * @param physicalNetworkId @@ -148,13 +149,14 @@ public interface NetworkService { * @param netmask * @param networkOwnerId * @param vpcId TODO + * @param sourceNat * @return * @throws InsufficientCapacityException * @throws ConcurrentOperationException * @throws ResourceAllocationException */ Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String vlan, - String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId) + String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; /* Requests an IP address for the guest nic */ diff --git a/api/src/com/cloud/network/rules/RulesService.java b/api/src/com/cloud/network/rules/RulesService.java index d47b38f9d43..45abd84a9ec 100644 --- a/api/src/com/cloud/network/rules/RulesService.java +++ b/api/src/com/cloud/network/rules/RulesService.java @@ -67,7 +67,7 @@ public interface RulesService { boolean applyPortForwardingRules(long ipAdddressId, Account caller) throws ResourceUnavailableException; - boolean enableStaticNat(long ipAddressId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException; + boolean enableStaticNat(long ipAddressId, long vmId, long networkId, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException; PortForwardingRule getPortForwardigRule(long ruleId); diff --git a/api/src/com/cloud/network/vpc/PrivateIp.java b/api/src/com/cloud/network/vpc/PrivateIp.java index 857fc226f30..eb6843339c5 100644 --- a/api/src/com/cloud/network/vpc/PrivateIp.java +++ b/api/src/com/cloud/network/vpc/PrivateIp.java @@ -44,5 +44,6 @@ public interface PrivateIp { String getMacAddress(); long getNetworkId(); + boolean getSourceNat(); } diff --git a/api/src/com/cloud/network/vpc/VpcGateway.java b/api/src/com/cloud/network/vpc/VpcGateway.java index 17566160ec3..e3530d08561 100644 --- a/api/src/com/cloud/network/vpc/VpcGateway.java +++ b/api/src/com/cloud/network/vpc/VpcGateway.java @@ -77,4 +77,8 @@ public interface VpcGateway extends Identity, ControlledEntity, InternalIdentity * @return */ State getState(); + /** + * @return + */ + boolean getSourceNat(); } diff --git a/api/src/com/cloud/network/vpc/VpcService.java b/api/src/com/cloud/network/vpc/VpcService.java index 07ce89b0a3f..23e276489c2 100644 --- a/api/src/com/cloud/network/vpc/VpcService.java +++ b/api/src/com/cloud/network/vpc/VpcService.java @@ -163,6 +163,7 @@ public interface VpcService { /** * Persists VPC private gateway in the Database. * + * * @param vpcId TODO * @param physicalNetworkId * @param vlan @@ -170,13 +171,14 @@ public interface VpcService { * @param gateway * @param netmask * @param gatewayOwnerId + * @param isSourceNat * @return * @throws InsufficientCapacityException * @throws ConcurrentOperationException * @throws ResourceAllocationException */ public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, - String gateway, String netmask, long gatewayOwnerId) throws ResourceAllocationException, + String gateway, String netmask, long gatewayOwnerId, Boolean isSourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException; /** diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index c8ccb67caf3..fa89521af0a 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -451,6 +451,6 @@ public interface UserVmService { UserVm restoreVM(RestoreVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException; - UserVm upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; + boolean upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException; } diff --git a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java index 9fd736f8543..20556957ff2 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreatePrivateGatewayCmd.java @@ -69,6 +69,11 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { required=true, description="the VPC network belongs to") private Long vpcId; + @Parameter(name=ApiConstants.SOURCE_NAT_SUPPORTED, type=CommandType.BOOLEAN, required=false, + description="source NAT supported value. Default value false. If 'true' source NAT is enabled on the private gateway" + + " 'false': sourcenat is not supported") + private Boolean isSourceNat; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -97,6 +102,13 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { return vpcId; } + public Boolean getIsSourceNat () { + if (isSourceNat == null) { + return false; + } + return true; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -111,7 +123,7 @@ public class CreatePrivateGatewayCmd extends BaseAsyncCreateCmd { PrivateGateway result = null; try { result = _vpcService.createVpcPrivateGateway(getVpcId(), getPhysicalNetworkId(), - getVlan(), getStartIp(), getGateway(), getNetmask(), getEntityOwnerId()); + getVlan(), getStartIp(), getGateway(), getNetmask(), getEntityOwnerId(), getIsSourceNat()); } catch (InsufficientCapacityException ex){ s_logger.info(ex); s_logger.trace(ex); diff --git a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java index a0ec68ef5dd..902dbae00aa 100644 --- a/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/nat/EnableStaticNatCmd.java @@ -120,7 +120,7 @@ public class EnableStaticNatCmd extends BaseCmd{ @Override public void execute() throws ResourceUnavailableException{ try { - boolean result = _rulesService.enableStaticNat(ipAddressId, virtualMachineId, getNetworkId(), false, getVmSecondaryIp()); + boolean result = _rulesService.enableStaticNat(ipAddressId, virtualMachineId, getNetworkId(), getVmSecondaryIp()); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 95d76599f70..0b33f568d8a 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -163,6 +163,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Override public void execute() { + s_logger.info("VOLSS: createSnapshotCmd starts:" + System.currentTimeMillis()); UserContext.current().setEventDetails("Volume Id: "+getVolumeId()); Snapshot snapshot = _snapshotService.createSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId())); if (snapshot != null) { @@ -172,6 +173,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId); } + s_logger.info("VOLSS: backupSnapshotCmd finishes:" + System.currentTimeMillis()); } 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 4fc65c37e58..4f2ac750ce5 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 @@ -22,11 +22,12 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import org.apache.cloudstack.api.*; import org.apache.cloudstack.api.response.ServiceOfferingResponse; +import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.log4j.Logger; -@APICommand(name = "scaleVirtualMachine", description="Scales the virtual machine to a new service offering.", responseObject=UserVmResponse.class) +@APICommand(name = "scaleVirtualMachine", description="Scales the virtual machine to a new service offering.", responseObject=SuccessResponse.class) public class ScaleVMCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(ScaleVMCmd.class.getName()); private static final String s_name = "scalevirtualmachineresponse"; @@ -83,7 +84,7 @@ public class ScaleVMCmd extends BaseCmd { @Override public void execute(){ //UserContext.current().setEventDetails("Vm Id: "+getId()); - UserVm result = null; + boolean result; try { result = _userVmService.upgradeVirtualMachine(this); } catch (ResourceUnavailableException ex) { @@ -99,9 +100,8 @@ public class ScaleVMCmd extends BaseCmd { s_logger.warn("Exception: ", ex); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); } - if (result != null){ - UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", result).get(0); - response.setResponseName(getCommandName()); + if (result){ + SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to scale vm"); diff --git a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java similarity index 91% rename from api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java rename to api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java index ea7bf60d6a3..f6d8b2c4a35 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vmsnapshot/RevertToVMSnapshotCmd.java @@ -37,11 +37,11 @@ import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.vm.snapshot.VMSnapshot; -@APICommand(name = "revertToSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class, since="4.2.0") -public class RevertToSnapshotCmd extends BaseAsyncCmd { +@APICommand(name = "revertToVMSnapshot",description = "Revert VM from a vmsnapshot.", responseObject = UserVmResponse.class, since="4.2.0") +public class RevertToVMSnapshotCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger - .getLogger(RevertToSnapshotCmd.class.getName()); - private static final String s_name = "reverttosnapshotresponse"; + .getLogger(RevertToVMSnapshotCmd.class.getName()); + private static final String s_name = "reverttovmsnapshotresponse"; @Parameter(name = ApiConstants.VM_SNAPSHOT_ID, type = CommandType.UUID, required = true,entityType=VMSnapshotResponse.class,description = "The ID of the vm snapshot") private Long vmSnapShotId; diff --git a/api/src/org/apache/cloudstack/api/response/PrivateGatewayResponse.java b/api/src/org/apache/cloudstack/api/response/PrivateGatewayResponse.java index 4123477dbfe..ca760626324 100644 --- a/api/src/org/apache/cloudstack/api/response/PrivateGatewayResponse.java +++ b/api/src/org/apache/cloudstack/api/response/PrivateGatewayResponse.java @@ -76,6 +76,10 @@ public class PrivateGatewayResponse extends BaseResponse implements ControlledEn private String state; + @SerializedName(ApiConstants.SOURCE_NAT_SUPPORTED) @Param(description = "Souce Nat enable status") + private Boolean sourceNat; + + @Override public String getObjectId() { return this.id; @@ -145,5 +149,11 @@ public class PrivateGatewayResponse extends BaseResponse implements ControlledEn public void setState(String state) { this.state = state; } + + public void setSourceNat(Boolean sourceNat) { + this.sourceNat = sourceNat; + } + + } diff --git a/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java index 301fa02ca29..8a28290e04b 100644 --- a/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java +++ b/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java @@ -16,31 +16,20 @@ // under the License. package org.apache.cloudstack.api.command.test; -import com.cloud.user.Account; -import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.vm.UserVmService; import junit.framework.Assert; import junit.framework.TestCase; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.region.AddRegionCmd; import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd; -import org.apache.cloudstack.api.response.RegionResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.region.Region; -import org.apache.cloudstack.region.RegionService; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mockito; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - - public class ScaleVMCmdTest extends TestCase{ private ScaleVMCmd scaleVMCmd; @@ -57,12 +46,11 @@ public class ScaleVMCmdTest extends TestCase{ public Long getId() { return 2L; } + @Override + public String getCommandName() { + return "scalevirtualmachineresponse"; + } }; - - //Account account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid"); - //UserContext.registerContext(1, account, null, true); - - } @@ -71,11 +59,10 @@ public class ScaleVMCmdTest extends TestCase{ UserVmService userVmService = Mockito.mock(UserVmService.class); - UserVm uservm = Mockito.mock(UserVm.class); try { Mockito.when( userVmService.upgradeVirtualMachine(scaleVMCmd)) - .thenReturn(uservm); + .thenReturn(true); }catch (Exception e){ Assert.fail("Received exception when success expected " +e.getMessage()); } @@ -83,13 +70,6 @@ public class ScaleVMCmdTest extends TestCase{ scaleVMCmd._userVmService = userVmService; responseGenerator = Mockito.mock(ResponseGenerator.class); - UserVmResponse userVmResponse = Mockito.mock(UserVmResponse.class); - List responseList = new ArrayList(); - responseList.add(userVmResponse); - - Mockito.when(responseGenerator.createUserVmResponse("virtualmachine",uservm)) - .thenReturn(responseList); - scaleVMCmd._responseGenerator = responseGenerator; scaleVMCmd.execute(); @@ -101,10 +81,9 @@ public class ScaleVMCmdTest extends TestCase{ UserVmService userVmService = Mockito.mock(UserVmService.class); try { - UserVm uservm = Mockito.mock(UserVm.class); Mockito.when( userVmService.upgradeVirtualMachine(scaleVMCmd)) - .thenReturn(null); + .thenReturn(false); }catch (Exception e){ Assert.fail("Received exception when success expected " +e.getMessage()); } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index ec4f0c8f9bb..bf9b5aaa7fd 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -569,7 +569,7 @@ removeFromGlobalLoadBalancerRule=15 listVMSnapshot=15 createVMSnapshot=15 deleteVMSnapshot=15 -revertToSnapshot=15 +revertToVMSnapshot=15 #### Baremetal commands addBaremetalHost=1 diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 7148e0710ca..b9bda4d9688 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -863,13 +863,16 @@ public class VirtualRoutingResource implements Manager { } public void assignVpcIpToRouter(final String routerIP, final boolean add, final String pubIP, - final String nicname, final String gateway, final String netmask, final String subnet) throws InternalErrorException { + final String nicname, final String gateway, final String netmask, final String subnet, boolean sourceNat) throws InternalErrorException { String args = ""; + String snatArgs = ""; if (add) { args += " -A "; + snatArgs += " -A "; } else { args += " -D "; + snatArgs += " -D "; } args += " -l "; @@ -887,6 +890,16 @@ public class VirtualRoutingResource implements Manager { if (result != null) { throw new InternalErrorException("KVM plugin \"vpc_ipassoc\" failed:"+result); } + if (sourceNat) { + snatArgs += " -l " + pubIP; + snatArgs += " -c " + nicname; + + result = routerProxy("vpc_privateGateway.sh", routerIP, snatArgs); + if (result != null) { + throw new InternalErrorException("KVM plugin \"vpc_privateGateway\" failed:"+result); + } + + } } private SetStaticRouteAnswer execute(SetStaticRouteCommand cmd) { diff --git a/core/src/com/cloud/storage/resource/StoragePoolResource.java b/core/src/com/cloud/storage/resource/StoragePoolResource.java index 5d352b0b651..fccfd0f5784 100644 --- a/core/src/com/cloud/storage/resource/StoragePoolResource.java +++ b/core/src/com/cloud/storage/resource/StoragePoolResource.java @@ -21,6 +21,8 @@ import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateCommand; +import com.cloud.agent.api.storage.CreateVolumeOVAAnswer; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; @@ -34,5 +36,7 @@ public interface StoragePoolResource { CopyVolumeAnswer execute(CopyVolumeCommand cmd); + CreateVolumeOVAAnswer execute(CreateVolumeOVACommand cmd); + CreateAnswer execute(CreateCommand cmd); } diff --git a/docs/en-US/elastic-ip.xml b/docs/en-US/elastic-ip.xml index b09d37d43e1..8ecbd75be70 100644 --- a/docs/en-US/elastic-ip.xml +++ b/docs/en-US/elastic-ip.xml @@ -21,16 +21,31 @@
About Elastic IP Elastic IP (EIP) addresses are the IP addresses that are associated with an account, and act - as static IP addresses. The account owner has complete control over the Elastic IP addresses - that belong to the account. You can allocate an Elastic IP to a VM of your choice from the EIP - pool of your account. Later if required you can reassign the IP address to a different VM. This - feature is extremely helpful during VM failure. Instead of replacing the VM which is down, the - IP address can be reassigned to a new VM in your account. Elastic IP service provides Static NAT - (1:1) service in an EIP-enabled basic zone. The default network offering, + as static IP addresses. The account owner has the complete control over the Elastic IP addresses + that belong to the account. As an account owner, you can allocate an Elastic IP to a VM of your + choice from the EIP pool of your account. Later if required you can reassign the IP address to a + different VM. This feature is extremely helpful during VM failure. Instead of replacing the VM + which is down, the IP address can be reassigned to a new VM in your account. + Similar to the public IP address, Elastic IP addresses are mapped to their associated + private IP addresses by using StaticNAT. The EIP service is equipped with StaticNAT (1:1) + service in an EIP-enabled basic zone. The default network offering, DefaultSharedNetscalerEIPandELBNetworkOffering, provides your network with EIP and ELB network - services if a NetScaler device is deployed in your zone. Similar to the public IP address, - Elastic IP addresses are also mapped to their associated private IP addresses by using Stactic - NAT. + services if a NetScaler device is deployed in your zone. Consider the following illustration for + more details. + + + + + + eip-ns-basiczone.png: Elastic IP in a NetScaler-enabled Basic Zone. + + + In the illustration, a NetScaler appliance is the default entry or exit point for the + &PRODUCT; instances, and firewall is the default entry or exit point for the rest of the data + center. Netscaler provides LB services and staticNAT service to the guest networks. The guest + traffic in the pods and the Management Server are on different subnets / VLANs. The policy-based + routing in the data center core switch sends the public traffic through the NetScaler, whereas + the rest of the data center goes through the firewall. The EIP work flow is as follows: @@ -48,7 +63,6 @@ supported by NetScaler, in which the source IP address is replaced in the packets generated by a VM in the private network with the public IP address. - This default public IP will be released in two cases: @@ -68,12 +82,12 @@ - However, for the deployments where public IPs are limited resources, you have the - flexibility to choose not to allocate a public IP by default. You can use the Associate Public - IP option to turn on or off the automatic public IP assignment in the EIP-enabled Basic zones. - If you turn off the automatic public IP assignment while creating a network offering, only a - private IP is assigned to a VM when the VM is deployed with that network offering. Later, the - user can acquire an IP for the VM and enable static NAT. + For the deployments where public IPs are limited resources, you have the flexibility to + choose not to allocate a public IP by default. You can use the Associate Public IP option to + turn on or off the automatic public IP assignment in the EIP-enabled Basic zones. If you turn + off the automatic public IP assignment while creating a network offering, only a private IP is + assigned to a VM when the VM is deployed with that network offering. Later, the user can acquire + an IP for the VM and enable static NAT. For more information on the Associate Public IP option, see . For more information on the Associate Public IP option, see the @@ -83,7 +97,6 @@ continue to get both public IP and private by default, irrespective of the network offering configuration. - New deployments which use the default shared network offering with EIP and ELB services to create a shared network in the Basic zone will continue allocating public IPs to each user VM. diff --git a/docs/en-US/images/eip-ns-basiczone.png b/docs/en-US/images/eip-ns-basiczone.png index 315ff55dab9..bc88570531a 100644 Binary files a/docs/en-US/images/eip-ns-basiczone.png and b/docs/en-US/images/eip-ns-basiczone.png differ diff --git a/patches/systemvm/debian/config/etc/dnsmasq.conf b/patches/systemvm/debian/config/etc/dnsmasq.conf.tmpl similarity index 100% rename from patches/systemvm/debian/config/etc/dnsmasq.conf rename to patches/systemvm/debian/config/etc/dnsmasq.conf.tmpl diff --git a/patches/systemvm/debian/config/etc/init.d/cloud-early-config b/patches/systemvm/debian/config/etc/init.d/cloud-early-config index b7bdb27aeee..a457f228653 100755 --- a/patches/systemvm/debian/config/etc/init.d/cloud-early-config +++ b/patches/systemvm/debian/config/etc/init.d/cloud-early-config @@ -442,6 +442,9 @@ setup_dnsmasq() { [ -z $DHCP_RANGE ] && [ $ETH0_IP ] && DHCP_RANGE=$ETH0_IP [ $ETH0_IP6 ] && DHCP_RANGE_IP6=$ETH0_IP6 [ -z $DOMAIN ] && DOMAIN="cloudnine.internal" + + #get the template + cp /etc/dnsmasq.conf.tmpl /etc/dnsmasq.conf if [ -n "$DOMAIN" ] then diff --git a/patches/systemvm/debian/config/opt/cloud/bin/vpc_privateGateway.sh b/patches/systemvm/debian/config/opt/cloud/bin/vpc_privateGateway.sh index a09d8f7e38b..3635e1cd44c 100755 --- a/patches/systemvm/debian/config/opt/cloud/bin/vpc_privateGateway.sh +++ b/patches/systemvm/debian/config/opt/cloud/bin/vpc_privateGateway.sh @@ -91,7 +91,7 @@ fi if [ "$Dflag" == "1" ] then - remove_sat $publicIp + remove_snat $publicIp unlock_exit $? $lock $locked fi diff --git a/patches/systemvm/debian/config/root/edithosts.sh b/patches/systemvm/debian/config/root/edithosts.sh index 1f98fbf96ae..8609da79efd 100755 --- a/patches/systemvm/debian/config/root/edithosts.sh +++ b/patches/systemvm/debian/config/root/edithosts.sh @@ -113,7 +113,8 @@ if [ $ipv6 ] then sed -i /$ipv6,/d $DHCP_HOSTS fi -sed -i /$host,/d $DHCP_HOSTS +# don't want to do this in the future, we can have same VM with multiple nics/entries +#sed -i /$host,/d $DHCP_HOSTS #put in the new entry 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 0064edf6a68..8fe8c88d0f8 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 @@ -1756,7 +1756,7 @@ ServerResource { String netmask = Long.toString(NetUtils.getCidrSize(ip.getVlanNetmask())); String subnet = NetUtils.getSubNet(ip.getPublicIp(), ip.getVlanNetmask()); _virtRouterResource.assignVpcIpToRouter(routerIP, ip.isAdd(), ip.getPublicIp(), - nicName, ip.getVlanGateway(), netmask, subnet); + nicName, ip.getVlanGateway(), netmask, subnet, ip.isSourceNat()); results[i++] = ip.getPublicIp() + " - success"; } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java index ee1b3245126..55bb1e98366 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -38,6 +38,8 @@ import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.UnregisterVMCommand; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; @@ -282,10 +284,18 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { cmd instanceof CreatePrivateTemplateFromVolumeCommand || cmd instanceof CreatePrivateTemplateFromSnapshotCommand || cmd instanceof CopyVolumeCommand || + cmd instanceof CreateVolumeOVACommand || + cmd instanceof PrepareOVAPackingCommand || cmd instanceof CreateVolumeFromSnapshotCommand) { needDelegation = true; } + /* Fang: remove this before checking in */ + // needDelegation = false; + if (cmd instanceof PrepareOVAPackingCommand || + cmd instanceof CreateVolumeOVACommand ) { + cmd.setContextParam("hypervisor", HypervisorType.VMware.toString()); + } if(needDelegation) { HostVO host = _hostDao.findById(hostId); assert(host != null); @@ -311,6 +321,8 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru { cmd instanceof CreatePrivateTemplateFromVolumeCommand || cmd instanceof CreatePrivateTemplateFromSnapshotCommand || cmd instanceof CopyVolumeCommand || + cmd instanceof CreateVolumeOVACommand || + cmd instanceof PrepareOVAPackingCommand || cmd instanceof CreateVolumeFromSnapshotCommand) { String workerName = _vmwareMgr.composeWorkerName(); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java index a2e517d1fdb..8c0603e29e7 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java @@ -25,6 +25,8 @@ import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.DeleteVMSnapshotCommand; import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; public interface VmwareStorageManager { @@ -33,6 +35,8 @@ public interface VmwareStorageManager { Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd); Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd); Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd); + Answer execute(VmwareHostService hostService, CreateVolumeOVACommand cmd); + Answer execute(VmwareHostService hostService, PrepareOVAPackingCommand cmd); Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd); Answer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd); Answer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd); diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java index 1f116455761..9f1351e96f3 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java @@ -18,12 +18,14 @@ package com.cloud.hypervisor.vmware.manager; import java.io.BufferedWriter; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStreamWriter; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Properties; import java.util.Map; import java.util.UUID; @@ -44,6 +46,10 @@ import com.cloud.agent.api.RevertToVMSnapshotAnswer; import com.cloud.agent.api.RevertToVMSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.PrepareOVAPackingAnswer; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; +import com.cloud.agent.api.storage.CreateVolumeOVAAnswer; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; @@ -102,6 +108,109 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { _timeout = NumbersUtil.parseInt(value, 1440) * 1000; } + //Fang note: use Answer here instead of the PrepareOVAPackingAnswer + @Override + public Answer execute(VmwareHostService hostService, PrepareOVAPackingCommand cmd) { + String secStorageUrl = ((PrepareOVAPackingCommand) cmd).getSecondaryStorageUrl(); + assert (secStorageUrl != null); + String installPath = cmd.getTemplatePath(); + String details = null; + boolean success = false; + String ovafileName = ""; + s_logger.info("Fang: execute OVAPacking cmd at vmwareMngImpl. "); + String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl); + // String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId); + String installFullPath = secondaryMountPoint + "/" + installPath; + + String templateName = installFullPath; // should be a file ending .ova; + s_logger.info("Fang: execute vmwareMgrImpl: templateNAme " + templateName); + // Fang: Dir list, if there is ova file, done; Fang: add answer cmd; + // if not, from ova.meta, create a new OVA file; + // change the install path to *.ova , not ova.meta; + // VmwareContext context = hostService.getServiceContext(cmd); //Fang: we may not have the CTX here + try { + if (templateName.endsWith(".ova")) { + if(new File(templateName).exists()) { + details = "OVA files exists. succeed. "; + return new Answer(cmd, true, details); + } else { + if (new File(templateName + ".meta").exists()) { //Fang parse the meta file + //execute the tar command; + s_logger.info("Fang: execute vmwareMgrImpl: getfromMeta " + templateName); + ovafileName = getOVAFromMetafile(templateName + ".meta"); + details = "OVA file in meta file is " + ovafileName; + return new Answer(cmd, true, details); + } else { + String msg = "Unable to find ova meta or ova file to prepare template (vmware)"; + s_logger.error(msg); + throw new Exception(msg); + } + } + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + //hostService.invalidateServiceContext(context); do not need context + s_logger.error("Unable to connect to remote service "); + details = "Unable to connect to remote service "; + return new Answer(cmd, false, details); + } + String msg = "Unable to execute PrepareOVAPackingCommand due to exception"; + s_logger.error(msg, e); + return new Answer(cmd, false, details); + } + return new Answer(cmd, true, details); + } + + //Fang: new command added; + // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature) + @Override + public Answer execute(VmwareHostService hostService, CreateVolumeOVACommand cmd) { + String secStorageUrl = ((CreateVolumeOVACommand) cmd).getSecondaryStorageUrl(); + assert (secStorageUrl != null); + String installPath = cmd.getVolPath(); + String details = null; + boolean success = false; + + s_logger.info("volss: execute CreateVolumeOVA cmd at vmwareMngImpl. "); + String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl); + // String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId); + s_logger.info("volss: mountPoint: " + secondaryMountPoint + "installPath:" + installPath); + String installFullPath = secondaryMountPoint + "/" + installPath; + + String volName = cmd.getVolName(); // should be a UUID, without ova ovf, etc; + s_logger.info("volss: execute vmwareMgrImpl: VolName " + volName); + // Fang: Dir list, if there is ova file, done; Note: add answer cmd; + + try { + if(new File(volName + ".ova").exists()) { + details = "OVA files exists. succeed. "; + return new CreateVolumeOVAAnswer(cmd, true, details); + } else { + File ovaFile = new File(installFullPath); + String exportDir = ovaFile.getParent(); + + s_logger.info("Fang: exportDir is (for VolumeOVA): " + exportDir); + s_logger.info("Sync file system before we package OVA..."); + + Script commandSync = new Script(true, "sync", 0, s_logger); + commandSync.execute(); + + Script command = new Script(false, "tar", 0, s_logger); + command.setWorkDir(exportDir); + command.add("-cf", volName + ".ova"); + command.add(volName + ".ovf"); // OVF file should be the first file in OVA archive + command.add(volName + "-disk0.vmdk"); + + s_logger.info("Package Volume OVA with commmand: " + command.toString()); + command.execute(); + return new CreateVolumeOVAAnswer(cmd, true, details); + } + } catch (Throwable e) { + s_logger.info("Exception for createVolumeOVA"); + } + return new CreateVolumeOVAAnswer(cmd, true, "fail to pack OVA for volume"); + } + @Override public Answer execute(VmwareHostService hostService, PrimaryStorageDownloadCommand cmd) { String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); @@ -570,11 +679,14 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl); String installPath = getTemplateRelativeDirInSecStorage(accountId, templateId); String installFullPath = secondaryMountPoint + "/" + installPath; - String installFullName = installFullPath + "/" + templateUniqueName + ".ova"; - String snapshotFullName = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId) - + "/" + backedUpSnapshotUuid + ".ova"; + String installFullOVAName = installFullPath + "/" + templateUniqueName + ".ova"; //Note: volss for tmpl + String snapshotRoot = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId); + String snapshotFullOVAName = snapshotRoot + "/" + backedUpSnapshotUuid + ".ova"; + String snapshotFullOvfName = snapshotRoot + "/" + backedUpSnapshotUuid + ".ovf"; String result; Script command; + String templateVMDKName = ""; + String snapshotFullVMDKName = snapshotRoot + "/"; synchronized(installPath.intern()) { command = new Script(false, "mkdir", _timeout, s_logger); @@ -591,40 +703,85 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } try { - command = new Script(false, "cp", _timeout, s_logger); - command.add(snapshotFullName); - command.add(installFullName); - result = command.execute(); - if(result != null) { - String msg = "unable to copy snapshot " + snapshotFullName + " to " + installFullPath; + if(new File(snapshotFullOVAName).exists()) { + command = new Script(false, "cp", _timeout, s_logger); + command.add(snapshotFullOVAName); + command.add(installFullOVAName); + result = command.execute(); + if(result != null) { + String msg = "unable to copy snapshot " + snapshotFullOVAName + " to " + installFullPath; + s_logger.error(msg); + throw new Exception(msg); + } + + // untar OVA file at template directory + command = new Script("tar", 0, s_logger); + command.add("--no-same-owner"); + command.add("-xf", installFullOVAName); + command.setWorkDir(installFullPath); + s_logger.info("Executing command: " + command.toString()); + result = command.execute(); + if(result != null) { + String msg = "unable to untar snapshot " + snapshotFullOVAName + " to " + + installFullPath; + s_logger.error(msg); + throw new Exception(msg); + } + + } else { // there is no ova file, only ovf originally; + if(new File(snapshotFullOvfName).exists()) { + command = new Script(false, "cp", _timeout, s_logger); + command.add(snapshotFullOvfName); + //command.add(installFullOvfName); + command.add(installFullPath); + result = command.execute(); + if(result != null) { + String msg = "unable to copy snapshot " + snapshotFullOvfName + " to " + installFullPath; + s_logger.error(msg); + throw new Exception(msg); + } + + File snapshotdir = new File(snapshotRoot); + File[] ssfiles = snapshotdir.listFiles(); + // List filenames = new ArrayList(); + for (int i = 0; i < ssfiles.length; i++) { + String vmdkfile = ssfiles[i].getName(); + if(vmdkfile.toLowerCase().startsWith(backedUpSnapshotUuid) && vmdkfile.toLowerCase().endsWith(".vmdk")) { + snapshotFullVMDKName += vmdkfile; + templateVMDKName += vmdkfile; + break; + } + } + if (snapshotFullVMDKName != null) { + command = new Script(false, "cp", _timeout, s_logger); + command.add(snapshotFullVMDKName); + command.add(installFullPath); + result = command.execute(); + s_logger.info("Copy VMDK file: " + snapshotFullVMDKName); + if(result != null) { + String msg = "unable to copy snapshot vmdk file " + snapshotFullVMDKName + " to " + installFullPath; + s_logger.error(msg); + throw new Exception(msg); + } + } + } else { + String msg = "unable to find any snapshot ova/ovf files" + snapshotFullOVAName + " to " + installFullPath; s_logger.error(msg); throw new Exception(msg); + } } - // untar OVA file at template directory - command = new Script("tar", 0, s_logger); - command.add("--no-same-owner"); - command.add("-xf", installFullName); - command.setWorkDir(installFullPath); - s_logger.info("Executing command: " + command.toString()); - result = command.execute(); - if(result != null) { - String msg = "unable to untar snapshot " + snapshotFullName + " to " - + installFullPath; - s_logger.error(msg); - throw new Exception(msg); - } - - long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); + long physicalSize = new File(installFullPath + "/" + templateVMDKName).length(); VmdkProcessor processor = new VmdkProcessor(); + // long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); Map params = new HashMap(); params.put(StorageLayer.InstanceConfigKey, _storage); processor.configure("VMDK Processor", params); long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); + writeMetaOvaForTemplate(installFullPath, backedUpSnapshotUuid + ".ovf", templateVMDKName, templateUniqueName, physicalSize); return new Ternary(installPath + "/" + templateUniqueName + ".ova", physicalSize, virtualSize); - } catch(Exception e) { // TODO, clean up left over files throw e; @@ -648,7 +805,8 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { out.newLine(); out.write("size=" + size); out.newLine(); - out.write("ova=true"); + //out.write("ova=true"); + out.write("ova=false"); //volss: the real ova file is not created out.newLine(); out.write("id=" + templateId); out.newLine(); @@ -670,6 +828,31 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } } + private void writeMetaOvaForTemplate(String installFullPath, String ovfFilename, String vmdkFilename, + String templateName, long diskSize) throws Exception { + + // TODO a bit ugly here + BufferedWriter out = null; + try { + out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(installFullPath + "/" + templateName +".ova.meta"))); + out.write("ova.filename=" + templateName + ".ova"); + out.newLine(); + out.write("version=1.0"); + out.newLine(); + out.write("ovf=" + ovfFilename); + out.newLine(); + out.write("numDisks=1"); + out.newLine(); + out.write("disk1.name=" + vmdkFilename); + out.newLine(); + out.write("disk1.size=" + diskSize); + out.newLine(); + } finally { + if(out != null) + out.close(); + } + } + private String createVolumeFromSnapshot(VmwareHypervisorHost hyperHost, DatastoreMO primaryDsMo, String newVolumeName, long accountId, long volumeId, String secStorageUrl, String snapshotBackupUuid) throws Exception { @@ -688,23 +871,35 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { if (backupName.contains("/")){ snapshotDir = backupName.split("/")[0]; } - String srcFileName = getOVFFilePath(srcOVAFileName); - if(srcFileName == null) { - Script command = new Script("tar", 0, s_logger); - command.add("--no-same-owner"); - command.add("-xf", srcOVAFileName); - command.setWorkDir(secondaryMountPoint + "/" + secStorageDir + "/" + snapshotDir); - s_logger.info("Executing command: " + command.toString()); - String result = command.execute(); - if(result != null) { - String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName; - s_logger.error(msg); - throw new Exception(msg); - } - } - srcFileName = getOVFFilePath(srcOVAFileName); - if(srcFileName == null) { + File ovafile = new File(srcOVAFileName); + String srcOVFFileName = secondaryMountPoint + "/" + secStorageDir + "/" + + backupName + ".ovf"; + File ovfFile = new File(srcOVFFileName); + // String srcFileName = getOVFFilePath(srcOVAFileName); + if (!ovfFile.exists()) { + srcOVFFileName = getOVFFilePath(srcOVAFileName); + if(srcOVFFileName == null && ovafile.exists() ) { // volss: ova file exists; o/w can't do tar + Script command = new Script("tar", 0, s_logger); + command.add("--no-same-owner"); + command.add("-xf", srcOVAFileName); + command.setWorkDir(secondaryMountPoint + "/" + secStorageDir + "/" + snapshotDir); + s_logger.info("Executing command: " + command.toString()); + String result = command.execute(); + if(result != null) { + String msg = "Unable to unpack snapshot OVA file at: " + srcOVAFileName; + s_logger.error(msg); + throw new Exception(msg); + } + } else { + String msg = "Unable to find snapshot OVA file at: " + srcOVAFileName; + s_logger.error(msg); + throw new Exception(msg); + } + + srcOVFFileName = getOVFFilePath(srcOVAFileName); + } + if(srcOVFFileName == null) { String msg = "Unable to locate OVF file in template package directory: " + srcOVAFileName; s_logger.error(msg); throw new Exception(msg); @@ -712,7 +907,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { VirtualMachineMO clonedVm = null; try { - hyperHost.importVmFromOVF(srcFileName, newVolumeName, primaryDsMo, "thin"); + hyperHost.importVmFromOVF(srcOVFFileName, newVolumeName, primaryDsMo, "thin"); clonedVm = hyperHost.findVmOnHyperHost(newVolumeName); if(clonedVm == null) throw new Exception("Unable to create container VM for volume creation"); @@ -774,7 +969,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { throw new Exception(msg); } - clonedVm.exportVm(exportPath, exportName, true, true); + clonedVm.exportVm(exportPath, exportName, false, false); //Note: volss: not to create ova. } finally { if(clonedVm != null) { clonedVm.detachAllDisks(); @@ -787,17 +982,31 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { String secondaryMountPoint = _mountService.getMountPoint(secStorageUrl); String snapshotMountRoot = secondaryMountPoint + "/" + getSnapshotRelativeDirInSecStorage(accountId, volumeId); - File file = new File(snapshotMountRoot + "/" + backupUuid + ".ova"); + File file = new File(snapshotMountRoot + "/" + backupUuid + ".ovf"); if(file.exists()) { - if(file.delete()) - return null; - - } else { - return "Backup file does not exist. backupUuid: " + backupUuid; - } - - return "Failed to delete snapshot backup file, backupUuid: " + backupUuid; - } + File snapshotdir = new File(snapshotMountRoot); + File[] ssfiles = snapshotdir.listFiles(); + // List filenames = new ArrayList(); + for (int i = 0; i < ssfiles.length; i++) { + String vmdkfile = ssfiles[i].getName(); + if(vmdkfile.toLowerCase().startsWith(backupUuid) && vmdkfile.toLowerCase().endsWith(".vmdk")) { + // filenames.add(vmdkfile); + new File(vmdkfile).delete(); + } + } + if(file.delete()) + return null; + } else { + File file1 = new File(snapshotMountRoot + "/" + backupUuid + ".ova"); + if(file1.exists()) { + if(file1.delete()) + return null; + } else { + return "Backup file does not exist. backupUuid: " + backupUuid; + } + } + return "Failed to delete snapshot backup file, backupUuid: " + backupUuid; + } private Pair copyVolumeToSecStorage(VmwareHostService hostService, VmwareHypervisorHost hyperHost, CopyVolumeCommand cmd, String vmName, long volumeId, String poolId, String volumePath, @@ -881,6 +1090,92 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return new Pair(volumeFolder, newVolume); } + //Fang: here I use a method to return the ovf and vmdk file names; Another way to do it: + // create a new class, and like TemplateLocation.java and create templateOvfInfo.java to handle it; + private String getOVAFromMetafile(String metafileName) throws Exception { + File ova_metafile = new File(metafileName); + Properties props = null; + FileInputStream strm = null; + String ovaFileName = ""; + s_logger.info("Fang: getOVAfromMetaFile: metafileName " + metafileName); + try { + strm = new FileInputStream(ova_metafile); + if (null == strm) { + String msg = "Cannot read ova meat file. Error"; + s_logger.error(msg); + throw new Exception(msg); + } + + s_logger.info("Fang: getOVAfromMetaFile: load strm " ); + if (null != ova_metafile) { + props = new Properties(); + props.load(strm); + if (props == null) { + s_logger.info("Fang: getOVAfromMetaFile: props is null. " ); + } + } + if (null != props) { + ovaFileName = props.getProperty("ova.filename"); + s_logger.info("Fang: ovafilename" + ovaFileName); + String ovfFileName = props.getProperty("ovf"); + s_logger.info("Fang: ovffilename" + ovfFileName); + int diskNum = Integer.parseInt(props.getProperty("numDisks")); + if (diskNum <= 0) { + String msg = "VMDK disk file number is 0. Error"; + s_logger.error(msg); + throw new Exception(msg); + } + String[] disks = new String[diskNum]; + for (int i = 0; i < diskNum; i++) { + //String diskNameKey = "disk" + Integer.toString(i+1) + ".name"; // Fang use this + String diskNameKey = "disk1.name"; + disks[i] = props.getProperty(diskNameKey); + s_logger.info("Fang: diskname " + disks[i]); + } + String exportDir = ova_metafile.getParent(); + s_logger.info("Fang: exportDir: " + exportDir); + // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature) + s_logger.info("Fang: Sync file system before we package OVA..., before tar "); + s_logger.info("Fang: ova: " + ovaFileName+ ", ovf:" + ovfFileName + ", vmdk:" + disks[0] + "."); + Script commandSync = new Script(true, "sync", 0, s_logger); + commandSync.execute(); + Script command = new Script(false, "tar", 0, s_logger); + command.setWorkDir(exportDir); //Fang: pass this in to the method? + command.add("-cf", ovaFileName); + command.add(ovfFileName); // OVF file should be the first file in OVA archive + for(String diskName: disks) { + command.add(diskName); + } + command.execute(); + s_logger.info("Fang: Package OVA for template in dir: " + exportDir + "cmd: " + command.toString()); + // to be safe, physically test existence of the target OVA file + if((new File(exportDir + ovaFileName)).exists()) { + s_logger.info("Fang: ova file is created and ready to extract "); + return (ovaFileName); + } else { + String msg = exportDir + File.separator + ovaFileName + ".ova is not created as expected"; + s_logger.error(msg); + throw new Exception(msg); + } + } else { + String msg = "Error reading the ova meta file: " + metafileName; + s_logger.error(msg); + throw new Exception(msg); + } + } catch (Exception e) { + return null; + //Do something, re-throw the exception + } finally { + if (strm != null) { + try { + strm.close(); + } catch (Exception e) { + } + } + } + + } + private String getOVFFilePath(String srcOVAFileName) { File file = new File(srcOVAFileName); assert(_storage != 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 99ad1ca60b2..030eff0148a 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 @@ -155,6 +155,10 @@ import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.routing.VpnUsersCfgCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; +import com.cloud.agent.api.storage.CreateVolumeOVAAnswer; +import com.cloud.agent.api.storage.PrepareOVAPackingAnswer; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; @@ -392,6 +396,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((DeleteStoragePoolCommand) cmd); } else if (clz == CopyVolumeCommand.class) { answer = execute((CopyVolumeCommand) cmd); + } else if (clz == CreateVolumeOVACommand.class) { + answer = execute((CreateVolumeOVACommand) cmd); + } else if (clz == PrepareOVAPackingCommand.class) { + answer = execute((PrepareOVAPackingCommand) cmd); } else if (clz == AttachVolumeCommand.class) { answer = execute((AttachVolumeCommand) cmd); } else if (clz == AttachIsoCommand.class) { @@ -1433,10 +1441,14 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } String args = ""; + String snatArgs = ""; + if (ip.isAdd()) { args += " -A "; + snatArgs += " -A "; } else { args += " -D "; + snatArgs += " -D "; } args += " -l "; @@ -1460,6 +1472,21 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (!result.first()) { throw new InternalErrorException("Unable to assign public IP address"); } + + if (ip.isSourceNat()) { + snatArgs += " -l "; + snatArgs += ip.getPublicIp(); + snatArgs += " -c "; + snatArgs += "eth" + ethDeviceNum; + + Pair result_gateway = SshHelper.sshExecute(routerIp, DEFAULT_DOMR_SSHPORT, "root", mgr.getSystemVMKeyFile(), null, + "/opt/cloud/bin/vpc_privateGateway.sh " + args); + + if (!result_gateway.first()) { + throw new InternalErrorException("Unable to configure source NAT for public IP address."); + } + + } } protected void assignPublicIpAddress(VirtualMachineMO vmMo, final String vmName, final String privateIpAddress, final String publicIpAddress, final boolean add, final boolean firstIP, @@ -3905,8 +3932,48 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + public CreateVolumeOVAAnswer execute(CreateVolumeOVACommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource CreateVolumeOVACommand: " + _gson.toJson(cmd)); + } + try { + VmwareContext context = getServiceContext(); + VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + return (CreateVolumeOVAAnswer) mgr.getStorageManager().execute(this, cmd); + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + invalidateServiceContext(); + } + String msg = "CreateVolumeOVACommand failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(msg, e); + return new CreateVolumeOVAAnswer(cmd, false, msg); + } + } + + protected Answer execute(PrepareOVAPackingCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource PrepareOVAPackingCommand: " + _gson.toJson(cmd)); + } + + try { + VmwareContext context = getServiceContext(); + VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + + return mgr.getStorageManager().execute(this, cmd); + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn("Encounter remote exception to vCenter, invalidate VMware session context"); + invalidateServiceContext(); + } + + String details = "PrepareOVAPacking for template failed due to " + VmwareHelper.getExceptionMessage(e); + s_logger.error(details, e); + return new PrepareOVAPackingAnswer(cmd, false, details); + } + } private boolean createVMFullClone(VirtualMachineMO vmTemplate, DatacenterMO dcMo, DatastoreMO dsMo, String vmdkName, ManagedObjectReference morDatastore, ManagedObjectReference morPool) throws Exception { diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java index ce42f67bf1d..95ba317fa2c 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageResourceHandler.java @@ -28,6 +28,9 @@ import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateVolumeOVAAnswer; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; import com.cloud.hypervisor.vmware.manager.VmwareHostService; import com.cloud.hypervisor.vmware.manager.VmwareStorageManager; @@ -76,6 +79,10 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe answer = execute((CreatePrivateTemplateFromSnapshotCommand)cmd); } else if(cmd instanceof CopyVolumeCommand) { answer = execute((CopyVolumeCommand)cmd); + } else if(cmd instanceof CreateVolumeOVACommand) { + answer = execute((CreateVolumeOVACommand)cmd); + } else if (cmd instanceof PrepareOVAPackingCommand) { + answer = execute((PrepareOVAPackingCommand)cmd); } else if(cmd instanceof CreateVolumeFromSnapshotCommand) { answer = execute((CreateVolumeFromSnapshotCommand)cmd); } else { @@ -138,6 +145,23 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe return _storageMgr.execute(this, cmd); } + private Answer execute(PrepareOVAPackingCommand cmd) { + s_logger.info("Fang: VmwareSecStorageResourceHandler: exec cmd. cmd is " + cmd.toString()); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Executing resource PrepareOVAPackingCommand: " + _gson.toJson(cmd)); + } + + return _storageMgr.execute(this, cmd); + } + + private CreateVolumeOVAAnswer execute(CreateVolumeOVACommand cmd) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Executing resource CreateVolumeOVACommand: " + _gson.toJson(cmd)); + } + + return (CreateVolumeOVAAnswer) _storageMgr.execute(this, cmd); + } + private Answer execute(CreateVolumeFromSnapshotCommand cmd) { if (s_logger.isDebugEnabled()) { s_logger.debug("Executing resource CreateVolumeFromSnapshotCommand: " + _gson.toJson(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 46ae35a4a54..bac361d9133 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 @@ -2217,11 +2217,14 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } String args = "vpc_ipassoc.sh " + routerIp; + String snatArgs = "vpc_privateGateway.sh " + routerIp; if (ip.isAdd()) { args += " -A "; + snatArgs += " -A "; } else { args += " -D "; + snatArgs+= " -D "; } args += " -l "; @@ -2244,6 +2247,17 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (result == null || result.isEmpty()) { throw new InternalErrorException("Xen plugin \"vpc_ipassoc\" failed."); } + + if (ip.isSourceNat()) { + snatArgs += " -l " + ip.getPublicIp(); + snatArgs += " -c " + "eth" + correctVif.getDevice(conn); + + result = callHostPlugin(conn, "vmops", "routerProxy", "args", snatArgs); + if (result == null || result.isEmpty()) { + throw new InternalErrorException("Xen plugin \"vcp_privateGateway\" failed."); + } + } + } catch (Exception e) { String msg = "Unable to assign public IP address due to " + e.toString(); s_logger.warn(msg, e); diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java index 1242be6f989..cc1cd1bf697 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java +++ b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java @@ -35,6 +35,8 @@ import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.routing.GlobalLoadBalancerConfigCommand; +import com.cloud.agent.api.routing.HealthCheckLBConfigAnswer; +import com.cloud.agent.api.routing.HealthCheckLBConfigCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; @@ -714,7 +716,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl List destinations = rule.getDestinations(); if ((destinations != null && !destinations.isEmpty()) || rule.isAutoScaleConfig()) { - LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, false, false, destinations, rule.getStickinessPolicies()); + LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, false, false, destinations, rule.getStickinessPolicies(), rule.getHealthCheckPolicies()); if (rule.isAutoScaleConfig()) { loadBalancer.setAutoScaleVmGroup(rule.getAutoScaleVmGroup()); } @@ -840,11 +842,75 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl return null; } + public List getElasticLBRulesHealthCheck(Network network, List rules) + throws ResourceUnavailableException { + + HealthCheckLBConfigAnswer answer = null; + List loadBalancingRules = new ArrayList(); + for (FirewallRule rule : rules) { + if (rule.getPurpose().equals(Purpose.LoadBalancing)) { + loadBalancingRules.add((LoadBalancingRule) rule); + } + } + + if (loadBalancingRules == null || loadBalancingRules.isEmpty()) { + return null; + } + + String errMsg = null; + ExternalLoadBalancerDeviceVO lbDeviceVO = getExternalLoadBalancerForNetwork(network); + + if (lbDeviceVO == null) { + s_logger.warn("There is no external load balancer device assigned to this network either network is not implement are already shutdown so just returning"); + return null; + } + + if (!isNetscalerDevice(lbDeviceVO.getDeviceName())) { + errMsg = "There are no NetScaler load balancer assigned for this network. So NetScaler element can not be handle elastic load balancer rules."; + s_logger.error(errMsg); + throw new ResourceUnavailableException(errMsg, this.getClass(), 0); + } + + List loadBalancersToApply = new ArrayList(); + for (int i = 0; i < loadBalancingRules.size(); i++) { + LoadBalancingRule rule = loadBalancingRules.get(i); + boolean revoked = (rule.getState().equals(FirewallRule.State.Revoke)); + String protocol = rule.getProtocol(); + String algorithm = rule.getAlgorithm(); + String lbUuid = rule.getUuid(); + String srcIp = _networkMgr.getIp(rule.getSourceIpAddressId()).getAddress().addr(); + int srcPort = rule.getSourcePortStart(); + List destinations = rule.getDestinations(); + + if ((destinations != null && !destinations.isEmpty()) || rule.isAutoScaleConfig()) { + LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, + false, false, destinations, null, rule.getHealthCheckPolicies()); + loadBalancersToApply.add(loadBalancer); + } + } + + if (loadBalancersToApply.size() > 0) { + int numLoadBalancersForCommand = loadBalancersToApply.size(); + LoadBalancerTO[] loadBalancersForCommand = loadBalancersToApply + .toArray(new LoadBalancerTO[numLoadBalancersForCommand]); + HealthCheckLBConfigCommand cmd = new HealthCheckLBConfigCommand(loadBalancersForCommand); + HostVO externalLoadBalancer = _hostDao.findById(lbDeviceVO.getHostId()); + answer = (HealthCheckLBConfigAnswer) _agentMgr.easySend(externalLoadBalancer.getId(), cmd); + return answer.getLoadBalancers(); + } + return null; + } + public List updateHealthChecks(Network network, List lbrules) { if (canHandle(network, Service.Lb) && canHandleLbRules(lbrules)) { try { - return getLBHealthChecks(network, lbrules); + + if (isBasicZoneNetwok(network)) { + return getElasticLBRulesHealthCheck(network, lbrules); + } else { + return getLBHealthChecks(network, lbrules); + } } catch (ResourceUnavailableException e) { s_logger.error("Error in getting the LB Rules from NetScaler " + e); } diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java index 677bc785b2c..98e14618248 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java +++ b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java @@ -1618,7 +1618,9 @@ public class NetscalerResource implements ServerResource { String srcIp = rule.getSrcIp(); String dstIP = rule.getDstIp(); String iNatRuleName = generateInatRuleName(srcIp, dstIP); + String rNatRuleName = generateRnatRuleName(srcIp, dstIP); inat iNatRule = null; + rnat rnatRule = null; if (!rule.revoked()) { try { @@ -1645,9 +1647,47 @@ public class NetscalerResource implements ServerResource { } s_logger.debug("Created Inat rule on the Netscaler device " + _ip + " to enable static NAT from " + srcIp + " to " + dstIP); } + try { + rnat[] rnatRules = rnat.get(_netscalerService); + if (rnatRules != null) { + for (rnat rantrule : rnatRules) { + if (rantrule.get_network().equalsIgnoreCase(rNatRuleName)) { + rnatRule = rantrule; + break; + } + } + } + } catch (nitro_exception e) { + throw e; + } + + if (rnatRule == null) { + rnatRule = new rnat(); + rnatRule.set_natip(srcIp); + rnatRule.set_network(dstIP); + rnatRule.set_netmask("255.255.255.255"); + try { + apiCallResult = rnat.update(_netscalerService, rnatRule); + } catch (nitro_exception e) { + if (e.getErrorCode() != NitroError.NS_RESOURCE_EXISTS) { + throw e; + } + } + s_logger.debug("Created Rnat rule on the Netscaler device " + _ip + " to enable revese static NAT from " + dstIP + " to " + srcIp); + } } else { try { inat.delete(_netscalerService, iNatRuleName); + rnat[] rnatRules = rnat.get(_netscalerService); + if (rnatRules != null) { + for (rnat rantrule : rnatRules) { + if (rantrule.get_network().equalsIgnoreCase(dstIP)) { + rnatRule = rantrule; + rnat.clear(_netscalerService, rnatRule); + break; + } + } + } } catch (nitro_exception e) { if (e.getErrorCode() != NitroError.NS_RESOURCE_NOT_EXISTS) { throw e; @@ -2257,6 +2297,7 @@ public class NetscalerResource implements ServerResource { } csMon.set_interval(hcp.getHealthcheckInterval()); + csMon.set_retries(Math.max(hcp.getHealthcheckThresshold(), hcp.getUnhealthThresshold()) + 1); csMon.set_resptimeout(hcp.getResponseTime()); csMon.set_failureretries(hcp.getUnhealthThresshold()); csMon.set_successretries(hcp.getHealthcheckThresshold()); @@ -3090,6 +3131,10 @@ public class NetscalerResource implements ServerResource { return genObjectName("Cloud-Inat", srcIp); } + private String generateRnatRuleName(String srcIp, String dstIP) { + return genObjectName("Cloud-Rnat", srcIp); + } + private String generateNSVirtualServerName(String srcIp, long srcPort) { return genObjectName("Cloud-VirtualServer", srcIp, srcPort); } diff --git a/pom.xml b/pom.xml index 9526d92c112..57073e3b19a 100644 --- a/pom.xml +++ b/pom.xml @@ -399,7 +399,7 @@ patches/systemvm/debian/config/etc/apache2/sites-available/default patches/systemvm/debian/config/etc/apache2/sites-available/default-ssl patches/systemvm/debian/config/etc/apache2/vhostexample.conf - patches/systemvm/debian/config/etc/dnsmasq.conf + patches/systemvm/debian/config/etc/dnsmasq.conf.tmpl patches/systemvm/debian/config/etc/vpcdnsmasq.conf patches/systemvm/debian/config/etc/ssh/sshd_config patches/systemvm/debian/config/etc/rsyslog.conf diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index f88f7c824d9..af87bbb274c 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -3145,6 +3145,7 @@ public class ApiResponseHelper implements ResponseGenerator { populateAccount(response, result.getAccountId()); populateDomain(response, result.getDomainId()); response.setState(result.getState().toString()); + response.setSourceNat(result.getSourceNat()); response.setObjectName("privategateway"); diff --git a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java index 2dbc4e3a123..f93bf7ae9b5 100644 --- a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java @@ -270,7 +270,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase lbDeviceVO = new ExternalLoadBalancerDeviceVO(host.getId(), pNetwork.getId(), ntwkDevice.getNetworkServiceProvder(), deviceName, capacity, dedicatedUse, gslbProvider); if (gslbProvider) { - lbDeviceVO.setGslbSitePrivateIP(gslbSitePublicIp); + lbDeviceVO.setGslbSitePublicIP(gslbSitePublicIp); lbDeviceVO.setGslbSitePrivateIP(gslbSitePrivateIp); } _externalLoadBalancerDeviceDao.persist(lbDeviceVO); diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index c44ff8e9239..01fa2522638 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -930,9 +930,9 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { } } if (isUserVmsDefaultNetwork || isDomRGuestOrPublicNetwork) { - return _configMgr.getServiceOfferingNetworkRate(vm.getServiceOfferingId(), vm.getDataCenterId()); + return _configMgr.getServiceOfferingNetworkRate(vm.getServiceOfferingId(), network.getDataCenterId()); } else { - return _configMgr.getNetworkOfferingNetworkRate(ntwkOff.getId(), vm.getDataCenterId()); + return _configMgr.getNetworkOfferingNetworkRate(ntwkOff.getId(), network.getDataCenterId()); } } @@ -1669,20 +1669,17 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { String[] cidr = network.getCidr().split("/"); List ips = getUsedIpsInNetwork(network); Set usedIps = new TreeSet(); - + for (String ip : ips) { if (requestedIp != null && requestedIp.equals(ip)) { s_logger.warn("Requested ip address " + requestedIp + " is already in use in network" + network); return null; } - + usedIps.add(NetUtils.ip2Long(ip)); } - - Set allPossibleIps = NetUtils.getAllIpsFromCidr(cidr[0], Integer.parseInt(cidr[1])); - if (usedIps.size() != 0) { - allPossibleIps.removeAll(usedIps); - } + + Set allPossibleIps = NetUtils.getAllIpsFromCidr(cidr[0], Integer.parseInt(cidr[1]), usedIps); String gateway = network.getGateway(); if ((gateway != null) && (allPossibleIps.contains(NetUtils.ip2Long(gateway)))) diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 8c389043834..85a80867e99 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.UUID; import javax.ejb.Local; @@ -490,17 +491,19 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } // if shared network in the advanced zone, then check the caller against the network for 'AccessType.UseNetwork' - if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId()) && zone.getNetworkType() == NetworkType.Advanced) { - Account caller = UserContext.current().getCaller(); - long callerUserId = UserContext.current().getCallerUserId(); - _accountMgr.checkAccess(caller, AccessType.UseNetwork, false, network); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Associate IP address called by the user " + callerUserId + " account " + ipOwner.getId()); + if (zone.getNetworkType() == NetworkType.Advanced) { + if (isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { + Account caller = UserContext.current().getCaller(); + long callerUserId = UserContext.current().getCallerUserId(); + _accountMgr.checkAccess(caller, AccessType.UseNetwork, false, network); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Associate IP address called by the user " + callerUserId + " account " + ipOwner.getId()); + } + return _networkMgr.allocateIp(ipOwner, false, caller, callerUserId, zone); + } else { + throw new InvalidParameterValueException("Associate IP address can only be called on the shared networks in the advanced zone" + + " with Firewall/Source Nat/Static Nat/Port Forwarding/Load balancing services enabled"); } - return _networkMgr.allocateIp(ipOwner, false, caller, callerUserId, zone); - } else { - throw new InvalidParameterValueException("Associate IP address can only be called on the shared networks in the advanced zone" + - " with Firewall/Source Nat/Static Nat/Port Forwarding/Load balancing services enabled"); } } } @@ -2128,6 +2131,29 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return getNetwork(network.getId()); } + + protected Set getAvailableIps(Network network, String requestedIp) { + String[] cidr = network.getCidr().split("/"); + List ips = _nicDao.listIpAddressInNetwork(network.getId()); + Set usedIps = new TreeSet(); + + for (String ip : ips) { + if (requestedIp != null && requestedIp.equals(ip)) { + s_logger.warn("Requested ip address " + requestedIp + " is already in use in network" + network); + return null; + } + + usedIps.add(NetUtils.ip2Long(ip)); + } + Set allPossibleIps = NetUtils.getAllIpsFromCidr(cidr[0], Integer.parseInt(cidr[1]), usedIps); + + String gateway = network.getGateway(); + if ((gateway != null) && (allPossibleIps.contains(NetUtils.ip2Long(gateway)))) + allPossibleIps.remove(NetUtils.ip2Long(gateway)); + + return allPossibleIps; + } + protected boolean canUpgrade(Network network, long oldNetworkOfferingId, long newNetworkOfferingId) { NetworkOffering oldNetworkOffering = _networkOfferingDao.findByIdIncludingRemoved(oldNetworkOfferingId); @@ -3379,8 +3405,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @DB - public Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, - String vlan, String startIp, String endIp, String gateway, String netmask, long networkOwnerId, Long vpcId) + public Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, + String vlan, String startIp, String endIp, String gateway, String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { Account owner = _accountMgr.getAccount(networkOwnerId); @@ -3448,7 +3474,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Long nextMac = mac + 1; dc.setMacAddress(nextMac); - privateIp = new PrivateIpVO(startIp, privateNetwork.getId(), nextMac, vpcId); + privateIp = new PrivateIpVO(startIp, privateNetwork.getId(), nextMac, vpcId, sourceNat); _privateIpDao.persist(privateIp); _dcDao.update(dc.getId(), dc); diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index 6977e80ed0f..611100955e7 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -1178,8 +1178,8 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian for (final PrivateIpAddress ipAddr : ipAddrList) { Network network = _networkModel.getNetwork(ipAddr.getNetworkId()); - IpAddressTO ip = new IpAddressTO(Account.ACCOUNT_ID_SYSTEM, ipAddr.getIpAddress(), add, false, - false, ipAddr.getVlanTag(), ipAddr.getGateway(), ipAddr.getNetmask(), ipAddr.getMacAddress(), + IpAddressTO ip = new IpAddressTO(Account.ACCOUNT_ID_SYSTEM, ipAddr.getIpAddress(), add, false, + ipAddr.getSourceNat(), ipAddr.getVlanTag(), ipAddr.getGateway(), ipAddr.getNetmask(), ipAddr.getMacAddress(), null, false); ip.setTrafficType(network.getTrafficType()); diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 8636d8503a3..23556354e3a 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -80,12 +80,14 @@ import com.cloud.utils.net.Ip; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.UserVmVO; +import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicSecondaryIpDao; import com.cloud.vm.dao.NicSecondaryIpVO; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; @Component @Local(value = { RulesManager.class, RulesService.class }) @@ -103,6 +105,8 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules @Inject UserVmDao _vmDao; @Inject + VMInstanceDao _vmInstanceDao; + @Inject AccountManager _accountMgr; @Inject NetworkManager _networkMgr; @@ -416,7 +420,12 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules @Override @ActionEvent(eventType = EventTypes.EVENT_ENABLE_STATIC_NAT, eventDescription = "enabling static nat") - public boolean enableStaticNat(long ipId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) + public boolean enableStaticNat(long ipId, long vmId, long networkId, String vmGuestIp) + throws NetworkRuleConflictException, ResourceUnavailableException { + return enableStaticNat(ipId, vmId, networkId, false, vmGuestIp); + } + + private boolean enableStaticNat(long ipId, long vmId, long networkId, boolean isSystemVm, String vmGuestIp) throws NetworkRuleConflictException, ResourceUnavailableException { UserContext ctx = UserContext.current(); Account caller = ctx.getCaller(); @@ -1370,7 +1379,7 @@ public class RulesManagerImpl extends ManagerBase implements RulesManager, Rules throw new CloudRuntimeException("Ip address is not associated with any network"); } - UserVmVO vm = _vmDao.findById(sourceIp.getAssociatedWithVmId()); + VMInstanceVO vm = _vmInstanceDao.findById(sourceIp.getAssociatedWithVmId()); Network network = _networkModel.getNetwork(networkId); if (network == null) { CloudRuntimeException ex = new CloudRuntimeException("Unable to find an ip address to map to specified vm id"); diff --git a/server/src/com/cloud/network/vpc/PrivateGatewayProfile.java b/server/src/com/cloud/network/vpc/PrivateGatewayProfile.java index 2595a6a0fa4..20947db0447 100644 --- a/server/src/com/cloud/network/vpc/PrivateGatewayProfile.java +++ b/server/src/com/cloud/network/vpc/PrivateGatewayProfile.java @@ -100,4 +100,9 @@ public class PrivateGatewayProfile implements PrivateGateway { public State getState() { return vpcGateway.getState(); } + + @Override + public boolean getSourceNat() { + return vpcGateway.getSourceNat(); + } } diff --git a/server/src/com/cloud/network/vpc/PrivateIpAddress.java b/server/src/com/cloud/network/vpc/PrivateIpAddress.java index 826bea22e25..2f3cf536e81 100644 --- a/server/src/com/cloud/network/vpc/PrivateIpAddress.java +++ b/server/src/com/cloud/network/vpc/PrivateIpAddress.java @@ -25,6 +25,7 @@ public class PrivateIpAddress implements PrivateIp{ String ipAddress; String macAddress; long networkId; + boolean sourceNat; /** * @param privateIp @@ -42,6 +43,7 @@ public class PrivateIpAddress implements PrivateIp{ this.netmask = netmask; this.macAddress = macAddress; this.networkId = privateIp.getNetworkId(); + this.sourceNat = privateIp.getSourceNat(); } @Override @@ -73,4 +75,9 @@ public class PrivateIpAddress implements PrivateIp{ public long getNetworkId() { return networkId; } + + @Override + public boolean getSourceNat() { + return sourceNat; + } } diff --git a/server/src/com/cloud/network/vpc/PrivateIpVO.java b/server/src/com/cloud/network/vpc/PrivateIpVO.java index e6616ae3899..952a0c2cf43 100644 --- a/server/src/com/cloud/network/vpc/PrivateIpVO.java +++ b/server/src/com/cloud/network/vpc/PrivateIpVO.java @@ -54,15 +54,19 @@ public class PrivateIpVO implements InternalIdentity { @Column(name="vpc_id") private Long vpcId; + + @Column(name="source_nat") + private boolean sourceNat; public PrivateIpVO() { - } + } - public PrivateIpVO(String ipAddress, long networkId, long macAddress, long vpcId) { + public PrivateIpVO(String ipAddress, long networkId, long macAddress, long vpcId, boolean sourceNat) { this.ipAddress = ipAddress; this.networkId = networkId; this.macAddress = macAddress; this.vpcId = vpcId; + this.sourceNat = sourceNat; } public void setTakenAt(Date takenDate) { @@ -92,4 +96,8 @@ public class PrivateIpVO implements InternalIdentity { public Long getVpcId() { return vpcId; } + public boolean getSourceNat() { + return sourceNat; + } + } diff --git a/server/src/com/cloud/network/vpc/VpcGatewayVO.java b/server/src/com/cloud/network/vpc/VpcGatewayVO.java index 718e4df82df..e8dcb46b211 100644 --- a/server/src/com/cloud/network/vpc/VpcGatewayVO.java +++ b/server/src/com/cloud/network/vpc/VpcGatewayVO.java @@ -29,7 +29,6 @@ import javax.persistence.Id; import javax.persistence.Table; import com.cloud.utils.db.GenericDao; -import org.apache.cloudstack.api.InternalIdentity; @Entity @@ -84,7 +83,10 @@ public class VpcGatewayVO implements VpcGateway { @Column(name="state") @Enumerated(value=EnumType.STRING) State state; - + + @Column(name="source_nat") + boolean sourceNat; + protected VpcGatewayVO(){ this.uuid = UUID.randomUUID().toString(); } @@ -101,9 +103,10 @@ public class VpcGatewayVO implements VpcGateway { * @param accountId TODO * @param domainId TODO * @param account_id + * @param sourceNat */ public VpcGatewayVO(String ip4Address, Type type, Long vpcId, long zoneId, Long networkId, String vlanTag, - String gateway, String netmask, long accountId, long domainId) { + String gateway, String netmask, long accountId, long domainId, boolean sourceNat) { this.ip4Address = ip4Address; this.type = type; this.vpcId = vpcId; @@ -116,6 +119,7 @@ public class VpcGatewayVO implements VpcGateway { this.accountId = accountId; this.domainId = domainId; this.state = State.Creating; + this.sourceNat = sourceNat; } @Override @@ -193,4 +197,10 @@ public class VpcGatewayVO implements VpcGateway { public void setState(State state) { this.state = state; } + + @Override + public boolean getSourceNat() { + return this.sourceNat; + } + } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 84a1ae956d2..1ccefe66915 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -1288,8 +1288,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_CREATE, eventDescription = "creating vpc private gateway", create=true) - public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, - String gateway, String netmask, long gatewayOwnerId) throws ResourceAllocationException, + public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, + String gateway, String netmask, long gatewayOwnerId, Boolean isSourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { //Validate parameters @@ -1315,11 +1315,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis //1) create private network String networkName = "vpc-" + vpc.getName() + "-privateNetwork"; Network privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkId, - vlan, ipAddress, null, gateway, netmask, gatewayOwnerId, vpcId); + vlan, ipAddress, null, gateway, netmask, gatewayOwnerId, vpcId, isSourceNat); //2) create gateway entry VpcGatewayVO gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), - privateNtwk.getId(), vlan, gateway, netmask, vpc.getAccountId(), vpc.getDomainId()); + privateNtwk.getId(), vlan, gateway, netmask, vpc.getAccountId(), vpc.getDomainId(), isSourceNat); _vpcGatewayDao.persist(gatewayVO); s_logger.debug("Created vpc gateway entry " + gatewayVO); diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index e8805ae8910..5bb770871ca 100755 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -582,7 +582,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } //Convert max storage size from GiB to bytes - if (resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage) { + if ((resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage) && max >= 0) { max = max * ResourceType.bytesToGiB; } diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 07f86c8b8c5..675cf26b53e 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -358,7 +358,6 @@ import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd; import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; -import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd; @@ -409,6 +408,8 @@ import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; +import com.cloud.agent.api.storage.CreateVolumeOVAAnswer; +import com.cloud.agent.api.storage.CreateVolumeOVACommand; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.Alert; import com.cloud.alert.AlertManager; @@ -575,6 +576,81 @@ import com.cloud.vm.dao.VMInstanceDao; import edu.emory.mathcs.backport.java.util.Arrays; import edu.emory.mathcs.backport.java.util.Collections; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd; +import org.apache.cloudstack.api.command.admin.autoscale.DeleteCounterCmd; +import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd; +import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd; +import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; +import org.apache.cloudstack.api.command.admin.cluster.UpdateClusterCmd; +import org.apache.cloudstack.api.command.admin.config.ListCfgsByCmd; +import org.apache.cloudstack.api.command.admin.config.ListHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd; +import org.apache.cloudstack.api.command.admin.config.UpdateHypervisorCapabilitiesCmd; +import org.apache.cloudstack.api.command.admin.ldap.LDAPConfigCmd; +import org.apache.cloudstack.api.command.admin.ldap.LDAPRemoveCmd; +import org.apache.cloudstack.api.command.admin.pod.CreatePodCmd; +import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; +import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; +import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; +import org.apache.cloudstack.api.command.admin.region.AddRegionCmd; +import org.apache.cloudstack.api.command.admin.region.RemoveRegionCmd; +import org.apache.cloudstack.api.command.admin.region.UpdateRegionCmd; +import org.apache.cloudstack.api.command.admin.swift.AddSwiftCmd; +import org.apache.cloudstack.api.command.admin.swift.ListSwiftsCmd; +import org.apache.cloudstack.api.command.admin.template.PrepareTemplateCmd; +import org.apache.cloudstack.api.command.admin.vlan.CreateVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.DeleteVlanIpRangeCmd; +import org.apache.cloudstack.api.command.admin.vlan.ListVlanIpRangesCmd; +import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; +import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd; +import org.apache.cloudstack.api.command.admin.vm.MigrateVirtualMachineWithVolumeCmd; +import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; +import org.apache.cloudstack.api.command.admin.zone.CreateZoneCmd; +import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; +import org.apache.cloudstack.api.command.admin.zone.MarkDefaultZoneForAccountCmd; +import org.apache.cloudstack.api.command.admin.zone.UpdateZoneCmd; +import org.apache.cloudstack.api.command.user.account.AddAccountToProjectCmd; +import org.apache.cloudstack.api.command.user.account.DeleteAccountFromProjectCmd; +import org.apache.cloudstack.api.command.user.account.ListAccountsCmd; +import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd; +import org.apache.cloudstack.api.command.user.address.AssociateIPAddrCmd; +import org.apache.cloudstack.api.command.user.address.DisassociateIPAddrCmd; +import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd; +import org.apache.cloudstack.api.command.user.config.ListCapabilitiesCmd; +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.ListEventTypesCmd; +import org.apache.cloudstack.api.command.user.event.ListEventsCmd; +import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd; +import org.apache.cloudstack.api.command.user.guest.ListGuestOsCmd; +import org.apache.cloudstack.api.command.user.job.ListAsyncJobsCmd; +import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.api.command.user.offering.ListDiskOfferingsCmd; +import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.command.user.region.ListRegionsCmd; +import org.apache.cloudstack.api.command.user.region.ha.gslb.*; +import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; +import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; +import org.apache.cloudstack.api.command.user.tag.CreateTagsCmd; +import org.apache.cloudstack.api.command.user.tag.DeleteTagsCmd; +import org.apache.cloudstack.api.command.user.tag.ListTagsCmd; +import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; +import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; +import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd; +import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd; +import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; + public class ManagementServerImpl extends ManagerBase implements ManagementServer { public static final Logger s_logger = Logger.getLogger(ManagementServerImpl.class.getName()); @@ -2837,7 +2913,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListZonesByCmd.class); cmdList.add(ListVMSnapshotCmd.class); cmdList.add(CreateVMSnapshotCmd.class); - cmdList.add(RevertToSnapshotCmd.class); + cmdList.add(RevertToVMSnapshotCmd.class); cmdList.add(DeleteVMSnapshotCmd.class); cmdList.add(AddIpToVmNicCmd.class); cmdList.add(RemoveIpFromVmNicCmd.class); @@ -3404,7 +3480,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe List extractURLList = _uploadDao.listByTypeUploadStatus(volumeId, Upload.Type.VOLUME, UploadVO.Status.DOWNLOAD_URL_CREATED); if (extractMode == Upload.Mode.HTTP_DOWNLOAD && extractURLList.size() > 0) { - return extractURLList.get(0).getId(); // If download url already + return extractURLList.get(0).getId(); // If download url already Note: volss // exists then return } else { UploadVO uploadJob = _uploadMonitor.createNewUploadEntry(sserver.getId(), volumeId, UploadVO.Status.COPY_IN_PROGRESS, Upload.Type.VOLUME, @@ -3456,6 +3532,19 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } String volumeLocalPath = "volumes/" + volume.getId() + "/" + cvAnswer.getVolumePath() + "." + getFormatForPool(srcPool); + //Fang: volss, handle the ova special case; + if (getFormatForPool(srcPool) == "ova") { + CreateVolumeOVACommand cvOVACmd = new CreateVolumeOVACommand(secondaryStorageURL, volumeLocalPath, cvAnswer.getVolumePath(), srcPool, copyvolumewait); + CreateVolumeOVAAnswer OVAanswer = null; + + try { + cvOVACmd.setContextParam("hypervisor", HypervisorType.VMware.toString()); + OVAanswer = (CreateVolumeOVAAnswer) _storageMgr.sendToPool(srcPool, cvOVACmd); //Fang: for extract volume, create the ova file here; + + } catch (StorageUnavailableException e) { + s_logger.debug("Storage unavailable"); + } + } // Update the DB that volume is copied and volumePath uploadJob.setUploadState(UploadVO.Status.COPY_COMPLETE); uploadJob.setLastUpdated(new Date()); diff --git a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java index 71010719333..25ae933740d 100755 --- a/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -562,10 +562,15 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem sql = SELECT_TEMPLATE_HOST_REF; groupByClause = " GROUP BY t.id, h.data_center_id "; } - if (((templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.community)) ||(zoneType != null && zoneId != null)) { + if ((templateFilter == TemplateFilter.featured) || (templateFilter == TemplateFilter.community)) { dataCenterJoin = " INNER JOIN data_center dc on (h.data_center_id = dc.id)"; } - + + if (zoneType != null) { + dataCenterJoin = " INNER JOIN template_host_ref thr on (t.id = thr.template_id) INNER JOIN host h on (thr.host_id = h.id)"; + dataCenterJoin += " INNER JOIN data_center dc on (h.data_center_id = dc.id)"; + } + if (templateFilter == TemplateFilter.sharedexecutable || templateFilter == TemplateFilter.shared ){ lpjoin = " INNER JOIN launch_permission lp ON t.id = lp.template_id "; } @@ -783,13 +788,15 @@ public class VMTemplateDaoImpl extends GenericDaoBase implem sql += " AND h.data_center_id = " +zoneId; } }else if (zoneId != null){ - sql += " AND tzr.zone_id = " +zoneId+ " AND tzr.removed is null" ; - if (zoneType != null){ - sql += " AND dc.networktype = " + zoneType; - } + sql += " AND tzr.zone_id = " +zoneId+ " AND tzr.removed is null" ; }else{ sql += " AND tzr.removed is null "; } + + if (zoneType != null){ + sql += " AND dc.networktype = '" + zoneType + "'"; + } + if (!showDomr){ sql += " AND t.type != '" +Storage.TemplateType.SYSTEM.toString() + "'"; } diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java index 491900b44e6..322f32eacdf 100755 --- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java @@ -30,6 +30,9 @@ import javax.inject.Inject; import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; +import org.apache.cloudstack.api.command.user.template.ExtractTemplateCmd; +import com.cloud.agent.api.storage.PrepareOVAPackingCommand; +import com.cloud.agent.api.storage.PrepareOVAPackingAnswer; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; import org.apache.cloudstack.engine.subsystem.api.storage.CommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -64,6 +67,10 @@ import com.cloud.utils.UriUtils; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.exception.AgentUnavailableException; +import com.cloud.exception.OperationTimedoutException; + @Local(value=TemplateAdapter.class) public class HypervisorTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter { @@ -188,6 +195,77 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te return template; } + @Override + public TemplateProfile prepareExtractTemplate(ExtractTemplateCmd extractcmd) { + TemplateProfile profile = super.prepareExtractTemplate(extractcmd); + VMTemplateVO template = (VMTemplateVO)profile.getTemplate(); + Long zoneId = profile.getZoneId(); + Long templateId = template.getId(); + + if (template.getHypervisorType() == HypervisorType.VMware) { + PrepareOVAPackingCommand cmd = null; + String zoneName=""; + List secondaryStorageHosts; + if (!template.isCrossZones() && zoneId != null) { + DataCenterVO zone = _dcDao.findById(zoneId); + zoneName = zone.getName(); + secondaryStorageHosts = _ssvmMgr.listSecondaryStorageHostsInOneZone(zoneId); + + s_logger.debug("Attempting to mark template host refs for template: " + template.getName() + " as destroyed in zone: " + zoneName); + + // Make sure the template is downloaded to all the necessary secondary storage hosts + + for (HostVO secondaryStorageHost : secondaryStorageHosts) { + long hostId = secondaryStorageHost.getId(); + List templateHostVOs = _tmpltHostDao.listByHostTemplate(hostId, templateId); + for (VMTemplateHostVO templateHostVO : templateHostVOs) { + if (templateHostVO.getDownloadState() == Status.DOWNLOAD_IN_PROGRESS) { + String errorMsg = "Please specify a template that is not currently being downloaded."; + s_logger.debug("Template: " + template.getName() + " is currently being downloaded to secondary storage host: " + secondaryStorageHost.getName() + "."); + throw new CloudRuntimeException(errorMsg); + } + String installPath = templateHostVO.getInstallPath(); + if (installPath != null) { + HostVO ssvmhost = _ssvmMgr.pickSsvmHost(secondaryStorageHost); + if( ssvmhost == null ) { + s_logger.warn("prepareOVAPacking (hyervisorTemplateAdapter): There is no secondary storage VM for secondary storage host " + secondaryStorageHost.getName()); + throw new CloudRuntimeException("PrepareExtractTemplate: can't locate ssvm for SecStorage Host."); + } + //Answer answer = _agentMgr.sendToSecStorage(secondaryStorageHost, new PrepareOVAPackingCommand(secondaryStorageHost.getStorageUrl(), installPath)); + cmd = new PrepareOVAPackingCommand(secondaryStorageHost.getStorageUrl(), installPath); + + if (cmd == null) { + s_logger.debug("Fang: PrepareOVAPacking cmd can't created. cmd is null ."); + throw new CloudRuntimeException("PrepareExtractTemplate: can't create a new cmd to packing ova."); + } else { + cmd.setContextParam("hypervisor", HypervisorType.VMware.toString()); + } + Answer answer = null; + s_logger.debug("Fang: PrepareOVAPAcking cmd, before send out. cmd: " + cmd.toString()); + try { + answer = _agentMgr.send(ssvmhost.getId(), cmd); + } catch (AgentUnavailableException e) { + s_logger.warn("Unable to packOVA for template: id: " + templateId + ", name " + ssvmhost.getName(), e); + } catch (OperationTimedoutException e) { + s_logger.warn("Unable to packOVA for template timeout. template id: " + templateId); + e.printStackTrace(); + } + + if (answer == null || !answer.getResult()) { + s_logger.debug("Failed to create OVA for template " + templateHostVO + " due to " + ((answer == null) ? "answer is null" : answer.getDetails())); + throw new CloudRuntimeException("PrepareExtractTemplate: Failed to create OVA for template extraction. "); + } + } + } + } + } else { + s_logger.debug("Failed to create OVA for template " + template + " due to zone non-existing."); + throw new CloudRuntimeException("PrepareExtractTemplate: Failed to create OVA for template extraction. "); + } + } + return profile; + } + @Override @DB public boolean delete(TemplateProfile profile) { boolean success = true; diff --git a/server/src/com/cloud/template/TemplateAdapter.java b/server/src/com/cloud/template/TemplateAdapter.java index 1f8f491cb25..9a2d877926d 100755 --- a/server/src/com/cloud/template/TemplateAdapter.java +++ b/server/src/com/cloud/template/TemplateAdapter.java @@ -22,6 +22,7 @@ import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; +import org.apache.cloudstack.api.command.user.template.ExtractTemplateCmd; import com.cloud.exception.ResourceAllocationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -56,6 +57,8 @@ public interface TemplateAdapter extends Adapter { public TemplateProfile prepareDelete(DeleteIsoCmd cmd); + public TemplateProfile prepareExtractTemplate(ExtractTemplateCmd cmd); + public boolean delete(TemplateProfile profile); public TemplateProfile prepare(boolean isIso, Long userId, String name, String displayText, Integer bits, diff --git a/server/src/com/cloud/template/TemplateAdapterBase.java b/server/src/com/cloud/template/TemplateAdapterBase.java index d3fd165121e..0940d3e2af1 100755 --- a/server/src/com/cloud/template/TemplateAdapterBase.java +++ b/server/src/com/cloud/template/TemplateAdapterBase.java @@ -26,6 +26,7 @@ import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; +import org.apache.cloudstack.api.command.user.template.ExtractTemplateCmd; 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.DataStoreRole; @@ -340,6 +341,18 @@ public abstract class TemplateAdapterBase extends AdapterBase implements Templat return new TemplateProfile(userId, template, zoneId); } + public TemplateProfile prepareExtractTemplate(ExtractTemplateCmd cmd) { + Long templateId = cmd.getId(); + Long userId = UserContext.current().getCallerUserId(); + Long zoneId = cmd.getZoneId(); + + VMTemplateVO template = _tmpltDao.findById(templateId.longValue()); + if (template == null) { + throw new InvalidParameterValueException("unable to find template with id " + templateId); + } + return new TemplateProfile(userId, template, zoneId); + } + public TemplateProfile prepareDelete(DeleteIsoCmd cmd) { Long templateId = cmd.getId(); Long userId = UserContext.current().getCallerUserId(); diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 63c6f6e6be0..8b5ee3aac1b 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -367,6 +367,13 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, String mode = cmd.getMode(); Long eventId = cmd.getStartEventId(); + VirtualMachineTemplate template = getTemplate(templateId); + if (template == null) { + throw new InvalidParameterValueException("unable to find template with id " + templateId); + } + TemplateAdapter adapter = getAdapter(template.getHypervisorType()); + TemplateProfile profile = adapter.prepareExtractTemplate(cmd); + // FIXME: async job needs fixing Long uploadId = extract(caller, templateId, url, zoneId, mode, eventId, false, null, _asyncMgr); if (uploadId != null){ diff --git a/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java b/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java old mode 100755 new mode 100644 index c0f827e655e..2d77429367a --- a/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java +++ b/server/src/com/cloud/upgrade/dao/Upgrade2214to30.java @@ -629,8 +629,8 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { s_logger.debug("Updating XenSever System Vms"); //XenServer try { - //Get 3.0.0 xenserer system Vm template Id - pstmt = conn.prepareStatement("select id from `cloud`.`vm_template` where name = 'systemvm-xenserver-3.0.0' and removed is null"); + //Get 3.0.0 or later xenserer system Vm template Id + pstmt = conn.prepareStatement("select max(id) from `cloud`.`vm_template` where name like 'systemvm-xenserver-%' and removed is null"); rs = pstmt.executeQuery(); if(rs.next()){ long templateId = rs.getLong(1); @@ -648,9 +648,9 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } else { if (xenserver){ - throw new CloudRuntimeException("3.0.0 XenServer SystemVm template not found. Cannot upgrade system Vms"); + throw new CloudRuntimeException("3.0.0 or later XenServer SystemVm template not found. Cannot upgrade system Vms"); } else { - s_logger.warn("3.0.0 XenServer SystemVm template not found. XenServer hypervisor is not used, so not failing upgrade"); + s_logger.warn("3.0.0 or later XenServer SystemVm template not found. XenServer hypervisor is not used, so not failing upgrade"); } } } catch (SQLException e) { @@ -660,8 +660,8 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { //KVM s_logger.debug("Updating KVM System Vms"); try { - //Get 3.0.0 KVM system Vm template Id - pstmt = conn.prepareStatement("select id from `cloud`.`vm_template` where name = 'systemvm-kvm-3.0.0' and removed is null"); + //Get 3.0.0 or later KVM system Vm template Id + pstmt = conn.prepareStatement("select max(id) from `cloud`.`vm_template` where name like 'systemvm-kvm-%' and removed is null"); rs = pstmt.executeQuery(); if(rs.next()){ long templateId = rs.getLong(1); @@ -679,9 +679,9 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } else { if (kvm){ - throw new CloudRuntimeException("3.0.0 KVM SystemVm template not found. Cannot upgrade system Vms"); + throw new CloudRuntimeException("3.0.0 or later KVM SystemVm template not found. Cannot upgrade system Vms"); } else { - s_logger.warn("3.0.0 KVM SystemVm template not found. KVM hypervisor is not used, so not failing upgrade"); + s_logger.warn("3.0.0 or later KVM SystemVm template not found. KVM hypervisor is not used, so not failing upgrade"); } } } catch (SQLException e) { @@ -691,8 +691,8 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { //VMware s_logger.debug("Updating VMware System Vms"); try { - //Get 3.0.0 VMware system Vm template Id - pstmt = conn.prepareStatement("select id from `cloud`.`vm_template` where name = 'systemvm-vmware-3.0.0' and removed is null"); + //Get 3.0.0 or later VMware system Vm template Id + pstmt = conn.prepareStatement("select max(id) from `cloud`.`vm_template` where name like 'systemvm-vmware-%' and removed is null"); rs = pstmt.executeQuery(); if(rs.next()){ long templateId = rs.getLong(1); @@ -710,9 +710,9 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } else { if (VMware){ - throw new CloudRuntimeException("3.0.0 VMware SystemVm template not found. Cannot upgrade system Vms"); + throw new CloudRuntimeException("3.0.0 or later VMware SystemVm template not found. Cannot upgrade system Vms"); } else { - s_logger.warn("3.0.0 VMware SystemVm template not found. VMware hypervisor is not used, so not failing upgrade"); + s_logger.warn("3.0.0 or later VMware SystemVm template not found. VMware hypervisor is not used, so not failing upgrade"); } } } catch (SQLException e) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 617994888bd..bc25bedde45 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1050,7 +1050,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Override @ActionEvent(eventType = EventTypes.EVENT_VM_SCALE, eventDescription = "scaling Vm") - public UserVm + public boolean upgradeVirtualMachine(ScaleVMCmd cmd) throws InvalidParameterValueException { Long vmId = cmd.getId(); Long newServiceOfferingId = cmd.getServiceOfferingId(); @@ -1076,8 +1076,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } // Dynamically upgrade the running vms + boolean success = false; if(vmInstance.getState().equals(State.Running)){ - boolean success = false; int retry = _scaleRetry; while (retry-- != 0) { // It's != so that it can match -1. try{ @@ -1095,7 +1095,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use vmInstance = _vmInstanceDao.findById(vmId); vmInstance = _itMgr.reConfigureVm(vmInstance, oldServiceOffering, existingHostHasCapacity); success = true; - return _vmDao.findById(vmInstance.getId()); + return success; }catch(InsufficientCapacityException e ){ s_logger.warn("Received exception while scaling ",e); } catch (ResourceUnavailableException e) { @@ -1112,11 +1112,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } } - if (!success) - return null; } - return _vmDao.findById(vmInstance.getId()); + return success; } diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index b0d6378e9e5..afffbec436b 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -64,6 +64,7 @@ import com.cloud.alert.AlertManager; import com.cloud.cluster.ClusterManager; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.DataCenter; @@ -131,6 +132,7 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.ResourceLimitService; import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; @@ -231,6 +233,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected VMSnapshotDao _vmSnapshotDao; @Inject protected VolumeDataFactory volFactory; + @Inject + protected ResourceLimitService _resourceLimitMgr; protected List _planners; public List getPlanners() { @@ -428,6 +432,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Cleaning up NICS"); _networkMgr.cleanupNics(profile); // Clean up volumes based on the vm's instance id + List rootVol = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT); this.volumeMgr.cleanupVolumes(vm.getId()); VirtualMachineGuru guru = getVmGuru(vm); @@ -462,6 +467,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Expunged " + vm); } + // Update Resource count + if (vm.getAccountId() != Account.ACCOUNT_ID_SYSTEM && !rootVol.isEmpty()) { + _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.volume); + _resourceLimitMgr.decrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, + new Long(rootVol.get(0).getSize())); + } return true; } diff --git a/server/test/com/cloud/network/MockNetworkManagerImpl.java b/server/test/com/cloud/network/MockNetworkManagerImpl.java index 7321b0d820b..409835c1dcd 100755 --- a/server/test/com/cloud/network/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/network/MockNetworkManagerImpl.java @@ -64,15 +64,23 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; -import com.cloud.vm.Nic; -import com.cloud.vm.NicProfile; -import com.cloud.vm.NicSecondaryIp; -import com.cloud.vm.NicVO; -import com.cloud.vm.ReservationContext; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; +import com.cloud.vm.*; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; +import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import java.util.List; +import java.util.Map; +import java.util.Set; + @Component @Local(value = { NetworkManager.class, NetworkService.class }) @@ -611,7 +619,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage */ @Override public Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String vlan, - String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId) + String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { // TODO Auto-generated method stub return null; diff --git a/server/test/com/cloud/network/MockRulesManagerImpl.java b/server/test/com/cloud/network/MockRulesManagerImpl.java index e5a6894d76d..200fd2c7462 100644 --- a/server/test/com/cloud/network/MockRulesManagerImpl.java +++ b/server/test/com/cloud/network/MockRulesManagerImpl.java @@ -76,8 +76,7 @@ public class MockRulesManagerImpl extends ManagerBase implements RulesManager, R @Override public boolean enableStaticNat(long ipAddressId, long vmId, long networkId, - boolean isSystemVm, String ipAddr) throws NetworkRuleConflictException, - ResourceUnavailableException { + String ipAddr) throws NetworkRuleConflictException, ResourceUnavailableException { // TODO Auto-generated method stub return false; } diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java index 22bbbe8d5df..8b0b1c797c0 100644 --- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java +++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java @@ -407,8 +407,8 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager, } @Override - public UserVm upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { - return null; //To change body of implemented methods use File | Settings | File Templates. + public boolean upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + return false; //To change body of implemented methods use File | Settings | File Templates. } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 99e16eb77da..3745fd14944 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -79,15 +79,25 @@ import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; -import com.cloud.vm.Nic; -import com.cloud.vm.NicProfile; -import com.cloud.vm.NicSecondaryIp; -import com.cloud.vm.NicVO; -import com.cloud.vm.ReservationContext; -import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; +import com.cloud.vm.*; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.api.command.admin.usage.ListTrafficTypeImplementorsCmd; +import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.api.command.user.network.ListNetworksCmd; +import org.apache.cloudstack.api.command.user.network.RestartNetworkCmd; +import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + @Component @Local(value = { NetworkManager.class, NetworkService.class }) @@ -625,7 +635,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkManage */ @Override public Network createPrivateNetwork(String networkName, String displayText, long physicalNetworkId, String vlan, - String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId) + String startIp, String endIP, String gateway, String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { // TODO Auto-generated method stub return null; diff --git a/server/test/com/cloud/vpc/MockVpcManagerImpl.java b/server/test/com/cloud/vpc/MockVpcManagerImpl.java index 0f269284127..baccbd045d2 100644 --- a/server/test/com/cloud/vpc/MockVpcManagerImpl.java +++ b/server/test/com/cloud/vpc/MockVpcManagerImpl.java @@ -164,7 +164,7 @@ public class MockVpcManagerImpl extends ManagerBase implements VpcManager { * @see com.cloud.network.vpc.VpcService#createVpcPrivateGateway(long, java.lang.Long, java.lang.String, java.lang.String, java.lang.String, java.lang.String, long) */ @Override - public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, String gateway, String netmask, long gatewayOwnerId) throws ResourceAllocationException, + public PrivateGateway createVpcPrivateGateway(long vpcId, Long physicalNetworkId, String vlan, String ipAddress, String gateway, String netmask, long gatewayOwnerId, Boolean isSourceNat) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { // TODO Auto-generated method stub return null; diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 2c7a6b4a271..407c0b6c85a 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -1158,3 +1158,6 @@ ALTER TABLE `cloud`.`account_details` MODIFY value varchar(255); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.apiserver.address', 'http://localhost:8081', 'Specify the address at which the Midonet API server can be contacted (if using Midonet)'); INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.providerrouter.id', 'd7c5e6a3-e2f4-426b-b728-b7ce6a0448e5', 'Specifies the UUID of the Midonet provider router (if using Midonet)'); + +alter table cloud.vpc_gateways add column source_nat boolean default false; +alter table cloud.private_ip_address add column source_nat boolean default false; diff --git a/test/integration/smoke/test_vm_snapshots.py b/test/integration/smoke/test_vm_snapshots.py new file mode 100644 index 00000000000..353d499f7a1 --- /dev/null +++ b/test/integration/smoke/test_vm_snapshots.py @@ -0,0 +1,308 @@ +# 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. + +# Import Local Modules +import marvin +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.integration.lib.utils import * +from marvin.integration.lib.base import * +from marvin.integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient + +class Services: + """Test Snapshots Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 200, # in MHz + "memory": 256, # In MBs + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "mgmt_server": { + "ipaddress": '1.2.2.152', + "username": "root", + "password": "password", + "port": 22, + }, + "templates": { + "displaytext": 'Template', + "name": 'Template', + "ostype": "CentOS 5.3 (64-bit)", + "templatefilter": 'self', + }, + "test_dir": "/tmp", + "random_data": "random.data", + "snapshot_name":"TestSnapshot", + "snapshot_displaytext":"Test", + "ostype": "CentOS 5.3 (64-bit)", + "sleep": 60, + "timeout": 10, + "mode": 'advanced', # Networking mode: Advanced, Basic + } + +class TestVmSnapshot(cloudstackTestCase): + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVmSnapshot, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostype"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["templates"]["ostypeid"] = template.ostypeid + cls.services["zoneid"] = cls.zone.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.name, + domainid=cls.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls.random_data_0 = random_gen(100) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + 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 = [] + return + + def tearDown(self): + try: + # Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @attr(tags=["advanced", "advancedns", "smoke"]) + def test_01_create_vm_snapshots(self): + + try: + # Login to VM and write data to file system + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "echo %s > %s/%s" % (self.random_data_0, self.services["test_dir"], self.services["random_data"]), + "cat %s/%s" % (self.services["test_dir"], self.services["random_data"]) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data has be write into temp file!" + ) + + time.sleep(self.services["sleep"]) + + vm_snapshot = VmSnapshot.create( + self.apiclient, + self.virtual_machine.id, + "false", + self.services["snapshot_name"], + self.services["snapshot_displaytext"] + ) + self.assertEqual( + vm_snapshot.state, + "Ready", + "Check the snapshot of vm is ready!" + ) + return + + @attr(tags=["advanced", "advancedns", "smoke"]) + def test_02_revert_vm_snapshots(self): + try: + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "rm -rf %s/%s" % (self.services["test_dir"], self.services["random_data"]), + "ls %s/%s" % (self.services["test_dir"], self.services["random_data"]) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + if str(result[0]).index("No such file or directory") == -1: + self.fail("Check the random data has be delete from temp file!") + + time.sleep(self.services["sleep"]) + + list_snapshot_response = VmSnapshot.list(self.apiclient,vmid=self.virtual_machine.id,listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + + self.assertEqual( + list_snapshot_response[0].state, + "Ready", + "Check the snapshot of vm is ready!" + ) + + VmSnapshot.revertToSnapshot(self.apiclient,list_snapshot_response[0].id) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + list_vm_response[0].state, + "Stopped", + "Check the state of vm is Stopped!" + ) + + cmd = startVirtualMachine.startVirtualMachineCmd() + cmd.id = list_vm_response[0].id + self.apiclient.startVirtualMachine(cmd) + + time.sleep(self.services["sleep"]) + + try: + ssh_client = self.virtual_machine.get_ssh_client(reconnect=True) + + cmds = [ + "cat %s/%s" % (self.services["test_dir"], self.services["random_data"]) + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine.ipaddress) + + self.assertEqual( + self.random_data_0, + result[0], + "Check the random data is equal with the ramdom file!" + ) + @attr(tags=["advanced", "advancedns", "smoke"]) + def test_03_delete_vm_snapshots(self): + + list_snapshot_response = VmSnapshot.list(self.apiclient,vmid=self.virtual_machine.id,listall=True) + + self.assertEqual( + isinstance(list_snapshot_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshot_response, + None, + "Check if snapshot exists in ListSnapshot" + ) + """ + cmd = deleteVMSnapshot.deleteVMSnapshotCmd() + cmd.vmsnapshotid = list_snapshot_response[0].id + self.apiclient.deleteVMSnapshot(cmd) + """ + VmSnapshot.deleteVMSnapshot(self.apiclient,list_snapshot_response[0].id) + + time.sleep(self.services["sleep"]*3) + + list_snapshot_response = VmSnapshot.list(self.apiclient,vmid=self.virtual_machine.id,listall=True) + + self.assertEqual( + list_snapshot_response, + None, + "Check list vm snapshot has be deleted" + ) diff --git a/test/selenium/ReadMe.txt b/test/selenium/ReadMe.txt index 30b0e0df7a0..bc968f1dc93 100644 --- a/test/selenium/ReadMe.txt +++ b/test/selenium/ReadMe.txt @@ -1,31 +1,41 @@ ############################################## + +Questions? Post'em @ dev@cloudstack.apache.org + +############################################## + This files contains following: 1) Installation requirements -2) Test Pre requisites -3) Running the Test and Generating the report +2) Testing pre-requisites +3) Running the Tests and Generating the report ############################################## ########################################################################################################################################## -1) Installtion Requirements +1) Installation Requirements +--------------------------- -1)Firefox depending on your OS (Good to have Firebug and Selenium IDE for troubleshooting and dev work) +1) Firefox depending on your OS (Good to have Firebug and Selenium IDE for troubleshooting and dev work) -2)Install Python 2.7. Recommend to use Active State Python +2) Install Python 2.7. 3) Now Open CMD/Terminal and type all of following -- pypm install pycrypto (Installs Pycrypto) -- pypm install paramiko (Install paramiko) +- pip install pycrypto (Installs Pycrypto) +- pip install paramiko (Install paramiko) - pip install unittest-xml-reporting (Install XML Test Runner) - pip install -U selenium (Installs Selenium) +4) Get PhoantomJS for your OS from http://phantomjs.org/ + +- PhantomJS will run selenium test in headless mode. Follow the instruction on PhantomJS.org. +- Make sure the executable is in PATH. (TIP: Drop it in Python27 folder :-)) 5) Now get the HTMLTestRunner for nice looking report generation. - http://tungwaiyip.info/software/HTMLTestRunner.html @@ -35,18 +45,22 @@ This files contains following: ########################################################################################################################################## 2) Test Prerequisites +--------------------- + +- Download and install CS. /cwiki.apache.org has links to Installation Guide and API reference. +- Log into the management server and Add a Zone. (Must be Advance Zone and Hypervisor type must be Xen) -- Download and install CS -- Log into the management server nad Add a Zone. (Must be Advance Zone and Hypervisor type must be Xen) ########################################################################################################################################## 3) Running the Test and Generating the report +--------------------------------------------- - Folder smoke contains main.py - main.py is the file where all the tests are serialized. - main.py supports HTML and XML reporting. Please refer to end of file to choose either. -- Typical usage is: python main.py for XML Reporting -- And python main.py >> results.html for HTML Reporting. +- Typical usage is: python main.py 10.1.1.10 >> result.xml for XML Reporting +- And python main.py 10.1.1.10 >> result.html for HTML Reporting. +- 10.1.1.10 (your management server IP) is an argument required for main. ########################################################################################################################################## diff --git a/test/selenium/lib/initialize.py b/test/selenium/lib/initialize.py index e8cc49adff4..55e5d9aa3b1 100644 --- a/test/selenium/lib/initialize.py +++ b/test/selenium/lib/initialize.py @@ -21,11 +21,26 @@ This will help pass webdriver (Browser instance) across our test cases. from selenium import webdriver +import sys DRIVER = None +MS_ip = None + def getOrCreateWebdriver(): global DRIVER - DRIVER = DRIVER or webdriver.Firefox() + DRIVER = DRIVER or webdriver.PhantomJS('phantomjs') # phantomjs executable must be in PATH. return DRIVER + +def getMSip(): + global MS_ip + if len(sys.argv) >= 3: + sys.exit("Only One argument is required .. Enter your Management Server IP") + + if len(sys.argv) == 1: + sys.exit("Atleast One argument is required .. Enter your Management Server IP") + + for arg in sys.argv[1:]: + MS_ip = arg + return MS_ip \ No newline at end of file diff --git a/test/selenium/smoke/Login_and_Accounts.py b/test/selenium/smoke/Login_and_Accounts.py index c5132d9754c..44b1e836b4c 100644 --- a/test/selenium/smoke/Login_and_Accounts.py +++ b/test/selenium/smoke/Login_and_Accounts.py @@ -34,11 +34,12 @@ class login(unittest.TestCase): def setUp(self): + MS_URL = initialize.getMSip() self.driver = initialize.getOrCreateWebdriver() - self.base_url = "http://10.223.49.206:8080/" # Your management Server IP goes here + self.base_url = "http://"+ MS_URL +":8080/" # Your management Server IP goes here self.verificationErrors = [] - + def test_login(self): # Here we will clear the test box for Username and Password and fill them with actual login data. diff --git a/test/selenium/smoke/main.py b/test/selenium/smoke/main.py index 86bb9308c2f..921192dc847 100644 --- a/test/selenium/smoke/main.py +++ b/test/selenium/smoke/main.py @@ -21,7 +21,7 @@ import xmlrunner global DRIVER - +global MS_ip # Import test cases diff --git a/tools/marvin/marvin/integration/lib/base.py b/tools/marvin/marvin/integration/lib/base.py index 1d86c6c1599..bebf2b50bcf 100755 --- a/tools/marvin/marvin/integration/lib/base.py +++ b/tools/marvin/marvin/integration/lib/base.py @@ -3045,3 +3045,40 @@ class ASA1000V: cmd = listCiscoAsa1000vResources.listCiscoAsa1000vResourcesCmd() [setattr(cmd, k, v) for k, v in kwargs.items()] return(apiclient.listCiscoAsa1000vResources(cmd)) + +class VmSnapshot: + """Manage VM Snapshot life cycle""" + def __init__(self, items): + self.__dict__.update(items) + @classmethod + def create(cls,apiclient,vmid,snapshotmemory="false",name=None,description=None): + cmd = createVMSnapshot.createVMSnapshotCmd() + cmd.virtualmachineid = vmid + + if snapshotmemory: + cmd.snapshotmemory = snapshotmemory + if name: + cmd.name = name + if description: + cmd.description = description + return VmSnapshot(apiclient.createVMSnapshot(cmd).__dict__) + + @classmethod + def list(cls, apiclient, **kwargs): + cmd = listVMSnapshot.listVMSnapshotCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVMSnapshot(cmd)) + + @classmethod + def revertToSnapshot(cls, apiclient,vmsnapshotid): + cmd = revertToVMSnapshot.revertToVMSnapshotCmd() + cmd.vmsnapshotid = vmsnapshotid + + return apiclient.revertToVMSnapshot(cmd) + + @classmethod + def deleteVMSnapshot(cls,apiclient,vmsnapshotid): + cmd = deleteVMSnapshot.deleteVMSnapshotCmd() + cmd.vmsnapshotid = vmsnapshotid + + return apiclient.deleteVMSnapshot(cmd) diff --git a/ui/index.jsp b/ui/index.jsp index 0db2589734f..8f31740a039 100644 --- a/ui/index.jsp +++ b/ui/index.jsp @@ -1662,6 +1662,7 @@ under the License. + diff --git a/ui/scripts/accounts.js b/ui/scripts/accounts.js index 7e82c0fc163..bad8435e27e 100644 --- a/ui/scripts/accounts.js +++ b/ui/scripts/accounts.js @@ -277,6 +277,7 @@ detailView: { name: 'Account details', + isMaximized: true, viewAll: { path: 'accounts.users', label: 'label.users' }, actions: { @@ -895,6 +896,56 @@ } }); } + }, + + // Granular settings for account + settings: { + title: 'Settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + $.ajax({ + url:createURL('listConfigurations&accountid=' + args.context.accounts[0].id), + data: { page: args.page, pageSize: pageSize, listAll: true }, + success:function(json){ + args.response.success({ + data:json.listconfigurationsresponse.configuration + + }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + + }, + actions: { + edit: function(args) { + // call updateAccountLevelParameters + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; + + $.ajax({ + url:createURL('updateConfiguration&accountid=' + args.context.accounts[0].id), + data:data, + success:function(json){ + var item = json.updateconfigurationresponse.configuration; + args.response.success({data:item}); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + + } + } + }) } } } diff --git a/ui/scripts/instanceWizard.js b/ui/scripts/instanceWizard.js index f3deb66d6ba..ff130d3e301 100644 --- a/ui/scripts/instanceWizard.js +++ b/ui/scripts/instanceWizard.js @@ -317,7 +317,15 @@ url: createURL('listAffinityGroups'), success: function(json) { var items = json.listaffinitygroupsresponse.affinitygroup; - args.response.success({data: {affinityGroups: items}}); + var data = { + affinityGroups: items + }; + if('affinityGroups' in args.context) { + $.extend(data, { + selectedObj: args.context.affinityGroups[0] + }); + } + args.response.success({data: data}); } }); }, diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js index 751c3605276..ab9d606b2b6 100644 --- a/ui/scripts/instances.js +++ b/ui/scripts/instances.js @@ -204,13 +204,7 @@ affinitygroupid: args.context.affinityGroups[0].id }); } - - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - zonetype: args.context.zoneType - }); - } - + $.ajax({ url: createURL('listVirtualMachines'), data: data, diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index 9b66083d4e6..d73974a89f5 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -89,20 +89,7 @@ dataType: "json", async: true, success: function(json) { - var zoneObjs; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - zoneObjs = json.listzonesresponse.zone; - } - else { //Basic type or Advanced type - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) - zoneObjs.push(items[i]); - } - } - } + var zoneObjs = json.listzonesresponse.zone; args.response.success({descriptionField: 'name', data: zoneObjs}); } }); @@ -227,20 +214,7 @@ dataType: "json", async: true, success: function(json) { - var zoneObjs; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - zoneObjs = json.listzonesresponse.zone; - } - else { //Basic type or Advanced type - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) - zoneObjs.push(items[i]); - } - } - } + var zoneObjs = json.listzonesresponse.zone; args.response.success({descriptionField: 'name', data: zoneObjs}); } }); @@ -398,18 +372,12 @@ if(args.context != null) { if("instances" in args.context) { - $.extend(data, { - virtualMachineId: args.context.instances[0].id - }); + $.extend(data, { + virtualMachineId: args.context.instances[0].id + }); } } - - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - zonetype: args.context.zoneType - }); - } - + $.ajax({ url: createURL('listVolumes'), data: data, diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 7aa0566fbef..3c4051cc481 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -184,16 +184,9 @@ dashboard: { dataProvider: function(args) { var dataFns = { - zoneCount: function(data) { - var data = {}; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - networktype: cloudStack.context.zoneType - }); - } + zoneCount: function(data) { $.ajax({ - url: createURL('listZones'), - data: data, + url: createURL('listZones'), success: function(json) { dataFns.podCount($.extend(data, { zoneCount: json.listzonesresponse.count ? @@ -250,12 +243,7 @@ type: 'routing', page: 1, pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; $.ajax({ url: createURL('listHosts'), data: data2, @@ -272,12 +260,7 @@ var data2 = { page: 1, pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; $.ajax({ url: createURL('listStoragePools'), data: data2, @@ -294,12 +277,7 @@ type: 'SecondaryStorage', page: 1, pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; $.ajax({ url: createURL('listHosts'), data: data2, @@ -333,12 +311,7 @@ projectid: -1, page: 1, pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; $.ajax({ url: createURL('listRouters'), data: data2, @@ -349,12 +322,7 @@ listAll: true, page: 1, pagesize: 1 //specifying pagesize as 1 because we don't need any embedded objects to be returned here. The only thing we need from API response is "count" property. - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data3, { - zonetype: cloudStack.context.zoneType - }); - } + }; $.ajax({ url: createURL('listRouters'), data: data3, @@ -2332,12 +2300,7 @@ var data2 = { forvpc: false - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; var routers = []; $.ajax({ url: createURL("listRouters&zoneid=" + selectedZoneObj.id + "&listAll=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), @@ -2817,12 +2780,7 @@ var data2 = { forvpc: true - }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + }; var routers = []; $.ajax({ url: createURL("listRouters&zoneid=" + selectedZoneObj.id + "&listAll=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), @@ -4708,12 +4666,7 @@ break; } } - } - - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - array1.push("&networktype=" + args.context.zoneType); - } - + } $.ajax({ url: createURL("listZones&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), dataType: "json", @@ -5566,6 +5519,55 @@ } } } + }, + + // Granular settings for zone + settings: { + title: 'Settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + $.ajax({ + url:createURL('listConfigurations&zoneid=' + args.context.physicalResources[0].id), + data: { page: args.page, pageSize: pageSize, listAll: true }, + success:function(json){ + args.response.success({ + data:json.listconfigurationsresponse.configuration + + }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + + }, + actions: { + edit: function(args) { + // call updateZoneLevelParamter + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; + + $.ajax({ + url:createURL('updateConfiguration&zoneid=' + args.context.physicalResources[0].id), + data:data, + success:function(json){ + var item = json.updateconfigurationresponse.configuration; + args.response.success({data:item}); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + } + } + }) } } } @@ -5660,12 +5662,12 @@ var searchByArgs = args.filterBy.search.value.length ? '&name=' + args.filterBy.search.value : ''; - var data = { page: args.page, pageSize: pageSize, type: 'routing', listAll: true }; - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - zonetype: args.context.zoneType - }); - } + var data = { + page: args.page, + pageSize: pageSize, + type: 'routing', + listAll: true + }; $.ajax({ url: createURL('listHosts' + searchByArgs), @@ -5714,11 +5716,7 @@ pageSize: pageSize, listAll: true }; - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - zonetype: args.context.zoneType - }); - } + $.ajax({ url: createURL('listStoragePools' + searchByArgs), data: data, @@ -5761,12 +5759,12 @@ var searchByArgs = args.filterBy.search.value.length ? '&name=' + args.filterBy.search.value : ''; - var data = { type: 'SecondaryStorage', page: args.page, pageSize: pageSize, listAll: true }; - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data, { - zonetype: args.context.zoneType - }); - } + var data = { + type: 'SecondaryStorage', + page: args.page, + pageSize: pageSize, + listAll: true + }; $.ajax({ url: createURL('listHosts' + searchByArgs), @@ -5850,18 +5848,11 @@ var listView = $.extend(true, {}, cloudStack.sections.system.subsections.virtualRouters.listView, { dataProvider: function (args) { var searchByArgs = args.filterBy.search.value.length ? - '&name=' + args.filterBy.search.value : ''; - - var data2 = {}; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + '&name=' + args.filterBy.search.value : ''; + var routers = []; $.ajax({ - url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + searchByArgs), - data: data2, + url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + searchByArgs), async: true, success: function(json) { var items = json.listroutersresponse.router ? @@ -5873,8 +5864,7 @@ // Get project routers $.ajax({ - url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + "&projectid=-1"), - data: data2, + url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + "&projectid=-1"), async: true, success: function(json) { var items = json.listroutersresponse.router ? @@ -5961,11 +5951,7 @@ var data2 = { forvpc: false }; - if(cloudStack.context.zoneType != null && cloudStack.context.zoneType.length > 0) { //Basic type or Advanced type - $.extend(data2, { - zonetype: cloudStack.context.zoneType - }); - } + var routers = []; $.ajax({ url: createURL("listRouters&zoneid=" + selectedZoneObj.id + "&listAll=true&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), @@ -6278,6 +6264,80 @@ } }, + scaleUp:{ + label:'scaleUp Router VM', + createForm: { + title: 'label.change.service.offering', + desc: '', + fields: { + + serviceOfferingId: { + label: 'label.compute.offering', + select: function(args) { + $.ajax({ + url: createURL('listServiceOfferings'), + data: { + issystem: true, + systemvmtype: 'domainrouter' + }, + success: function(json) { + var serviceofferings = json.listserviceofferingsresponse.serviceoffering; + var items = []; + $(serviceofferings).each(function() { + // if(this.id != args.context.routers[0].serviceofferingid) { + items.push({id: this.id, description: this.name}); //default one (i.e. "System Offering For Software Router") doesn't have displaytext property. So, got to use name property instead. + + }); + args.response.success({data: items}); + } + }); + } + } + } + }, + + action: function(args) { + $.ajax({ + url: createURL("scaleVirtualMachine&id=" + args.context.routers[0].id + "&serviceofferingid=" + args.data.serviceOfferingId), + dataType: "json", + async: true, + success: function(json) { + var jid = json.scalevirtualmachineresponse.jobid; + args.response.success( + {_custom: + {jobId: jid, + getUpdatedItem: function(json) { + return json.queryasyncjobresultresponse.jobresult.virtualmachine; + }, + getActionFilter: function() { + return vmActionfilter; + } + } + } + ); + }, + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + } + + }); + }, + messages: { + confirm: function(args) { + return 'Do you really want to scale up the Router VM ?'; + }, + notification: function(args) { + + return 'Router VM Scaled Up'; + } + }, + notification: { + poll: pollAsyncJobResult + } + + }, + + viewConsole: { label: 'label.view.console', action: { @@ -9085,6 +9145,57 @@ }); } } + }, + + // Granular settings for cluster + settings: { + title: 'Settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + $.ajax({ + url:createURL('listConfigurations&clusterid=' + args.context.clusters[0].id), + data: { page: args.page, pageSize: pageSize, listAll: true }, + success:function(json){ + args.response.success({ + data:json.listconfigurationsresponse.configuration + + }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + + }, + actions: { + edit: function(args) { + // call updateClusterLevelParameters + + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; + + $.ajax({ + url:createURL('updateConfiguration&clusterid=' + args.context.clusters[0].id), + data:data, + success:function(json){ + var item = json.updateconfigurationresponse.configuration; + args.response.success({data:item}); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + + } + } + }) } } } @@ -9135,11 +9246,7 @@ } else { array1.push("&hostid=" + args.context.instances[0].hostid); } - - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - array1.push("&zonetype=" + args.context.zoneType); - } - + $.ajax({ url: createURL("listHosts&type=Routing" + array1.join("") + "&page=" + args.page + "&pagesize=" + pageSize), dataType: "json", @@ -10524,6 +10631,7 @@ detailView: { name: "Primary storage details", + isMaximized: true, actions: { edit: { label: 'label.edit', @@ -10708,6 +10816,57 @@ } }); } + }, + + // Granular settings for storage pool + settings: { + title: 'Settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + + $.ajax({ + url:createURL('listConfigurations&storageid=' + args.context.primarystorages[0].id), + data: { page: args.page, pageSize: pageSize, listAll: true }, + success:function(json){ + args.response.success({ + data:json.listconfigurationsresponse.configuration + + }); + + }, + + error:function(json){ + args.response.error(parseXMLHttpResponse(json)); + + } + }); + + }, + actions: { + edit: function(args) { + // call updateStorageLevelParameters + var data = { + name: args.data.jsonObj.name, + value: args.data.value + }; + + $.ajax({ + url:createURL('updateConfiguration&storageid=' + args.context.primarystorages[0].id), + data:data, + success:function(json){ + var item = json.updateconfigurationresponse.configuration; + args.response.success({data:item}); + }, + + error: function(json) { + args.response.error(parseXMLHttpResponse(json)); + } + + }); + + } + } + }) } } } @@ -10747,10 +10906,6 @@ } array1.push("&zoneid=" + args.context.zones[0].id); - if(args.context.zoneType != null && args.context.zoneType.length > 0) { //Basic type or Advanced type - array1.push("&zonetype=" + args.context.zoneType); - } - $.ajax({ url: createURL("listHosts&type=SecondaryStorage&page=" + args.page + "&pagesize=" + pageSize + array1.join("")), dataType: "json", @@ -10851,6 +11006,7 @@ detailView: { name: 'Secondary storage details', + isMaximized: true, actions: { remove: { label: 'label.action.delete.secondary.storage' , @@ -10907,6 +11063,27 @@ }); } } + + // Granular settings for storage pool for secondary storage is not required + /* settings: { + title: 'label.menu.global.settings', + custom: cloudStack.uiCustom.granularSettings({ + dataProvider: function(args) { + args.response.success({ + data: [ + { name: 'config.param.1', value: 1 }, + { name: 'config.param.2', value: 2 } + ] + }); + }, + actions: { + edit: function(args) { + // call updateStorageLevelParameters + args.response.success(); + } + } + }) + } */ } } } @@ -11816,9 +11993,9 @@ if (jsonObj.state == 'Running') { allowedActions.push("stop"); - + allowedActions.push("scaleUp"); // if(jsonObj.vpcid != null) - allowedActions.push("restart"); + allowedActions.push("restart"); allowedActions.push("viewConsole"); if (isAdmin()) @@ -11826,7 +12003,8 @@ } else if (jsonObj.state == 'Stopped') { allowedActions.push("start"); - allowedActions.push("remove"); + allowedActions.push("scaleUp"); + allowedActions.push("remove"); if(jsonObj.vpcid != null) allowedActions.push("changeService"); diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js index 52e1135c681..91038b904fa 100644 --- a/ui/scripts/templates.js +++ b/ui/scripts/templates.js @@ -116,27 +116,13 @@ dataType: "json", async: true, success: function(json) { - var zoneObjs; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - zoneObjs.push({id: items[i].id, description: items[i].name}); - } + var zoneObjs= []; + var items = json.listzonesresponse.zone; + if(items != null) { + for(var i = 0; i < items.length; i++) { + zoneObjs.push({id: items[i].id, description: items[i].name}); } - } - else { //Basic type or Advanced type - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) { - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } - } - } + } if (isAdmin() && !(cloudStack.context.projects && cloudStack.context.projects[0])){ zoneObjs.unshift({id: -1, description: "All Zones"}); } @@ -548,27 +534,14 @@ async: true, success: function(json) { var zoneObjs = []; - var items = json.listzonesresponse.zone; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].id != args.context.templates[0].zoneid) { //destination zone must be different from source zone - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } + var items = json.listzonesresponse.zone; + if(items != null) { + for(var i = 0; i < items.length; i++) { + if(items[i].id != args.context.templates[0].zoneid) { //destination zone must be different from source zone + zoneObjs.push({id: items[i].id, description: items[i].name}); + } } - } - else { //Basic type or Advanced type - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) { //type must be matched - if(items[i].id != args.context.templates[0].zoneid) { //destination zone must be different from source zone - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } - } - } - } + } args.response.success({data: zoneObjs}); } }); @@ -894,27 +867,13 @@ dataType: "json", async: true, success: function(json) { - var zoneObjs; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - zoneObjs.push({id: items[i].id, description: items[i].name}); - } + var zoneObjs = []; + var items = json.listzonesresponse.zone; + if(items != null) { + for(var i = 0; i < items.length; i++) { + zoneObjs.push({id: items[i].id, description: items[i].name}); } - } - else { //Basic type or Advanced type - zoneObjs = []; - var items = json.listzonesresponse.zone; - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) { - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } - } - } + } if (isAdmin() && !(cloudStack.context.projects && cloudStack.context.projects[0])){ zoneObjs.unshift({id: -1, description: "All Zones"}); } @@ -1224,27 +1183,14 @@ async: true, success: function(json) { var zoneObjs = []; - var items = json.listzonesresponse.zone; - if(args.context.zoneType == null || args.context.zoneType == '') { //all types - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].id != args.context.isos[0].zoneid) { //destination zone must be different from source zone - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } + var items = json.listzonesresponse.zone; + if(items != null) { + for(var i = 0; i < items.length; i++) { + if(items[i].id != args.context.isos[0].zoneid) { //destination zone must be different from source zone + zoneObjs.push({id: items[i].id, description: items[i].name}); + } } - } - else { //Basic type or Advanced type - if(items != null) { - for(var i = 0; i < items.length; i++) { - if(items[i].networktype == args.context.zoneType) { //type must be matched - if(items[i].id != args.context.isos[0].zoneid) { //destination zone must be different from source zone - zoneObjs.push({id: items[i].id, description: items[i].name}); - } - } - } - } - } + } args.response.success({data: zoneObjs}); } }); diff --git a/ui/scripts/ui-custom/granularSettings.js b/ui/scripts/ui-custom/granularSettings.js new file mode 100644 index 00000000000..02d5c1fcbaa --- /dev/null +++ b/ui/scripts/ui-custom/granularSettings.js @@ -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. + +(function($, cloudStack) { + cloudStack.uiCustom.granularSettings = function(args) { + var dataProvider = args.dataProvider; + var actions = args.actions; + + return function(args) { + var context = args.context; + + var listView = { + id: 'settings', + fields: { + name: { label: 'label.name' }, + value: { label: 'label.value', editable: true } + }, + actions: { + edit: { + label: 'label.change.value', + action: actions.edit + } + }, + dataProvider: dataProvider + }; + + var $listView = $('
').listView({ context: context, listView: listView }); + + return $listView; + } + }; +}(jQuery, cloudStack)); diff --git a/ui/scripts/ui-custom/instanceWizard.js b/ui/scripts/ui-custom/instanceWizard.js index bdc05c11bf7..ea7acf8dca5 100644 --- a/ui/scripts/ui-custom/instanceWizard.js +++ b/ui/scripts/ui-custom/instanceWizard.js @@ -86,7 +86,7 @@ }); }; - var makeSelects = function(name, data, fields, options) { + var makeSelects = function(name, data, fields, options, selectedObj) { var $selects = $('
'); options = options ? options : {}; @@ -151,7 +151,11 @@ .append($('
').addClass('desc').html(_s(this[fields.desc]))) ) .data('json-obj', this); - + + if(selectedObj != null && selectedObj.id == item.id) { + $select.find('input[type=checkbox]').attr('checked', 'checked'); + } + $selects.append($select); if (item._singleSelect) { @@ -493,14 +497,20 @@ ) ); $step.find('.select-container').append( - makeSelects('affinity-groups', args.data.affinityGroups, { - name: 'name', - desc: 'description', - id: 'id' - }, { - type: 'checkbox', - 'wizard-field': 'affinity-groups' - }) + makeSelects( + 'affinity-groups', + args.data.affinityGroups, + { + name: 'name', + desc: 'description', + id: 'id' + }, + { + type: 'checkbox', + 'wizard-field': 'affinity-groups' + }, + args.data.selectedObj + ) ); } else { $step.find('.select-container').append($('

').html(_l('message.no.affinity.groups'))); diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 27b14d16e61..a1272fda31c 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -653,6 +653,11 @@ }); }); + var itemState=multiRule._itemState ? item[multiRule._itemState] :item.state; + var $itemState = $('').html(_s(itemState)); + $tr.append($('').addClass('state').appendTo($tr).append("Application State - ").append($itemState)); + + if (itemActions) { var $itemActions = $('').addClass('actions item-actions'); diff --git a/ui/scripts/vm_snapshots.js b/ui/scripts/vm_snapshots.js index 0d6305bfbe4..190bf7521f3 100644 --- a/ui/scripts/vm_snapshots.js +++ b/ui/scripts/vm_snapshots.js @@ -170,7 +170,7 @@ }, action: function(args) { $.ajax({ - url: createURL("revertToSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id), + url: createURL("revertToVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id), dataType: "json", async: true, success: function(json) { diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index a4834f26c72..db964e6ffcd 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -737,6 +737,12 @@ actions:{ add:{ label:'Add Private Gateway', + preFilter: function(args) { + if(isAdmin() || isDomainAdmin() ) + return true; + else + return false; + }, createForm:{ title: 'label.add.new.gateway', desc: 'message.add.new.gateway.to.vpc', diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java index 5988dd5f337..9f28d5b36b2 100755 --- a/utils/src/com/cloud/utils/net/NetUtils.java +++ b/utils/src/com/cloud/utils/net/NetUtils.java @@ -627,7 +627,7 @@ public class NetUtils { return result; } - public static Set getAllIpsFromCidr(String cidr, long size) { + public static Set getAllIpsFromCidr(String cidr, long size, Set usedIps) { assert (size < 32) : "You do know this is not for ipv6 right? Keep it smaller than 32 but you have " + size; Set result = new TreeSet(); long ip = ip2Long(cidr); @@ -639,8 +639,12 @@ public class NetUtils { end++; end = (end << (32 - size)) - 2; - while (start <= end) { - result.add(start); + int maxIps = 255; // get 255 ips as maximum + while (start <= end && maxIps > 0) { + if (!usedIps.contains(start)){ + result.add(start); + maxIps--; + } start++; } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 4503349edf1..660d9632baa 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1281,6 +1281,7 @@ public class VirtualMachineMO extends BaseMO { long totalBytesDownloaded = 0; List deviceUrls = leaseInfo.getDeviceUrl(); + s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis()); if(deviceUrls != null) { OvfFile[] ovfFiles = new OvfFile[deviceUrls.size()]; for (int i = 0; i < deviceUrls.size(); i++) { @@ -1328,7 +1329,7 @@ public class VirtualMachineMO extends BaseMO { // tar files into OVA if(packToOva) { - // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature) + // Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature) s_logger.info("Sync file system before we package OVA..."); Script commandSync = new Script(true, "sync", 0, s_logger); @@ -1351,8 +1352,11 @@ public class VirtualMachineMO extends BaseMO { } else { s_logger.error(exportDir + File.separator + exportName + ".ova is not created as expected"); } + } else { + success = true; } } + s_logger.info("volss: copy vmdk and ovf file finishes " + System.currentTimeMillis()); } catch(Throwable e) { s_logger.error("Unexpected exception ", e); } finally { diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java index e69a3d26c56..87c79096d60 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java @@ -528,7 +528,7 @@ public class VmwareClient { PropertySpec pSpec = new PropertySpec(); pSpec.setType(type); pSpec.setAll(false); - pSpec.getPathSet().add(name); + pSpec.getPathSet().add("name"); ObjectSpec oSpec = new ObjectSpec(); oSpec.setObj(root);