diff --git a/LICENSE.header b/LICENSE.header new file mode 100644 index 00000000000..4eacb643179 --- /dev/null +++ b/LICENSE.header @@ -0,0 +1,16 @@ +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. diff --git a/agent/pom.xml b/agent/pom.xml index 4e5caae80df..2036ae0ca39 100644 --- a/agent/pom.xml +++ b/agent/pom.xml @@ -115,38 +115,5 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-antrun-plugin - - [1.7,) - - run - - - - - - - - - - - - diff --git a/api/src/com/cloud/deploy/DeployDestination.java b/api/src/com/cloud/deploy/DeployDestination.java index 05625d60b0f..4ded5ebe7a1 100644 --- a/api/src/com/cloud/deploy/DeployDestination.java +++ b/api/src/com/cloud/deploy/DeployDestination.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.deploy; +import java.io.Serializable; import java.util.Map; import com.cloud.dc.DataCenter; @@ -26,7 +27,9 @@ import com.cloud.storage.StoragePool; import com.cloud.storage.Volume; import com.cloud.utils.NumbersUtil; -public class DeployDestination { +public class DeployDestination implements Serializable { + private static final long serialVersionUID = 7113840781939014695L; + DataCenter _dc; Pod _pod; Cluster _cluster; @@ -76,28 +79,28 @@ public class DeployDestination { @Override public boolean equals(Object obj) { DeployDestination that = (DeployDestination)obj; - if (this._dc == null || that._dc == null) { + if (_dc == null || that._dc == null) { return false; } - if (this._dc.getId() != that._dc.getId()) { + if (_dc.getId() != that._dc.getId()) { return false; } - if (this._pod == null || that._pod == null) { + if (_pod == null || that._pod == null) { return false; } - if (this._pod.getId() != that._pod.getId()) { + if (_pod.getId() != that._pod.getId()) { return false; } - if (this._cluster == null || that._cluster == null) { + if (_cluster == null || that._cluster == null) { return false; } - if (this._cluster.getId() != that._cluster.getId()) { + if (_cluster.getId() != that._cluster.getId()) { return false; } - if (this._host == null || that._host == null) { + if (_host == null || that._host == null) { return false; } - return this._host.getId() == that._host.getId(); + return _host.getId() == that._host.getId(); } @Override diff --git a/api/src/com/cloud/deploy/DeploymentPlanner.java b/api/src/com/cloud/deploy/DeploymentPlanner.java index 35f406d04f9..eb62cb178bb 100644 --- a/api/src/com/cloud/deploy/DeploymentPlanner.java +++ b/api/src/com/cloud/deploy/DeploymentPlanner.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.deploy; +import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -89,7 +90,9 @@ public interface DeploymentPlanner extends Adapter { Shared, Dedicated; } - public static class ExcludeList { + public static class ExcludeList implements Serializable { + private static final long serialVersionUID = -482175549460148301L; + private Set _dcIds; private Set _podIds; private Set _clusterIds; @@ -173,6 +176,12 @@ public interface DeploymentPlanner extends Adapter { _poolIds.add(poolId); } + public void removePool(long poolId) { + if (_poolIds != null) { + _poolIds.remove(poolId); + } + } + public void addDataCenter(long dataCenterId) { if (_dcIds == null) { _dcIds = new HashSet(); diff --git a/api/src/com/cloud/deploy/HAPlanner.java b/api/src/com/cloud/deploy/HAPlanner.java new file mode 100644 index 00000000000..aeb5083c522 --- /dev/null +++ b/api/src/com/cloud/deploy/HAPlanner.java @@ -0,0 +1,21 @@ +// 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.deploy; + + +public interface HAPlanner extends DeploymentPlanner { +} \ No newline at end of file diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index 5a342cd284d..e88f0104ea7 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -455,6 +455,11 @@ public class EventTypes { //Alert generation public static final String ALERT_GENERATE = "ALERT.GENERATE"; + // OpenDaylight + public static final String EVENT_EXTERNAL_OPENDAYLIGHT_ADD_CONTROLLER = "PHYSICAL.ODLCONTROLLER.ADD"; + public static final String EVENT_EXTERNAL_OPENDAYLIGHT_DELETE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.DELETE"; + public static final String EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER = "PHYSICAL.ODLCONTROLLER.CONFIGURE"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking @@ -760,6 +765,11 @@ public class EventTypes { entityEventDetails.put(EVENT_AUTOSCALEVMGROUP_DISABLE, AutoScaleVmGroup.class.getName()); entityEventDetails.put(EVENT_GUEST_VLAN_RANGE_DEDICATE, GuestVlan.class.getName()); entityEventDetails.put(EVENT_DEDICATED_GUEST_VLAN_RANGE_RELEASE, GuestVlan.class.getName()); + + // OpenDaylight + entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_ADD_CONTROLLER, "OpenDaylightController"); + entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_DELETE_CONTROLLER, "OpenDaylightController"); + entityEventDetails.put(EVENT_EXTERNAL_OPENDAYLIGHT_CONFIGURE_CONTROLLER, "OpenDaylightController"); } public static String getEntityForEvent(String eventName) { diff --git a/api/src/com/cloud/exception/CloudException.java b/api/src/com/cloud/exception/CloudException.java index f898719028b..f35249b0732 100644 --- a/api/src/com/cloud/exception/CloudException.java +++ b/api/src/com/cloud/exception/CloudException.java @@ -27,6 +27,7 @@ import com.cloud.utils.exception.CSExceptionErrorCode; */ public class CloudException extends Exception { + private static final long serialVersionUID = 8784427323859682503L; // This holds a list of uuids and their names. Add uuid:fieldname pairs protected ArrayList idList = new ArrayList(); @@ -58,10 +59,10 @@ public class CloudException extends Exception { } public void setCSErrorCode(int cserrcode) { - this.csErrorCode = cserrcode; + csErrorCode = cserrcode; } public int getCSErrorCode() { - return this.csErrorCode; + return csErrorCode; } } diff --git a/api/src/com/cloud/exception/OperationTimedoutException.java b/api/src/com/cloud/exception/OperationTimedoutException.java index d531c326c37..fe27408eb4e 100644 --- a/api/src/com/cloud/exception/OperationTimedoutException.java +++ b/api/src/com/cloud/exception/OperationTimedoutException.java @@ -28,7 +28,15 @@ public class OperationTimedoutException extends CloudException { long _agentId; long _seqId; int _time; - Command[] _cmds; + + // TODO + // I did a reference search on usage of getCommands() and found none + // + // to prevent serialization problems across boundaries, I'm disabling serialization of _cmds here + // getCommands() will still be available within the same serialization boundary, but it will be lost + // when exception is propagated across job boundaries. + // + transient Command[] _cmds; boolean _isActive; public OperationTimedoutException(Command[] cmds, long agentId, long seqId, int time, boolean isActive) { diff --git a/api/src/com/cloud/host/Host.java b/api/src/com/cloud/host/Host.java index 9d811255e14..689ed12b64e 100755 --- a/api/src/com/cloud/host/Host.java +++ b/api/src/com/cloud/host/Host.java @@ -78,6 +78,11 @@ public interface Host extends StateObject, Identity, InternalIdentity { */ String getPrivateIpAddress(); + /** + * @return the ip address of the host. + */ + String getStorageUrl(); + /** * @return the ip address of the host attached to the storage network. */ diff --git a/api/src/com/cloud/hypervisor/HypervisorGuru.java b/api/src/com/cloud/hypervisor/HypervisorGuru.java index 03493726176..4a35cecb887 100644 --- a/api/src/com/cloud/hypervisor/HypervisorGuru.java +++ b/api/src/com/cloud/hypervisor/HypervisorGuru.java @@ -75,4 +75,6 @@ public interface HypervisorGuru extends Adapter { * */ List finalizeExpungeNics(VirtualMachine vm, List nics); + + List finalizeExpungeVolumes(VirtualMachine vm); } diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index d19c999f16e..2197c2b7143 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network; +import java.io.Serializable; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -33,7 +34,7 @@ import com.cloud.utils.fsm.StateObject; /** * owned by an account. */ -public interface Network extends ControlledEntity, StateObject, InternalIdentity, Identity { +public interface Network extends ControlledEntity, StateObject, InternalIdentity, Identity, Serializable { public enum GuestType { Shared, Isolated @@ -47,9 +48,9 @@ public interface Network extends ControlledEntity, StateObject, I public static final Service Dns = new Service("Dns", Capability.AllowDnsSuffixModification); public static final Service Gateway = new Service("Gateway"); public static final Service Firewall = new Service("Firewall", Capability.SupportedProtocols, Capability.MultipleIps, Capability.TrafficStatistics, - Capability.SupportedTrafficDirection, Capability.SupportedEgressProtocols); + Capability.SupportedTrafficDirection, Capability.SupportedEgressProtocols); public static final Service Lb = new Service("Lb", Capability.SupportedLBAlgorithms, Capability.SupportedLBIsolation, Capability.SupportedProtocols, - Capability.TrafficStatistics, Capability.LoadBalancingSupportedIps, Capability.SupportedStickinessMethods, Capability.ElasticLb, Capability.LbSchemes); + Capability.TrafficStatistics, Capability.LoadBalancingSupportedIps, Capability.SupportedStickinessMethods, Capability.ElasticLb, Capability.LbSchemes); public static final Service UserData = new Service("UserData"); public static final Service SourceNat = new Service("SourceNat", Capability.SupportedSourceNatTypes, Capability.RedundantRouter); public static final Service StaticNat = new Service("StaticNat", Capability.ElasticIp); @@ -128,6 +129,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Provider CiscoVnmc = new Provider("CiscoVnmc", true); // add new Ovs provider public static final Provider Ovs = new Provider("Ovs", false); + public static final Provider Opendaylight = new Provider("Opendaylight", false); private final String name; private final boolean isExternal; @@ -213,7 +215,7 @@ public interface Network extends ControlledEntity, StateObject, I Allocated("Indicates the network configuration is in allocated but not setup"), Setup("Indicates the network configuration is setup"), Implementing( "Indicates the network configuration is being implemented"), Implemented("Indicates the network configuration is in use"), Shutdown( - "Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed"); + "Indicates the network configuration is being destroyed"), Destroy("Indicates that the network is destroyed"); protected static final StateMachine2 s_fsm = new StateMachine2(); @@ -223,7 +225,7 @@ public interface Network extends ControlledEntity, StateObject, I s_fsm.addTransition(State.Implementing, Event.OperationFailed, State.Shutdown); s_fsm.addTransition(State.Implemented, Event.DestroyNetwork, State.Shutdown); s_fsm.addTransition(State.Shutdown, Event.OperationSucceeded, State.Allocated); - s_fsm.addTransition(State.Shutdown, Event.OperationFailed, State.Implemented); + s_fsm.addTransition(State.Shutdown, Event.OperationFailed, State.Shutdown); s_fsm.addTransition(State.Setup, Event.DestroyNetwork, State.Destroy); s_fsm.addTransition(State.Allocated, Event.DestroyNetwork, State.Destroy); } diff --git a/api/src/com/cloud/network/NetworkModel.java b/api/src/com/cloud/network/NetworkModel.java index fcb6a2e58f4..f6555db565e 100644 --- a/api/src/com/cloud/network/NetworkModel.java +++ b/api/src/com/cloud/network/NetworkModel.java @@ -60,6 +60,8 @@ public interface NetworkModel { */ List listPublicIpsAssignedToGuestNtwk(long accountId, long associatedNetworkId, Boolean sourceNat); + List listPublicIpsAssignedToGuestNtwk(long associatedNetworkId, Boolean sourceNat); + List getSystemAccountNetworkOfferings(String... offeringNames); List getNics(long vmId); diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 57e95c947cd..489bc6cebf0 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -62,7 +62,7 @@ public interface NetworkService { Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceAllocationException; - List searchForNetworks(ListNetworksCmd cmd); + Pair, Integer> searchForNetworks(ListNetworksCmd cmd); boolean deleteNetwork(long networkId); @@ -168,7 +168,7 @@ public interface NetworkService { InsufficientCapacityException; /* Requests an IP address for the guest nic */ - NicSecondaryIp allocateSecondaryGuestIP(Account account, long zoneId, Long nicId, Long networkId, String ipaddress) throws InsufficientAddressCapacityException; + NicSecondaryIp allocateSecondaryGuestIP(long nicId, String ipaddress) throws InsufficientAddressCapacityException; boolean releaseSecondaryIpFromNic(long ipAddressId); diff --git a/api/src/com/cloud/network/Networks.java b/api/src/com/cloud/network/Networks.java index e4034a5d174..b2c04cda00c 100755 --- a/api/src/com/cloud/network/Networks.java +++ b/api/src/com/cloud/network/Networks.java @@ -94,7 +94,7 @@ public class Networks { return uri.getSchemeSpecificPart(); } }, - Mido("mido", String.class), Pvlan("pvlan", String.class), Vxlan("vxlan", Long.class), UnDecided(null, null); + Mido("mido", String.class), Pvlan("pvlan", String.class), Vxlan("vxlan", Long.class), UnDecided(null, null), OpenDaylight("opendaylight", String.class); private final String scheme; private final Class type; diff --git a/api/src/com/cloud/network/PhysicalNetwork.java b/api/src/com/cloud/network/PhysicalNetwork.java index cfa236d3898..5c348c21b7a 100644 --- a/api/src/com/cloud/network/PhysicalNetwork.java +++ b/api/src/com/cloud/network/PhysicalNetwork.java @@ -33,7 +33,7 @@ public interface PhysicalNetwork extends Identity, InternalIdentity { } public enum IsolationMethod { - VLAN, L3, GRE, STT, VNS, MIDO, SSP, VXLAN; + VLAN, L3, GRE, STT, VNS, MIDO, SSP, VXLAN, ODL; } public enum BroadcastDomainRange { diff --git a/api/src/com/cloud/network/security/SecurityGroupService.java b/api/src/com/cloud/network/security/SecurityGroupService.java index 1ee504fb9a4..d8b3346f54a 100644 --- a/api/src/com/cloud/network/security/SecurityGroupService.java +++ b/api/src/com/cloud/network/security/SecurityGroupService.java @@ -47,5 +47,5 @@ public interface SecurityGroupService { public List authorizeSecurityGroupEgress(AuthorizeSecurityGroupEgressCmd cmd); - public boolean securityGroupRulesForVmSecIp(Long nicId, Long networkId, String secondaryIp, boolean ruleAction); + public boolean securityGroupRulesForVmSecIp(long nicId, String secondaryIp, boolean ruleAction); } diff --git a/api/src/com/cloud/server/ResourceTag.java b/api/src/com/cloud/server/ResourceTag.java index 93dccbadb79..89458fc9614 100644 --- a/api/src/com/cloud/server/ResourceTag.java +++ b/api/src/com/cloud/server/ResourceTag.java @@ -25,11 +25,9 @@ public interface ResourceTag extends ControlledEntity, Identity, InternalIdentit // FIXME - extract enum to another interface as its used both by resourceTags and resourceMetaData code public enum ResourceObjectType { UserVm(true, true), Template(true, true), ISO(true, false), Volume(true, true), Snapshot(true, false), Network(true, true), Nic(false, true), LoadBalancer(true, true), PortForwardingRule( - true, true), FirewallRule(true, true), - - SecurityGroup(true, false), PublicIpAddress(true, true), Project(true, false), Vpc(true, true), NetworkACL(true, true), StaticRoute(true, false), VMSnapshot(true, false), RemoteAccessVpn( - true, true), Zone(false, true), ServiceOffering(false, true), Storage(false, true), PrivateGateway(false, true), NetworkACLList(false, true), VpnGateway(false, - true), CustomerGateway(false, true), VpnConnection(false, true); + true, true), FirewallRule(true, true), SecurityGroup(true, false), PublicIpAddress(true, true), Project(true, false), Vpc(true, true), NetworkACL(true, true), StaticRoute( + true, false), VMSnapshot(true, false), RemoteAccessVpn(true, true), Zone(false, true), ServiceOffering(false, true), Storage(false, true), PrivateGateway(false, + true), NetworkACLList(false, true), VpnGateway(false, true), CustomerGateway(false, true), VpnConnection(false, true), User(true, true), DiskOffering(false, true); ResourceObjectType(boolean resourceTagsSupport, boolean resourceMetadataSupport) { this.resourceTagsSupport = resourceTagsSupport; diff --git a/api/src/com/cloud/storage/DataStoreRole.java b/api/src/com/cloud/storage/DataStoreRole.java index 0a62a569e55..cc20cc0ce96 100644 --- a/api/src/com/cloud/storage/DataStoreRole.java +++ b/api/src/com/cloud/storage/DataStoreRole.java @@ -24,13 +24,13 @@ public enum DataStoreRole { Primary("primary"), Image("image"), ImageCache("imagecache"), Backup("backup"); public boolean isImageStore() { - return (this.role.equalsIgnoreCase("image") || this.role.equalsIgnoreCase("imagecache")) ? true : false; + return (role.equalsIgnoreCase("image") || role.equalsIgnoreCase("imagecache")) ? true : false; } private final String role; DataStoreRole(String type) { - this.role = type; + role = type; } public static DataStoreRole getRole(String role) { diff --git a/api/src/com/cloud/storage/Storage.java b/api/src/com/cloud/storage/Storage.java index f1868a7a1b3..2175c9b1a0c 100755 --- a/api/src/com/cloud/storage/Storage.java +++ b/api/src/com/cloud/storage/Storage.java @@ -28,6 +28,8 @@ public class Storage { OVA(true, true, true, "ova"), VHDX(true, true, true, "vhdx"), BAREMETAL(false, false, false, "BAREMETAL"), + VMDK(true, true, false, "vmdk"), + VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"); private final boolean thinProvisioned; diff --git a/api/src/com/cloud/storage/StorageService.java b/api/src/com/cloud/storage/StorageService.java index ac3c385695f..7fc1e16ff3a 100644 --- a/api/src/com/cloud/storage/StorageService.java +++ b/api/src/com/cloud/storage/StorageService.java @@ -17,8 +17,8 @@ package com.cloud.storage; import java.net.UnknownHostException; +import java.util.Map; -import org.apache.cloudstack.api.command.admin.storage.AddImageStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; @@ -92,19 +92,23 @@ public interface StorageService { boolean deleteSecondaryStagingStore(DeleteSecondaryStagingStoreCmd cmd); - ImageStore discoverImageStore(AddImageStoreCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException; + public ImageStore discoverImageStore(String name, String url, String providerName, Long dcId, Map details) throws IllegalArgumentException, DiscoveryException, + InvalidParameterValueException; - /** - * Prepare NFS secondary storage for object store migration - * - * @param cmd - * - the command specifying secondaryStorageId - * @return the storage pool - * @throws ResourceUnavailableException - * TODO - * @throws InsufficientCapacityException - * TODO + + /** + * Migrate existing NFS to use object store. + * @param name object store name. + * @param url object store url. + * @param providerName object store provider Name. + * @param details object store other details + * @return Object store created. + * @throws IllegalArgumentException + * @throws DiscoveryException + * @throws InvalidParameterValueException */ - public ImageStore prepareSecondaryStorageForObjectStoreMigration(Long storeId) throws ResourceUnavailableException, InsufficientCapacityException; + public ImageStore migrateToObjectStore(String name, String url, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, + InvalidParameterValueException; + } diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index bb5ee08b512..84f0872f909 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -189,7 +189,5 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba Long getVmSnapshotChainSize(); - void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve); - Integer getHypervisorSnapshotReserve(); } diff --git a/api/src/com/cloud/user/ResourceLimitService.java b/api/src/com/cloud/user/ResourceLimitService.java index e878edd56ee..de2773a955c 100644 --- a/api/src/com/cloud/user/ResourceLimitService.java +++ b/api/src/com/cloud/user/ResourceLimitService.java @@ -140,7 +140,7 @@ public interface ResourceLimitService { public long getResourceCount(Account account, ResourceType type); /** - * Checks if a limit has been exceeded for an account depending on the displayResource flag + * Checks if a limit has been exceeded for an account if displayResource flag is on * * @param account * @param type @@ -153,7 +153,7 @@ public interface ResourceLimitService { void checkResourceLimit(Account account, ResourceType type, Boolean displayResource, long... count) throws ResourceAllocationException; /** - * Increments the resource count depending on the displayResource flag + * Increments the resource count if displayResource flag is on * * @param accountId * @param type @@ -163,7 +163,7 @@ public interface ResourceLimitService { void incrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta); /** - * Increments/Decrements the resource count depending on the displayResource flag + * Increments/Decrements the resource count depending on the displayResource flag is turned on or off respectively * * @param accountId * @param type @@ -172,5 +172,13 @@ public interface ResourceLimitService { */ void changeResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta); + /** + * Decrements the resource count if displayResource flag is on + * + * @param accountId + * @param type + * @param displayResource + * @param delta + */ void decrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta); } diff --git a/api/src/com/cloud/vm/NicProfile.java b/api/src/com/cloud/vm/NicProfile.java index ab18ca094f9..4dd7f68ee33 100644 --- a/api/src/com/cloud/vm/NicProfile.java +++ b/api/src/com/cloud/vm/NicProfile.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.vm; +import java.io.Serializable; import java.net.URI; import org.apache.cloudstack.api.InternalIdentity; @@ -27,7 +28,9 @@ import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; import com.cloud.vm.Nic.ReservationStrategy; -public class NicProfile implements InternalIdentity { +public class NicProfile implements InternalIdentity, Serializable { + private static final long serialVersionUID = 4997005771736090304L; + long id; long networkId; BroadcastDomainType broadcastType; @@ -57,6 +60,7 @@ public class NicProfile implements InternalIdentity { String name; String requestedIpv4; String requestedIpv6; + String uuid; public String getDns1() { return dns1; @@ -142,6 +146,10 @@ public class NicProfile implements InternalIdentity { this.format = format; } + public void setUuid(String uuid) { + this.uuid = uuid; + } + public void setTrafficType(TrafficType trafficType) { this.trafficType = trafficType; } @@ -215,29 +223,34 @@ public class NicProfile implements InternalIdentity { return strategy; } + public String getUuid() { + return uuid; + } + public NicProfile(Nic nic, Network network, URI broadcastUri, URI isolationUri, Integer networkRate, boolean isSecurityGroupEnabled, String name) { - this.id = nic.getId(); - this.networkId = network.getId(); - this.gateway = nic.getGateway(); - this.mode = network.getMode(); - this.broadcastType = network.getBroadcastDomainType(); - this.trafficType = network.getTrafficType(); - this.ip4Address = nic.getIp4Address(); - this.format = nic.getAddressFormat(); - this.ip6Address = nic.getIp6Address(); - this.macAddress = nic.getMacAddress(); - this.reservationId = nic.getReservationId(); - this.strategy = nic.getReservationStrategy(); - this.deviceId = nic.getDeviceId(); - this.defaultNic = nic.isDefaultNic(); + id = nic.getId(); + networkId = network.getId(); + gateway = nic.getGateway(); + mode = network.getMode(); + broadcastType = network.getBroadcastDomainType(); + trafficType = network.getTrafficType(); + ip4Address = nic.getIp4Address(); + format = nic.getAddressFormat(); + ip6Address = nic.getIp6Address(); + macAddress = nic.getMacAddress(); + reservationId = nic.getReservationId(); + strategy = nic.getReservationStrategy(); + deviceId = nic.getDeviceId(); + defaultNic = nic.isDefaultNic(); this.broadcastUri = broadcastUri; this.isolationUri = isolationUri; - this.netmask = nic.getNetmask(); + netmask = nic.getNetmask(); this.isSecurityGroupEnabled = isSecurityGroupEnabled; - this.vmId = nic.getInstanceId(); + vmId = nic.getInstanceId(); this.name = name; - this.ip6Cidr = nic.getIp6Cidr(); - this.ip6Gateway = nic.getIp6Gateway(); + ip6Cidr = nic.getIp6Cidr(); + ip6Gateway = nic.getIp6Gateway(); + uuid = nic.getUuid(); if (networkRate != null) { this.networkRate = networkRate; @@ -245,7 +258,7 @@ public class NicProfile implements InternalIdentity { } public NicProfile(ReservationStrategy strategy, String ip4Address, String macAddress, String gateway, String netmask) { - this.format = AddressFormat.Ip4; + format = AddressFormat.Ip4; this.ip4Address = ip4Address; this.macAddress = macAddress; this.gateway = gateway; @@ -274,11 +287,11 @@ public class NicProfile implements InternalIdentity { } public boolean isSecurityGroupEnabled() { - return this.isSecurityGroupEnabled; + return isSecurityGroupEnabled; } public void setSecurityGroupEnabled(boolean enabled) { - this.isSecurityGroupEnabled = enabled; + isSecurityGroupEnabled = enabled; } public String getRequestedIpv4() { @@ -286,36 +299,36 @@ public class NicProfile implements InternalIdentity { } public void deallocate() { - this.gateway = null; - this.mode = null; - this.format = null; - this.broadcastType = null; - this.trafficType = null; - this.ip4Address = null; - this.ip6Address = null; - this.macAddress = null; - this.reservationId = null; - this.strategy = null; - this.deviceId = null; - this.broadcastUri = null; - this.isolationUri = null; - this.netmask = null; - this.dns1 = null; - this.dns2 = null; + gateway = null; + mode = null; + format = null; + broadcastType = null; + trafficType = null; + ip4Address = null; + ip6Address = null; + macAddress = null; + reservationId = null; + strategy = null; + deviceId = null; + broadcastUri = null; + isolationUri = null; + netmask = null; + dns1 = null; + dns2 = null; } @Override public String toString() { return new StringBuilder("NicProfile[").append(id) - .append("-") - .append(vmId) - .append("-") - .append(reservationId) - .append("-") - .append(ip4Address) - .append("-") - .append(broadcastUri) - .toString(); + .append("-") + .append(vmId) + .append("-") + .append(reservationId) + .append("-") + .append(ip4Address) + .append("-") + .append(broadcastUri) + .toString(); } public String getIp6Gateway() { diff --git a/api/src/com/cloud/vm/VirtualMachineName.java b/api/src/com/cloud/vm/VirtualMachineName.java index c5fc3de5ab1..4bbcb64ebca 100755 --- a/api/src/com/cloud/vm/VirtualMachineName.java +++ b/api/src/com/cloud/vm/VirtualMachineName.java @@ -26,6 +26,19 @@ import com.cloud.dc.Vlan; public class VirtualMachineName { public static final String SEPARATOR = "-"; + public static boolean isValidCloudStackVmName(String name, String instance) { + String[] parts = name.split(SEPARATOR); + if (parts.length <= 1) { + return false; + } + + if (!parts[parts.length - 1].equals(instance)) { + return false; + } + + return true; + } + public static String getVnetName(long vnetId) { StringBuilder vnet = new StringBuilder(); Formatter formatter = new Formatter(vnet); diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 8f596b7c2a0..287fff3035b 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -72,7 +72,6 @@ public class ApiConstants { public static final String DISPLAY_VM = "displayvm"; public static final String DISPLAY_OFFERING = "displayoffering"; public static final String DISPLAY_VOLUME = "displayvolume"; - public static final String CUSTOM_PARAMETERS = "customparameters"; public static final String DNS1 = "dns1"; public static final String DNS2 = "dns2"; public static final String IP6_DNS1 = "ip6dns1"; @@ -210,6 +209,7 @@ public class ApiConstants { public static final String SENT_BYTES = "sentbytes"; public static final String SERVICE_OFFERING_ID = "serviceofferingid"; public static final String SHOW_CAPACITIES = "showcapacities"; + public static final String SHOW_REMOVED = "showremoved"; public static final String SIZE = "size"; public static final String SNAPSHOT_ID = "snapshotid"; public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; @@ -554,6 +554,6 @@ public class ApiConstants { } public enum VMDetails { - all, group, nics, stats, secgrp, tmpl, servoff, iso, volume, min, affgrp; + all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp; } } diff --git a/api/src/org/apache/cloudstack/api/ServerApiException.java b/api/src/org/apache/cloudstack/api/ServerApiException.java index 81184e3f4f3..a8bb2ed71c9 100644 --- a/api/src/org/apache/cloudstack/api/ServerApiException.java +++ b/api/src/org/apache/cloudstack/api/ServerApiException.java @@ -82,4 +82,8 @@ public class ServerApiException extends CloudRuntimeException { _description = description; } + @Override + public String getMessage() { + return _description; + } } diff --git a/api/src/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java b/api/src/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java index 61f6f49091c..b573bea3e84 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java @@ -24,7 +24,6 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.storage.AddImageStoreCmd; import org.apache.cloudstack.api.response.ImageStoreResponse; import org.apache.cloudstack.api.response.ZoneResponse; @@ -74,20 +73,15 @@ public class AddSecondaryStorageCmd extends BaseCmd { } @Override - public void execute() { - AddImageStoreCmd cmd = new AddImageStoreCmd(); - cmd.setUrl(this.getUrl()); - cmd.setZoneId(this.getZoneId()); - cmd.setProviderName("NFS"); - - try { - ImageStore result = _storageService.discoverImageStore(cmd); + public void execute(){ + try{ + ImageStore result = _storageService.discoverImageStore(null, getUrl(), "NFS", getZoneId(), null); ImageStoreResponse storeResponse = null; - if (result != null) { - storeResponse = _responseGenerator.createImageStoreResponse(result); - storeResponse.setResponseName(getCommandName()); - storeResponse.setObjectName("secondarystorage"); - this.setResponseObject(storeResponse); + if (result != null ) { + storeResponse = _responseGenerator.createImageStoreResponse(result); + storeResponse.setResponseName(getCommandName()); + storeResponse.setObjectName("secondarystorage"); + setResponseObject(storeResponse); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add secondary storage"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java index 5dcedff2392..5e5bd3dcde5 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/storage/AddImageStoreCmd.java @@ -95,7 +95,7 @@ public class AddImageStoreCmd extends BaseCmd { } public String getProviderName() { - return this.providerName; + return providerName; } public void setUrl(String url) { @@ -129,15 +129,15 @@ public class AddImageStoreCmd extends BaseCmd { } @Override - public void execute() { - try { - ImageStore result = _storageService.discoverImageStore(this); + public void execute(){ + try{ + ImageStore result = _storageService.discoverImageStore(getName(), getUrl(), getProviderName(), getZoneId(), getDetails()); ImageStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createImageStoreResponse(result); storeResponse.setResponseName(getCommandName()); storeResponse.setObjectName("imagestore"); - this.setResponseObject(storeResponse); + setResponseObject(storeResponse); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add secondary storage"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/AddS3Cmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/AddS3Cmd.java index e665b7d0d24..32897e4d59d 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/storage/AddS3Cmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/storage/AddS3Cmd.java @@ -86,33 +86,27 @@ public final class AddS3Cmd extends BaseCmd { public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { - AddImageStoreCmd cmd = new AddImageStoreCmd() { - @Override - public Map getDetails() { - Map dm = new HashMap(); - dm.put(ApiConstants.S3_ACCESS_KEY, getAccessKey()); - dm.put(ApiConstants.S3_SECRET_KEY, getSecretKey()); - dm.put(ApiConstants.S3_END_POINT, getEndPoint()); - dm.put(ApiConstants.S3_BUCKET_NAME, getBucketName()); - if (getHttpsFlag() != null) { - dm.put(ApiConstants.S3_HTTPS_FLAG, getHttpsFlag().toString()); - } - if (getConnectionTimeout() != null) { - dm.put(ApiConstants.S3_CONNECTION_TIMEOUT, getConnectionTimeout().toString()); - } - if (getMaxErrorRetry() != null) { - dm.put(ApiConstants.S3_MAX_ERROR_RETRY, getMaxErrorRetry().toString()); - } - if (getSocketTimeout() != null) { - dm.put(ApiConstants.S3_SOCKET_TIMEOUT, getSocketTimeout().toString()); - } - return dm; - } - }; - cmd.setProviderName("S3"); + Map dm = new HashMap(); + dm.put(ApiConstants.S3_ACCESS_KEY, getAccessKey()); + dm.put(ApiConstants.S3_SECRET_KEY, getSecretKey()); + dm.put(ApiConstants.S3_END_POINT, getEndPoint()); + dm.put(ApiConstants.S3_BUCKET_NAME, getBucketName()); + if (getHttpsFlag() != null) { + dm.put(ApiConstants.S3_HTTPS_FLAG, getHttpsFlag().toString()); + } + if (getConnectionTimeout() != null) { + dm.put(ApiConstants.S3_CONNECTION_TIMEOUT, getConnectionTimeout().toString()); + } + if (getMaxErrorRetry() != null) { + dm.put(ApiConstants.S3_MAX_ERROR_RETRY, getMaxErrorRetry().toString()); + } + if (getSocketTimeout() != null) { + dm.put(ApiConstants.S3_SOCKET_TIMEOUT, getSocketTimeout().toString()); + } - try { - ImageStore result = _storageService.discoverImageStore(cmd); + + try{ + ImageStore result = _storageService.discoverImageStore(null, null, "S3", null, dm); ImageStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createImageStoreResponse(result); diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java deleted file mode 100644 index a57f2dfeffc..00000000000 --- a/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -package org.apache.cloudstack.api.command.admin.storage; - -import org.apache.log4j.Logger; - -import org.apache.cloudstack.api.APICommand; -import org.apache.cloudstack.api.ApiCommandJobType; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiErrorCode; -import org.apache.cloudstack.api.BaseAsyncCmd; -import org.apache.cloudstack.api.Parameter; -import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.response.ImageStoreResponse; -import org.apache.cloudstack.context.CallContext; - -import com.cloud.event.EventTypes; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.storage.ImageStore; -import com.cloud.user.Account; - -@APICommand(name = "prepareSecondaryStorageForMigration", - description = "Prepare a NFS secondary storage to migrate to use object store like S3", - responseObject = ImageStoreResponse.class) -public class PrepareSecondaryStorageForMigrationCmd extends BaseAsyncCmd { - public static final Logger s_logger = Logger.getLogger(PrepareSecondaryStorageForMigrationCmd.class.getName()); - private static final String s_name = "preparesecondarystorageformigrationresponse"; - - ///////////////////////////////////////////////////// - //////////////// API parameters ///////////////////// - ///////////////////////////////////////////////////// - - @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class, required = true, description = "Secondary image store ID") - private Long id; - - ///////////////////////////////////////////////////// - /////////////////// Accessors /////////////////////// - ///////////////////////////////////////////////////// - - public Long getId() { - return id; - } - - ///////////////////////////////////////////////////// - /////////////// API Implementation/////////////////// - ///////////////////////////////////////////////////// - - @Override - public String getCommandName() { - return s_name; - } - - @Override - public ApiCommandJobType getInstanceType() { - return ApiCommandJobType.ImageStore; - } - - @Override - public Long getInstanceId() { - return getId(); - } - - @Override - public long getEntityOwnerId() { - Account account = CallContext.current().getCallingAccount(); - if (account != null) { - return account.getId(); - } - - return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked - } - - @Override - public String getEventType() { - return EventTypes.EVENT_MIGRATE_PREPARE_SECONDARY_STORAGE; - } - - @Override - public String getEventDescription() { - return "preparing secondary storage: " + getId() + " for object store migration"; - } - - @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException { - ImageStore result = _storageService.prepareSecondaryStorageForObjectStoreMigration(getId()); - if (result != null) { - ImageStoreResponse response = _responseGenerator.createImageStoreResponse(result); - response.setResponseName(getCommandName()); - response.setResponseName("secondarystorage"); - setResponseObject(response); - } else { - throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare secondary storage for object store migration"); - } - } -} diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java new file mode 100644 index 00000000000..983a01c7fbe --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/storage/UpdateCloudToUseObjectStoreCmd.java @@ -0,0 +1,142 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.storage; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ImageStoreResponse; + +import com.cloud.exception.DiscoveryException; +import com.cloud.storage.ImageStore; +import com.cloud.user.Account; + +@APICommand(name = "updateCloudToUseObjectStore", description = "Migrate current NFS secondary storages to use object store.", responseObject = ImageStoreResponse.class, since = "4.3.0") +public class UpdateCloudToUseObjectStoreCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(UpdateCloudToUseObjectStoreCmd.class.getName()); + private static final String s_name = "updatecloudtouseobjectstoreresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="the name for the image store") + private String name; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, description="the URL for the image store") + private String url; + + @Parameter(name=ApiConstants.PROVIDER, type=CommandType.STRING, + required=true, description="the image store provider name") + private String providerName; + + @Parameter(name=ApiConstants.DETAILS, type=CommandType.MAP, description="the details for the image store. Example: details[0].key=accesskey&details[0].value=s389ddssaa&details[1].key=secretkey&details[1].value=8dshfsss") + private Map details; + + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public String getUrl() { + return url; + } + + public String getName() { + return name; + } + + public Map getDetails() { + Map detailsMap = null; + if (details != null && !details.isEmpty()) { + detailsMap = new HashMap(); + Collection props = details.values(); + Iterator iter = props.iterator(); + while (iter.hasNext()) { + HashMap detail = (HashMap) iter.next(); + String key = detail.get("key"); + String value = detail.get("value"); + detailsMap.put(key, value); + } + } + return detailsMap; + } + + public String getProviderName() { + return providerName; + } + + public void setUrl(String url) { + this.url = url; + } + + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + public void setDetails(Map details) { + this.details = details; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public void execute(){ + try{ + ImageStore result = _storageService.migrateToObjectStore(getName(), getUrl(), getProviderName(), getDetails()); + ImageStoreResponse storeResponse = null; + if (result != null ) { + storeResponse = _responseGenerator.createImageStoreResponse(result); + storeResponse.setResponseName(getCommandName()); + storeResponse.setObjectName("imagestore"); + setResponseObject(storeResponse); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add secondary storage"); + } + } catch (DiscoveryException ex) { + s_logger.warn("Exception: ", ex); + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage()); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java b/api/src/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java index 3b684f41a63..173c95e2c0d 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/swift/AddSwiftCmd.java @@ -27,7 +27,6 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.command.admin.storage.AddImageStoreCmd; import org.apache.cloudstack.api.response.ImageStoreResponse; import com.cloud.exception.DiscoveryException; @@ -91,27 +90,19 @@ public class AddSwiftCmd extends BaseCmd { @Override public void execute() { - AddImageStoreCmd cmd = new AddImageStoreCmd() { - @Override - public Map getDetails() { - Map dm = new HashMap(); - dm.put(ApiConstants.ACCOUNT, getAccount()); - dm.put(ApiConstants.USERNAME, getUsername()); - dm.put(ApiConstants.KEY, getKey()); - return dm; - } - }; - cmd.setProviderName("Swift"); - cmd.setUrl(this.getUrl()); + Map dm = new HashMap(); + dm.put(ApiConstants.ACCOUNT, getAccount()); + dm.put(ApiConstants.USERNAME, getUsername()); + dm.put(ApiConstants.KEY, getKey()); - try { - ImageStore result = _storageService.discoverImageStore(cmd); + try{ + ImageStore result = _storageService.discoverImageStore(null, getUrl(), "Swift", null, dm); ImageStoreResponse storeResponse = null; if (result != null) { storeResponse = _responseGenerator.createImageStoreResponse(result); storeResponse.setResponseName(getCommandName()); storeResponse.setObjectName("secondarystorage"); - this.setResponseObject(storeResponse); + setResponseObject(storeResponse); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Swift secondary storage"); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java b/api/src/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java index fdb81ea3ee0..370815d625c 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/systemvm/ScaleSystemVMCmd.java @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.systemvm; -import org.apache.log4j.Logger; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,6 +31,7 @@ import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; @@ -39,15 +43,8 @@ import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -@APICommand(name = "scaleSystemVm", - responseObject = SystemVmResponse.class, - description = "Scale the service offering for a system vm (console proxy or secondary storage). " + "The system vm must be in a \"Stopped\" state for " - + "this command to take effect.") +@APICommand(name = "scaleSystemVm", responseObject = SystemVmResponse.class, description = "Scale the service offering for a system vm (console proxy or secondary storage). " + + "The system vm must be in a \"Stopped\" state for " + "this command to take effect.") public class ScaleSystemVMCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(UpgradeVMCmd.class.getName()); private static final String s_name = "changeserviceforsystemvmresponse"; @@ -59,17 +56,11 @@ public class ScaleSystemVMCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, description = "The ID of the system vm") private Long id; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - required = true, - description = "the service offering ID to apply to the system vm") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the service offering ID to apply to the system vm") private Long serviceOfferingId; - @Parameter(name = ApiConstants.CUSTOM_PARAMETERS, - type = CommandType.MAP, - description = "name value pairs of custom parameters for cpu, memory and cpunumber. example customparameters[i].name=value") - private Map customParameters; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpu, memory and cpunumber. example details[i].name=value") + private Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -83,10 +74,10 @@ public class ScaleSystemVMCmd extends BaseAsyncCmd { return serviceOfferingId; } - public Map getCustomParameters() { + public Map getDetails() { Map customparameterMap = new HashMap(); - if (customParameters != null && customParameters.size() != 0) { - Collection parameterCollection = customParameters.values(); + if (details != null && details.size() != 0) { + Collection parameterCollection = details.values(); Iterator iter = parameterCollection.iterator(); while (iter.hasNext()) { HashMap value = (HashMap)iter.next(); diff --git a/api/src/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java b/api/src/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java index a89abb8c7ab..da0564e9e67 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/systemvm/UpgradeSystemVMCmd.java @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.systemvm; -import org.apache.log4j.Logger; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -28,21 +31,15 @@ import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.SystemVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.exception.InvalidParameterValueException; import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; import com.cloud.vm.VirtualMachine; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -@APICommand(name = "changeServiceForSystemVm", - responseObject = SystemVmResponse.class, - description = "Changes the service offering for a system vm (console proxy or secondary storage). " + "The system vm must be in a \"Stopped\" state for " - + "this command to take effect.") +@APICommand(name = "changeServiceForSystemVm", responseObject = SystemVmResponse.class, description = "Changes the service offering for a system vm (console proxy or secondary storage). " + + "The system vm must be in a \"Stopped\" state for " + "this command to take effect.") public class UpgradeSystemVMCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpgradeVMCmd.class.getName()); private static final String s_name = "changeserviceforsystemvmresponse"; @@ -54,17 +51,11 @@ public class UpgradeSystemVMCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = SystemVmResponse.class, required = true, description = "The ID of the system vm") private Long id; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - required = true, - description = "the service offering ID to apply to the system vm") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the service offering ID to apply to the system vm") private Long serviceOfferingId; - @Parameter(name = ApiConstants.CUSTOM_PARAMETERS, - type = CommandType.MAP, - description = "name value pairs of custom parameters for cpu, memory and cpunumber. example customparameters[i].name=value") - private Map customParameters; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpu, memory and cpunumber. example details[i].name=value") + private Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -78,10 +69,10 @@ public class UpgradeSystemVMCmd extends BaseCmd { return serviceOfferingId; } - public Map getCustomParameters() { + public Map getDetails() { Map customparameterMap = new HashMap(); - if (customParameters != null && customParameters.size() != 0) { - Collection parameterCollection = customParameters.values(); + if (details != null && details.size() != 0) { + Collection parameterCollection = details.values(); Iterator iter = parameterCollection.iterator(); while (iter.hasNext()) { HashMap value = (HashMap)iter.next(); diff --git a/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java index dbaac6e742a..cd3dd7ea9bf 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java @@ -147,6 +147,9 @@ public class CreateVlanIpRangeCmd extends BaseCmd { } public String getVlan() { + if (vlan == null || vlan.isEmpty()) { + vlan = "untagged"; + } return vlan; } diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java index 367dec91ce7..0761a643452 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java @@ -17,7 +17,6 @@ package org.apache.cloudstack.api.command.user.affinitygroup; import org.apache.log4j.Logger; - import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; @@ -79,9 +78,9 @@ public class ListAffinityGroupsCmd extends BaseListAccountResourcesCmd { @Override public void execute() { - ListResponse response = - _queryService.listAffinityGroups(id, affinityGroupName, affinityGroupType, virtualMachineId, this.getAccountName(), this.getDomainId(), this.isRecursive(), - this.listAll(), this.getStartIndex(), this.getPageSizeVal()); + ListResponse response = _queryService.listAffinityGroups(id, affinityGroupName, + affinityGroupType, virtualMachineId, this.getAccountName(), this.getDomainId(), this.isRecursive(), + this.listAll(), this.getStartIndex(), this.getPageSizeVal(), this.getKeyword()); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java b/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 3e66e52249f..95b771fcc43 100644 --- a/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -73,6 +73,9 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "the ID of the zone") private Long zoneId; + @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description="show removed ISOs as well") + private Boolean showRemoved; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -109,6 +112,10 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd { return zoneId; } + public Boolean getShowRemoved() { + return (showRemoved != null ? showRemoved : false); + } + public boolean listInReadyState() { Account account = CallContext.current().getCallingAccount(); // It is account specific if account is admin type and domainId and accountName are not null diff --git a/api/src/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java b/api/src/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java index 72c52df4630..111fac31174 100644 --- a/api/src/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/network/ListNetworksCmd.java @@ -19,8 +19,6 @@ package org.apache.cloudstack.api.command.user.network; import java.util.ArrayList; import java.util.List; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListTaggedResourcesCmd; @@ -30,8 +28,10 @@ import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; import com.cloud.network.Network; +import com.cloud.utils.Pair; @APICommand(name = "listNetworks", description = "Lists all available networks.", responseObject = NetworkResponse.class) public class ListNetworksCmd extends BaseListTaggedResourcesCmd { @@ -53,24 +53,16 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.IS_SYSTEM, type = CommandType.BOOLEAN, description = "true if network is system, false otherwise") private Boolean isSystem; - @Parameter(name = ApiConstants.ACL_TYPE, - type = CommandType.STRING, - description = "list networks by ACL (access control list) type. Supported values are Account and Domain") + @Parameter(name = ApiConstants.ACL_TYPE, type = CommandType.STRING, description = "list networks by ACL (access control list) type. Supported values are Account and Domain") private String aclType; @Parameter(name = ApiConstants.TRAFFIC_TYPE, type = CommandType.STRING, description = "type of the traffic") private String trafficType; - @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, - type = CommandType.UUID, - entityType = PhysicalNetworkResponse.class, - description = "list networks by physical network id") + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, description = "list networks by physical network id") private Long physicalNetworkId; - @Parameter(name = ApiConstants.SUPPORTED_SERVICES, - type = CommandType.LIST, - collectionType = CommandType.STRING, - description = "list networks supporting certain services") + @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, collectionType = CommandType.STRING, description = "list networks supporting certain services") private List supportedServices; @Parameter(name = ApiConstants.RESTART_REQUIRED, type = CommandType.BOOLEAN, description = "list networks by restartRequired") @@ -154,15 +146,14 @@ public class ListNetworksCmd extends BaseListTaggedResourcesCmd { @Override public void execute() { - List networks = _networkService.searchForNetworks(this); + Pair, Integer> networks = _networkService.searchForNetworks(this); ListResponse response = new ListResponse(); List networkResponses = new ArrayList(); - for (Network network : networks) { + for (Network network : networks.first()) { NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(network); networkResponses.add(networkResponse); } - - response.setResponses(networkResponses); + response.setResponses(networkResponses, networks.second()); response.setResponseName(getCommandName()); this.setResponseObject(response); } diff --git a/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java index 78bfc186d1c..87f94f72aa1 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/CopyTemplateCmd.java @@ -59,8 +59,7 @@ public class CopyTemplateCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.SOURCE_ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, - required = true, - description = "ID of the zone the template is currently hosted on.") + description = "ID of the zone the template is currently hosted on. If not specified and template is cross-zone, then we will sync this template to region wide image store.") private Long sourceZoneId; ///////////////////////////////////////////////////// @@ -137,7 +136,7 @@ public class CopyTemplateCmd extends BaseAsyncCmd { } response.setResponseName(getCommandName()); - this.setResponseObject(response); + setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to copy template"); } diff --git a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index 7e347f3e32f..d393c6a5fdf 100644 --- a/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -65,6 +65,9 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd { @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, description = "list templates by zoneId") private Long zoneId; + @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description="show removed templates as well") + private Boolean showRemoved; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -89,6 +92,10 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd { return zoneId; } + public Boolean getShowRemoved() { + return (showRemoved != null ? showRemoved : false); + } + public boolean listInReadyState() { Account account = CallContext.current().getCallingAccount(); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java index 09a912293db..c0e8d3e16a6 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/AddIpToVmNicCmd.java @@ -16,8 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -28,6 +26,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -39,10 +38,10 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; -import com.cloud.user.Account; import com.cloud.utils.net.NetUtils; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; +import com.cloud.vm.VirtualMachine; @APICommand(name = "addIpToNic", description = "Assigns secondary IP to NIC", responseObject = NicSecondaryIpResponse.class) public class AddIpToVmNicCmd extends BaseAsyncCmd { @@ -52,11 +51,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NIC_ID, - type = CommandType.UUID, - entityType = NicResponse.class, - required = true, - description = "the ID of the nic to which you want to assign private IP") + @Parameter(name = ApiConstants.NIC_ID, type = CommandType.UUID, entityType = NicResponse.class, required = true, description = "the ID of the nic to which you want to assign private IP") private Long nicId; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, required = false, description = "Secondary IP Address") @@ -70,36 +65,19 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { return "nic_secondary_ips"; } - public String getAccountName() { - return CallContext.current().getCallingAccount().getAccountName(); - } - - public long getDomainId() { - return CallContext.current().getCallingAccount().getDomainId(); - } - - private long getZoneId() { - Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); - if (ntwk == null) { - throw new InvalidParameterValueException("Can't find zone id for specified"); - } - return ntwk.getDataCenterId(); - } - - public Long getNetworkId() { + private long getNetworkId() { Nic nic = _entityMgr.findById(Nic.class, nicId); if (nic == null) { throw new InvalidParameterValueException("Can't find network id for specified nic"); } - Long networkId = nic.getNetworkId(); - return networkId; + return nic.getNetworkId(); } - public Long getNicId() { + public long getNicId() { return nicId; } - public String getIpaddress() { + private String getIpaddress() { if (ipAddr != null) { return ipAddr; } else { @@ -107,18 +85,12 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { } } - public NetworkType getNetworkType() { + private NetworkType getNetworkType() { Network ntwk = _entityMgr.findById(Network.class, getNetworkId()); DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId()); return dc.getNetworkType(); } - @Override - public long getEntityOwnerId() { - Account caller = CallContext.current().getCallingAccount(); - return caller.getAccountId(); - } - @Override public String getEventType() { return EventTypes.EVENT_NET_IP_ASSIGN; @@ -126,7 +98,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { @Override public String getEventDescription() { - return "associating ip to nic id: " + getNetworkId() + " in zone " + getZoneId(); + return "associating ip to nic id=" + getNicId() + " belonging to network id=" + getNetworkId(); } ///////////////////////////////////////////////////// @@ -156,7 +128,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { } try { - result = _networkService.allocateSecondaryGuestIP(_accountService.getAccount(getEntityOwnerId()), getZoneId(), getNicId(), getNetworkId(), getIpaddress()); + result = _networkService.allocateSecondaryGuestIP(getNicId(), getIpaddress()); } catch (InsufficientAddressCapacityException e) { throw new InvalidParameterValueException("Allocating guest ip for nic failed"); } @@ -166,7 +138,7 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { if (getNetworkType() == NetworkType.Basic) { // add security group rules for the secondary ip addresses boolean success = false; - success = _securityGroupService.securityGroupRulesForVmSecIp(getNicId(), getNetworkId(), secondaryIp, true); + success = _securityGroupService.securityGroupRulesForVmSecIp(getNicId(), secondaryIp, true); if (success == false) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set security group rules for the secondary ip"); } @@ -197,4 +169,16 @@ public class AddIpToVmNicCmd extends BaseAsyncCmd { return ApiCommandJobType.IpAddress; } + @Override + public long getEntityOwnerId() { + Nic nic = _entityMgr.findById(Nic.class, nicId); + if (nic == null) { + throw new InvalidParameterValueException("Can't find nic for id specified"); + } + long vmId = nic.getInstanceId(); + VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmId); + + return vm.getAccountId(); + } + } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 2472a422539..eb19db43866 100755 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -24,8 +24,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ACL; @@ -47,6 +45,7 @@ import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -66,9 +65,7 @@ import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.uservm.UserVm; -@APICommand(name = "deployVirtualMachine", - description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", - responseObject = UserVmResponse.class) +@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class) public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { public static final Logger s_logger = Logger.getLogger(DeployVMCmd.class.getName()); @@ -78,27 +75,15 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.ZONE_ID, - type = CommandType.UUID, - entityType = ZoneResponse.class, - required = true, - description = "availability zone for the virtual machine") + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, description = "availability zone for the virtual machine") private Long zoneId; @ACL - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - required = true, - description = "the ID of the service offering for the virtual machine") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the ID of the service offering for the virtual machine") private Long serviceOfferingId; @ACL - @Parameter(name = ApiConstants.TEMPLATE_ID, - type = CommandType.UUID, - entityType = TemplateResponse.class, - required = true, - description = "the ID of the template for the virtual machine") + @Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template for the virtual machine") private Long templateId; @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine") @@ -111,31 +96,21 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { @Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, description = "an optional account for the virtual machine. Must be used with domainId.") private String accountName; - @Parameter(name = ApiConstants.DOMAIN_ID, - type = CommandType.UUID, - entityType = DomainResponse.class, - description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.") + @Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class, description = "an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.") private Long domainId; //Network information @ACL(accessType = AccessType.UseNetwork) - @Parameter(name = ApiConstants.NETWORK_IDS, - type = CommandType.LIST, - collectionType = CommandType.UUID, - entityType = NetworkResponse.class, - description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") + @Parameter(name = ApiConstants.NETWORK_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = NetworkResponse.class, description = "list of network ids used by virtual machine. Can't be specified with ipToNetworkList parameter") private List networkIds; //DataDisk information @ACL - @Parameter(name = ApiConstants.DISK_OFFERING_ID, - type = CommandType.UUID, - entityType = DiskOfferingResponse.class, - description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," - + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " - + "offering for the data disk volume. If the templateId parameter passed is from a Template object," - + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " - + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") + @Parameter(name = ApiConstants.DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine. If the template is of ISO format," + + " the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the " + + "offering for the data disk volume. If the templateId parameter passed is from a Template object," + + " the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is " + + "from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") private Long diskOfferingId; @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, description = "the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") @@ -147,43 +122,27 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { @Parameter(name = ApiConstants.HYPERVISOR, type = CommandType.STRING, description = "the hypervisor on which to deploy the virtual machine") private String hypervisor; - @Parameter(name = ApiConstants.USER_DATA, - type = CommandType.STRING, - description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", - length = 32768) + @Parameter(name = ApiConstants.USER_DATA, type = CommandType.STRING, description = "an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding. Using HTTP POST(via POST body), you can send up to 32K of data after base64 encoding.", length = 32768) private String userData; @Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine") private String sshKeyPairName; - @Parameter(name = ApiConstants.HOST_ID, - type = CommandType.UUID, - entityType = HostResponse.class, - description = "destination Host ID to deploy the VM to - parameter available for root admin only") + @Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only") private Long hostId; @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, - type = CommandType.LIST, - collectionType = CommandType.UUID, - entityType = SecurityGroupResponse.class, - description = "comma separated list of security groups id that going to be applied to the virtual machine. " - + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") + @Parameter(name = ApiConstants.SECURITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups id that going to be applied to the virtual machine. " + + "Should be passed only when vm is created from a zone with Basic Network support." + " Mutually exclusive with securitygroupnames parameter") private List securityGroupIdList; @ACL - @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, - type = CommandType.LIST, - collectionType = CommandType.STRING, - entityType = SecurityGroupResponse.class, - description = "comma separated list of security groups names that going to be applied to the virtual machine." - + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") + @Parameter(name = ApiConstants.SECURITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = SecurityGroupResponse.class, description = "comma separated list of security groups names that going to be applied to the virtual machine." + + " Should be passed only when vm is created from a zone with Basic Network support. " + "Mutually exclusive with securitygroupids parameter") private List securityGroupNameList; - @Parameter(name = ApiConstants.IP_NETWORK_LIST, - type = CommandType.MAP, - description = "ip to network mapping. Can't be specified with networkIds parameter." - + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid") + @Parameter(name = ApiConstants.IP_NETWORK_LIST, type = CommandType.MAP, description = "ip to network mapping. Can't be specified with networkIds parameter." + + " Example: iptonetworklist[0].ip=10.10.10.11&iptonetworklist[0].ipv6=fc00:1234:5678::abcd&iptonetworklist[0].networkid=uuid - requests to use ip 10.10.10.11 in network id=uuid") private Map ipToNetworkList; @Parameter(name = ApiConstants.IP_ADDRESS, type = CommandType.STRING, description = "the ip address for default vm's network") @@ -192,48 +151,30 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { @Parameter(name = ApiConstants.IP6_ADDRESS, type = CommandType.STRING, description = "the ipv6 address for default vm's network") private String ip6Address; - @Parameter(name = ApiConstants.KEYBOARD, - type = CommandType.STRING, - description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") + @Parameter(name = ApiConstants.KEYBOARD, type = CommandType.STRING, description = "an optional keyboard device type for the virtual machine. valid value can be one of de,de-ch,es,fi,fr,fr-be,fr-ch,is,it,jp,nl-be,no,pt,uk,us") private String keyboard; @Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class, description = "Deploy vm for the project") private Long projectId; - @Parameter(name = ApiConstants.START_VM, - type = CommandType.BOOLEAN, - description = "true if network offering supports specifying ip ranges; defaulted to true if not specified") + @Parameter(name = ApiConstants.START_VM, type = CommandType.BOOLEAN, description = "true if network offering supports specifying ip ranges; defaulted to true if not specified") private Boolean startVm; @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, - type = CommandType.LIST, - collectionType = CommandType.UUID, - entityType = AffinityGroupResponse.class, - description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." - + " Mutually exclusive with affinitygroupnames parameter") + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are going to be applied to the virtual machine." + + " Mutually exclusive with affinitygroupnames parameter") private List affinityGroupIdList; @ACL - @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, - type = CommandType.LIST, - collectionType = CommandType.STRING, - entityType = AffinityGroupResponse.class, - description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." - + "Mutually exclusive with affinitygroupids parameter") + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are going to be applied to the virtual machine." + + "Mutually exclusive with affinitygroupids parameter") private List affinityGroupNameList; - @Parameter(name = ApiConstants.DISPLAY_VM, - type = CommandType.BOOLEAN, - since = "4.2", - description = "an optional field, whether to the display the vm to the end user or not.") + @Parameter(name = ApiConstants.DISPLAY_VM, type = CommandType.BOOLEAN, since = "4.2", description = "an optional field, whether to the display the vm to the end user or not.") private Boolean displayVm; - @Parameter(name = ApiConstants.CUSTOM_PARAMETERS, - type = CommandType.MAP, - since = "4.3", - description = "used to specify the custom parameters.") - private Map customParameters; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, since = "4.3", description = "used to specify the custom parameters.") + private Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -261,10 +202,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { return domainId; } - public Map getCustomParameters() { + public Map getDetails() { Map customparameterMap = new HashMap(); - if (customParameters != null && customParameters.size() != 0) { - Collection parameterCollection = customParameters.values(); + if (details != null && details.size() != 0) { + Collection parameterCollection = details.values(); Iterator iter = parameterCollection.iterator(); while (iter.hasNext()) { HashMap value = (HashMap)iter.next(); @@ -554,27 +495,23 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { if (getNetworkIds() != null) { throw new InvalidParameterValueException("Can't specify network Ids in Basic zone"); } else { - vm = - _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name, displayName, - diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, - getAffinityGroupIdList(), getCustomParameters(), getCustomId()); + vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name, displayName, diskOfferingId, + size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), + getDetails(), getCustomId()); } } else { if (zone.isSecurityGroupEnabled()) { - vm = - _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(), owner, name, - displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, - keyboard, getAffinityGroupIdList(), getCustomParameters(), getCustomId()); + vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(), owner, name, + displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, + getAffinityGroupIdList(), getDetails(), getCustomId()); } else { if (getSecurityGroupIdList() != null && !getSecurityGroupIdList().isEmpty()) { throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone"); } - vm = - _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName, diskOfferingId, size, - group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), - getCustomParameters(), getCustomId()); - + vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName, diskOfferingId, size, group, + getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), getDetails(), + getCustomId()); } } @@ -599,5 +536,4 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd { throw new ServerApiException(ApiErrorCode.RESOURCE_ALLOCATION_ERROR, ex.getMessage()); } } - } diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java index 8cb95756367..95d4990bf57 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListNicsCmd.java @@ -19,8 +19,6 @@ package org.apache.cloudstack.api.command.user.vm; import java.util.ArrayList; import java.util.List; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -29,9 +27,11 @@ import org.apache.cloudstack.api.BaseListCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -55,6 +55,9 @@ public class ListNicsCmd extends BaseListCmd { @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "the ID of the vm") private Long vmId; + @Parameter(name = ApiConstants.NETWORK_ID, type = CommandType.UUID, entityType = NetworkResponse.class, description = "list nic of the specific vm's network") + private Long networkId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -79,6 +82,10 @@ public class ListNicsCmd extends BaseListCmd { return vmId; } + public Long getNetworkId() { + return networkId; + } + @Override public long getEntityOwnerId() { Account caller = CallContext.current().getCallingAccount(); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java index 3f161c1698d..28637fbbb35 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/ListVMsCmd.java @@ -95,7 +95,7 @@ public class ListVMsCmd extends BaseListTaggedResourcesCmd { type = CommandType.LIST, collectionType = CommandType.STRING, description = "comma separated list of host details requested, " - + "value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, iso, volume, min, affgrp]." + + "value can be a list of [all, group, nics, stats, secgrp, tmpl, servoff, diskoff, iso, volume, min, affgrp]." + " If no parameter is passed in, the details will be defaulted to all") private List viewDetails; diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java index 9b86577f8e3..24582472b53 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/RemoveIpFromVmNicCmd.java @@ -142,7 +142,7 @@ public class RemoveIpFromVmNicCmd extends BaseAsyncCmd { if (getNetworkType() == NetworkType.Basic) { //remove the security group rules for this secondary ip boolean success = false; - success = _securityGroupService.securityGroupRulesForVmSecIp(nicSecIp.getNicId(), nicSecIp.getNetworkId(), nicSecIp.getIp4Address(), false); + success = _securityGroupService.securityGroupRulesForVmSecIp(nicSecIp.getNicId(), nicSecIp.getIp4Address(), false); if (success == false) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set security group rules for the secondary ip"); } 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 e122834810d..d23430095d2 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 @@ -16,20 +16,24 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; - -import org.apache.log4j.Logger; +import java.util.Map; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; 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; import com.cloud.event.EventTypes; import com.cloud.exception.ConcurrentOperationException; @@ -39,11 +43,6 @@ import com.cloud.exception.VirtualMachineMigrationException; import com.cloud.user.Account; import com.cloud.uservm.UserVm; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - @APICommand(name = "scaleVirtualMachine", description = "Scales the virtual machine to a new service offering.", responseObject = SuccessResponse.class) public class ScaleVMCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(ScaleVMCmd.class.getName()); @@ -58,17 +57,11 @@ public class ScaleVMCmd extends BaseAsyncCmd { private Long id; @ACL - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - required = true, - description = "the ID of the service offering for the virtual machine") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the ID of the service offering for the virtual machine") private Long serviceOfferingId; - @Parameter(name = ApiConstants.CUSTOM_PARAMETERS, - type = CommandType.MAP, - description = "name value pairs of custom parameters for cpu, memory and cpunumber. example customparameters[i].name=value") - private Map customParameters; + @Parameter(name = ApiConstants.DETAILS, type = BaseCmd.CommandType.MAP, description = "name value pairs of custom parameters for cpu,memory and cpunumber. example details[i].name=value") + private Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -83,12 +76,12 @@ public class ScaleVMCmd extends BaseAsyncCmd { } //instead of reading a map directly we are using collections. - //it is because customParameters.values() cannot be cast to a map. + //it is because details.values() cannot be cast to a map. //it gives a exception - public Map getCustomParameters() { + public Map getDetails() { Map customparameterMap = new HashMap(); - if (customParameters != null && customParameters.size() != 0) { - Collection parameterCollection = customParameters.values(); + if (details != null && details.size() != 0) { + Collection parameterCollection = details.values(); Iterator iter = parameterCollection.iterator(); while (iter.hasNext()) { HashMap value = (HashMap)iter.next(); diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java index 033717d0bc1..12ab711afce 100644 --- a/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java @@ -16,7 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.user.vm; -import org.apache.log4j.Logger; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -27,6 +30,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; @@ -34,13 +38,8 @@ import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; import com.cloud.uservm.UserVm; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - @APICommand(name = "changeServiceForVirtualMachine", responseObject = UserVmResponse.class, description = "Changes the service offering for a virtual machine. " - + "The virtual machine must be in a \"Stopped\" state for " + "this command to take effect.") + + "The virtual machine must be in a \"Stopped\" state for " + "this command to take effect.") public class UpgradeVMCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpgradeVMCmd.class.getName()); private static final String s_name = "changeserviceforvirtualmachineresponse"; @@ -52,17 +51,11 @@ public class UpgradeVMCmd extends BaseCmd { @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = UserVmResponse.class, required = true, description = "The ID of the virtual machine") private Long id; - @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, - type = CommandType.UUID, - entityType = ServiceOfferingResponse.class, - required = true, - description = "the service offering ID to apply to the virtual machine") + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, required = true, description = "the service offering ID to apply to the virtual machine") private Long serviceOfferingId; - @Parameter(name = ApiConstants.CUSTOM_PARAMETERS, - type = CommandType.MAP, - description = "name value pairs of custom parameters for cpu, memory and cpunumber. example customparameters[i].name=value") - private Map customParameters; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpu, memory and cpunumber. example details[i].name=value") + private Map details; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -76,10 +69,10 @@ public class UpgradeVMCmd extends BaseCmd { return serviceOfferingId; } - public Map getCustomParameters() { + public Map getDetails() { Map customparameterMap = new HashMap(); - if (customParameters != null && customParameters.size() != 0) { - Collection parameterCollection = customParameters.values(); + if (details != null && details.size() != 0) { + Collection parameterCollection = details.values(); Iterator iter = parameterCollection.iterator(); while (iter.hasNext()) { HashMap value = (HashMap)iter.next(); diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java index 69d1ca7c2ee..618be7f3958 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/AddResourceDetailCmd.java @@ -21,13 +21,12 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseAsyncCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.server.ResourceTag; @@ -35,7 +34,7 @@ import com.cloud.server.ResourceTag; @APICommand(name = "addResourceDetail", description = "Adds detail for the Resource.", responseObject = SuccessResponse.class) public class AddResourceDetailCmd extends BaseAsyncCmd { public static final Logger s_logger = Logger.getLogger(AddResourceDetailCmd.class.getName()); - private static final String s_name = "addResourceDetailresponse"; + private static final String s_name = "addresourcedetailresponse"; ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -47,11 +46,7 @@ public class AddResourceDetailCmd extends BaseAsyncCmd { @Parameter(name = ApiConstants.RESOURCE_TYPE, type = CommandType.STRING, required = true, description = "type of the resource") private String resourceType; - @Parameter(name = ApiConstants.RESOURCE_ID, - type = CommandType.STRING, - required = true, - collectionType = CommandType.STRING, - description = "resource id to create the details for") + @Parameter(name = ApiConstants.RESOURCE_ID, type = CommandType.STRING, required = true, collectionType = CommandType.STRING, description = "resource id to create the details for") private String resourceId; ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/response/NicResponse.java b/api/src/org/apache/cloudstack/api/response/NicResponse.java index 7089f884a51..3dd8b297a2b 100644 --- a/api/src/org/apache/cloudstack/api/response/NicResponse.java +++ b/api/src/org/apache/cloudstack/api/response/NicResponse.java @@ -18,14 +18,13 @@ package org.apache.cloudstack.api.response; import java.util.List; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; import com.cloud.serializer.Param; import com.cloud.vm.Nic; +import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = Nic.class) @@ -95,6 +94,10 @@ public class NicResponse extends BaseResponse { @Param(description = "the Secondary ipv4 addr of nic") private List secondaryIps; + @SerializedName(ApiConstants.DEVICE_ID) + @Param(description = "device id for the network when plugged into the virtual machine", since = "4.4") + private String deviceId; + public String getId() { return id; } @@ -159,6 +162,10 @@ public class NicResponse extends BaseResponse { this.ip6Address = ip6Address; } + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + @Override public int hashCode() { final int prime = 31; diff --git a/api/src/org/apache/cloudstack/api/response/ProviderResponse.java b/api/src/org/apache/cloudstack/api/response/ProviderResponse.java index 5d6ec0d5ca3..4d410837476 100644 --- a/api/src/org/apache/cloudstack/api/response/ProviderResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ProviderResponse.java @@ -18,14 +18,13 @@ package org.apache.cloudstack.api.response; import java.util.List; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; import org.apache.cloudstack.api.EntityReference; import com.cloud.network.PhysicalNetworkServiceProvider; import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; @EntityReference(value = PhysicalNetworkServiceProvider.class) @SuppressWarnings("unused") diff --git a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java index 3ca2a1c81b1..26ecb5bb088 100644 --- a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java +++ b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java @@ -21,8 +21,6 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseResponse; @@ -32,6 +30,7 @@ import com.cloud.network.router.VirtualRouter; import com.cloud.serializer.Param; import com.cloud.uservm.UserVm; import com.cloud.vm.VirtualMachine; +import com.google.gson.annotations.SerializedName; @SuppressWarnings("unused") @EntityReference(value = {VirtualMachine.class, UserVm.class, VirtualRouter.class}) @@ -140,6 +139,14 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp @Param(description = "the name of the service offering of the virtual machine") private String serviceOfferingName; + @SerializedName(ApiConstants.DISK_OFFERING_ID) + @Param(description = "the ID of the disk offering of the virtual machine") + private String diskOfferingId; + + @SerializedName("diskofferingname") + @Param(description = "the name of the disk offering of the virtual machine") + private String diskOfferingName; + @SerializedName("forvirtualnetwork") @Param(description = "the virtual network for the service offering") private Boolean forVirtualNetwork; @@ -229,7 +236,7 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp private Set tags; @SerializedName(ApiConstants.DETAILS) - @Param(description = "Template details in key/value pairs.", since = "4.2.1") + @Param(description = "Vm details in key/value pairs.", since = "4.2.1") private Map details; @SerializedName(ApiConstants.SSH_KEYPAIR) @@ -247,10 +254,15 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp @SerializedName(ApiConstants.IS_DYNAMICALLY_SCALABLE) @Param(description = "true if vm contains XS/VMWare tools inorder to support dynamic scaling of VM cpu/memory.") private Boolean isDynamicallyScalable; + @SerializedName(ApiConstants.SERVICE_STATE) @Param(description = "State of the Service from LB rule") private String serviceState; + @SerializedName(ApiConstants.OS_TYPE_ID) + @Param(description = "OS type id of the vm", since = "4.4") + private Long osTypeId; + public UserVmResponse() { securityGroupList = new LinkedHashSet(); nics = new LinkedHashSet(); @@ -383,6 +395,14 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp return serviceOfferingName; } + public String getDiskOfferingId() { + return diskOfferingId; + } + + public String getDiskOfferingName() { + return diskOfferingName; + } + public Boolean getForVirtualNetwork() { return forVirtualNetwork; } @@ -602,6 +622,14 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp this.serviceOfferingName = serviceOfferingName; } + public void setDiskOfferingId(String diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + public void setDiskOfferingName(String diskOfferingName) { + this.diskOfferingName = diskOfferingName; + } + public void setCpuNumber(Integer cpuNumber) { this.cpuNumber = cpuNumber; } @@ -715,4 +743,8 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp public void setDetails(Map details) { this.details = details; } + + public void setOsTypeId(Long osTypeId) { + this.osTypeId = osTypeId; + } } diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java index 32f13f80f22..e73f5263a4f 100644 --- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java +++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java @@ -45,6 +45,7 @@ public interface ExternalNetworkDeviceManager extends Manager { public static final NetworkDevice PaloAltoFirewall = new NetworkDevice("PaloAltoFirewall", Network.Provider.PaloAlto.getName()); public static final NetworkDevice NiciraNvp = new NetworkDevice("NiciraNvp", Network.Provider.NiciraNvp.getName()); public static final NetworkDevice CiscoVnmc = new NetworkDevice("CiscoVnmc", Network.Provider.CiscoVnmc.getName()); + public static final NetworkDevice OpenDaylightController = new NetworkDevice("OpenDaylightController", Network.Provider.Opendaylight.getName()); public NetworkDevice(String deviceName, String ntwkServiceprovider) { _name = deviceName; diff --git a/api/src/org/apache/cloudstack/query/QueryService.java b/api/src/org/apache/cloudstack/query/QueryService.java index 792ed04c3da..65a6b064b7c 100644 --- a/api/src/org/apache/cloudstack/query/QueryService.java +++ b/api/src/org/apache/cloudstack/query/QueryService.java @@ -18,10 +18,6 @@ package org.apache.cloudstack.query; import java.util.List; -import org.apache.cloudstack.affinity.AffinityGroupResponse; -import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; -import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd; -import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.command.admin.storage.ListImageStoresCmd; import org.apache.cloudstack.api.command.admin.storage.ListSecondaryStagingStoresCmd; import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; @@ -43,6 +39,10 @@ import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd; import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd; import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd; import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd; +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.command.admin.host.ListHostsCmd; +import org.apache.cloudstack.api.command.admin.internallb.ListInternalLBVMsCmd; +import org.apache.cloudstack.api.command.admin.router.ListRoutersCmd; import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; @@ -118,8 +118,9 @@ public interface QueryService { public ListResponse listIsos(ListIsosCmd cmd); - public ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, - String accountName, Long domainId, boolean isRecursive, boolean listAll, Long startIndex, Long pageSize); + public ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, + String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive, + boolean listAll, Long startIndex, Long pageSize, String keyword); public List listResourceDetails(ListResourceDetailsCmd cmd); diff --git a/api/test/com/cloud/network/NetworksTest.java b/api/test/com/cloud/network/NetworksTest.java index 849b1b4f73f..c9102d3a02a 100644 --- a/api/test/com/cloud/network/NetworksTest.java +++ b/api/test/com/cloud/network/NetworksTest.java @@ -22,6 +22,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import com.cloud.dc.Vlan; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; import com.cloud.utils.exception.CloudRuntimeException; @@ -55,6 +56,20 @@ public class NetworksTest { Assert.assertEquals("id2 should be \"2\"", "2", id2); } + @Test + public void vlanValueTest() throws URISyntaxException { + String uri1 = "vlan://1"; + String uri2 = "1"; + String vtag = BroadcastDomainType.Vlan.getValueFrom(BroadcastDomainType.fromString(uri1)); + Assert.assertEquals("vtag should be \"1\"", "1", vtag); + BroadcastDomainType tiep1 = BroadcastDomainType.getTypeOf(uri1); + Assert.assertEquals("the type of uri1 should be 'Vlan'", BroadcastDomainType.Vlan, tiep1); + BroadcastDomainType tiep2 = BroadcastDomainType.getTypeOf(uri2); + Assert.assertEquals("the type of uri1 should be 'Undecided'", BroadcastDomainType.UnDecided, tiep2); + BroadcastDomainType tiep3 = BroadcastDomainType.getTypeOf(Vlan.UNTAGGED); + Assert.assertEquals("the type of uri1 should be 'vlan'", BroadcastDomainType.Native, tiep3); + } + @Test public void vlanIsolationTypeTest() throws URISyntaxException { String uri1 = "vlan://1"; diff --git a/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java b/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java index 121659b46ed..6f5ab3e1cd9 100644 --- a/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java +++ b/api/test/org/apache/cloudstack/api/command/test/AddIpToVmNicTest.java @@ -25,7 +25,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Matchers; import org.mockito.Mockito; - import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.command.user.vm.AddIpToVmNicCmd; import org.apache.cloudstack.api.command.user.vm.RemoveIpFromVmNicCmd; @@ -39,7 +38,6 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.NetworkService; -import com.cloud.user.Account; import com.cloud.vm.NicSecondaryIp; public class AddIpToVmNicTest extends TestCase { @@ -68,7 +66,7 @@ public class AddIpToVmNicTest extends TestCase { NicSecondaryIp secIp = Mockito.mock(NicSecondaryIp.class); Mockito.when( - networkService.allocateSecondaryGuestIP(Matchers.any(Account.class), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyString())) + networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.anyString())) .thenReturn(secIp); ipTonicCmd._networkService = networkService; @@ -88,7 +86,7 @@ public class AddIpToVmNicTest extends TestCase { AddIpToVmNicCmd ipTonicCmd = Mockito.mock(AddIpToVmNicCmd.class); Mockito.when( - networkService.allocateSecondaryGuestIP(Matchers.any(Account.class), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyString())) + networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.anyString())) .thenReturn(null); ipTonicCmd._networkService = networkService; diff --git a/api/test/org/apache/cloudstack/api/command/test/AddSecondaryStorageCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/AddSecondaryStorageCmdTest.java index d0b2d85f8d5..d68cfb4ca67 100644 --- a/api/test/org/apache/cloudstack/api/command/test/AddSecondaryStorageCmdTest.java +++ b/api/test/org/apache/cloudstack/api/command/test/AddSecondaryStorageCmdTest.java @@ -16,6 +16,12 @@ // under the License. package org.apache.cloudstack.api.command.test; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; + +import java.util.Map; + import junit.framework.Assert; import junit.framework.TestCase; @@ -56,7 +62,8 @@ public class AddSecondaryStorageCmdTest extends TestCase { ImageStore store = Mockito.mock(ImageStore.class); - Mockito.when(resourceService.discoverImageStore(addImageStoreCmd)).thenReturn(store); + Mockito.when(resourceService.discoverImageStore(anyString(), anyString(), anyString(), anyLong(), (Map)anyObject())) + .thenReturn(store); ResponseGenerator responseGenerator = Mockito.mock(ResponseGenerator.class); addImageStoreCmd._responseGenerator = responseGenerator; @@ -83,7 +90,8 @@ public class AddSecondaryStorageCmdTest extends TestCase { StorageService resourceService = Mockito.mock(StorageService.class); addImageStoreCmd._storageService = resourceService; - Mockito.when(resourceService.discoverImageStore(addImageStoreCmd)).thenReturn(null); + Mockito.when(resourceService.discoverImageStore(anyString(), anyString(), anyString(), anyLong(), (Map)anyObject())) + .thenReturn(null); try { addImageStoreCmd.execute(); diff --git a/awsapi/pom.xml b/awsapi/pom.xml index 3d269eda69a..3c2bbe16399 100644 --- a/awsapi/pom.xml +++ b/awsapi/pom.xml @@ -317,6 +317,16 @@ + + org.apache.maven.plugins + maven-checkstyle-plugin + ${cs.checkstyle.version} + + + none + + + org.apache.maven.plugins maven-surefire-plugin @@ -408,6 +418,7 @@ + diff --git a/build/checkstyle/pom.xml b/build/checkstyle/pom.xml deleted file mode 100644 index 5984802398c..00000000000 --- a/build/checkstyle/pom.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - 4.0.0 - Apache CloudStack Checkstyle Configuration - build-checkstyle - - org.apache.cloudstack - cloud-maven-standard - 4.4.0-SNAPSHOT - ../../maven-standard/pom.xml - - diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 384a0778bb2..f186fbf7848 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -14,6 +14,21 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +label.port=Port +label.remove.ldap=Remove LDAP +message.remove.ldap=Are you sure you want to delete the LDAP configuration? +label.configure.ldap=Configure LDAP +message.configure.ldap=Please confirm you would like to configure LDAP. +label.ldap.configuration=LDAP Configuration +label.ldap.port=LDAP port +label.create.nfs.secondary.staging.store=Create NFS secondary staging store +label.volatile=Volatile +label.planner.mode=Planner mode +label.deployment.planner=Deployment planner +label.quiesce.vm=Quiesce VM +label.smb.username=SMB Username +label.smb.password=SMB Password +label.smb.domain=SMB Domain label.hypervisors=Hypervisors label.home=Home label.sockets=Sockets @@ -1252,6 +1267,11 @@ label.zoneWizard.trafficType.management=Management\: Traffic between CloudStack\ label.zoneWizard.trafficType.public=Public\: Traffic between the internet and virtual machines in the cloud. label.zoneWizard.trafficType.storage=Storage\: Traffic between primary and secondary storage servers, such as VM templates and snapshots label.ldap.group.name=LDAP Group +label.openDaylight=OpenDaylight +label.opendaylight.controllers=OpenDaylight Controllers +label.opendaylight.controllerdetail=OpenDaylight Controller Details +label.add.OpenDaylight.device=Add OpenDaylight Controller +label.delete.OpenDaylight.device=Delete OpenDaylight Controller managed.state=Managed State message.acquire.new.ip.vpc=Please confirm that you would like to acquire a new IP for this VPC. message.acquire.new.ip=Please confirm that you would like to acquire a new IP for this network. diff --git a/client/pom.xml b/client/pom.xml index fc01113c7a0..abbc25dbcbf 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -130,6 +130,11 @@ cloud-plugin-network-vxlan ${project.version} + + org.apache.cloudstack + cloud-plugin-network-opendaylight + ${project.version} + org.apache.cloudstack cloud-plugin-hypervisor-xen @@ -176,6 +181,11 @@ cloud-plugin-planner-user-dispersing ${project.version} + + org.apache.cloudstack + cloud-plugin-planner-skip-heurestics + ${project.version} + org.apache.cloudstack cloud-plugin-planner-user-concentrated-pod @@ -201,6 +211,11 @@ cloud-mom-rabbitmq ${project.version} + + org.apache.cloudstack + cloud-mom-inmemory + ${project.version} + mysql mysql-connector-java diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index a05310bff77..c5ac7fd67af 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -266,7 +266,7 @@ deleteImageStore=1 createSecondaryStagingStore=1 listSecondaryStagingStores=1 deleteSecondaryStagingStore=1 -prepareSecondaryStorageForMigration=1 +updateCloudToUseObjectStore=1 #### host commands addHost=3 @@ -706,3 +706,8 @@ importLdapUsers=3 #### juniper-contrail commands createServiceInstance=1 + +### OpenDaylight plugin commands +addOpenDaylightController=1 +deleteOpenDaylightController=1 +listOpenDaylightControllers=1 diff --git a/core/pom.xml b/core/pom.xml index cdcfd536b93..975e9f64072 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -36,6 +36,11 @@ cloud-engine-api ${project.version} + + org.apache.cloudstack + cloud-framework-security + ${project.version} + commons-codec commons-codec diff --git a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml index be11a1f711f..5e799c06483 100644 --- a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml +++ b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml @@ -77,6 +77,11 @@ + + + + diff --git a/core/resources/META-INF/cloudstack/planner/spring-core-lifecycle-planner-context-inheritable.xml b/core/resources/META-INF/cloudstack/planner/spring-core-lifecycle-planner-context-inheritable.xml index 715f86d9c28..80779e43466 100644 --- a/core/resources/META-INF/cloudstack/planner/spring-core-lifecycle-planner-context-inheritable.xml +++ b/core/resources/META-INF/cloudstack/planner/spring-core-lifecycle-planner-context-inheritable.xml @@ -38,4 +38,9 @@ value="org.apache.cloudstack.affinity.AffinityGroupProcessor" /> + + + + + diff --git a/core/src/com/cloud/agent/api/SecStorageSetupCommand.java b/core/src/com/cloud/agent/api/SecStorageSetupCommand.java index 3c8d7300064..18710320dc8 100644 --- a/core/src/com/cloud/agent/api/SecStorageSetupCommand.java +++ b/core/src/com/cloud/agent/api/SecStorageSetupCommand.java @@ -16,50 +16,21 @@ // under the License. package com.cloud.agent.api; -import com.cloud.agent.api.LogLevel.Log4jLevel; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; + import com.cloud.agent.api.to.DataStoreTO; public class SecStorageSetupCommand extends Command { private DataStoreTO store; private String secUrl; - private Certificates certs; + private KeystoreManager.Certificates certs; - public static class Certificates { - @LogLevel(Log4jLevel.Off) - private String privKey; - @LogLevel(Log4jLevel.Off) - private String privCert; - @LogLevel(Log4jLevel.Off) - private String certChain; - - public Certificates() { - - } - - public Certificates(String prvKey, String privCert, String certChain) { - this.privKey = prvKey; - this.privCert = privCert; - this.certChain = certChain; - } - - public String getPrivKey() { - return this.privKey; - } - - public String getPrivCert() { - return this.privCert; - } - - public String getCertChain() { - return this.certChain; - } - } public SecStorageSetupCommand() { super(); } - public SecStorageSetupCommand(DataStoreTO store, String secUrl, Certificates certs) { + public SecStorageSetupCommand(DataStoreTO store, String secUrl, KeystoreManager.Certificates certs) { super(); this.secUrl = secUrl; this.certs = certs; @@ -75,8 +46,8 @@ public class SecStorageSetupCommand extends Command { return secUrl; } - public Certificates getCerts() { - return this.certs; + public KeystoreManager.Certificates getCerts() { + return certs; } public void setSecUrl(String secUrl) { diff --git a/core/src/com/cloud/agent/api/storage/MigrateVolumeCommand.java b/core/src/com/cloud/agent/api/storage/MigrateVolumeCommand.java index b82d8481f2c..b73a48a1b37 100644 --- a/core/src/com/cloud/agent/api/storage/MigrateVolumeCommand.java +++ b/core/src/com/cloud/agent/api/storage/MigrateVolumeCommand.java @@ -25,6 +25,7 @@ public class MigrateVolumeCommand extends Command { long volumeId; String volumePath; StorageFilerTO pool; + String attachedVmName; public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool pool) { this.volumeId = volumeId; @@ -32,6 +33,13 @@ public class MigrateVolumeCommand extends Command { this.pool = new StorageFilerTO(pool); } + public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool pool, String attachedVmName) { + this.volumeId = volumeId; + this.volumePath = volumePath; + this.pool = new StorageFilerTO(pool); + this.attachedVmName = attachedVmName; + } + @Override public boolean executeInSequence() { return true; @@ -48,4 +56,8 @@ public class MigrateVolumeCommand extends Command { public StorageFilerTO getPool() { return pool; } + + public String getAttachedVmName() { + return attachedVmName; + } } \ No newline at end of file diff --git a/core/src/com/cloud/host/HostInfo.java b/core/src/com/cloud/host/HostInfo.java index 89fb8061aa0..4a95aa8d862 100644 --- a/core/src/com/cloud/host/HostInfo.java +++ b/core/src/com/cloud/host/HostInfo.java @@ -21,5 +21,6 @@ public final class HostInfo { public static final String HOST_OS = "Host.OS"; //Fedora, XenServer, Ubuntu, etc public static final String HOST_OS_VERSION = "Host.OS.Version"; //12, 5.5, 9.10, etc public static final String HOST_OS_KERNEL_VERSION = "Host.OS.Kernel.Version"; //linux-2.6.31 etc - + public static final String XS620_SNAPSHOT_HOTFIX = "xs620_snapshot_hotfix"; } + diff --git a/core/src/com/cloud/storage/template/OVAProcessor.java b/core/src/com/cloud/storage/template/OVAProcessor.java new file mode 100644 index 00000000000..0db3bb00e0a --- /dev/null +++ b/core/src/com/cloud/storage/template/OVAProcessor.java @@ -0,0 +1,158 @@ +// 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.storage.template; + +import java.io.File; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.cloud.exception.InternalErrorException; +import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.StorageLayer; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.script.Script; + +@Local(value = Processor.class) +public class OVAProcessor extends AdapterBase implements Processor { + private static final Logger s_logger = Logger.getLogger(OVAProcessor.class); + + StorageLayer _storage; + + @Override + public FormatInfo process(String templatePath, ImageFormat format, String templateName) throws InternalErrorException { + if (format != null) { + if (s_logger.isInfoEnabled()) { + s_logger.info("We currently don't handle conversion from " + format + " to OVA."); + } + return null; + } + + s_logger.info("Template processing. templatePath: " + templatePath + ", templateName: " + templateName); + String templateFilePath = templatePath + File.separator + templateName + "." + ImageFormat.OVA.getFileExtension(); + if (!_storage.exists(templateFilePath)) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Unable to find the vmware template file: " + templateFilePath); + } + return null; + } + + s_logger.info("Template processing - untar OVA package. templatePath: " + templatePath + ", templateName: " + templateName); + String templateFileFullPath = templatePath + File.separator + templateName + "." + ImageFormat.OVA.getFileExtension(); + File templateFile = new File(templateFileFullPath); + + Script command = new Script("tar", 0, s_logger); + command.add("--no-same-owner"); + command.add("-xf", templateFileFullPath); + command.setWorkDir(templateFile.getParent()); + String result = command.execute(); + if (result != null) { + s_logger.info("failed to untar OVA package due to " + result + ". templatePath: " + templatePath + ", templateName: " + templateName); + return null; + } + + FormatInfo info = new FormatInfo(); + info.format = ImageFormat.OVA; + info.filename = templateName + "." + ImageFormat.OVA.getFileExtension(); + info.size = _storage.getSize(templateFilePath); + info.virtualSize = getTemplateVirtualSize(templatePath, info.filename); + + // delete original OVA file + // templateFile.delete(); + return info; + } + + @Override + public Long getVirtualSize(File file) { + try { + long size = getTemplateVirtualSize(file.getParent(), file.getName()); + return size; + } catch (Exception e) { + + } + return file.length(); + } + + public long getTemplateVirtualSize(String templatePath, String templateName) throws InternalErrorException { + // get the virtual size from the OVF file meta data + long virtualSize = 0; + String templateFileFullPath = templatePath.endsWith(File.separator) ? templatePath : templatePath + File.separator; + templateFileFullPath += templateName.endsWith(ImageFormat.OVA.getFileExtension()) ? templateName : templateName + "." + ImageFormat.OVA.getFileExtension(); + String ovfFileName = getOVFFilePath(templateFileFullPath); + if (ovfFileName == null) { + String msg = "Unable to locate OVF file in template package directory: " + templatePath; + s_logger.error(msg); + throw new InternalErrorException(msg); + } + try { + Document ovfDoc = null; + ovfDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(ovfFileName)); + Element disk = (Element)ovfDoc.getElementsByTagName("Disk").item(0); + virtualSize = Long.parseLong(disk.getAttribute("ovf:capacity")); + String allocationUnits = disk.getAttribute("ovf:capacityAllocationUnits"); + if ((virtualSize != 0) && (allocationUnits != null)) { + long units = 1; + if (allocationUnits.equalsIgnoreCase("KB") || allocationUnits.equalsIgnoreCase("KiloBytes") || allocationUnits.equalsIgnoreCase("byte * 2^10")) { + units = 1024; + } else if (allocationUnits.equalsIgnoreCase("MB") || allocationUnits.equalsIgnoreCase("MegaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^20")) { + units = 1024 * 1024; + } else if (allocationUnits.equalsIgnoreCase("GB") || allocationUnits.equalsIgnoreCase("GigaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^30")) { + units = 1024 * 1024 * 1024; + } + virtualSize = virtualSize * units; + } else { + throw new InternalErrorException("Failed to read capacity and capacityAllocationUnits from the OVF file: " + ovfFileName); + } + return virtualSize; + } catch (Exception e) { + String msg = "Unable to parse OVF XML document to get the virtual disk size due to" + e; + s_logger.error(msg); + throw new InternalErrorException(msg); + } + } + + private String getOVFFilePath(String srcOVAFileName) { + File file = new File(srcOVAFileName); + assert (_storage != null); + String[] files = _storage.listFiles(file.getParent()); + if (files != null) { + for (String fileName : files) { + if (fileName.toLowerCase().endsWith(".ovf")) { + File ovfFile = new File(fileName); + return file.getParent() + File.separator + ovfFile.getName(); + } + } + } + return null; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey); + if (_storage == null) { + throw new ConfigurationException("Unable to get storage implementation"); + } + + return true; + } +} diff --git a/core/src/com/cloud/storage/template/VmdkProcessor.java b/core/src/com/cloud/storage/template/VmdkProcessor.java index be201438a1c..2c08447e9f9 100644 --- a/core/src/com/cloud/storage/template/VmdkProcessor.java +++ b/core/src/com/cloud/storage/template/VmdkProcessor.java @@ -16,22 +16,24 @@ // under the License. package com.cloud.storage.template; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; import javax.ejb.Local; import javax.naming.ConfigurationException; -import javax.xml.parsers.DocumentBuilderFactory; import org.apache.log4j.Logger; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import com.cloud.exception.InternalErrorException; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.utils.component.AdapterBase; -import com.cloud.utils.script.Script; @Local(value = Processor.class) public class VmdkProcessor extends AdapterBase implements Processor { @@ -49,7 +51,7 @@ public class VmdkProcessor extends AdapterBase implements Processor { } s_logger.info("Template processing. templatePath: " + templatePath + ", templateName: " + templateName); - String templateFilePath = templatePath + File.separator + templateName + "." + ImageFormat.OVA.getFileExtension(); + String templateFilePath = templatePath + File.separator + templateName + "." + ImageFormat.VMDK.getFileExtension(); if (!_storage.exists(templateFilePath)) { if (s_logger.isInfoEnabled()) { s_logger.info("Unable to find the vmware template file: " + templateFilePath); @@ -57,28 +59,12 @@ public class VmdkProcessor extends AdapterBase implements Processor { return null; } - s_logger.info("Template processing - untar OVA package. templatePath: " + templatePath + ", templateName: " + templateName); - String templateFileFullPath = templatePath + File.separator + templateName + "." + ImageFormat.OVA.getFileExtension(); - File templateFile = new File(templateFileFullPath); - - Script command = new Script("tar", 0, s_logger); - command.add("--no-same-owner"); - command.add("-xf", templateFileFullPath); - command.setWorkDir(templateFile.getParent()); - String result = command.execute(); - if (result != null) { - s_logger.info("failed to untar OVA package due to " + result + ". templatePath: " + templatePath + ", templateName: " + templateName); - return null; - } - FormatInfo info = new FormatInfo(); - info.format = ImageFormat.OVA; - info.filename = templateName + "." + ImageFormat.OVA.getFileExtension(); + info.format = ImageFormat.VMDK; + info.filename = templateName + "." + ImageFormat.VMDK.getFileExtension(); info.size = _storage.getSize(templateFilePath); info.virtualSize = getTemplateVirtualSize(templatePath, info.filename); - // delete original OVA file - // templateFile.delete(); return info; } @@ -94,56 +80,37 @@ public class VmdkProcessor extends AdapterBase implements Processor { } public long getTemplateVirtualSize(String templatePath, String templateName) throws InternalErrorException { - // get the virtual size from the OVF file meta data long virtualSize = 0; String templateFileFullPath = templatePath.endsWith(File.separator) ? templatePath : templatePath + File.separator; - templateFileFullPath += templateName.endsWith(ImageFormat.OVA.getFileExtension()) ? templateName : templateName + "." + ImageFormat.OVA.getFileExtension(); - String ovfFileName = getOVFFilePath(templateFileFullPath); - if (ovfFileName == null) { - String msg = "Unable to locate OVF file in template package directory: " + templatePath; - s_logger.error(msg); - throw new InternalErrorException(msg); - } - try { - Document ovfDoc = null; - ovfDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(ovfFileName)); - Element disk = (Element)ovfDoc.getElementsByTagName("Disk").item(0); - virtualSize = Long.parseLong(disk.getAttribute("ovf:capacity")); - String allocationUnits = disk.getAttribute("ovf:capacityAllocationUnits"); - if ((virtualSize != 0) && (allocationUnits != null)) { - long units = 1; - if (allocationUnits.equalsIgnoreCase("KB") || allocationUnits.equalsIgnoreCase("KiloBytes") || allocationUnits.equalsIgnoreCase("byte * 2^10")) { - units = 1024; - } else if (allocationUnits.equalsIgnoreCase("MB") || allocationUnits.equalsIgnoreCase("MegaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^20")) { - units = 1024 * 1024; - } else if (allocationUnits.equalsIgnoreCase("GB") || allocationUnits.equalsIgnoreCase("GigaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^30")) { - units = 1024 * 1024 * 1024; - } - virtualSize = virtualSize * units; - } else { - throw new InternalErrorException("Failed to read capacity and capacityAllocationUnits from the OVF file: " + ovfFileName); - } - return virtualSize; - } catch (Exception e) { - String msg = "Unable to parse OVF XML document to get the virtual disk size due to" + e; - s_logger.error(msg); - throw new InternalErrorException(msg); - } - } + templateFileFullPath += templateName.endsWith(ImageFormat.VMDK.getFileExtension()) ? templateName : templateName + "." + ImageFormat.VMDK.getFileExtension(); + String vmdkHeader = ""; - private String getOVFFilePath(String srcOVAFileName) { - File file = new File(srcOVAFileName); - assert (_storage != null); - String[] files = _storage.listFiles(file.getParent()); - if (files != null) { - for (String fileName : files) { - if (fileName.toLowerCase().endsWith(".ovf")) { - File ovfFile = new File(fileName); - return file.getParent() + File.separator + ovfFile.getName(); + try { + FileReader fileReader = new FileReader(templateFileFullPath); + BufferedReader bufferedReader = new BufferedReader(fileReader); + Pattern regex = Pattern.compile("(RW|RDONLY|NOACCESS) (\\d+) (FLAT|SPARSE|ZERO|VMFS|VMFSSPARSE|VMFSDRM|VMFSRAW)"); + String line = null; + while((line = bufferedReader.readLine()) != null) { + Matcher m = regex.matcher(line); + if (m.find( )) { + long sectors = Long.parseLong(m.group(2)); + virtualSize = sectors * 512; + break; } } + bufferedReader.close(); + } catch(FileNotFoundException ex) { + String msg = "Unable to open file '" + templateFileFullPath + "' " + ex.toString(); + s_logger.error(msg); + throw new InternalErrorException(msg); + } catch(IOException ex) { + String msg = "Unable read open file '" + templateFileFullPath + "' " + ex.toString(); + s_logger.error(msg); + throw new InternalErrorException(msg); } - return null; + + s_logger.debug("vmdk file had size="+virtualSize); + return virtualSize; } @Override diff --git a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java index 7fcde1e8d4f..446c61f9a7b 100644 --- a/core/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/core/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -16,6 +16,9 @@ // under the License. package org.apache.cloudstack.storage.command; +import java.util.HashMap; +import java.util.Map; + import com.cloud.agent.api.Command; import com.cloud.agent.api.to.DataTO; @@ -24,6 +27,7 @@ public final class CopyCommand extends Command implements StorageSubSystemComman private DataTO destTO; private DataTO cacheTO; boolean executeInSequence = false; + Map options = new HashMap(); public CopyCommand(DataTO srcData, DataTO destData, int timeout, boolean executeInSequence) { super(); @@ -66,4 +70,12 @@ public final class CopyCommand extends Command implements StorageSubSystemComman return this.getWait() * 1000; } + public void setOptions(Map options) { + this.options = options; + } + + public Map getOptions() { + return options; + } + } diff --git a/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index 03b460902bf..7d1ac2d7e5e 100644 --- a/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -16,8 +16,11 @@ // under the License. package org.apache.cloudstack.storage.to; +import java.util.ArrayList; + import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.commons.lang.ArrayUtils; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; @@ -34,6 +37,9 @@ public class SnapshotObjectTO implements DataTO { private HypervisorType hypervisorType; private long id; private boolean quiescevm; + private String[] parents; + private Long physicalSize = (long) 0; + public SnapshotObjectTO() { @@ -49,9 +55,17 @@ public class SnapshotObjectTO implements DataTO { } SnapshotInfo parentSnapshot = snapshot.getParent(); + ArrayList parentsArry = new ArrayList(); if (parentSnapshot != null) { this.parentSnapshotPath = parentSnapshot.getPath(); + while(parentSnapshot != null) { + parentsArry.add(parentSnapshot.getPath()); + parentSnapshot = parentSnapshot.getParent(); + } + parents = parentsArry.toArray(new String[parentsArry.size()]); + ArrayUtils.reverse(parents); } + this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); this.hypervisorType = snapshot.getHypervisorType(); @@ -81,6 +95,14 @@ public class SnapshotObjectTO implements DataTO { this.path = path; } + public Long getPhysicalSize() { + return this.physicalSize; + } + + public void setPhysicalSize(Long physicalSize ) { + this.physicalSize = physicalSize; + } + public VolumeObjectTO getVolume() { return volume; } @@ -139,6 +161,10 @@ public class SnapshotObjectTO implements DataTO { this.quiescevm = quiescevm; } + public String[] getParents() { + return parents; + } + @Override public String toString() { return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path").append(path).append("]").toString(); diff --git a/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java b/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java index d266eb3aabe..9edbf102471 100644 --- a/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java +++ b/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java @@ -81,7 +81,10 @@ public class CheckOnHostCommandTest { return "10.1.1.1"; }; - @Override + public String getStorageUrl() { + return null; + } + public String getStorageIpAddress() { return "10.1.1.2"; }; diff --git a/deps/XenServerJava/pom.xml b/deps/XenServerJava/pom.xml index 6633e067e81..5c885b49808 100644 --- a/deps/XenServerJava/pom.xml +++ b/deps/XenServerJava/pom.xml @@ -40,29 +40,20 @@ org.apache.maven.plugins maven-checkstyle-plugin + ${cs.checkstyle.version} + + + none + + org.apache.cloudstack - build-checkstyle - ${parent.version} + checkstyle + + ${project.parent.version} - - - process-classes - - check - - - - - true - tooling/checkstyle.xml - true - true - ${project.basedir} - **\/* - diff --git a/deps/XenServerJava/src/com/xensource/xenapi/Event.java b/deps/XenServerJava/src/com/xensource/xenapi/Event.java index 68594fb7fe9..ec438140fe2 100644 --- a/deps/XenServerJava/src/com/xensource/xenapi/Event.java +++ b/deps/XenServerJava/src/com/xensource/xenapi/Event.java @@ -300,4 +300,19 @@ public class Event extends XenAPIObject { return Types.toString(result); } + public static Map properFrom(Connection c, Set classes, String token, Double timeout) throws BadServerResponse, XenAPIException, XmlRpcException, + Types.SessionNotRegistered, + Types.EventsLost { + String method_call = "event.from"; + String session = c.getSessionReference(); + Object[] method_params = {Marshalling.toXMLRPC(session), Marshalling.toXMLRPC(classes), Marshalling.toXMLRPC(token), Marshalling.toXMLRPC(timeout)}; + Map response = c.dispatch(method_call, method_params); + Object result = response.get("Value"); + Map value = (Map)result; + Map from = new HashMap(); + from.put("token", value.get("token")); + from.put("events", Types.toSetOfEventRecord(value.get("events"))); + return from; + } + } \ No newline at end of file diff --git a/deps/XenServerJava/src/com/xensource/xenapi/Types.java b/deps/XenServerJava/src/com/xensource/xenapi/Types.java index c6451ff035b..777d580e0b1 100644 --- a/deps/XenServerJava/src/com/xensource/xenapi/Types.java +++ b/deps/XenServerJava/src/com/xensource/xenapi/Types.java @@ -1279,6 +1279,17 @@ public class Types String p1 = ErrorDescription.length > 1 ? ErrorDescription[1] : ""; throw new Types.CrlNameInvalid(p1); } + if (ErrorDescription[0].equals("VDI_NOT_SPARSE")) + { + String p1 = ErrorDescription.length > 1 ? ErrorDescription[1] : ""; + throw new Types.VdiNotSparse(p1); + } + if (ErrorDescription[0].equals("VDI_TOO_SMALL")) + { + String p1 = ErrorDescription.length > 1 ? ErrorDescription[1] : ""; + String p2 = ErrorDescription.length > 2 ? ErrorDescription[2] : ""; + throw new Types.VdiTooSmall(p1, p2); + } if (ErrorDescription[0].equals("HOST_POWER_ON_MODE_DISABLED")) { throw new Types.HostPowerOnModeDisabled(); @@ -7822,6 +7833,45 @@ public class Types } + /** + * The VDI is too small. Please resize it to at least the minimum size. + */ + public static class VdiTooSmall extends XenAPIException { + public final String vdi; + public final String minimumSize; + + /** + * Create a new VdiTooSmall + * + * @param vdi + * @param minimumSize + */ + public VdiTooSmall(String vdi, String minimumSize) { + super("The VDI is too small. Please resize it to at least the minimum size."); + this.vdi = vdi; + this.minimumSize = minimumSize; + } + + } + + /** + * The VDI is not stored using a sparse format. It is not possible to query and manipulate only the changed blocks (or 'block differences' or 'disk deltas') between two VDIs. Please select a VDI which uses a sparse-aware technology such as VHD. + */ + public static class VdiNotSparse extends XenAPIException { + public final String vdi; + + /** + * Create a new VdiNotSparse + * + * @param vdi + */ + public VdiNotSparse(String vdi) { + super("The VDI is not stored using a sparse format. It is not possible to query and manipulate only the changed blocks (or 'block differences' or 'disk deltas') between two VDIs. Please select a VDI which uses a sparse-aware technology such as VHD."); + this.vdi = vdi; + } + + } + /** * The hosts in this pool are not homogeneous. */ diff --git a/deps/XenServerJava/src/com/xensource/xenapi/VDI.java b/deps/XenServerJava/src/com/xensource/xenapi/VDI.java index 2c32c84e9fa..992a6a6d898 100644 --- a/deps/XenServerJava/src/com/xensource/xenapi/VDI.java +++ b/deps/XenServerJava/src/com/xensource/xenapi/VDI.java @@ -1608,6 +1608,29 @@ public class VDI extends XenAPIObject { return; } + /** + * Copy either a full VDI or the block differences between two VDIs into either a fresh VDI or an existing VDI. + * + * @param sr The destination SR (only required if the destination VDI is not specified + * @param baseVdi The base VDI (only required if copying only changed blocks, by default all blocks will be copied) + * @param intoVdi The destination VDI to copy blocks into (if omitted then a destination SR must be provided and a fresh VDI will be created) + * @return Task + */ + public Task copyAsync2(Connection c, SR sr, VDI baseVdi, VDI intoVdi) throws + BadServerResponse, + XenAPIException, + XmlRpcException, + Types.VdiReadonly, + Types.VdiTooSmall, + Types.VdiNotSparse { + String method_call = "Async.VDI.copy"; + String session = c.getSessionReference(); + Object[] method_params = {Marshalling.toXMLRPC(session), Marshalling.toXMLRPC(this.ref), Marshalling.toXMLRPC(sr), Marshalling.toXMLRPC(baseVdi), Marshalling.toXMLRPC(intoVdi)}; + Map response = c.dispatch(method_call, method_params); + Object result = response.get("Value"); + return Types.toTask(result); + } + /** * Make a fresh VDI in the specified SR and copy the supplied VDI's data to the new disk * diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java index 80497b1dd5f..ac79860df31 100644 --- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java @@ -48,9 +48,10 @@ import com.cloud.utils.fsm.NoTransitionException; * Manages allocating resources to vms. */ public interface VirtualMachineManager extends Manager { + static final ConfigKey ExecuteInSequence = new ConfigKey("Advanced", Boolean.class, "execute.in.sequence.hypervisor.commands", "false", - "If set to true, StartCommand, StopCommand, CopyCommand, MigrateCommand will be synchronized on the agent side." - + " If set to false, these commands become asynchronous. Default value is false.", true); + "If set to true, StartCommand, StopCommand, CopyCommand, MigrateCommand will be synchronized on the agent side." + + " If set to false, these commands become asynchronous. Default value is false.", false); public interface Topics { public static final String VM_POWER_STATE = "vm.powerstate"; @@ -93,39 +94,29 @@ public interface VirtualMachineManager extends Manager { boolean stateTransitTo(VirtualMachine vm, VirtualMachine.Event e, Long hostId) throws NoTransitionException; - void advanceStart(String vmUuid, Map params) throws InsufficientCapacityException, ResourceUnavailableException, - ConcurrentOperationException, OperationTimedoutException; + void advanceStart(String vmUuid, Map params, DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException, + ConcurrentOperationException, OperationTimedoutException; - void advanceStart(String vmUuid, Map params, DeploymentPlan planToDeploy) throws InsufficientCapacityException, - ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; + void advanceStart(String vmUuid, Map params, DeploymentPlan planToDeploy, DeploymentPlanner planner) throws InsufficientCapacityException, + ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; - void orchestrateStart(String vmUuid, Map params, DeploymentPlan planToDeploy) throws InsufficientCapacityException, + void orchestrateStart(String vmUuid, Map params, DeploymentPlan planToDeploy, DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; void advanceStop(String vmUuid, boolean cleanupEvenIfUnableToStop) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException; - void orchestrateStop(String vmUuid, boolean cleanupEvenIfUnableToStop) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException; - void advanceExpunge(String vmUuid) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException; void destroy(String vmUuid) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException; - void migrateAway(String vmUuid, long hostId) throws InsufficientServerCapacityException; + void migrateAway(String vmUuid, long hostId, DeploymentPlanner planner) throws InsufficientServerCapacityException; void migrate(String vmUuid, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException; - void orchestrateMigrate(String vmUuid, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException; - - void migrateWithStorage(String vmUuid, long srcId, long destId, Map volumeToPool) throws ResourceUnavailableException, - ConcurrentOperationException; - - void orchestrateMigrateWithStorage(String vmUuid, long srcId, long destId, Map volumeToPool) throws ResourceUnavailableException, - ConcurrentOperationException; + void migrateWithStorage(String vmUuid, long srcId, long destId, Map volumeToPool) throws ResourceUnavailableException, ConcurrentOperationException; void reboot(String vmUuid, Map params) throws InsufficientCapacityException, ResourceUnavailableException; - void orchestrateReboot(String vmUuid, Map params) throws InsufficientCapacityException, ResourceUnavailableException; - void advanceReboot(String vmUuid, Map params) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, OperationTimedoutException; @@ -142,8 +133,6 @@ public interface VirtualMachineManager extends Manager { void storageMigration(String vmUuid, StoragePool storagePoolId); - void orchestrateStorageMigration(String vmUuid, StoragePool storagePoolId); - /** * @param vmInstance * @param newServiceOffering @@ -166,10 +155,7 @@ public interface VirtualMachineManager extends Manager { * @throws ResourceUnavailableException * @throws InsufficientCapacityException */ - NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException; - - NicProfile orchestrateAddVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, + NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; /** @@ -181,8 +167,6 @@ public interface VirtualMachineManager extends Manager { */ boolean removeNicFromVm(VirtualMachine vm, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException; - boolean orchestrateRemoveNicFromVm(VirtualMachine vm, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException; - /** * @param vm * @param network @@ -192,9 +176,6 @@ public interface VirtualMachineManager extends Manager { * @throws ConcurrentOperationException */ boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException; - - boolean orchestrateRemoveVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException; - /** * @param nic * @param hypervisorType @@ -209,17 +190,11 @@ public interface VirtualMachineManager extends Manager { */ VirtualMachineTO toVmTO(VirtualMachineProfile profile); - VirtualMachine reConfigureVm(String vmUuid, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException; - - VirtualMachine orchestrateReConfigureVm(String vmUuid, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, - ConcurrentOperationException; + VirtualMachine reConfigureVm(String vmUuid, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException, + InsufficientServerCapacityException; void findHostAndMigrate(String vmUuid, Long newSvcOfferingId, DeploymentPlanner.ExcludeList excludeHostList) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException; void migrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long newSvcOfferingId) throws ResourceUnavailableException, ConcurrentOperationException; - - void orchestrateMigrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long newSvcOfferingId) throws ResourceUnavailableException, - ConcurrentOperationException; - } diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java index fc196e24568..c036c99b63d 100755 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java @@ -208,11 +208,13 @@ public interface NetworkOrchestrationService { boolean isSecondaryIpSetForNic(long nicId); - List listVmNics(Long vmId, Long nicId); + List listVmNics(long vmId, Long nicId, Long networkId); Nic savePlaceholderNic(Network network, String ip4Address, String ip6Address, Type vmType); DhcpServiceProvider getDhcpServiceProvider(Network network); void removeDhcpServiceInSubnet(Nic nic); + + boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType); } diff --git a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index a9ccc06617a..f79d3361b97 100644 --- a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -44,6 +44,7 @@ import com.cloud.utils.fsm.NoTransitionException; import com.cloud.vm.DiskProfile; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.framework.config.ConfigKey; /** * VolumeOrchestrationService is a PURE orchestration service on CloudStack @@ -53,6 +54,21 @@ import com.cloud.vm.VirtualMachineProfile; * to provision volumes. */ public interface VolumeOrchestrationService { + + static final ConfigKey CustomDiskOfferingMinSize = new ConfigKey("Advanced", + Long.class, + "custom.diskoffering.size.min", + "1", + "Minimum size in GB for custom disk offering.", + true + ); + static final ConfigKey CustomDiskOfferingMaxSize = new ConfigKey("Advanced", + Long.class, + "custom.diskoffering.size.max", + "1024", + "Maximum size in GB for custom disk offering.", + true + ); VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) throws ConcurrentOperationException, StorageUnavailableException; @@ -78,6 +94,8 @@ public interface VolumeOrchestrationService { void cleanupVolumes(long vmId) throws ConcurrentOperationException; + void disconnectVolumesFromHost(long vmId, long hostId); + void migrateVolumes(VirtualMachine vm, VirtualMachineTO vmTo, Host srcHost, Host destHost, Map volumeToPool); boolean storageMigration(VirtualMachineProfile vm, StoragePool destPool) throws StorageUnavailableException; diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java index 210d4ae2fbe..5ebef031c5c 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java @@ -42,4 +42,6 @@ public interface DataStoreManager { List listImageStores(); List listImageCacheStores(); + + boolean isRegionStore(DataStore store); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java index b812f6efd99..4657316dd8a 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/EndPointSelector.java @@ -23,11 +23,17 @@ import java.util.List; public interface EndPointSelector { EndPoint select(DataObject srcData, DataObject destData); + EndPoint select(DataObject srcData, DataObject destData, StorageAction action); + EndPoint select(DataObject object); EndPoint select(DataStore store); + EndPoint select(DataObject object, StorageAction action); + List selectAll(DataStore store); EndPoint select(Scope scope, Long storeId); + + EndPoint selectHypervisorHost(Scope scope); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java index f3399cd733e..c4dfc5ceb7e 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/PrimaryDataStoreDriver.java @@ -21,9 +21,19 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; +import com.cloud.host.Host; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; + public interface PrimaryDataStoreDriver extends DataStoreDriver { public ChapInfo getChapInfo(VolumeInfo volumeInfo); + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore); + + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore); + + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool); + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback); public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java index d5255f40407..59e59a60662 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java @@ -30,4 +30,6 @@ public interface SnapshotDataFactory { SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role); List listSnapshotOnCache(long snapshotId); + + SnapshotInfo getReadySnapshotOnCache(long snapshotId); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java index e953eb6e21b..000b9ec4e60 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java @@ -25,4 +25,6 @@ public interface SnapshotService { boolean deleteSnapshot(SnapshotInfo snapshot); boolean revertSnapshot(Long snapshotId); + + void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java new file mode 100644 index 00000000000..4fbb20ed29e --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cloudstack.engine.subsystem.api.storage; + +public enum StorageAction { + TAKESNAPSHOT, + BACKUPSNAPSHOT, + DELETESNAPSHOT +} diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index 5cba79ad2b4..f93f4efac83 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -22,6 +22,7 @@ import com.cloud.agent.api.Answer; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering.DiskCacheMode; import com.cloud.storage.Volume; +import com.cloud.vm.VirtualMachine; public interface VolumeInfo extends DataObject, Volume { boolean isAttachedVM(); @@ -35,6 +36,7 @@ public interface VolumeInfo extends DataObject, Volume { Long getLastPoolId(); String getAttachedVmName(); + VirtualMachine getAttachedVM(); void processEventOnly(ObjectInDataStoreStateMachine.Event event); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index bcc4e7f9c54..3f676ae73dc 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -44,6 +44,10 @@ public interface VolumeService { ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore); + boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore); + + void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore); + /** * Creates the volume based on the given criteria * diff --git a/engine/components-api/src/com/cloud/deploy/DeploymentPlanningManager.java b/engine/components-api/src/com/cloud/deploy/DeploymentPlanningManager.java index b61e89ddcea..de2fc0eaf5a 100644 --- a/engine/components-api/src/com/cloud/deploy/DeploymentPlanningManager.java +++ b/engine/components-api/src/com/cloud/deploy/DeploymentPlanningManager.java @@ -39,11 +39,12 @@ public interface DeploymentPlanningManager extends Manager { * * */ - DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, - AffinityConflictException; + DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, + ExcludeList avoids, DeploymentPlanner planner) throws InsufficientServerCapacityException, AffinityConflictException; - String finalizeReservation(DeployDestination plannedDestination, VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) - throws InsufficientServerCapacityException, AffinityConflictException; + String finalizeReservation(DeployDestination plannedDestination, + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) + throws InsufficientServerCapacityException, AffinityConflictException; void cleanupVMReservations(); } diff --git a/engine/components-api/src/com/cloud/network/IpAddressManager.java b/engine/components-api/src/com/cloud/network/IpAddressManager.java index e7061f9ecd9..9ccc9466f70 100644 --- a/engine/components-api/src/com/cloud/network/IpAddressManager.java +++ b/engine/components-api/src/com/cloud/network/IpAddressManager.java @@ -21,7 +21,6 @@ import java.util.List; import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.dc.DataCenter; -import com.cloud.dc.Pod; import com.cloud.dc.Vlan.VlanType; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; @@ -169,8 +168,8 @@ public interface IpAddressManager { int getRuleCountForIp(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state); - public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) throws InsufficientAddressCapacityException; + public String allocateGuestIP(Network network, String requestedIp) throws InsufficientAddressCapacityException; - String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod, Account caller, String requestedIp) throws InsufficientAddressCapacityException; + String allocatePublicIpForGuestNic(Network network, Long podId, Account ipOwner, String requestedIp) throws InsufficientAddressCapacityException; } diff --git a/engine/components-api/src/com/cloud/resource/ResourceManager.java b/engine/components-api/src/com/cloud/resource/ResourceManager.java index e623b9c6da9..95fb3853717 100755 --- a/engine/components-api/src/com/cloud/resource/ResourceManager.java +++ b/engine/components-api/src/com/cloud/resource/ResourceManager.java @@ -1,3 +1,4 @@ + // 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 @@ -83,6 +84,8 @@ public interface ResourceManager extends ResourceService { public boolean maintain(final long hostId) throws AgentUnavailableException; + public boolean checkAndMaintain(final long hostId); + @Override public boolean deleteHost(long hostId, boolean isForced, boolean isForceDeleteStorage); diff --git a/engine/components-api/src/com/cloud/template/TemplateManager.java b/engine/components-api/src/com/cloud/template/TemplateManager.java index 83fedcd89a8..0a07f6b4a3d 100755 --- a/engine/components-api/src/com/cloud/template/TemplateManager.java +++ b/engine/components-api/src/com/cloud/template/TemplateManager.java @@ -101,6 +101,8 @@ public interface TemplateManager { DataStore getImageStore(long zoneId, long tmpltId); + DataStore getImageStore(long tmpltId); + Long getTemplateSize(long templateId, long zoneId); DataStore getImageStore(String storeUuid, Long zoneId); diff --git a/engine/components-api/src/com/cloud/vm/VmWork.java b/engine/components-api/src/com/cloud/vm/VmWork.java index 751db997d90..ed9f44cb9bb 100644 --- a/engine/components-api/src/com/cloud/vm/VmWork.java +++ b/engine/components-api/src/com/cloud/vm/VmWork.java @@ -25,10 +25,13 @@ public class VmWork implements Serializable { long accountId; long vmId; - public VmWork(long userId, long accountId, long vmId) { + String handlerName; + + public VmWork(long userId, long accountId, long vmId, String handlerName) { this.userId = userId; this.accountId = accountId; this.vmId = vmId; + this.handlerName = handlerName; } public long getUserId() { @@ -42,4 +45,8 @@ public class VmWork implements Serializable { public long getVmId() { return vmId; } + + public String getHandlerName() { + return handlerName; + } } diff --git a/engine/components-api/src/com/cloud/vm/VmWorkConstants.java b/engine/components-api/src/com/cloud/vm/VmWorkConstants.java new file mode 100644 index 00000000000..20e40b7f84b --- /dev/null +++ b/engine/components-api/src/com/cloud/vm/VmWorkConstants.java @@ -0,0 +1,23 @@ +// 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.vm; + +public interface VmWorkConstants { + public static final String VM_WORK_QUEUE = "VmWorkJobQueue"; + public static final String VM_WORK_JOB_DISPATCHER = "VmWorkJobDispatcher"; + public static final String VM_WORK_JOB_WAKEUP_DISPATCHER = "VmWorkJobWakeupDispatcher"; +} diff --git a/engine/components-api/src/com/cloud/vm/VmWorkJobHandler.java b/engine/components-api/src/com/cloud/vm/VmWorkJobHandler.java new file mode 100644 index 00000000000..d49a041bf48 --- /dev/null +++ b/engine/components-api/src/com/cloud/vm/VmWorkJobHandler.java @@ -0,0 +1,25 @@ +// 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.vm; + +import org.apache.cloudstack.jobs.JobInfo; + +import com.cloud.utils.Pair; + +public interface VmWorkJobHandler { + Pair handleVmWorkJob(VmWork work) throws Exception; +} diff --git a/engine/components-api/src/com/cloud/vm/VmWorkJobHandlerProxy.java b/engine/components-api/src/com/cloud/vm/VmWorkJobHandlerProxy.java new file mode 100644 index 00000000000..ce10a83c7cd --- /dev/null +++ b/engine/components-api/src/com/cloud/vm/VmWorkJobHandlerProxy.java @@ -0,0 +1,133 @@ +// 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.vm; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; + +import com.google.gson.Gson; + +import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; +import org.apache.cloudstack.jobs.JobInfo; + +import com.cloud.serializer.GsonHelper; +import com.cloud.utils.Pair; + +/** + * VmWorkJobHandlerProxy can not be used as standalone due to run-time + * reflection usage in its implementation, run-time reflection conflicts with Spring proxy mode. + * It means that we can not instantiate VmWorkJobHandlerProxy beans directly in Spring and expect + * it can handle VmWork directly from there. + * + */ +public class VmWorkJobHandlerProxy implements VmWorkJobHandler { + + private static final Logger s_logger = Logger.getLogger(VmWorkJobHandlerProxy.class); + + private Object _target; + private Map, Method> _handlerMethodMap = new HashMap, Method>(); + + private Gson _gsonLogger; + + public VmWorkJobHandlerProxy(Object target) { + _gsonLogger = GsonHelper.getGsonLogger(); + + buildLookupMap(target.getClass()); + _target = target; + } + + private void buildLookupMap(Class hostClass) { + Class clz = hostClass; + while (clz != null && clz != Object.class) { + Method[] hostHandlerMethods = clz.getDeclaredMethods(); + + for (Method method : hostHandlerMethods) { + if (isVmWorkJobHandlerMethod(method)) { + Class paramType = method.getParameterTypes()[0]; + assert (_handlerMethodMap.get(paramType) == null); + + method.setAccessible(true); + _handlerMethodMap.put(paramType, method); + } + } + + clz = clz.getSuperclass(); + } + } + + @SuppressWarnings("deprecation") + private boolean isVmWorkJobHandlerMethod(Method method) { + if (method.getParameterTypes().length != 1) + return false; + + Class returnType = method.getReturnType(); + if (!Pair.class.isAssignableFrom(returnType)) + return false; + + Class paramType = method.getParameterTypes()[0]; + if (!VmWork.class.isAssignableFrom(paramType)) + return false; + + return true; + } + + private Method getHandlerMethod(Class paramType) { + return _handlerMethodMap.get(paramType); + } + + @SuppressWarnings("unchecked") + @Override + public Pair handleVmWorkJob(VmWork work) throws Exception { + + Method method = getHandlerMethod(work.getClass()); + if (method != null) { + + try { + if (s_logger.isDebugEnabled()) + s_logger.debug("Execute VM work job: " + work.getClass().getName() + _gsonLogger.toJson(work)); + + Object obj = method.invoke(_target, work); + + if (s_logger.isDebugEnabled()) + s_logger.debug("Done executing VM work job: " + work.getClass().getName() + _gsonLogger.toJson(work)); + + assert (obj instanceof Pair); + return (Pair)obj; + } catch (InvocationTargetException e) { + s_logger.error("Invocation exception, caused by: " + e.getCause()); + + // legacy CloudStack code relies on checked exception for error handling + // we need to re-throw the real exception here + if (e.getCause() != null && e.getCause() instanceof Exception) { + s_logger.info("Rethrow exception " + e.getCause()); + throw (Exception)e.getCause(); + } + + throw e; + } + } else { + s_logger.error("Unable to find handler for VM work job: " + work.getClass().getName() + _gsonLogger.toJson(work)); + + RuntimeException ex = new RuntimeException("Unable to find handler for VM work job: " + work.getClass().getName()); + return new Pair(JobInfo.Status.FAILED, JobSerializerHelper.toObjectSerializedString(ex)); + } + } +} diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkSerializer.java b/engine/components-api/src/com/cloud/vm/VmWorkSerializer.java similarity index 100% rename from engine/orchestration/src/com/cloud/vm/VmWorkSerializer.java rename to engine/components-api/src/com/cloud/vm/VmWorkSerializer.java diff --git a/engine/components-api/src/com/cloud/vm/snapshot/VMSnapshotManager.java b/engine/components-api/src/com/cloud/vm/snapshot/VMSnapshotManager.java index 7d233ca7d30..e7e3372b0db 100644 --- a/engine/components-api/src/com/cloud/vm/snapshot/VMSnapshotManager.java +++ b/engine/components-api/src/com/cloud/vm/snapshot/VMSnapshotManager.java @@ -42,5 +42,4 @@ public interface VMSnapshotManager extends VMSnapshotService, Manager { boolean syncVMSnapshot(VMInstanceVO vm, Long hostId); boolean hasActiveVMSnapshotTasks(Long vmId); - } diff --git a/engine/orchestration/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml b/engine/orchestration/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml index fc3bae3129f..fd5299c8f70 100644 --- a/engine/orchestration/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml +++ b/engine/orchestration/resources/META-INF/cloudstack/core/spring-engine-orchestration-core-context.xml @@ -60,7 +60,6 @@ - + - + + + + + + + + - + diff --git a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java index cdfe744841f..d51df220195 100755 --- a/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java +++ b/engine/orchestration/src/com/cloud/agent/manager/AgentManagerImpl.java @@ -1475,20 +1475,12 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl List hosts = sc.list(); for (HostVO host : hosts) { - long hostId = host.getId(); - DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); - HostPodVO podVO = _podDao.findById(host.getPodId()); - String hostDesc = "name: " + host.getName() + " (id:" + hostId + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); - - if (host.getType() != Host.Type.Storage) { -// List vos = _vmDao.listByHostId(hostId); -// List vosMigrating = _vmDao.listVmsMigratingFromHost(hostId); -// if (vos.isEmpty() && vosMigrating.isEmpty()) { -// _alertMgr.sendAlert(AlertManager.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "Migration Complete for host " + hostDesc, "Host [" -// + hostDesc -// + "] is ready for maintenance"); -// _resourceMgr.resourceStateTransitTo(host, ResourceState.Event.InternalEnterMaintenance, _msId); -// } + if (_resourceMgr.checkAndMaintain(host.getId())) { + DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); + HostPodVO podVO = _podDao.findById(host.getPodId()); + String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "Migration Complete for host " + hostDesc, "Host [" + + hostDesc + "] is ready for maintenance"); } } } catch (Throwable th) { diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 7201569b970..628528a33c4 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -58,7 +58,6 @@ import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.Outcome; import org.apache.cloudstack.framework.jobs.dao.VmWorkJobDao; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; -import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; import org.apache.cloudstack.framework.messagebus.MessageBus; @@ -118,6 +117,7 @@ import com.cloud.dc.dao.HostPodDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.domain.dao.DomainDao; @@ -182,7 +182,7 @@ import com.cloud.utils.db.DB; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionCallbackWithException; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionLegacy; @@ -205,9 +205,11 @@ import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Local(value = VirtualMachineManager.class) -public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, Listener, Configurable { +public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable { private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class); + public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName(); + private static final String VM_SYNC_ALERT_SUBJECT = "VM state sync alert"; @Inject @@ -270,6 +272,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac protected AffinityGroupVMMapDao _affinityGroupVMMapDao; @Inject protected EntityManager _entityMgr; + @Inject ConfigDepot _configDepot; @@ -312,6 +315,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject protected AsyncJobManager _jobMgr; + VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); + Map _vmGurus = new HashMap(); protected StateMachine2 _stateMachine; @@ -454,7 +459,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return; } - advanceStop(vm, false); + advanceStop(vm.getUuid(), false); + vm = _vmDao.findByUuid(vm.getUuid()); try { if (!stateTransitTo(vm, VirtualMachine.Event.ExpungeOperation, vm.getHostId())) { @@ -479,6 +485,36 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac List nicExpungeCommands = hvGuru.finalizeExpungeNics(vm, profile.getNics()); _networkMgr.cleanupNics(profile); + s_logger.debug("Cleaning up hypervisor data structures (ex. SRs in XenServer) for managed storage"); + + List volumeExpungeCommands = hvGuru.finalizeExpungeVolumes(vm); + + Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId(); + + if (volumeExpungeCommands != null && hostId != null) { + Commands cmds = new Commands(Command.OnError.Stop); + + for (Command volumeExpungeCommand : volumeExpungeCommands) { + cmds.addCommand(volumeExpungeCommand); + } + + _agentMgr.send(hostId, cmds); + + if (!cmds.isSuccessful()) { + for (Answer answer : cmds.getAnswers()) { + if (!answer.getResult()) { + s_logger.warn("Failed to expunge vm due to: " + answer.getDetails()); + + throw new CloudRuntimeException("Unable to expunge " + vm + " due to " + answer.getDetails()); + } + } + } + } + + if (hostId != null) { + volumeMgr.disconnectVolumesFromHost(vm.getId(), hostId); + } + // Clean up volumes based on the vm's instance id volumeMgr.cleanupVolumes(vm.getId()); @@ -490,7 +526,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac // send hypervisor-dependent commands before removing List finalizeExpungeCommands = hvGuru.finalizeExpunge(vm); if (finalizeExpungeCommands != null && finalizeExpungeCommands.size() > 0) { - Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId(); if (hostId != null) { Commands cmds = new Commands(Command.OnError.Stop); for (Command command : finalizeExpungeCommands) { @@ -521,6 +556,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean start() { + // TODO, initial delay is hardcoded + _executor.scheduleAtFixedRate(new TransitionTask(), 5000, VmJobStateReportInterval.value(), TimeUnit.SECONDS); _executor.scheduleAtFixedRate(new CleanupTask(), VmOpCleanupInterval.value(), VmOpCleanupInterval.value(), TimeUnit.SECONDS); cancelWorkItems(_nodeId); return true; @@ -556,7 +593,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void start(String vmUuid, Map params, DeploymentPlan planToDeploy) { try { - advanceStart(vmUuid, params, planToDeploy); + advanceStart(vmUuid, params, planToDeploy, null); } catch (ConcurrentOperationException e) { throw new CloudRuntimeException("Unable to start a VM due to concurrent operation", e).add(VirtualMachine.class, vmUuid); } catch (InsufficientCapacityException e) { @@ -697,20 +734,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } @Override - public void advanceStart(String vmUuid, Map params) + public void advanceStart(String vmUuid, Map params, DeploymentPlanner planner) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { - - advanceStart(vmUuid, params, null); + advanceStart(vmUuid, params, null, planner); } @Override - public void advanceStart(String vmUuid, Map params, DeploymentPlan planToDeploy) throws InsufficientCapacityException, + public void advanceStart(String vmUuid, Map params, DeploymentPlan planToDeploy, DeploymentPlanner planner) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance - orchestrateStart(vmUuid, params, planToDeploy); + orchestrateStart(vmUuid, params, planToDeploy, planner); } else { Outcome outcome = startVmThroughJobQueue(vmUuid, params, planToDeploy); @@ -722,19 +758,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; } } } + @Override - public void orchestrateStart(String vmUuid, Map params, DeploymentPlan planToDeploy) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException { + public void orchestrateStart(String vmUuid, Map params, DeploymentPlan planToDeploy, DeploymentPlanner planner) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { CallContext cctxt = CallContext.current(); Account account = cctxt.getCallingAccount(); @@ -848,10 +885,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, account, params); + Account owner = _entityMgr.findById(Account.class, vm.getAccountId()); + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, owner, params); DeployDestination dest = null; try { - dest = _dpMgr.planDeployment(vmProfile, plan, avoids); + dest = _dpMgr.planDeployment(vmProfile, plan, avoids, planner); } catch (AffinityConflictException e2) { s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2); throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); @@ -1225,10 +1263,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void advanceStop(String vmUuid, boolean cleanUpEvenIfUnableToStop) - throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { + throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateStop(vmUuid, cleanUpEvenIfUnableToStop); } else { @@ -1242,21 +1280,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof AgentUnavailableException) - throw (AgentUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof OperationTimedoutException) - throw (OperationTimedoutException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof AgentUnavailableException) + throw (AgentUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof OperationTimedoutException) + throw (OperationTimedoutException)jobResult; } } } - @Override - public void orchestrateStop(String vmUuid, boolean cleanUpEvenIfUnableToStop) throws AgentUnavailableException, OperationTimedoutException, - ConcurrentOperationException { + private void orchestrateStop(String vmUuid, boolean cleanUpEvenIfUnableToStop) throws AgentUnavailableException, OperationTimedoutException, ConcurrentOperationException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); advanceStop(vm, cleanUpEvenIfUnableToStop); @@ -1490,13 +1526,15 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Destroying vm " + vm); } - advanceStop(vm, VmDestroyForcestop.value()); + advanceStop(vmUuid, VmDestroyForcestop.value()); if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(), null)) { s_logger.debug("Unable to delete all snapshots for " + vm); throw new CloudRuntimeException("Unable to delete vm snapshots for " + vm); } + // reload the vm object from db + vm = _vmDao.findByUuid(vmUuid); try { if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); @@ -1520,7 +1558,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void storageMigration(String vmUuid, StoragePool destPool) { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateStorageMigration(vmUuid, destPool); } else { @@ -1534,16 +1572,15 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof RuntimeException) - throw (RuntimeException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof RuntimeException) + throw (RuntimeException)jobResult; } } } - @Override - public void orchestrateStorageMigration(String vmUuid, StoragePool destPool) { + private void orchestrateStorageMigration(String vmUuid, StoragePool destPool) { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); try { @@ -1600,10 +1637,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void migrate(String vmUuid, long srcHostId, DeployDestination dest) - throws ResourceUnavailableException, ConcurrentOperationException { + throws ResourceUnavailableException, ConcurrentOperationException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateMigrate(vmUuid, srcHostId, dest); } else { @@ -1617,20 +1654,19 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof RuntimeException) - throw (RuntimeException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof RuntimeException) + throw (RuntimeException)jobResult; } } } - @Override - public void orchestrateMigrate(String vmUuid, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException { + private void orchestrateMigrate(String vmUuid, long srcHostId, DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null) { if (s_logger.isDebugEnabled()) { @@ -1872,10 +1908,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void migrateWithStorage(String vmUuid, long srcHostId, long destHostId, Map volumeToPool) - throws ResourceUnavailableException, ConcurrentOperationException { + throws ResourceUnavailableException, ConcurrentOperationException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateMigrateWithStorage(vmUuid, srcHostId, destHostId, volumeToPool); } else { @@ -1889,7 +1925,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); + Object jobException = _jobMgr.unmarshallResultObject(outcome.getJob()); if (jobException != null) { if (jobException instanceof ResourceUnavailableException) throw (ResourceUnavailableException)jobException; @@ -1899,9 +1935,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - @Override - public void orchestrateMigrateWithStorage(String vmUuid, long srcHostId, long destHostId, Map volumeToPool) throws ResourceUnavailableException, - ConcurrentOperationException { + private void orchestrateMigrateWithStorage(String vmUuid, long srcHostId, long destHostId, Map volumeToPool) throws ResourceUnavailableException, + ConcurrentOperationException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); @@ -2039,7 +2074,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } @Override - public void migrateAway(String vmUuid, long srcHostId) throws InsufficientServerCapacityException { + public void migrateAway(String vmUuid, long srcHostId, DeploymentPlanner planner) throws InsufficientServerCapacityException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); if (vm == null) { s_logger.debug("Unable to find a VM for " + vmUuid); @@ -2072,7 +2107,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac while (true) { try { - dest = _dpMgr.planDeployment(profile, plan, excludes); + dest = _dpMgr.planDeployment(profile, plan, excludes, planner); } catch (AffinityConflictException e2) { s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2); throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); @@ -2100,7 +2135,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } try { - advanceStop(vm, true); + advanceStop(vmUuid, true); throw new CloudRuntimeException("Unable to migrate " + vm); } catch (ResourceUnavailableException e) { s_logger.debug("Unable to stop VM due to " + e.getMessage()); @@ -2152,10 +2187,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void advanceReboot(String vmUuid, Map params) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateReboot(vmUuid, params); } else { @@ -2169,21 +2204,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof InsufficientCapacityException) - throw (InsufficientCapacityException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof InsufficientCapacityException) + throw (InsufficientCapacityException)jobResult; } } } - @Override - public void orchestrateReboot(String vmUuid, Map params) throws InsufficientCapacityException, ConcurrentOperationException, - ResourceUnavailableException { + private void orchestrateReboot(String vmUuid, Map params) throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); DataCenter dc = _entityMgr.findById(DataCenter.class, vm.getDataCenterId()); @@ -2659,7 +2693,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (agentState == State.Shutdowned) { if (serverState == State.Running || serverState == State.Starting || serverState == State.Stopping) { try { - advanceStop(vm, true); + advanceStop(vm.getUuid(), true); } catch (AgentUnavailableException e) { assert (false) : "How do we hit this with forced on?"; return null; @@ -2829,7 +2863,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - if (VmJobEnabled.value()) { + if(VmJobEnabled.value()) { if (ping.getHostVmStateReport() != null && ping.getHostVmStateReport().size() > 0) { _syncMgr.processHostVmStatePingReport(agentId, ping.getHostVmStateReport()); } @@ -2940,9 +2974,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return; } try { - lock.addRef(); - List instances = - _vmDao.findVMInTransition(new Date(new Date().getTime() - (AgentManager.Wait.value() * 1000)), State.Starting, State.Stopping); + scanStalledVMInTransitionStateOnDisconnectedHosts(); + + List instances = _vmDao.findVMInTransition(new Date(new Date().getTime() - (AgentManager.Wait.value() * 1000)), State.Starting, State.Stopping); for (VMInstanceVO instance : instances) { State state = instance.getState(); if (state == State.Stopping) { @@ -3074,10 +3108,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) - throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance return orchestrateAddVmToNetwork(vm, network, requested); } else { @@ -3091,32 +3125,26 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, outcome.getJob().getId()); - if (jobVo.getResultCode() == JobInfo.Status.SUCCEEDED.ordinal()) { - - NicProfile nic = (NicProfile)JobSerializerHelper.fromObjectSerializedString(jobVo.getResult()); - return nic; - } else { - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof InsufficientCapacityException) - throw (InsufficientCapacityException)jobException; - else if (jobException instanceof RuntimeException) - throw (RuntimeException)jobException; - } - throw new RuntimeException("Job failed with unhandled exception"); + Object jobException = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobException != null) { + if (jobException instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobException; + else if (jobException instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobException; + else if (jobException instanceof InsufficientCapacityException) + throw (InsufficientCapacityException)jobException; + else if (jobException instanceof RuntimeException) + throw (RuntimeException)jobException; + else if (jobException instanceof Long) + return requested; } + + throw new RuntimeException("Unexpected job execution result"); } } - @Override - public NicProfile orchestrateAddVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, - ResourceUnavailableException, - InsufficientCapacityException { + private NicProfile orchestrateAddVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { CallContext cctx = CallContext.current(); s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); @@ -3183,10 +3211,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean removeNicFromVm(VirtualMachine vm, Nic nic) - throws ConcurrentOperationException, ResourceUnavailableException { + throws ConcurrentOperationException, ResourceUnavailableException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance return orchestrateRemoveNicFromVm(vm, nic); } else { @@ -3200,29 +3228,23 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, outcome.getJob().getId()); - - if (jobVo.getResultCode() == JobInfo.Status.SUCCEEDED.ordinal()) { - Boolean result = (Boolean)JobSerializerHelper.fromObjectSerializedString(jobVo.getResult()); - return result; - } else { - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; - else if (jobException instanceof RuntimeException) - throw (RuntimeException)jobException; - } - - throw new RuntimeException("Job failed with un-handled exception"); + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof RuntimeException) + throw (RuntimeException)jobResult; + else if (jobResult instanceof Boolean) + return (Boolean)jobResult; } + + throw new RuntimeException("Job failed with un-handled exception"); } } - @Override - public boolean orchestrateRemoveNicFromVm(VirtualMachine vm, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException { + private boolean orchestrateRemoveNicFromVm(VirtualMachine vm, Nic nic) throws ConcurrentOperationException, ResourceUnavailableException { CallContext cctx = CallContext.current(); VMInstanceVO vmVO = _vmDao.findById(vm.getId()); NetworkVO network = _networkDao.findById(nic.getNetworkId()); @@ -3288,9 +3310,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return orchestrateRemoveVmFromNetwork(vm, network, broadcastUri); } - @Override @DB - public boolean orchestrateRemoveVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { + private boolean orchestrateRemoveVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { CallContext cctx = CallContext.current(); VMInstanceVO vmVO = _vmDao.findById(vm.getId()); ReservationContext context = new ReservationContextImpl(null, null, cctx.getCallingUser(), cctx.getCallingAccount()); @@ -3400,7 +3421,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac DeployDestination dest = null; try { - dest = _dpMgr.planDeployment(profile, plan, excludes); + dest = _dpMgr.planDeployment(profile, plan, excludes, null); } catch (AffinityConflictException e2) { s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2); throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); @@ -3430,9 +3451,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void migrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long oldSvcOfferingId) - throws ResourceUnavailableException, ConcurrentOperationException { + throws ResourceUnavailableException, ConcurrentOperationException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance orchestrateMigrateForScale(vmUuid, srcHostId, dest, oldSvcOfferingId); } else { @@ -3446,19 +3467,18 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; } } } - @Override - public void orchestrateMigrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long oldSvcOfferingId) - throws ResourceUnavailableException, ConcurrentOperationException { + private void orchestrateMigrateForScale(String vmUuid, long srcHostId, DeployDestination dest, Long oldSvcOfferingId) + throws ResourceUnavailableException, ConcurrentOperationException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); s_logger.info("Migrating " + vm + " to " + dest); @@ -3678,11 +3698,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public VMInstanceVO reConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, - boolean reconfiguringOnExistingHost) - throws ResourceUnavailableException, ConcurrentOperationException { + boolean reconfiguringOnExistingHost) + throws ResourceUnavailableException, InsufficientServerCapacityException, ConcurrentOperationException { AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); - if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)) { + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { // avoid re-entrance return orchestrateReConfigureVm(vmUuid, oldServiceOffering, reconfiguringOnExistingHost); } else { @@ -3697,27 +3717,26 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new RuntimeException("Execution excetion", e); } - AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, outcome.getJob().getId()); - if (jobVo.getResultCode() == JobInfo.Status.SUCCEEDED.ordinal()) { - return _entityMgr.findById(VMInstanceVO.class, vm.getId()); - } else { - Throwable jobException = retriveExecutionException(outcome.getJob()); - if (jobException != null) { - if (jobException instanceof ResourceUnavailableException) - throw (ResourceUnavailableException)jobException; - else if (jobException instanceof ConcurrentOperationException) - throw (ConcurrentOperationException)jobException; + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof InsufficientServerCapacityException) + throw (InsufficientServerCapacityException)jobResult; + else if (jobResult instanceof Throwable) { + s_logger.error("Unhandled exception", (Throwable)jobResult); + throw new RuntimeException("Unhandled exception", (Throwable)jobResult); } - - throw new RuntimeException("Failed with un-handled exception"); } + + return (VMInstanceVO)vm; } } - @Override - public VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, boolean reconfiguringOnExistingHost) - throws ResourceUnavailableException, - ConcurrentOperationException { + private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, + ConcurrentOperationException { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); long newServiceofferingId = vm.getServiceOfferingId(); @@ -3802,26 +3821,26 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac Long vmId = (Long)args; List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vmId); + VirtualMachine.Type.Instance, vmId); if (pendingWorkJobs.size() == 0) { // there is no pending operation job VMInstanceVO vm = _vmDao.findById(vmId); if (vm != null) { switch (vm.getPowerState()) { - case PowerOn: - handlePowerOnReportWithNoPendingJobsOnVM(vm); - break; + case PowerOn: + handlePowerOnReportWithNoPendingJobsOnVM(vm); + break; - case PowerOff: - handlePowerOffReportWithNoPendingJobsOnVM(vm); - break; + case PowerOff: + handlePowerOffReportWithNoPendingJobsOnVM(vm); + break; - // PowerUnknown shouldn't be reported, it is a derived - // VM power state from host state (host un-reachable - case PowerUnknown: - default: - assert (false); - break; + // PowerUnknown shouldn't be reported, it is a derived + // VM power state from host state (host un-reachable + case PowerUnknown: + default: + assert (false); + break; } } else { s_logger.warn("VM " + vmId + " no longer exists when processing VM state report"); @@ -3834,98 +3853,98 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private void handlePowerOnReportWithNoPendingJobsOnVM(VMInstanceVO vm) { // - // 1) handle left-over transitional VM states - // 2) handle out of band VM live migration - // 3) handle out of sync stationary states, marking VM from Stopped to Running with - // alert messages + // 1) handle left-over transitional VM states + // 2) handle out of band VM live migration + // 3) handle out of sync stationary states, marking VM from Stopped to Running with + // alert messages // switch (vm.getState()) { - case Starting: - try { - stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unexpected VM state transition exception, race-condition?", e); - } + case Starting: + try { + stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unexpected VM state transition exception, race-condition?", e); + } - // we need to alert admin or user about this risky state transition - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), - VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + - ") state is sync-ed (Starting -> Running) from out-of-context transition. VM network environment may need to be reset"); - break; + // we need to alert admin or user about this risky state transition + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), + VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + + ") state is sync-ed (Starting -> Running) from out-of-context transition. VM network environment may need to be reset"); + break; - case Running: - try { - if (vm.getHostId() != null && vm.getHostId().longValue() != vm.getPowerHostId().longValue()) - s_logger.info("Detected out of band VM migration from host " + vm.getHostId() + " to host " + vm.getPowerHostId()); - stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unexpected VM state transition exception, race-condition?", e); - } - break; + case Running: + try { + if (vm.getHostId() != null && vm.getHostId().longValue() != vm.getPowerHostId().longValue()) + s_logger.info("Detected out of band VM migration from host " + vm.getHostId() + " to host " + vm.getPowerHostId()); + stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unexpected VM state transition exception, race-condition?", e); + } + break; - case Stopping: - case Stopped: - try { - stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unexpected VM state transition exception, race-condition?", e); - } - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), - VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState() + - " -> Running) from out-of-context transition. VM network environment may need to be reset"); - break; + case Stopping: + case Stopped: + try { + stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unexpected VM state transition exception, race-condition?", e); + } + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), + VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState() + + " -> Running) from out-of-context transition. VM network environment may need to be reset"); + break; - case Destroyed: - case Expunging: - s_logger.info("Receive power on report when VM is in destroyed or expunging state. vm: " + case Destroyed: + case Expunging: + s_logger.info("Receive power on report when VM is in destroyed or expunging state. vm: " + vm.getId() + ", state: " + vm.getState()); - break; + break; - case Migrating: - try { - stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unexpected VM state transition exception, race-condition?", e); - } - break; + case Migrating: + try { + stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOnReport, vm.getPowerHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unexpected VM state transition exception, race-condition?", e); + } + break; - case Error: - default: - s_logger.info("Receive power on report when VM is in error or unexpected state. vm: " + case Error: + default: + s_logger.info("Receive power on report when VM is in error or unexpected state. vm: " + vm.getId() + ", state: " + vm.getState()); - break; + break; } } private void handlePowerOffReportWithNoPendingJobsOnVM(VMInstanceVO vm) { - // 1) handle left-over transitional VM states - // 2) handle out of sync stationary states, schedule force-stop to release resources + // 1) handle left-over transitional VM states + // 2) handle out of sync stationary states, schedule force-stop to release resources // switch (vm.getState()) { - case Starting: - case Stopping: - case Stopped: - case Migrating: - try { - stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOffReport, vm.getPowerHostId()); - } catch (NoTransitionException e) { - s_logger.warn("Unexpected VM state transition exception, race-condition?", e); - } - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), - VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState() + - " -> Stopped) from out-of-context transition."); - // TODO: we need to forcely release all resource allocation - break; + case Starting: + case Stopping: + case Stopped: + case Migrating: + try { + stateTransitTo(vm, VirtualMachine.Event.FollowAgentPowerOffReport, vm.getPowerHostId()); + } catch (NoTransitionException e) { + s_logger.warn("Unexpected VM state transition exception, race-condition?", e); + } + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), + VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") state is sync-ed (" + vm.getState() + + " -> Stopped) from out-of-context transition."); + // TODO: we need to forcely release all resource allocation + break; - case Running: - case Destroyed: - case Expunging: - break; + case Running: + case Destroyed: + case Expunging: + break; - case Error: - default: - break; + case Error: + default: + break; } } @@ -3935,8 +3954,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac // VMs in expunging state (this need to be handled specially) // // checking condition - // 1) no pending VmWork job - // 2) on hostId host and host is UP + // 1) no pending VmWork job + // 2) on hostId host and host is UP // // When host is UP, soon or later we will get a report from the host about the VM, // however, if VM is missing from the host report (it may happen in out of band changes @@ -3974,17 +3993,17 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac // We now only alert administrator about this situation _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_SYNC, vm.getDataCenterId(), vm.getPodIdToDeployIn(), - VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") is stuck in " + vm.getState() + - " state and its host is unreachable for too long"); + VM_SYNC_ALERT_SUBJECT, "VM " + vm.getHostName() + "(" + vm.getInstanceName() + ") is stuck in " + vm.getState() + + " state and its host is unreachable for too long"); } } // VMs that in transitional state without recent power state report private List listStalledVMInTransitionStateOnUpHost(long hostId, Date cutTime) { String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status = 'UP' " + - "AND h.id = ? AND i.power_state_update_time < ? AND i.host_id = h.id " + - "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + - "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; + "AND h.id = ? AND i.power_state_update_time < ? AND i.host_id = h.id " + + "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + + "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; List l = new ArrayList(); TransactionLegacy txn = null; @@ -4016,9 +4035,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac // VMs that in transitional state and recently have power state update private List listVMInTransitionStateWithRecentReportOnUpHost(long hostId, Date cutTime) { String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status = 'UP' " + - "AND h.id = ? AND i.power_state_update_time > ? AND i.host_id = h.id " + - "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + - "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; + "AND h.id = ? AND i.power_state_update_time > ? AND i.host_id = h.id " + + "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + + "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; List l = new ArrayList(); TransactionLegacy txn = null; @@ -4047,9 +4066,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private List listStalledVMInTransitionStateOnDisconnectedHosts(Date cutTime) { String sql = "SELECT i.* FROM vm_instance as i, host as h WHERE h.status != 'UP' " + - "AND i.power_state_update_time < ? AND i.host_id = h.id " + - "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + - "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; + "AND i.power_state_update_time < ? AND i.host_id = h.id " + + "AND (i.state ='Starting' OR i.state='Stopping' OR i.state='Migrating') " + + "AND i.id NOT IN (SELECT w.vm_instance_id FROM vm_work_job AS w JOIN async_job AS j ON w.id = j.id WHERE j.job_status = ?)"; List l = new ArrayList(); TransactionLegacy txn = null; @@ -4101,10 +4120,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - public class VmJobSyncOutcome extends OutcomeImpl { + public class VmJobVirtualMachineOutcome extends OutcomeImpl { private long _vmId; - public VmJobSyncOutcome(final AsyncJob job, final long vmId) { + public VmJobVirtualMachineOutcome(final AsyncJob job, final long vmId) { super(VirtualMachine.class, job, VmJobCheckInterval.value(), new Predicate() { @Override public boolean checkCondition() { @@ -4125,27 +4144,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } - public Throwable retriveExecutionException(AsyncJob job) { - assert (job != null); - assert (job.getDispatcher().equals(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER)); - - AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId()); - if (jobVo != null && jobVo.getResult() != null) { - Object obj = JobSerializerHelper.fromSerializedString(job.getResult()); - - if (obj != null && obj instanceof Throwable) - return (Throwable)obj; - } - return null; - } - // // TODO build a common pattern to reduce code duplication in following methods // no time for this at current iteration // public Outcome startVmThroughJobQueue(final String vmUuid, - final Map params, - final DeploymentPlan planToDeploy) { + final Map params, + final DeploymentPlan planToDeploy) { final CallContext context = CallContext.current(); final User callingUser = context.getCallingUser(); @@ -4153,14 +4158,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { + public Object[] doInTransaction(TransactionStatus status) { VmWorkJobVO workJob = null; _vmDao.lockRow(vm.getId(), true); List pendingWorkJobs = _workJobDao.listPendingWorkJobs(VirtualMachine.Type.Instance, - vm.getId(), VmWorkStart.class.getName()); + vm.getId(), VmWorkStart.class.getName()); if (pendingWorkJobs.size() > 0) { assert (pendingWorkJobs.size() == 1); @@ -4168,7 +4173,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkStart.class.getName()); workJob.setAccountId(callingAccount.getId()); @@ -4176,27 +4181,26 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob.setStep(VmWorkJobVO.Step.Starting); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) - VmWorkStart workInfo = new VmWorkStart(callingUser.getId(), callingAccount.getId(), vm.getId()); + VmWorkStart workInfo = new VmWorkStart(callingUser.getId(), callingAccount.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER); workInfo.setPlan(planToDeploy); workInfo.setParams(params); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - // Transaction syntax sugar has a cost here - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmStateSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), - VirtualMachine.PowerState.PowerOn, vm.getId(), null); + return new VmStateSyncOutcome((VmWorkJobVO)result[0], + VirtualMachine.PowerState.PowerOn, vm.getId(), null); } public Outcome stopVmThroughJobQueue(final String vmUuid, final boolean cleanup) { @@ -4206,14 +4210,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { + public Object[] doInTransaction(TransactionStatus status) { _vmDao.lockRow(vm.getId(), true); List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkStop.class.getName()); + vm.getType(), vm.getId(), + VmWorkStop.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4222,7 +4226,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkStop.class.getName()); workJob.setAccountId(account.getId()); @@ -4230,28 +4234,28 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob.setStep(VmWorkJobVO.Step.Prepare); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) - VmWorkStop workInfo = new VmWorkStop(user.getId(), account.getId(), vm.getId(), cleanup); + VmWorkStop workInfo = new VmWorkStop(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, cleanup); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmStateSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), - VirtualMachine.PowerState.PowerOff, vm.getId(), null); + return new VmStateSyncOutcome((VmWorkJobVO)result[0], + VirtualMachine.PowerState.PowerOff, vm.getId(), null); } public Outcome rebootVmThroughJobQueue(final String vmUuid, - final Map params) { + final Map params) { final CallContext context = CallContext.current(); final Account account = context.getCallingAccount(); @@ -4259,14 +4263,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { + public Object[] doInTransaction(TransactionStatus status) { _vmDao.lockRow(vm.getId(), true); List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkReboot.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkReboot.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4275,7 +4279,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkReboot.class.getName()); workJob.setAccountId(account.getId()); @@ -4283,24 +4287,24 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob.setStep(VmWorkJobVO.Step.Prepare); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) - VmWorkReboot workInfo = new VmWorkReboot(user.getId(), account.getId(), vm.getId(), params); + VmWorkReboot workInfo = new VmWorkReboot(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, params); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), - vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], + vm.getId()); } public Outcome migrateVmThroughJobQueue(final String vmUuid, final long srcHostId, final DeployDestination dest) { @@ -4310,15 +4314,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkMigrate.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkMigrate.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4328,35 +4330,35 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkMigrate.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) - VmWorkMigrate workInfo = new VmWorkMigrate(user.getId(), account.getId(), vm.getId(), srcHostId, dest); + VmWorkMigrate workInfo = new VmWorkMigrate(user.getId(), account.getId(), vm.getId(), VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, dest); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmStateSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), - VirtualMachine.PowerState.PowerOn, vm.getId(), vm.getPowerHostId()); + return new VmStateSyncOutcome((VmWorkJobVO)result[0], + VirtualMachine.PowerState.PowerOn, vm.getId(), vm.getPowerHostId()); } public Outcome migrateVmWithStorageThroughJobQueue( - final String vmUuid, final long srcHostId, final long destHostId, - final Map volumeToPool) { + final String vmUuid, final long srcHostId, final long destHostId, + final Map volumeToPool) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); @@ -4364,15 +4366,15 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { + public Object[] doInTransaction(TransactionStatus status) { _vmDao.lockRow(vm.getId(), true); List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkMigrateWithStorage.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkMigrateWithStorage.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4382,35 +4384,35 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkMigrate.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkMigrateWithStorage workInfo = new VmWorkMigrateWithStorage(user.getId(), account.getId(), vm.getId(), - srcHostId, destHostId, volumeToPool); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, destHostId, volumeToPool); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmStateSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), - VirtualMachine.PowerState.PowerOn, vm.getId(), destHostId); + return new VmStateSyncOutcome((VmWorkJobVO)result[0], + VirtualMachine.PowerState.PowerOn, vm.getId(), destHostId); } public Outcome migrateVmForScaleThroughJobQueue( - final String vmUuid, final long srcHostId, final DeployDestination dest, final Long newSvcOfferingId) { + final String vmUuid, final long srcHostId, final DeployDestination dest, final Long newSvcOfferingId) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); @@ -4418,15 +4420,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkMigrateForScale.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkMigrateForScale.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4436,34 +4436,35 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkMigrate.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkMigrateForScale workInfo = new VmWorkMigrateForScale(user.getId(), account.getId(), vm.getId(), - srcHostId, dest, newSvcOfferingId); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, srcHostId, dest, newSvcOfferingId); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } public Outcome migrateVmStorageThroughJobQueue( - final String vmUuid, final StoragePool destPool) { + final String vmUuid, final StoragePool destPool) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); @@ -4471,15 +4472,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkStorageMigration.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkStorageMigration.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4489,48 +4488,47 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkStorageMigration.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkStorageMigration workInfo = new VmWorkStorageMigration(user.getId(), account.getId(), vm.getId(), - destPool); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, destPool); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } public Outcome addVmToNetworkThroughJobQueue( - final VirtualMachine vm, final Network network, final NicProfile requested) { + final VirtualMachine vm, final Network network, final NicProfile requested) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); final Account account = context.getCallingAccount(); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkAddVmToNetwork.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkAddVmToNetwork.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4540,48 +4538,46 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkAddVmToNetwork.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkAddVmToNetwork workInfo = new VmWorkAddVmToNetwork(user.getId(), account.getId(), vm.getId(), - network, requested); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network.getId(), requested); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } public Outcome removeNicFromVmThroughJobQueue( - final VirtualMachine vm, final Nic nic) { + final VirtualMachine vm, final Nic nic) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); final Account account = context.getCallingAccount(); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkRemoveNicFromVm.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkRemoveNicFromVm.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4591,48 +4587,46 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkRemoveNicFromVm.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkRemoveNicFromVm workInfo = new VmWorkRemoveNicFromVm(user.getId(), account.getId(), vm.getId(), - nic); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, nic.getId()); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } public Outcome removeVmFromNetworkThroughJobQueue( - final VirtualMachine vm, final Network network, final URI broadcastUri) { + final VirtualMachine vm, final Network network, final URI broadcastUri) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); final Account account = context.getCallingAccount(); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkRemoveVmFromNetwork.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkRemoveVmFromNetwork.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4642,34 +4636,34 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkRemoveVmFromNetwork.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkRemoveVmFromNetwork workInfo = new VmWorkRemoveVmFromNetwork(user.getId(), account.getId(), vm.getId(), - network, broadcastUri); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, network, broadcastUri); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } public Outcome reconfigureVmThroughJobQueue( - final String vmUuid, final ServiceOffering oldServiceOffering, final boolean reconfiguringOnExistingHost) { + final String vmUuid, final ServiceOffering newServiceOffering, final boolean reconfiguringOnExistingHost) { final CallContext context = CallContext.current(); final User user = context.getCallingUser(); @@ -4677,15 +4671,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac final VMInstanceVO vm = _vmDao.findByUuid(vmUuid); - Transaction.execute(new TransactionCallbackNoReturn() { + Object[] result = Transaction.execute(new TransactionCallback() { @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - _vmDao.lockRow(vm.getId(), true); + public Object[] doInTransaction(TransactionStatus status) { List pendingWorkJobs = _workJobDao.listPendingWorkJobs( - VirtualMachine.Type.Instance, vm.getId(), - VmWorkReconfigure.class.getName()); + VirtualMachine.Type.Instance, vm.getId(), + VmWorkReconfigure.class.getName()); VmWorkJobVO workJob = null; if (pendingWorkJobs != null && pendingWorkJobs.size() > 0) { @@ -4695,30 +4687,165 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac workJob = new VmWorkJobVO(context.getContextId()); - workJob.setDispatcher(VmWorkJobDispatcher.VM_WORK_JOB_DISPATCHER); + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); workJob.setCmd(VmWorkReconfigure.class.getName()); workJob.setAccountId(account.getId()); workJob.setUserId(user.getId()); workJob.setVmType(vm.getType()); workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); // save work context info (there are some duplications) VmWorkReconfigure workInfo = new VmWorkReconfigure(user.getId(), account.getId(), vm.getId(), - oldServiceOffering, reconfiguringOnExistingHost); + VirtualMachineManagerImpl.VM_WORK_JOB_HANDLER, newServiceOffering.getId(), reconfiguringOnExistingHost); workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); - _jobMgr.submitAsyncJob(workJob, VmWorkJobDispatcher.VM_WORK_QUEUE, vm.getId()); + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); } - context.putContextParameter("workJob", workJob); - context.putContextParameter("jobId", new Long(workJob.getId())); + return new Object[] {workJob, new Long(workJob.getId())}; } }); - final long jobId = (Long)context.getContextParameter("jobId"); + final long jobId = (Long)result[1]; AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); - return new VmJobSyncOutcome((VmWorkJobVO)context.getContextParameter("workJob"), vm.getId()); + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], vm.getId()); } + private Pair orchestrateStart(VmWorkStart work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + + orchestrateStart(vm.getUuid(), work.getParams(), work.getPlan(), null); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateStop(VmWorkStop work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + + orchestrateStop(vm.getUuid(), work.isCleanup()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateMigrate(VmWorkMigrate work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + + orchestrateMigrate(vm.getUuid(), work.getSrcHostId(), work.getDeployDestination()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateMigrateWithStorage(VmWorkMigrateWithStorage work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + orchestrateMigrateWithStorage(vm.getUuid(), + work.getSrcHostId(), + work.getDestHostId(), + work.getVolumeToPool()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateMigrateForScale(VmWorkMigrateForScale work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + orchestrateMigrateForScale(vm.getUuid(), + work.getSrcHostId(), + work.getDeployDestination(), + work.getNewServiceOfferringId()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateReboot(VmWorkReboot work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + orchestrateReboot(vm.getUuid(), work.getParams()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateAddVmToNetwork(VmWorkAddVmToNetwork work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + + Network network = _networkDao.findById(work.getNetworkId()); + NicProfile nic = orchestrateAddVmToNetwork(vm, network, + work.getRequestedNicProfile()); + + return new Pair(JobInfo.Status.SUCCEEDED, _jobMgr.marshallResultObject(new Long(nic.getId()))); + } + + private Pair orchestrateRemoveNicFromVm(VmWorkRemoveNicFromVm work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + NicVO nic = _entityMgr.findById(NicVO.class, work.getNicId()); + boolean result = orchestrateRemoveNicFromVm(vm, nic); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Boolean(result))); + } + + private Pair orchestrateRemoveVmFromNetwork(VmWorkRemoveVmFromNetwork work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + boolean result = orchestrateRemoveVmFromNetwork(vm, + work.getNetwork(), work.getBroadcastUri()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Boolean(result))); + } + + private Pair orchestrateReconfigure(VmWorkReconfigure work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + + ServiceOffering newServiceOffering = _offeringDao.findById(vm.getId(), work.getNewServiceOfferingId()); + + reConfigureVm(vm.getUuid(), newServiceOffering, + work.isSameHost()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateStorageMigration(VmWorkStorageMigration work) throws Exception { + VMInstanceVO vm = _entityMgr.findById(VMInstanceVO.class, work.getVmId()); + if (vm == null) { + s_logger.info("Unable to find vm " + work.getVmId()); + } + assert (vm != null); + orchestrateStorageMigration(vm.getUuid(), work.getDestStoragePool()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + @Override + public Pair handleVmWorkJob(VmWork work) throws Exception { + return _jobHandlerProxy.handleVmWorkJob(work); + } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkAddVmToNetwork.java b/engine/orchestration/src/com/cloud/vm/VmWorkAddVmToNetwork.java index 97396f12693..a56259b93ea 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkAddVmToNetwork.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkAddVmToNetwork.java @@ -16,27 +16,25 @@ // under the License. package com.cloud.vm; -import com.cloud.network.Network; - public class VmWorkAddVmToNetwork extends VmWork { private static final long serialVersionUID = 8861516006586736813L; - Network network; + Long networkId; NicProfile requstedNicProfile; - public VmWorkAddVmToNetwork(long userId, long accountId, long vmId, - Network network, NicProfile requested) { - super(userId, accountId, vmId); + public VmWorkAddVmToNetwork(long userId, long accountId, long vmId, String handlerName, + Long networkId, NicProfile requested) { + super(userId, accountId, vmId, handlerName); - this.network = network; - this.requstedNicProfile = requested; + this.networkId = networkId; + requstedNicProfile = requested; } - public Network getNetwork() { - return this.network; + public Long getNetworkId() { + return networkId; } public NicProfile getRequestedNicProfile() { - return this.requstedNicProfile; + return requstedNicProfile; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkJobDispatcher.java b/engine/orchestration/src/com/cloud/vm/VmWorkJobDispatcher.java index 7534363b921..285c8a2831e 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkJobDispatcher.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkJobDispatcher.java @@ -16,32 +16,42 @@ // under the License. package com.cloud.vm; +import java.util.Map; + import javax.inject.Inject; import org.apache.log4j.Logger; + import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobDispatcher; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; import org.apache.cloudstack.jobs.JobInfo; +import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.vm.dao.VMInstanceDao; public class VmWorkJobDispatcher extends AdapterBase implements AsyncJobDispatcher { private static final Logger s_logger = Logger.getLogger(VmWorkJobDispatcher.class); - public static final String VM_WORK_QUEUE = "VmWorkJobQueue"; - public static final String VM_WORK_JOB_DISPATCHER = "VmWorkJobDispatcher"; - public static final String VM_WORK_JOB_WAKEUP_DISPATCHER = "VmWorkJobWakeupDispatcher"; - - @Inject - private VirtualMachineManagerImpl _vmMgr; + @Inject private VirtualMachineManagerImpl _vmMgr; @Inject private AsyncJobManager _asyncJobMgr; - @Inject - private VMInstanceDao _instanceDao; + @Inject private VMInstanceDao _instanceDao; + + private Map _handlers; + + public VmWorkJobDispatcher() { + } + + public Map getHandlers() { + return _handlers; + } + + public void setHandlers(Map handlers) { + _handlers = handlers; + } @Override public void runJob(AsyncJob job) { @@ -51,103 +61,51 @@ public class VmWorkJobDispatcher extends AdapterBase implements AsyncJobDispatch assert (cmd != null); if (s_logger.isDebugEnabled()) - s_logger.debug("Run VM work job: " + cmd); + s_logger.debug("Run VM work job: " + cmd + ", job origin: " + job.getRelated()); Class workClz = null; try { workClz = Class.forName(job.getCmd()); } catch (ClassNotFoundException e) { - s_logger.error("VM work class " + cmd + " is not found", e); + s_logger.error("VM work class " + cmd + " is not found" + ", job origin: " + job.getRelated(), e); _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, e.getMessage()); return; } work = VmWorkSerializer.deserialize(workClz, job.getCmdInfo()); - assert (work != null); - if (work == null) { - s_logger.error("Unable to deserialize VM work " + job.getCmd() + ", job info: " + job.getCmdInfo()); + assert(work != null); + if(work == null) { + s_logger.error("Unable to deserialize VM work " + job.getCmd() + ", job info: " + job.getCmdInfo() + ", job origin: " + job.getRelated()); _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, "Unable to deserialize VM work"); return; } + if (_handlers == null || _handlers.isEmpty()) { + s_logger.error("Invalid startup configuration, no work job handler is found. cmd: " + job.getCmd() + ", job info: " + job.getCmdInfo() + + ", job origin: " + job.getRelated()); + _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, "Invalid startup configuration. no job handler is found"); + return; + } + + VmWorkJobHandler handler = _handlers.get(work.getHandlerName()); + + if (handler == null) { + s_logger.error("Unable to find work job handler. handler name: " + work.getHandlerName() + ", job cmd: " + job.getCmd() + + ", job info: " + job.getCmdInfo() + ", job origin: " + job.getRelated()); + _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, "Unable to find work job handler"); + return; + } + CallContext.register(work.getUserId(), work.getAccountId(), job.getRelated()); - VMInstanceVO vm = _instanceDao.findById(work.getVmId()); - if (vm == null) { - s_logger.info("Unable to find vm " + work.getVmId()); - } - assert (vm != null); - if (work instanceof VmWorkStart) { - VmWorkStart workStart = (VmWorkStart)work; - _vmMgr.orchestrateStart(vm.getUuid(), workStart.getParams(), workStart.getPlan()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkStop) { - VmWorkStop workStop = (VmWorkStop)work; - _vmMgr.orchestrateStop(vm.getUuid(), workStop.isCleanup()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkMigrate) { - VmWorkMigrate workMigrate = (VmWorkMigrate)work; - _vmMgr.orchestrateMigrate(vm.getUuid(), workMigrate.getSrcHostId(), workMigrate.getDeployDestination()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkMigrateWithStorage) { - VmWorkMigrateWithStorage workMigrateWithStorage = (VmWorkMigrateWithStorage)work; - _vmMgr.orchestrateMigrateWithStorage(vm.getUuid(), - workMigrateWithStorage.getSrcHostId(), - workMigrateWithStorage.getDestHostId(), - workMigrateWithStorage.getVolumeToPool()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkMigrateForScale) { - VmWorkMigrateForScale workMigrateForScale = (VmWorkMigrateForScale)work; - _vmMgr.orchestrateMigrateForScale(vm.getUuid(), - workMigrateForScale.getSrcHostId(), - workMigrateForScale.getDeployDestination(), - workMigrateForScale.getNewServiceOfferringId()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkReboot) { - VmWorkReboot workReboot = (VmWorkReboot)work; - _vmMgr.orchestrateReboot(vm.getUuid(), workReboot.getParams()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkAddVmToNetwork) { - VmWorkAddVmToNetwork workAddVmToNetwork = (VmWorkAddVmToNetwork)work; - NicProfile nic = _vmMgr.orchestrateAddVmToNetwork(vm, workAddVmToNetwork.getNetwork(), - workAddVmToNetwork.getRequestedNicProfile()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, - JobSerializerHelper.toObjectSerializedString(nic)); - } else if (work instanceof VmWorkRemoveNicFromVm) { - VmWorkRemoveNicFromVm workRemoveNicFromVm = (VmWorkRemoveNicFromVm)work; - boolean result = _vmMgr.orchestrateRemoveNicFromVm(vm, workRemoveNicFromVm.getNic()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, - JobSerializerHelper.toObjectSerializedString(new Boolean(result))); - } else if (work instanceof VmWorkRemoveVmFromNetwork) { - VmWorkRemoveVmFromNetwork workRemoveVmFromNetwork = (VmWorkRemoveVmFromNetwork)work; - boolean result = _vmMgr.orchestrateRemoveVmFromNetwork(vm, - workRemoveVmFromNetwork.getNetwork(), workRemoveVmFromNetwork.getBroadcastUri()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, - JobSerializerHelper.toObjectSerializedString(new Boolean(result))); - } else if (work instanceof VmWorkReconfigure) { - VmWorkReconfigure workReconfigure = (VmWorkReconfigure)work; - _vmMgr.reConfigureVm(vm.getUuid(), workReconfigure.getNewServiceOffering(), - workReconfigure.isSameHost()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else if (work instanceof VmWorkStorageMigration) { - VmWorkStorageMigration workStorageMigration = (VmWorkStorageMigration)work; - _vmMgr.orchestrateStorageMigration(vm.getUuid(), workStorageMigration.getDestStoragePool()); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.SUCCEEDED, 0, null); - } else { - assert (false); - s_logger.error("Unhandled VM work command: " + job.getCmd()); + Pair result = handler.handleVmWorkJob(work); + _asyncJobMgr.completeAsyncJob(job.getId(), result.first(), 0, result.second()); - RuntimeException e = new RuntimeException("Unsupported VM work command: " + job.getCmd()); - String exceptionJson = JobSerializerHelper.toSerializedString(e); - s_logger.error("Serialize exception object into json: " + exceptionJson); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, exceptionJson); - } - } catch (Throwable e) { - s_logger.error("Unable to complete " + job, e); + } catch(Throwable e) { + s_logger.error("Unable to complete " + job + ", job origin:" + job.getRelated(), e); - String exceptionJson = JobSerializerHelper.toSerializedString(e); - s_logger.info("Serialize exception object into json: " + exceptionJson); - _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, exceptionJson); + RuntimeException ex = new RuntimeException("Job failed due to exception " + e.getMessage()); + _asyncJobMgr.completeAsyncJob(job.getId(), JobInfo.Status.FAILED, 0, _asyncJobMgr.marshallResultObject(ex)); } finally { CallContext.unregister(); } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkJobWakeupDispatcher.java b/engine/orchestration/src/com/cloud/vm/VmWorkJobWakeupDispatcher.java index 5704f978720..520a55042e3 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkJobWakeupDispatcher.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkJobWakeupDispatcher.java @@ -40,6 +40,12 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.component.AdapterBase; import com.cloud.vm.dao.VMInstanceDao; +/** + * Please note: VmWorkJobWakeupDispatcher is not currently in use. It is designed for event-driven based + * job processing model. + * + * Current code base uses blocking calls to wait for job completion + */ public class VmWorkJobWakeupDispatcher extends AdapterBase implements AsyncJobDispatcher { private static final Logger s_logger = Logger.getLogger(VmWorkJobWakeupDispatcher.class); diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkMigrate.java b/engine/orchestration/src/com/cloud/vm/VmWorkMigrate.java index 107f5fef3c1..5bcea9a8aca 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkMigrate.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkMigrate.java @@ -38,8 +38,9 @@ public class VmWorkMigrate extends VmWork { private Map storage; long srcHostId; - public VmWorkMigrate(long userId, long accountId, long vmId, long srcHostId, DeployDestination dst) { - super(userId, accountId, vmId); + public VmWorkMigrate(long userId, long accountId, long vmId, String handlerName, + long srcHostId, DeployDestination dst) { + super(userId, accountId, vmId, handlerName); this.srcHostId = srcHostId; zoneId = dst.getDataCenter() != null ? dst.getDataCenter().getId() : null; podId = dst.getPod() != null ? dst.getPod().getId() : null; diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkMigrateForScale.java b/engine/orchestration/src/com/cloud/vm/VmWorkMigrateForScale.java index 399ea91c895..0dd4d88c218 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkMigrateForScale.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkMigrateForScale.java @@ -25,12 +25,12 @@ public class VmWorkMigrateForScale extends VmWork { DeployDestination deployDestination; Long newSvcOfferingId; - public VmWorkMigrateForScale(long userId, long accountId, long vmId, long srcHostId, + public VmWorkMigrateForScale(long userId, long accountId, long vmId, String handlerName, long srcHostId, DeployDestination dest, Long newSvcOfferingId) { - super(userId, accountId, vmId); + super(userId, accountId, vmId, handlerName); this.srcHostId = srcHostId; - this.deployDestination = dest; + deployDestination = dest; this.newSvcOfferingId = newSvcOfferingId; } @@ -39,10 +39,10 @@ public class VmWorkMigrateForScale extends VmWork { } public DeployDestination getDeployDestination() { - return this.deployDestination; + return deployDestination; } public Long getNewServiceOfferringId() { - return this.newSvcOfferingId; + return newSvcOfferingId; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkMigrateWithStorage.java b/engine/orchestration/src/com/cloud/vm/VmWorkMigrateWithStorage.java index 75024dba49c..ee30c74abfb 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkMigrateWithStorage.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkMigrateWithStorage.java @@ -28,10 +28,10 @@ public class VmWorkMigrateWithStorage extends VmWork { long destHostId; Map volumeToPool; - public VmWorkMigrateWithStorage(long userId, long accountId, long vmId, long srcHostId, + public VmWorkMigrateWithStorage(long userId, long accountId, long vmId, String handlerName, long srcHostId, long destHostId, Map volumeToPool) { - super(userId, accountId, vmId); + super(userId, accountId, vmId, handlerName); this.srcHostId = srcHostId; this.destHostId = destHostId; @@ -39,14 +39,14 @@ public class VmWorkMigrateWithStorage extends VmWork { } public long getSrcHostId() { - return this.srcHostId; + return srcHostId; } public long getDestHostId() { - return this.destHostId; + return destHostId; } public Map getVolumeToPool() { - return this.volumeToPool; + return volumeToPool; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkReboot.java b/engine/orchestration/src/com/cloud/vm/VmWorkReboot.java index fecaaef1fb1..63c7d004968 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkReboot.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkReboot.java @@ -28,20 +28,12 @@ public class VmWorkReboot extends VmWork { // use serialization friendly map private Map rawParams; - public VmWorkReboot(long userId, long accountId, long vmId, Map params) { - super(userId, accountId, vmId); + public VmWorkReboot(long userId, long accountId, long vmId, String handlerName, Map params) { + super(userId, accountId, vmId, handlerName); setParams(params); } - public Map getRawParams() { - return rawParams; - } - - public void setRawParams(Map params) { - rawParams = params; - } - public Map getParams() { Map map = new HashMap(); diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkReconfigure.java b/engine/orchestration/src/com/cloud/vm/VmWorkReconfigure.java index 6e2b6d8a2b0..17abeb24537 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkReconfigure.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkReconfigure.java @@ -16,28 +16,27 @@ // under the License. package com.cloud.vm; -import com.cloud.offering.ServiceOffering; public class VmWorkReconfigure extends VmWork { private static final long serialVersionUID = -4517030323758086615L; - ServiceOffering newServiceOffering; + Long newServiceOfferingId; boolean sameHost; - public VmWorkReconfigure(long userId, long accountId, long vmId, - ServiceOffering newServiceOffering, boolean sameHost) { + public VmWorkReconfigure(long userId, long accountId, long vmId, String handlerName, + Long newServiceOfferingId, boolean sameHost) { - super(userId, accountId, vmId); + super(userId, accountId, vmId, handlerName); - this.newServiceOffering = newServiceOffering; + this.newServiceOfferingId = newServiceOfferingId; this.sameHost = sameHost; } - public ServiceOffering getNewServiceOffering() { - return this.newServiceOffering; + public Long getNewServiceOfferingId() { + return newServiceOfferingId; } public boolean isSameHost() { - return this.sameHost; + return sameHost; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkRemoveNicFromVm.java b/engine/orchestration/src/com/cloud/vm/VmWorkRemoveNicFromVm.java index 8efe77bdda5..50f158e421d 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkRemoveNicFromVm.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkRemoveNicFromVm.java @@ -19,15 +19,15 @@ package com.cloud.vm; public class VmWorkRemoveNicFromVm extends VmWork { private static final long serialVersionUID = -4265657031064437923L; - Nic nic; + Long nicId; - public VmWorkRemoveNicFromVm(long userId, long accountId, long vmId, Nic nic) { - super(userId, accountId, vmId); + public VmWorkRemoveNicFromVm(long userId, long accountId, long vmId, String handlerName, Long nicId) { + super(userId, accountId, vmId, handlerName); - this.nic = nic; + this.nicId = nicId; } - public Nic getNic() { - return this.nic; + public Long getNicId() { + return nicId; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkRemoveVmFromNetwork.java b/engine/orchestration/src/com/cloud/vm/VmWorkRemoveVmFromNetwork.java index 0e94c2f35f9..535b8d00faa 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkRemoveVmFromNetwork.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkRemoveVmFromNetwork.java @@ -26,18 +26,18 @@ public class VmWorkRemoveVmFromNetwork extends VmWork { Network network; URI broadcastUri; - public VmWorkRemoveVmFromNetwork(long userId, long accountId, long vmId, Network network, URI broadcastUri) { - super(userId, accountId, vmId); + public VmWorkRemoveVmFromNetwork(long userId, long accountId, long vmId, String handlerName, Network network, URI broadcastUri) { + super(userId, accountId, vmId, handlerName); this.network = network; this.broadcastUri = broadcastUri; } public Network getNetwork() { - return this.network; + return network; } public URI getBroadcastUri() { - return this.broadcastUri; + return broadcastUri; } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkStart.java b/engine/orchestration/src/com/cloud/vm/VmWorkStart.java index e023801445d..f1b2efdbeac 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkStart.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkStart.java @@ -50,8 +50,8 @@ public class VmWorkStart extends VmWork { // use serialization friendly map private Map rawParams; - public VmWorkStart(long userId, long accountId, long vmId) { - super(userId, accountId, vmId); + public VmWorkStart(long userId, long accountId, long vmId, String handlerName) { + super(userId, accountId, vmId, handlerName); } public DeploymentPlan getPlan() { @@ -63,13 +63,13 @@ public class VmWorkStart extends VmWork { if (reservationId != null) { Journal journal = new Journal.LogJournal("VmWorkStart", s_logger); context = new ReservationContextImpl(reservationId, journal, - CallContext.current().getCallingUser(), - CallContext.current().getCallingAccount()); + CallContext.current().getCallingUser(), + CallContext.current().getCallingAccount()); } DeploymentPlan plan = new DataCenterDeployment( - dcId, podId, clusterId, hostId, poolId, physicalNetworkId, - context); + dcId, podId, clusterId, hostId, poolId, physicalNetworkId, + context); return plan; } @@ -118,7 +118,7 @@ public class VmWorkStart extends VmWork { rawParams = new HashMap(); for (Map.Entry entry : params.entrySet()) { rawParams.put(entry.getKey().getName(), JobSerializerHelper.toObjectSerializedString( - entry.getValue() instanceof Serializable ? (Serializable)entry.getValue() : entry.getValue().toString())); + entry.getValue() instanceof Serializable ? (Serializable)entry.getValue() : entry.getValue().toString())); } } } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkStop.java b/engine/orchestration/src/com/cloud/vm/VmWorkStop.java index f0bc88594e9..6d4148000c8 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkStop.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkStop.java @@ -21,8 +21,8 @@ public class VmWorkStop extends VmWork { private final boolean cleanup; - public VmWorkStop(long userId, long accountId, long vmId, boolean cleanup) { - super(userId, accountId, vmId); + public VmWorkStop(long userId, long accountId, long vmId, String handlerName, boolean cleanup) { + super(userId, accountId, vmId, handlerName); this.cleanup = cleanup; } diff --git a/engine/orchestration/src/com/cloud/vm/VmWorkStorageMigration.java b/engine/orchestration/src/com/cloud/vm/VmWorkStorageMigration.java index 76a35b23cf0..2b2f8e841ed 100644 --- a/engine/orchestration/src/com/cloud/vm/VmWorkStorageMigration.java +++ b/engine/orchestration/src/com/cloud/vm/VmWorkStorageMigration.java @@ -23,13 +23,13 @@ public class VmWorkStorageMigration extends VmWork { StoragePool destPool; - public VmWorkStorageMigration(long userId, long accountId, long vmId, StoragePool destPool) { - super(userId, accountId, vmId); + public VmWorkStorageMigration(long userId, long accountId, long vmId, String handlerName, StoragePool destPool) { + super(userId, accountId, vmId, handlerName); this.destPool = destPool; } public StoragePool getDestStoragePool() { - return this.destPool; + return destPool; } } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index 40b0f447392..36481ab5a43 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -189,7 +189,7 @@ public class VMEntityManagerImpl implements VMEntityManager { while (true) { DeployDestination dest = null; try { - dest = _dpMgr.planDeployment(vmProfile, plan, exclude); + dest = _dpMgr.planDeployment(vmProfile, plan, exclude, null); } catch (AffinityConflictException e) { throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java index ed8909cda48..7f2e4fff240 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VirtualMachineEntityImpl.java @@ -57,7 +57,6 @@ public class VirtualMachineEntityImpl implements VirtualMachineEntity { this.vmEntityVO.setOwner(owner); this.vmEntityVO.setHostname(hostName); this.vmEntityVO.setDisplayname(displayName); - this.vmEntityVO.setSpeed(speed); this.vmEntityVO.setComputeTags(computeTags); this.vmEntityVO.setRootDiskTags(rootDiskTags); this.vmEntityVO.setNetworkIds(networks); @@ -76,7 +75,6 @@ public class VirtualMachineEntityImpl implements VirtualMachineEntity { this.vmEntityVO.setOwner(owner); this.vmEntityVO.setHostname(hostName); this.vmEntityVO.setDisplayname(displayName); - this.vmEntityVO.setSpeed(speed); this.vmEntityVO.setComputeTags(computeTags); this.vmEntityVO.setRootDiskTags(rootDiskTags); this.vmEntityVO.setNetworkIds(networks); diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java index cbe02ff0941..7bf784110e3 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -36,8 +36,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -47,6 +45,7 @@ import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.region.PortableIpDao; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; @@ -413,41 +412,37 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NetworkOfferingVO offering = null; //#1 - quick cloud network offering if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true, - Availability.Optional, null, new HashMap>(), true, Network.GuestType.Shared, false, null, true, null, - true, false, null, false, null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true, + Availability.Optional, null, new HashMap>(), true, Network.GuestType.Shared, false, null, true, null, true, + false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } //#2 - SG enabled network offering if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, - "Offering for Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, - defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", + TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, + null, true, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } //#3 - shared network offering with no SG service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true, - Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, - null, false, null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true, + Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, + null, true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } //#4 - default isolated offering with Source nat service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService, + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService, "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null, - defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, - false, null, true); + defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, + true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); @@ -455,8 +450,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra //#5 - default vpc offering with LB service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); @@ -467,21 +461,18 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB) == null) { //remove LB service defaultVPCOffProviders.remove(Service.Lb); - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, - "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, - Availability.Optional, null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, - null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, + "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional, + null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } //#7 - isolated offering with source nat disabled if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service", - TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, - null, true, null, true, false, null, false, null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service", + TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, + true, null, true, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } @@ -503,10 +494,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra internalLbOffProviders.put(Service.SourceNat, defaultVpcProvider); if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, - "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, - internalLbOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true); + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, + "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders, + true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); offering.setInternalLb(true); offering.setPublicLb(false); @@ -536,8 +526,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra serviceCapabilityMap.put(Service.StaticNat, eip); if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) { - offering = - _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, + offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null, netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true); offering.setState(NetworkOffering.State.Enabled); @@ -568,9 +557,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra SearchBuilder podVlanMapSB = _podVlanMapDao.createSearchBuilder(); podVlanMapSB.and("podId", podVlanMapSB.entity().getPodId(), Op.EQ); AssignIpAddressFromPodVlanSearch.join("podVlanMapSB", podVlanMapSB, podVlanMapSB.entity().getVlanDbId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), - JoinType.INNER); - AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), - JoinType.INNER); + JoinType.INNER); + AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER); AssignIpAddressFromPodVlanSearch.done(); @@ -602,15 +590,15 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override public List setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault) - throws ConcurrentOperationException { + throws ConcurrentOperationException { return setupNetwork(owner, offering, null, plan, name, displayText, false, null, null, null, null, true); } @Override @DB - public List setupNetwork(final Account owner, final NetworkOffering offering, final Network predefined, final DeploymentPlan plan, - final String name, final String displayText, boolean errorIfAlreadySetup, final Long domainId, final ACLType aclType, final Boolean subdomainAccess, - final Long vpcId, final Boolean isDisplayNetworkEnabled) throws ConcurrentOperationException { + public List setupNetwork(final Account owner, final NetworkOffering offering, final Network predefined, final DeploymentPlan plan, final String name, + final String displayText, boolean errorIfAlreadySetup, final Long domainId, final ACLType aclType, final Boolean subdomainAccess, final Long vpcId, + final Boolean isDisplayNetworkEnabled) throws ConcurrentOperationException { Account locked = _accountDao.acquireInLockTable(owner.getId()); if (locked == null) { @@ -618,9 +606,10 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } try { - if (predefined == null || - (offering.getTrafficType() != TrafficType.Guest && predefined.getCidr() == null && predefined.getBroadcastUri() == null && !(predefined.getBroadcastDomainType() == BroadcastDomainType.Vlan || - predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch || predefined.getBroadcastDomainType() == BroadcastDomainType.Vxlan))) { + if (predefined == null + || (offering.getTrafficType() != TrafficType.Guest && predefined.getCidr() == null && predefined.getBroadcastUri() == null && !(predefined + .getBroadcastDomainType() == BroadcastDomainType.Vlan || predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch || predefined + .getBroadcastDomainType() == BroadcastDomainType.Vxlan))) { List configs = _networksDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId()); if (configs.size() > 0) { if (s_logger.isDebugEnabled()) { @@ -628,8 +617,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } if (errorIfAlreadySetup) { - InvalidParameterValueException ex = - new InvalidParameterValueException("Found existing network configuration (with specified id) for offering (with specified id)"); + InvalidParameterValueException ex = new InvalidParameterValueException( + "Found existing network configuration (with specified id) for offering (with specified id)"); ex.addProxyObject(offering.getUuid(), "offeringId"); ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId"); throw ex; @@ -667,13 +656,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { - NetworkVO vo = - new NetworkVO(id, network, offering.getId(), guru.getName(), owner.getDomainId(), owner.getId(), relatedFile, name, displayText, - predefined.getNetworkDomain(), offering.getGuestType(), plan.getDataCenterId(), plan.getPhysicalNetworkId(), aclType, - offering.getSpecifyIpRanges(), vpcId); + NetworkVO vo = new NetworkVO(id, network, offering.getId(), guru.getName(), owner.getDomainId(), owner.getId(), relatedFile, name, displayText, predefined + .getNetworkDomain(), offering.getGuestType(), plan.getDataCenterId(), plan.getPhysicalNetworkId(), aclType, offering.getSpecifyIpRanges(), vpcId); vo.setDisplayNetwork(isDisplayNetworkEnabled == null ? true : isDisplayNetworkEnabled); networks.add(_networksDao.persist(vo, vo.getGuestType() == Network.GuestType.Isolated, - finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId()))); + finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId()))); if (domainId != null && aclType == ACLType.Domain) { _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null ? true : subdomainAccess); @@ -699,7 +686,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override @DB public void allocate(final VirtualMachineProfile vm, final LinkedHashMap networks) throws InsufficientCapacityException, - ConcurrentOperationException { + ConcurrentOperationException { Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override @@ -771,7 +758,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @DB @Override public Pair allocateNic(NicProfile requested, Network network, Boolean isDefaultNic, int deviceId, VirtualMachineProfile vm) - throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException { NetworkVO ntwkVO = _networksDao.findById(network.getId()); s_logger.debug("Allocating nic for vm " + vm.getVirtualMachine() + " in network " + network + " with requested profile " + requested); @@ -802,8 +789,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra vo = _nicDao.persist(vo); Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); - NicProfile vmNic = - new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), + NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); return new Pair(vmNic, Integer.valueOf(deviceId)); @@ -918,7 +904,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override @DB public Pair implementNetwork(long networkId, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { + ResourceUnavailableException, InsufficientCapacityException { Pair implemented = new Pair(null, null); NetworkVO network = _networksDao.findById(networkId); @@ -1017,7 +1003,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override public void implementNetworkElementsAndResources(DeployDestination dest, ReservationContext context, Network network, NetworkOffering offering) - throws ConcurrentOperationException, InsufficientAddressCapacityException, ResourceUnavailableException, InsufficientCapacityException { + throws ConcurrentOperationException, InsufficientAddressCapacityException, ResourceUnavailableException, InsufficientCapacityException { // Associate a source NAT IP (if one isn't already associated with the network) if this is a // 1) 'Isolated' or 'Shared' guest virtual network in the advance zone @@ -1027,9 +1013,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra boolean sharedSourceNat = offering.getSharedSourceNat(); DataCenter zone = _dcDao.findById(network.getDataCenterId()); - if (!sharedSourceNat && - _networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat) && - (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { + if (!sharedSourceNat && _networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat) + && (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { List ips = null; Account owner = _entityMgr.findById(Account.class, network.getAccountId()); @@ -1057,8 +1042,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // because the serializer would look up the NetworkVO class's table and retrieve the // network id instead of the physical network id. // So just throw this exception as is. We may need to TBD by changing the serializer. - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (s_logger.isDebugEnabled()) { @@ -1066,8 +1051,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } if (!element.implement(network, offering, dest, context)) { - CloudRuntimeException ex = - new CloudRuntimeException("Failed to implement provider " + element.getProvider().getName() + " for network with specified id"); + CloudRuntimeException ex = new CloudRuntimeException("Failed to implement provider " + element.getProvider().getName() + " for network with specified id"); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -1079,8 +1063,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (!reprogramNetworkRules(network.getId(), CallContext.current().getCallingAccount(), network)) { s_logger.warn("Failed to re-program the network as a part of network " + network + " implement"); // see DataCenterVO.java - ResourceUnavailableException ex = - new ResourceUnavailableException("Unable to apply network rules as a part of network " + network + " implement", DataCenter.class, + ResourceUnavailableException ex = new ResourceUnavailableException("Unable to apply network rules as a part of network " + network + " implement", DataCenter.class, network.getDataCenterId()); ex.addProxyObject(_entityMgr.findById(DataCenter.class, network.getDataCenterId()).getUuid()); throw ex; @@ -1113,9 +1096,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NetworkOfferingVO offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); //there are no egress rules then apply the default egress rule DataCenter zone = _dcDao.findById(network.getDataCenterId()); - if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) && - _networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) && - (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) && _networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) + && (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { // add default egress rule to accept the traffic _firewallMgr.applyDefaultEgressFirewallRule(network.getId(), offering.getEgressDefaultPolicy(), true); } @@ -1170,11 +1152,11 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } protected boolean prepareElement(NetworkElement element, Network network, NicProfile profile, VirtualMachineProfile vmProfile, DeployDestination dest, - ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { element.prepare(network, profile, vmProfile, dest, context); if (vmProfile.getType() == Type.User && element.getProvider() != null) { - if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp) && - _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider()) && element instanceof DhcpServiceProvider) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp) + && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, element.getProvider()) && element instanceof DhcpServiceProvider) { DhcpServiceProvider sp = (DhcpServiceProvider)element; Map dhcpCapabilities = element.getCapabilities().get(Service.Dhcp); String supportsMultipleSubnets = dhcpCapabilities.get(Capability.DhcpAccrossMultipleSubnets); @@ -1185,8 +1167,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } sp.addDhcpEntry(network, profile, vmProfile, dest, context); } - if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) && - _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.UserData, element.getProvider()) && element instanceof UserDataServiceProvider) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.UserData) + && _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.UserData, element.getProvider()) && element instanceof UserDataServiceProvider) { UserDataServiceProvider sp = (UserDataServiceProvider)element; sp.addPasswordAndUserdata(network, profile, vmProfile, dest, context); } @@ -1206,8 +1188,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra _networksDao.changeActiveNicsBy(networkId, count); } - if (nic.getVmType() == VirtualMachine.Type.User || - (nic.getVmType() == VirtualMachine.Type.DomainRouter && _networksDao.findById(networkId).getTrafficType() == TrafficType.Guest)) { + if (nic.getVmType() == VirtualMachine.Type.User + || (nic.getVmType() == VirtualMachine.Type.DomainRouter && _networksDao.findById(networkId).getTrafficType() == TrafficType.Guest)) { _networksDao.setCheckForGc(networkId); } } @@ -1215,8 +1197,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } @Override - public void prepare(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException { + public void prepare(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException { List nics = _nicDao.listByVmId(vmProfile.getId()); // we have to implement default nics first - to ensure that default network elements start up first in multiple @@ -1248,8 +1230,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override public NicProfile prepareNic(VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context, long nicId, Network network) - throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, - ResourceUnavailableException { + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, + ResourceUnavailableException { Integer networkRate = _networkModel.getNetworkRate(network.getId(), vmProfile.getId()); NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); @@ -1269,7 +1251,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra profile = new NicProfile(nic, network, broadcastUri, isolationUri, - networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); + networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); guru.reserve(profile, network, vmProfile, dest, context); nic.setIp4Address(profile.getIp4Address()); nic.setAddressFormat(profile.getFormat()); @@ -1288,8 +1270,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra updateNic(nic, network.getId(), 1); } else { - profile = - new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), + profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); guru.updateNicProfile(profile, network); nic.setState(Nic.State.Reserved); @@ -1300,15 +1281,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to prepare for " + nic); } if (!prepareElement(element, network, profile, vmProfile, dest, context)) { - throw new InsufficientAddressCapacityException("unable to configure the dhcp service, due to insufficiant address capacity", Network.class, - network.getId()); + throw new InsufficientAddressCapacityException("unable to configure the dhcp service, due to insufficiant address capacity", Network.class, network.getId()); } } } @@ -1327,8 +1307,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); - NicProfile profile = - new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), + NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); if (guru instanceof NetworkMigrationResponder) { if (!((NetworkMigrationResponder)guru).prepareMigration(profile, network, vm, dest, context)) { @@ -1339,8 +1318,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (element instanceof NetworkMigrationResponder) { if (!((NetworkMigrationResponder)element).prepareMigration(profile, network, vm, dest, context)) { @@ -1379,8 +1358,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (element instanceof NetworkMigrationResponder) { ((NetworkMigrationResponder)element).commitMigration(nicSrc, network, src, src_context, dst_context); @@ -1410,8 +1389,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (element instanceof NetworkMigrationResponder) { ((NetworkMigrationResponder)element).rollbackMigration(nicDst, network, dst, src_context, dst_context); @@ -1454,9 +1433,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); nic.setState(Nic.State.Releasing); _nicDao.update(nic.getId(), nic); - NicProfile profile = - new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel.isSecurityGroupSupportedInNetwork(network), - _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); + NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel + .isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getHypervisorType(), network)); if (guru.release(profile, vmProfile, nic.getReservationId())) { applyProfileToNicForRelease(nic, profile); nic.setState(Nic.State.Allocated); @@ -1485,8 +1463,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to release " + profile); @@ -1517,12 +1495,22 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } protected void removeNic(VirtualMachineProfile vm, NicVO nic) { + + if (nic.getReservationStrategy() == Nic.ReservationStrategy.Start && nic.getState() != Nic.State.Allocated) { + // Nics with reservation strategy 'Start' should go through release phase in the Nic life cycle. + // Ensure that release is performed before Nic is to be removed to avoid resource leaks. + try { + releaseNic(vm, nic.getId()); + } catch (Exception ex) { + s_logger.warn("Failed to release nic: " + nic.toString() + " as part of remove operation due to", ex); + } + } + nic.setState(Nic.State.Deallocating); _nicDao.update(nic.getId(), nic); NetworkVO network = _networksDao.findById(nic.getNetworkId()); - NicProfile profile = - new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), - network)); + NicProfile profile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag( + vm.getHypervisorType(), network)); /* * We need to release the nics with a Create ReservationStrategy here @@ -1533,8 +1521,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra for (NetworkElement element : networkElements) { if (providersToImplement.contains(element.getProvider())) { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + - " either doesn't exist or is not enabled in physical network id: " + network.getPhysicalNetworkId()); + throw new CloudRuntimeException("Service provider " + element.getProvider().getName() + " either doesn't exist or is not enabled in physical network id: " + + network.getPhysicalNetworkId()); } if (s_logger.isDebugEnabled()) { s_logger.debug("Asking " + element.getName() + " to release " + nic); @@ -1551,8 +1539,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } // remove the dhcpservice ip if this is the last nic in subnet. - if (vm.getType() == Type.User && isDhcpAccrossMultipleSubnetsSupported(network) && isLastNicInSubnet(nic) && network.getTrafficType() == TrafficType.Guest && - network.getGuestType() == GuestType.Shared) { + if (vm.getType() == Type.User && isDhcpAccrossMultipleSubnetsSupported(network) && isLastNicInSubnet(nic) && network.getTrafficType() == TrafficType.Guest + && network.getGuestType() == GuestType.Shared) { removeDhcpServiceInSubnet(nic); } NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); @@ -1625,9 +1613,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override @DB public Network createGuestNetwork(long networkOfferingId, final String name, final String displayText, final String gateway, final String cidr, String vlanId, - String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, Boolean subdomainAccess, - final Long vpcId, final String ip6Gateway, final String ip6Cidr, final Boolean isDisplayNetworkEnabled, final String isolatedPvlan) - throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, Boolean subdomainAccess, + final Long vpcId, final String ip6Gateway, final String ip6Cidr, final Boolean isDisplayNetworkEnabled, final String isolatedPvlan) + throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); // this method supports only guest network creation @@ -1639,14 +1627,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra final boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, aclType); //check resource limits if (updateResourceCount) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.network, isDisplayNetworkEnabled); } // Validate network offering if (ntwkOff.getState() != NetworkOffering.State.Enabled) { // see NetworkOfferingVO - InvalidParameterValueException ex = - new InvalidParameterValueException("Can't use specified network offering id as its stat is not " + NetworkOffering.State.Enabled); + InvalidParameterValueException ex = new InvalidParameterValueException("Can't use specified network offering id as its stat is not " + NetworkOffering.State.Enabled); ex.addProxyObject(ntwkOff.getUuid(), "networkOfferingId"); throw ex; } @@ -1684,8 +1671,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // if zone is basic, only Shared network offerings w/o source nat service are allowed if (!(ntwkOff.getGuestType() == GuestType.Shared && !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + - " with disabled " + Service.SourceNat.getName() + " service are allowed"); + throw new InvalidParameterValueException("For zone of type " + NetworkType.Basic + " only offerings of " + "guestType " + GuestType.Shared + " with disabled " + + Service.SourceNat.getName() + " service are allowed"); } if (domainId == null || domainId != Domain.ROOT_DOMAIN) { @@ -1747,8 +1734,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (vlanSpecified) { //don't allow to specify vlan tag used by physical network for dynamic vlan allocation if (_dcDao.findVnet(zoneId, pNtwk.getId(), vlanId).size() > 0) { - throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + - zone.getName()); + throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + + zone.getName()); } String uri = BroadcastDomainType.fromString(vlanId).toString(); // For Isolated networks, don't allow to create network with vlan that already exists in the zone @@ -1775,8 +1762,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra int vnetsAllocatedToAccount = _datacenterVnetDao.countVnetsAllocatedToAccount(zoneId, owner.getAccountId()); int vnetsDedicatedToAccount = _datacenterVnetDao.countVnetsDedicatedToAccount(zoneId, owner.getAccountId()); if (vnetsAllocatedToAccount < vnetsDedicatedToAccount) { - throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + - " to the vlan range dedicated to the owner " + owner.getAccountName()); + throw new InvalidParameterValueException("Specified vlan " + vlanId + " doesn't belong" + " to the vlan range dedicated to the owner " + + owner.getAccountName()); } } } @@ -1785,8 +1772,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } else { // don't allow to creating shared network with given Vlan ID, if there already exists a isolated network or // shared network with same Vlan ID in the zone - if (_networksDao.countByZoneUriAndGuestType(zoneId, uri, GuestType.Isolated) > 0 || - _networksDao.countByZoneUriAndGuestType(zoneId, uri, GuestType.Shared) > 0) { + if (_networksDao.countByZoneUriAndGuestType(zoneId, uri, GuestType.Isolated) > 0 || _networksDao.countByZoneUriAndGuestType(zoneId, uri, GuestType.Shared) > 0) { throw new InvalidParameterValueException("There is a isolated/shared network with vlan id: " + vlanId + " already exists " + "in zone " + zoneId); } } @@ -1795,8 +1781,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // If networkDomain is not specified, take it from the global configuration if (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Dns)) { - Map dnsCapabilities = - _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), Service.Dns); + Map dnsCapabilities = _networkModel.getNetworkOfferingServiceCapabilities(_entityMgr.findById(NetworkOffering.class, networkOfferingId), + Service.Dns); String isUpdateDnsSupported = dnsCapabilities.get(Capability.AllowDnsSuffixModification); if (isUpdateDnsSupported == null || !Boolean.valueOf(isUpdateDnsSupported)) { if (networkDomain != null) { @@ -1821,8 +1807,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // validate network domain if (!NetUtils.verifyDomainName(networkDomain)) { throw new InvalidParameterValueException("Invalid network domain. Total length shouldn't exceed 190 chars. Each domain " - + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + + "label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } } } @@ -1831,14 +1817,13 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // In Advance zone Cidr for Shared networks and Isolated networks w/o source nat service can't be NULL - 2.2.x // limitation, remove after we introduce support for multiple ip ranges // with different Cidrs for the same Shared network - boolean cidrRequired = - zone.getNetworkType() == NetworkType.Advanced && - ntwkOff.getTrafficType() == TrafficType.Guest && - (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated && !_networkModel.areServicesSupportedByNetworkOffering( - ntwkOff.getId(), Service.SourceNat))); + boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced + && ntwkOff.getTrafficType() == TrafficType.Guest + && (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated && !_networkModel.areServicesSupportedByNetworkOffering( + ntwkOff.getId(), Service.SourceNat))); if (cidr == null && ip6Cidr == null && cidrRequired) { - throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" + " type " + Network.GuestType.Shared + - " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); + throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" + " type " + Network.GuestType.Shared + + " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled"); } // No cidr can be specified in Basic zone @@ -1895,8 +1880,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } } - List networks = - setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, isDisplayNetworkEnabled); + List networks = setupNetwork(owner, ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType, subdomainAccessFinal, vpcId, + isDisplayNetworkEnabled); Network network = null; if (networks == null || networks.isEmpty()) { @@ -1917,7 +1902,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } if (updateResourceCount) { - _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network); + _resourceLimitMgr.incrementResourceCount(owner.getId(), ResourceType.network, isDisplayNetworkEnabled); } return network; @@ -2055,8 +2040,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (providersToShutdown.contains(element.getProvider())) { try { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName() + - " either doesn't exist or not enabled in the physical network " + _networkModel.getPhysicalNetworkId(network)); + s_logger.warn("Unable to complete shutdown of the network elements due to element: " + element.getName() + + " either doesn't exist or not enabled in the physical network " + _networkModel.getPhysicalNetworkId(network)); success = false; } if (s_logger.isDebugEnabled()) { @@ -2143,8 +2128,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra if (providersToDestroy.contains(element.getProvider())) { try { if (!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network), element.getProvider().getName())) { - s_logger.warn("Unable to complete destroy of the network elements due to element: " + element.getName() + - " either doesn't exist or not enabled in the physical network " + _networkModel.getPhysicalNetworkId(network)); + s_logger.warn("Unable to complete destroy of the network elements due to element: " + element.getName() + + " either doesn't exist or not enabled in the physical network " + _networkModel.getPhysicalNetworkId(network)); success = false; } @@ -2206,7 +2191,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra NetworkOffering ntwkOff = _entityMgr.findById(NetworkOffering.class, networkFinal.getNetworkOfferingId()); boolean updateResourceCount = resourceCountNeedsUpdate(ntwkOff, networkFinal.getAclType()); if (updateResourceCount) { - _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network); + _resourceLimitMgr.decrementResourceCount(networkFinal.getAccountId(), ResourceType.network, networkFinal.getDisplayNetwork()); } } } @@ -2221,7 +2206,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return success; } - private boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType) { + @Override + public boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType) { //Update resource count only for Isolated account specific non-system networks boolean updateResourceCount = (ntwkOff.getGuestType() == GuestType.Isolated && !ntwkOff.isSystemOnly() && aclType == ACLType.Account); return updateResourceCount; @@ -2329,7 +2315,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override public boolean startNetwork(long networkId, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { + InsufficientCapacityException { // Check if network exists NetworkVO network = _networksDao.findById(networkId); @@ -2351,8 +2337,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } @Override - public boolean restartNetwork(Long networkId, Account callerAccount, User callerUser, boolean cleanup) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { + public boolean restartNetwork(Long networkId, Account callerAccount, User callerUser, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException, + InsufficientCapacityException { NetworkVO network = _networksDao.findById(networkId); @@ -2450,8 +2436,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra protected boolean isSharedNetworkWithServices(Network network) { assert (network != null); DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); - if (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced && - isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { + if (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced + && isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { return true; } return false; @@ -2459,24 +2445,24 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra protected boolean isSharedNetworkOfferingWithServices(long networkOfferingId) { NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); - if ((networkOffering.getGuestType() == Network.GuestType.Shared) && - (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering( - networkOfferingId, Service.Lb))) { + if ((networkOffering.getGuestType() == Network.GuestType.Shared) + && (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering( + networkOfferingId, Service.Lb))) { return true; } return false; } @Override - public List listVmNics(Long vmId, Long nicId) { + public List listVmNics(long vmId, Long nicId, Long networkId) { List result = null; - if (nicId == null) { + if (nicId == null && networkId == null) { result = _nicDao.listByVmId(vmId); } else { - result = _nicDao.listByVmIdAndNicId(vmId, nicId); + result = _nicDao.listByVmIdAndNicIdAndNtwkId(vmId, nicId, networkId); } return result; } @@ -2694,8 +2680,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra try { // delete default egress rule DataCenter zone = _dcDao.findById(network.getDataCenterId()); - if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) && - (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { + if (_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Firewall) + && (network.getGuestType() == Network.GuestType.Isolated || (network.getGuestType() == Network.GuestType.Shared && zone.getNetworkType() == NetworkType.Advanced))) { // add default egress rule to accept the traffic _firewallMgr.applyDefaultEgressFirewallRule(network.getId(), _networkModel.getNetworkEgressDefaultPolicy(networkId), false); } @@ -2909,8 +2895,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // check that provider is supported if (checkPhysicalNetwork) { if (!_pNSPDao.isServiceProviderEnabled(physicalNetworkId, provider, service)) { - throw new UnsupportedServiceException("Provider " + provider + " is either not enabled or doesn't " + "support service " + service + - " in physical network id=" + physicalNetworkId); + throw new UnsupportedServiceException("Provider " + provider + " is either not enabled or doesn't " + "support service " + service + " in physical network id=" + + physicalNetworkId); } } @@ -2966,8 +2952,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra @Override public NicProfile createNicForVm(Network network, NicProfile requested, ReservationContext context, VirtualMachineProfile vmProfile, boolean prepare) - throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, - ResourceUnavailableException { + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException, ConcurrentOperationException, InsufficientCapacityException, + ResourceUnavailableException { VirtualMachine vm = vmProfile.getVirtualMachine(); DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); @@ -3010,9 +2996,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId()); NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, network.getGuruName()); - NicProfile profile = - new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network), - _networkModel.getNetworkTag(vm.getHypervisorType(), network)); + NicProfile profile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), networkRate, + _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vm.getHypervisorType(), network)); guru.updateNicProfile(profile, network); profiles.add(profile); } @@ -3154,9 +3139,9 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra } public static final ConfigKey NetworkGcWait = new ConfigKey(Integer.class, "network.gc.wait", "Advanced", "600", - "Time (in seconds) to wait before shutting down a network that's not in used", false); + "Time (in seconds) to wait before shutting down a network that's not in used", false); public static final ConfigKey NetworkGcInterval = new ConfigKey(Integer.class, "network.gc.interval", "Advanced", "600", - "Seconds to wait before checking for networks to shutdown", true); + "Seconds to wait before checking for networks to shutdown", true); @Override public ConfigKey[] getConfigKeys() { diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index efaa288fc37..0d6d7185320 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -38,6 +38,7 @@ 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.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; @@ -72,6 +73,8 @@ import com.cloud.exception.InsufficientStorageCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; @@ -141,6 +144,10 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati SnapshotDataFactory snapshotFactory; @Inject ConfigDepot _configDepot; + @Inject + HostDao _hostDao; + @Inject + SnapshotService _snapshotSrv; private final StateMachine2 _volStateMachine; protected List _storagePoolAllocators; @@ -150,7 +157,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } public void setStoragePoolAllocators(List storagePoolAllocators) { - this._storagePoolAllocators = storagePoolAllocators; + _storagePoolAllocators = storagePoolAllocators; } protected List _podAllocators; @@ -160,7 +167,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } public void setPodAllocators(List podAllocators) { - this._podAllocators = podAllocators; + _podAllocators = podAllocators; } protected VolumeOrchestrator() { @@ -169,14 +176,13 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @Override public VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, Long destPoolClusterId, HypervisorType dataDiskHyperType) - throws ConcurrentOperationException, StorageUnavailableException { + throws ConcurrentOperationException, StorageUnavailableException { // Find a destination storage pool with the specified criteria DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volume.getDiskOfferingId()); ; - DiskProfile dskCh = - new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(), - diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), null); + DiskProfile dskCh = new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), + diskOffering.getTagsArray(), diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), null); dskCh.setHyperType(dataDiskHyperType); DataCenter destPoolDataCenter = _entityMgr.findById(DataCenter.class, destPoolDcId); Pod destPoolPod = _entityMgr.findById(Pod.class, destPoolPodId); @@ -197,8 +203,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, Long templateId) { - VolumeVO newVol = - new VolumeVO(oldVol.getVolumeType(), oldVol.getName(), oldVol.getDataCenterId(), oldVol.getDomainId(), oldVol.getAccountId(), oldVol.getDiskOfferingId(), + VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(), oldVol.getName(), oldVol.getDataCenterId(), oldVol.getDomainId(), oldVol.getAccountId(), oldVol.getDiskOfferingId(), oldVol.getSize(), oldVol.getMinIops(), oldVol.getMaxIops(), oldVol.get_iScsiName()); if (templateId != null) { newVol.setTemplateId(templateId); @@ -253,8 +258,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati @DB @Override - public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, UserVm vm) - throws StorageUnavailableException { + public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, UserVm vm) throws StorageUnavailableException { Account account = _entityMgr.findById(Account.class, volume.getAccountId()); final HashSet poolsToAvoid = new HashSet(); @@ -289,8 +293,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } else { List rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT); if (rootVolumesOfVm.size() != 1) { - throw new CloudRuntimeException("The VM " + vm.getHostName() + - " has more than one ROOT volume and is in an invalid state. Please contact Cloud Support."); + throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state. Please contact Cloud Support."); } else { VolumeVO rootVolumeOfVm = rootVolumesOfVm.get(0); StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); @@ -331,6 +334,16 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati VolumeInfo vol = volFactory.getVolume(volume.getId()); DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); + // sync snapshot to region store if necessary + DataStore snapStore = snapInfo.getDataStore(); + long snapVolId = snapInfo.getVolumeId(); + try { + _snapshotSrv.syncVolumeSnapshotsToRegionStore(snapVolId, snapStore); + } catch (Exception ex) { + // log but ignore the sync error to avoid any potential S3 down issue, it should be sync next time + s_logger.warn(ex.getMessage(), ex); + } + // create volume on primary from snapshot AsyncCallFuture future = volService.createVolumeFromSnapshot(vol, store, snapInfo); try { VolumeApiResult result = future.get(); @@ -357,16 +370,16 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } return new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), ss.getSize(), diskOffering.getTagsArray(), - diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), Storage.ImageFormat.ISO != template.getFormat() ? template.getId() : null); + diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), Storage.ImageFormat.ISO != template.getFormat() ? template.getId() : null); } else { - return new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), - diskOffering.getTagsArray(), diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), null); + return new DiskProfile(volume.getId(), volume.getVolumeType(), volume.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(), + diskOffering.getUseLocalStorage(), diskOffering.isRecreatable(), null); } } @DB public VolumeInfo copyVolumeFromSecToPrimary(VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate template, DataCenter dc, Pod pod, Long clusterId, - ServiceOffering offering, DiskOffering diskOffering, List avoids, long size, HypervisorType hyperType) throws NoTransitionException { + ServiceOffering offering, DiskOffering diskOffering, List avoids, long size, HypervisorType hyperType) throws NoTransitionException { final HashSet avoidPools = new HashSet(avoids); DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); @@ -393,13 +406,11 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } @DB - public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate template, DataCenter dc, Pod pod, Long clusterId, - ServiceOffering offering, DiskOffering diskOffering, List avoids, long size, HypervisorType hyperType) { - StoragePool pool = null; + public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate template, DataCenter dc, Pod pod, Long clusterId, ServiceOffering offering, + DiskOffering diskOffering, List avoids, long size, HypervisorType hyperType) { + volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType); - if (diskOffering != null && diskOffering.isCustomized()) { - diskOffering.setDiskSize(size); - } + StoragePool pool = null; DiskProfile dskCh = null; if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) { @@ -408,14 +419,18 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); } + if (diskOffering != null && diskOffering.isCustomized()) { + dskCh.setSize(size); + } + dskCh.setHyperType(hyperType); final HashSet avoidPools = new HashSet(avoids); pool = findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(), vm, avoidPools); if (pool == null) { - s_logger.warn("Unable to find storage pool when create volume " + volume.getName()); - throw new CloudRuntimeException("Unable to find storage pool when create volume" + volume.getName()); + s_logger.warn("Unable to find suitable primary storage when creating volume " + volume.getName()); + throw new CloudRuntimeException("Unable to find suitable primary storage when creating volume " + volume.getName()); } if (s_logger.isDebugEnabled()) { @@ -427,7 +442,6 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati AsyncCallFuture future = null; boolean isNotCreatedFromTemplate = volume.getTemplateId() == null ? true : false; if (isNotCreatedFromTemplate) { - volume = updateHypervisorSnapshotReserveForVolume(diskOffering, volume, hyperType); future = volService.createVolumeAsync(volume, store); } else { TemplateInfo templ = tmplFactory.getTemplate(template.getId(), DataStoreRole.Image); @@ -542,7 +556,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati protected DiskProfile toDiskProfile(Volume vol, DiskOffering offering) { return new DiskProfile(vol.getId(), vol.getVolumeType(), vol.getName(), offering.getId(), vol.getSize(), offering.getTagsArray(), offering.getUseLocalStorage(), - offering.isRecreatable(), vol.getTemplateId()); + offering.isRecreatable(), vol.getTemplateId()); } @Override @@ -552,8 +566,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } else { size = (size * 1024 * 1024 * 1024); } - VolumeVO vol = - new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, offering.getMinIops(), offering.getMaxIops(), null); + VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, offering.getMinIops(), offering.getMaxIops(), + null); if (vm != null) { vol.setInstanceId(vm.getId()); } @@ -572,16 +586,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati // Save usage event and update resource count for user vm volumes if (vm.getType() == VirtualMachine.Type.User) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, - vol.getAccountId(), - vol.getDataCenterId(), - vol.getId(), - vol.getName(), - offering.getId(), - null, - size, - Volume.class.getName(), - vol.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offering.getId(), null, size, + Volume.class.getName(), vol.getUuid()); _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume); _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, new Long(vol.getSize())); @@ -590,16 +596,15 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } @Override - public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, VirtualMachineTemplate template, VirtualMachine vm, - Account owner) { + public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, VirtualMachineTemplate template, VirtualMachine vm, Account owner) { assert (template.getFormat() != ImageFormat.ISO) : "ISO is not a template really...."; Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId()); if (rootDisksize != null) { size = (rootDisksize * 1024 * 1024 * 1024); } - VolumeVO vol = - new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, offering.getMinIops(), offering.getMaxIops(), null); + VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, offering.getMinIops(), offering.getMaxIops(), + null); vol.setFormat(getSupportedImageFormatForCluster(template.getHypervisorType())); if (vm != null) { vol.setInstanceId(vm.getId()); @@ -624,8 +629,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati offeringId = offering.getId(); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, null, - size, Volume.class.getName(), vol.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, vol.getAccountId(), vol.getDataCenterId(), vol.getId(), vol.getName(), offeringId, null, size, + Volume.class.getName(), vol.getUuid()); _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume); _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, new Long(vol.getSize())); @@ -642,29 +647,29 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati return ImageFormat.OVA; } else if (hyperType == HypervisorType.Ovm) { return ImageFormat.RAW; + } else if (hyperType == HypervisorType.Hyperv) { + return ImageFormat.VHD; } else { return null; } } private VolumeInfo copyVolume(StoragePool rootDiskPool, VolumeInfo volume, VirtualMachine vm, VirtualMachineTemplate rootDiskTmplt, DataCenter dcVO, Pod pod, - DiskOffering diskVO, ServiceOffering svo, HypervisorType rootDiskHyperType) throws NoTransitionException { + DiskOffering diskVO, ServiceOffering svo, HypervisorType rootDiskHyperType) throws NoTransitionException { if (!volume.getFormat().equals(getSupportedImageFormatForCluster(rootDiskHyperType))) { - throw new InvalidParameterValueException("Failed to attach volume to VM since volumes format " + volume.getFormat().getFileExtension() + - " is not compatible with the vm hypervisor type"); + throw new InvalidParameterValueException("Failed to attach volume to VM since volumes format " + volume.getFormat().getFileExtension() + + " is not compatible with the vm hypervisor type"); } - VolumeInfo volumeOnPrimary = - copyVolumeFromSecToPrimary(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList(), volume.getSize(), - rootDiskHyperType); + VolumeInfo volumeOnPrimary = copyVolumeFromSecToPrimary(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList(), + volume.getSize(), rootDiskHyperType); return volumeOnPrimary; } @Override - public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, Volume rootVolumeOfVm, VolumeInfo volume, HypervisorType rootDiskHyperType) - throws NoTransitionException { + public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, Volume rootVolumeOfVm, VolumeInfo volume, HypervisorType rootDiskHyperType) throws NoTransitionException { VirtualMachineTemplate rootDiskTmplt = _entityMgr.findById(VirtualMachineTemplate.class, vm.getTemplateId()); DataCenter dcVO = _entityMgr.findById(DataCenter.class, vm.getDataCenterId()); Pod pod = _entityMgr.findById(Pod.class, vm.getPodIdToDeployIn()); @@ -698,8 +703,8 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati long vmTemplateId = vm.getTemplateId(); if (volTemplateId != null && volTemplateId.longValue() != vmTemplateId) { if (s_logger.isDebugEnabled()) { - s_logger.debug("switchVolume: Old Volume's templateId: " + volTemplateId + " does not match the VM's templateId: " + vmTemplateId + - ", updating templateId in the new Volume"); + s_logger.debug("switchVolume: Old Volume's templateId: " + volTemplateId + " does not match the VM's templateId: " + vmTemplateId + + ", updating templateId in the new Volume"); } templateIdToUse = vmTemplateId; } @@ -756,8 +761,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati for (VolumeVO vol : volumesForVm) { if (vol.getVolumeType().equals(Type.ROOT)) { // Destroy volume if not already destroyed - boolean volumeAlreadyDestroyed = - (vol.getState() == Volume.State.Destroy || vol.getState() == Volume.State.Expunged || vol.getState() == Volume.State.Expunging); + boolean volumeAlreadyDestroyed = (vol.getState() == Volume.State.Destroy || vol.getState() == Volume.State.Expunged || vol.getState() == Volume.State.Expunging); if (!volumeAlreadyDestroyed) { volService.destroyVolume(vol.getId()); } else { @@ -787,6 +791,22 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } } + @Override + public void disconnectVolumesFromHost(long vmId, long hostId) { + HostVO host = _hostDao.findById(hostId); + + List volumesForVm = _volsDao.findByInstance(vmId); + + if (volumesForVm != null) { + for (VolumeVO volumeForVm : volumesForVm) { + VolumeInfo volumeInfo = volFactory.getVolume(volumeForVm.getId()); + DataStore dataStore = dataStoreMgr.getDataStore(volumeForVm.getPoolId(), DataStoreRole.Primary); + + volService.disconnectVolumeFromHost(volumeInfo, host, dataStore); + } + } + } + @Override @DB public Volume migrateVolume(Volume volume, StoragePool destPool) throws StorageUnavailableException { @@ -998,8 +1018,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati if (s_logger.isDebugEnabled()) { s_logger.debug("Local volume " + vol + " cannot be recreated on storagepool " + assignedPool + " assigned by deploymentPlanner"); } - throw new CloudRuntimeException("Local volume " + vol + " cannot be recreated on storagepool " + assignedPool + - " assigned by deploymentPlanner"); + throw new CloudRuntimeException("Local volume " + vol + " cannot be recreated on storagepool " + assignedPool + " assigned by deploymentPlanner"); } else { //Check if storage migration is enabled in config if (StorageHAMigrationEnabled.value()) { @@ -1023,7 +1042,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } else { if (vol.getPoolId() == null) { throw new StorageUnavailableException("Volume has no pool associate and also no storage pool assigned in DeployDestination, Unable to create " + vol, - Volume.class, vol.getId()); + Volume.class, vol.getId()); } if (s_logger.isDebugEnabled()) { s_logger.debug("No need to recreate the volume: " + vol + ", since it already has a pool assigned: " + vol.getPoolId() + ", adding disk to VM"); @@ -1102,8 +1121,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } @Override - public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, - ConcurrentOperationException { + public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws StorageUnavailableException, InsufficientStorageCapacityException, ConcurrentOperationException { if (dest == null) { if (s_logger.isDebugEnabled()) { @@ -1158,18 +1176,18 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati return true; } - public static final ConfigKey MaxVolumeSize = new ConfigKey(Long.class, "storage.max.volume.size", "Storage", "2000", - "The maximum size for a volume (in GB).", true); + public static final ConfigKey MaxVolumeSize = new ConfigKey(Long.class, "storage.max.volume.size", "Storage", "2000", "The maximum size for a volume (in GB).", + true); public static final ConfigKey RecreatableSystemVmEnabled = new ConfigKey(Boolean.class, "recreate.systemvm.enabled", "Advanced", "false", - "If true, will recreate system vm root disk whenever starting system vm", true); + "If true, will recreate system vm root disk whenever starting system vm", true); public static final ConfigKey StorageHAMigrationEnabled = new ConfigKey(Boolean.class, "enable.ha.storage.migration", "Storage", "true", - "Enable/disable storage migration across primary storage during HA", true); + "Enable/disable storage migration across primary storage during HA", true); @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {RecreatableSystemVmEnabled, MaxVolumeSize, StorageHAMigrationEnabled}; + return new ConfigKey[] {RecreatableSystemVmEnabled, MaxVolumeSize, StorageHAMigrationEnabled, CustomDiskOfferingMaxSize, CustomDiskOfferingMinSize}; } @Override diff --git a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index a3747c06123..6dd1d4b491c 100644 --- a/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -184,7 +184,6 @@ - @@ -251,6 +250,7 @@ + @@ -330,6 +330,8 @@ + + diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java b/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java index f9261a96b87..4e35bf5de96 100644 --- a/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java @@ -54,6 +54,10 @@ public class UsageEventDaoImpl extends GenericDaoBase implem private static final String COPY_ALL_EVENTS = "INSERT INTO cloud_usage.usage_event (id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size) " + "SELECT id, type, account_id, created, zone_id, resource_id, resource_name, offering_id, template_id, size, resource_type, virtual_size FROM cloud.usage_event vmevt WHERE vmevt.id <= ?"; + private static final String COPY_EVENT_DETAILS = "INSERT INTO cloud_usage.usage_event_details (id, usage_event_id, name, value) " + + "SELECT id, usage_event_id, name, value FROM cloud.usage_event_details vmevtDetails WHERE vmevtDetails.usage_event_id > ? and vmevtDetails.usage_event_id <= ? "; + private static final String COPY_ALL_EVENT_DETAILS = "INSERT INTO cloud_usage.usage_event_details (id, usage_event_id, name, value) " + + "SELECT id, usage_event_id, name, value FROM cloud.usage_event_details vmevtDetails WHERE vmevtDetails.usage_event_id <= ?"; private static final String MAX_EVENT = "select max(id) from cloud.usage_event where created <= ?"; @Inject protected UsageEventDetailsDao usageEventDetailsDao; @@ -96,6 +100,7 @@ public class UsageEventDaoImpl extends GenericDaoBase implem long recentEventId = getMostRecentEventId(); long maxEventId = getMaxEventId(endDate); TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); + // Copy events from cloud db to usage db String sql = COPY_EVENTS; if (recentEventId == 0) { if (s_logger.isDebugEnabled()) { @@ -115,12 +120,39 @@ public class UsageEventDaoImpl extends GenericDaoBase implem pstmt.setLong(i++, maxEventId); pstmt.executeUpdate(); txn.commit(); - return findRecentEvents(endDate); } catch (Exception ex) { txn.rollback(); s_logger.error("error copying events from cloud db to usage db", ex); throw new CloudRuntimeException(ex.getMessage()); } + + // Copy event details from cloud db to usage db + sql = COPY_EVENT_DETAILS; + if (recentEventId == 0) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("no recent event date, copying all event detailss"); + } + sql = COPY_ALL_EVENT_DETAILS; + } + + pstmt = null; + try { + txn.start(); + pstmt = txn.prepareAutoCloseStatement(sql); + int i = 1; + if (recentEventId != 0) { + pstmt.setLong(i++, recentEventId); + } + pstmt.setLong(i++, maxEventId); + pstmt.executeUpdate(); + txn.commit(); + } catch (Exception ex) { + txn.rollback(); + s_logger.error("error copying event details from cloud db to usage db", ex); + throw new CloudRuntimeException(ex.getMessage()); + } + + return findRecentEvents(endDate); } @DB diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java index 229bef0bb63..aee5a5754fb 100644 --- a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java @@ -23,7 +23,6 @@ import com.cloud.event.UsageEventDetailsVO; import com.cloud.utils.db.GenericDao; public interface UsageEventDetailsDao extends GenericDao { - Map findDetails(long eventId); void persist(long eventId, Map details); diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java index e959d089552..35d77c12b08 100644 --- a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java @@ -16,17 +16,11 @@ // under the License. package com.cloud.event.dao; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ejb.Local; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -41,8 +35,6 @@ import com.cloud.utils.db.TransactionLegacy; public class UsageEventDetailsDaoImpl extends GenericDaoBase implements UsageEventDetailsDao { public static final Logger s_logger = Logger.getLogger(UsageEventDetailsDaoImpl.class.getName()); - private static final String EVENT_DETAILS_QUERY = "SELECT details.id, details.usage_event_id, details.name, details.value FROM `cloud`.`usage_event_details` details WHERE details.usage_event_id = ?"; - protected final SearchBuilder EventDetailsSearch; protected final SearchBuilder DetailSearch; @@ -79,45 +71,6 @@ public class UsageEventDetailsDaoImpl extends GenericDaoBase findDetails(long eventId) { - Connection conn = null; - PreparedStatement pstmt = null; - ResultSet resultSet = null; - Map details = new HashMap(); - try { - conn = TransactionLegacy.getStandaloneConnection(); - - pstmt = conn.prepareStatement(EVENT_DETAILS_QUERY); - pstmt.setLong(1, eventId); - resultSet = pstmt.executeQuery(); - - while (resultSet.next()) { - details.put(resultSet.getString(3), resultSet.getString(4)); - } - - } catch (SQLException e) { - throw new CloudRuntimeException("Error while executing SQL prepared statement", e); - } catch (Throwable e) { - throw new CloudRuntimeException("Caught: " + e); - } finally { - if (pstmt != null) { - try { - pstmt.close(); - } catch (SQLException e) { - } - } - if (conn != null) { - try { - conn.close(); - } catch (SQLException e) { - } - } - } - - return details; - } - @Override public void persist(long eventId, Map details) { TransactionLegacy txn = TransactionLegacy.currentTxn(); diff --git a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java index 13fb64e4e9f..0c556c843d9 100644 --- a/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/NetworkDaoImpl.java @@ -195,6 +195,7 @@ public class NetworkDaoImpl extends GenericDaoBase implements N NetworksRegularUserCanCreateSearch = createSearchBuilder(Long.class); NetworksRegularUserCanCreateSearch.and("aclType", NetworksRegularUserCanCreateSearch.entity().getAclType(), Op.EQ); + NetworksRegularUserCanCreateSearch.and("displayNetwork", NetworksRegularUserCanCreateSearch.entity().getDisplayNetwork(), Op.EQ); NetworksRegularUserCanCreateSearch.select(null, Func.COUNT, NetworksRegularUserCanCreateSearch.entity().getId()); SearchBuilder join4 = _accountsDao.createSearchBuilder(); join4.and("account", join4.entity().getAccountId(), Op.EQ); @@ -549,6 +550,7 @@ public class NetworkDaoImpl extends GenericDaoBase implements N public long countNetworksUserCanCreate(long ownerId) { SearchCriteria sc = NetworksRegularUserCanCreateSearch.create(); sc.setParameters("aclType", ACLType.Account); + sc.setParameters("displayNetwork", 1); sc.setJoinParameters("accounts", "account", ownerId); sc.setJoinParameters("ntwkOff", "specifyVlan", false); return customSearch(sc, null).get(0); diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java index b554a230278..3873dd272f4 100755 --- a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java @@ -97,8 +97,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering this.rateMbps = rateMbps; this.multicastRateMbps = multicastRateMbps; this.offerHA = offerHA; - this.limitCpuUse = false; - this.volatileVm = false; + limitCpuUse = false; + volatileVm = false; this.defaultUse = defaultUse; this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); } @@ -174,16 +174,16 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering offering.getSystemUse(), true, offering.getDomainId()); - this.cpu = offering.getCpu(); - this.ramSize = offering.getRamSize(); - this.speed = offering.getSpeed(); - this.rateMbps = offering.getRateMbps(); - this.multicastRateMbps = offering.getMulticastRateMbps(); - this.offerHA = offering.getOfferHA(); - this.limitCpuUse = offering.getLimitCpuUse(); - this.volatileVm = offering.getVolatileVm(); - this.hostTag = offering.getHostTag(); - this.vmType = offering.getSystemVmType(); + cpu = offering.getCpu(); + ramSize = offering.getRamSize(); + speed = offering.getSpeed(); + rateMbps = offering.getRateMbps(); + multicastRateMbps = offering.getMulticastRateMbps(); + offerHA = offering.getOfferHA(); + limitCpuUse = offering.getLimitCpuUse(); + volatileVm = offering.getVolatileVm(); + hostTag = offering.getHostTag(); + vmType = offering.getSystemVmType(); } @Override @@ -325,6 +325,6 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } public void setDynamicFlag(boolean isdynamic) { - this.isDynamic = isdynamic; + isDynamic = isdynamic; } } diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java index 944847f9298..901e07c70ef 100755 --- a/engine/schema/src/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/com/cloud/storage/VolumeVO.java @@ -587,7 +587,6 @@ public class VolumeVO implements Volume { this.state = state; } - @Override public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { this.hypervisorSnapshotReserve = hypervisorSnapshotReserve; } diff --git a/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java b/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java index d42ef66d4e1..f55352b2ef2 100644 --- a/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java +++ b/engine/schema/src/com/cloud/storage/dao/SnapshotDao.java @@ -57,13 +57,4 @@ public interface SnapshotDao extends GenericDao, StateDao listAllByStatus(Snapshot.State... status); - /** - * Gets the Total Secondary Storage space (in bytes) used by snapshots - * allocated for an account - * - * @param account - * @return total Secondary Storage space allocated - */ - long secondaryStorageUsedForAccount(long accountId); - } diff --git a/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java index f15319849c7..204447c6909 100644 --- a/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/engine/schema/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -36,7 +36,6 @@ import com.cloud.storage.Snapshot.Type; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.VolumeDaoImpl.SumCount; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; @@ -46,7 +45,6 @@ import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; -import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; @@ -72,7 +70,6 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private SearchBuilder InstanceIdSearch; private SearchBuilder StatusSearch; private GenericSearchBuilder CountSnapshotsByAccount; - private GenericSearchBuilder secondaryStorageSearch; @Inject ResourceTagDao _tagsDao; @Inject @@ -178,6 +175,7 @@ public class SnapshotDaoImpl extends GenericDaoBase implements CountSnapshotsByAccount = createSearchBuilder(Long.class); CountSnapshotsByAccount.select(null, Func.COUNT, null); CountSnapshotsByAccount.and("account", CountSnapshotsByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + CountSnapshotsByAccount.and("status", CountSnapshotsByAccount.entity().getState(), SearchCriteria.Op.NIN); CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); CountSnapshotsByAccount.done(); @@ -193,12 +191,6 @@ public class SnapshotDaoImpl extends GenericDaoBase implements InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); InstanceIdSearch.done(); - - secondaryStorageSearch = createSearchBuilder(SumCount.class); - secondaryStorageSearch.select("sum", Func.SUM, secondaryStorageSearch.entity().getSize()); - secondaryStorageSearch.and("accountId", secondaryStorageSearch.entity().getAccountId(), Op.EQ); - secondaryStorageSearch.and("isRemoved", secondaryStorageSearch.entity().getRemoved(), Op.NULL); - secondaryStorageSearch.done(); } @Override @@ -277,6 +269,7 @@ public class SnapshotDaoImpl extends GenericDaoBase implements public Long countSnapshotsForAccount(long accountId) { SearchCriteria sc = CountSnapshotsByAccount.create(); sc.setParameters("account", accountId); + sc.setParameters("status", State.Error, State.Destroyed); return customSearch(sc, null).get(0); } @@ -333,15 +326,4 @@ public class SnapshotDaoImpl extends GenericDaoBase implements return true; } - @Override - public long secondaryStorageUsedForAccount(long accountId) { - SearchCriteria sc = secondaryStorageSearch.create(); - sc.setParameters("accountId", accountId); - List storageSpace = customSearch(sc, null); - if (storageSpace != null) { - return storageSpace.get(0).sum; - } else { - return 0; - } - } } diff --git a/engine/schema/src/com/cloud/storage/dao/SnapshotDetailsVO.java b/engine/schema/src/com/cloud/storage/dao/SnapshotDetailsVO.java index 9bc4b4c4726..35a724435e7 100644 --- a/engine/schema/src/com/cloud/storage/dao/SnapshotDetailsVO.java +++ b/engine/schema/src/com/cloud/storage/dao/SnapshotDetailsVO.java @@ -44,6 +44,10 @@ public class SnapshotDetailsVO implements ResourceDetail { @Column(name = "value") String value; + public SnapshotDetailsVO() { + + } + public SnapshotDetailsVO(Long resourceId, String name, String value) { this.resourceId = resourceId; this.name = name; diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade2214to30.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade2214to30.java index 62fdcf10dd2..58dd916ea6e 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade2214to30.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade2214to30.java @@ -34,6 +34,7 @@ import org.apache.log4j.Logger; import com.cloud.offering.NetworkOffering; import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.crypt.EncryptionSecretKeyChecker; +import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; @@ -110,19 +111,6 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { return new File[] {new File(script)}; } - protected void closePstmts(List pstmt2Close){ - for(PreparedStatement pstmt : pstmt2Close) { - try { - if (pstmt != null && !pstmt.isClosed()) { - pstmt.close(); - } - } catch (SQLException e) { - // It's not possible to recover from this and we need to continue closing - e.printStackTrace(); - } - } - } - private void setupPhysicalNetworks(Connection conn) { /** * for each zone: @@ -374,7 +362,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (SQLException e) { throw new CloudRuntimeException("Exception while adding PhysicalNetworks", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -454,7 +442,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable encrypt host_details values ", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } s_logger.debug("Done encrypting host details"); } @@ -500,7 +488,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable encrypt vm_instance vnc_password ", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } s_logger.debug("Done encrypting vm_instance vnc_password"); } @@ -533,7 +521,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable encrypt user secret key ", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } s_logger.debug("Done encrypting user keys"); } @@ -566,7 +554,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable encrypt vpn_users password ", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } s_logger.debug("Done encrypting vpn_users password"); } @@ -687,7 +675,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (SQLException e) { throw new CloudRuntimeException("Unable to create service/provider map for network offerings", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -725,7 +713,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (SQLException e) { throw new CloudRuntimeException("Unable to update domain network ref", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -759,7 +747,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (SQLException e) { throw new CloudRuntimeException("Unable to create service/provider map for networks", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -873,7 +861,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } catch (SQLException e) { } - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -904,7 +892,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { } catch (SQLException e) { throw new CloudRuntimeException("Unable to update op_host_capacity table. ", e); } finally { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -1011,7 +999,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } catch (SQLException e) { } - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } @@ -1084,7 +1072,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { zoneIds.add(rs.getLong(1)); } } catch (SQLException e) { - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); throw new CloudRuntimeException("Unable to switch networks to the new network offering", e); } @@ -1167,7 +1155,7 @@ public class Upgrade2214to30 extends Upgrade30xBase implements DbUpgrade { pstmt.close(); } catch (SQLException e) { } - closePstmts(pstmt2Close); + TransactionLegacy.closePstmts(pstmt2Close); } } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java index a331b132abe..a933d03c071 100755 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade410to420.java @@ -744,7 +744,7 @@ public class Upgrade410to420 implements DbUpgrade { pstmt.close(); } else { if (hypervisorsListInUse.contains(hypervisorAndTemplateName.getKey())) { - // throw new CloudRuntimeException("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. Cannot upgrade system Vms"); + throw new CloudRuntimeException("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. Cannot upgrade system Vms"); } else { s_logger.warn("4.2.0 " + hypervisorAndTemplateName.getKey() + " SystemVm template not found. " + hypervisorAndTemplateName.getKey() + " hypervisor is not used, so not failing upgrade"); diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade421to430.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade421to430.java index 6df44ec7011..268a27d2582 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade421to430.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade421to430.java @@ -21,8 +21,11 @@ import java.io.File; import java.io.UnsupportedEncodingException; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Types; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.cloud.utils.crypt.DBEncryptionUtil; @@ -88,6 +91,39 @@ public class Upgrade421to430 implements DbUpgrade { pstmt.setString(3, desc); pstmt.executeUpdate(); } + + /** + * if encrypted, decrypt the ldap hostname and port and then update as they are not encrypted now. + */ + pstmt = conn.prepareStatement("SELECT conf.value FROM `cloud`.`configuration` conf WHERE conf.name='ldap.hostname'"); + ResultSet resultSet = pstmt.executeQuery(); + String hostname = null; + String port; + int portNumber = 0; + if (resultSet.next()) { + hostname = DBEncryptionUtil.decrypt(resultSet.getString(1)); + } + + pstmt = conn.prepareStatement("SELECT conf.value FROM `cloud`.`configuration` conf WHERE conf.name='ldap.port'"); + resultSet = pstmt.executeQuery(); + if (resultSet.next()) { + port = DBEncryptionUtil.decrypt(resultSet.getString(1)); + if (StringUtils.isNotBlank(port)) { + portNumber = Integer.valueOf(port); + } + } + + if (StringUtils.isNotBlank(hostname)) { + pstmt = conn.prepareStatement("INSERT INTO `cloud`.`ldap_configuration`(hostname, port) VALUES(?,?)"); + pstmt.setString(1, hostname); + if (portNumber != 0) { + pstmt.setInt(2, portNumber); + } else { + pstmt.setNull(2, Types.INTEGER); + } + pstmt.executeUpdate(); + } + } catch (SQLException e) { throw new CloudRuntimeException("Unable to insert ldap configuration values ", e); } catch (UnsupportedEncodingException e) { diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java index 9582033bd69..32f63e6fb26 100644 --- a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java +++ b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java @@ -18,7 +18,6 @@ package com.cloud.usage.dao; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Date; @@ -434,8 +433,8 @@ public class UsageDaoImpl extends GenericDaoBase implements Usage } else { pstmt.setNull(15, Types.BIGINT); } - pstmt.setTimestamp(16, new Timestamp(usageRecord.getStartDate().getTime())); - pstmt.setTimestamp(17, new Timestamp(usageRecord.getEndDate().getTime())); + pstmt.setString(16, DateUtil.getDateDisplayString(s_gmtTimeZone, usageRecord.getStartDate())); + pstmt.setString(17, DateUtil.getDateDisplayString(s_gmtTimeZone, usageRecord.getEndDate())); if (usageRecord.getVirtualSize() != null) { pstmt.setLong(18, usageRecord.getSize()); } else { diff --git a/engine/schema/src/com/cloud/vm/dao/DomainRouterDao.java b/engine/schema/src/com/cloud/vm/dao/DomainRouterDao.java index 02cf68d7b75..e3f75fabd7e 100755 --- a/engine/schema/src/com/cloud/vm/dao/DomainRouterDao.java +++ b/engine/schema/src/com/cloud/vm/dao/DomainRouterDao.java @@ -69,7 +69,7 @@ public interface DomainRouterDao extends GenericDao { * @param podId id of the pod. null if to get all. * @return list of DomainRouterVO */ - public List listByPodId(Long podId); + public List listRunningByPodId(Long podId); /** * list virtual machine routers by pod id. pass in null to get all @@ -94,7 +94,7 @@ public interface DomainRouterDao extends GenericDao { * @param id * @return */ - public List listByDomain(Long id); + public List listRunningByDomain(Long id); List findBy(long accountId, long dcId, Role role); @@ -147,5 +147,9 @@ public interface DomainRouterDao extends GenericDao { */ void removeRouterFromGuestNetwork(long routerId, long guestNetworkId); - List listByClusterId(Long clusterId); + List listRunningByClusterId(Long clusterId); + + List listRunningByAccountId(long accountId); + + List listRunningByDataCenter(long dcId); } diff --git a/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index 6bee6441e49..6b62f568739 100755 --- a/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -54,6 +54,7 @@ import com.cloud.vm.VirtualMachine.State; public class DomainRouterDaoImpl extends GenericDaoBase implements DomainRouterDao { protected SearchBuilder AllFieldsSearch; + protected SearchBuilder RunningSearch; protected SearchBuilder IdNetworkIdStatesSearch; protected SearchBuilder HostUpSearch; protected SearchBuilder StateNetworkTypeSearch; @@ -139,10 +140,19 @@ public class DomainRouterDaoImpl extends GenericDaoBase im OutsidePodSearch.done(); clusterSearch = createSearchBuilder(); + clusterSearch.and("state", clusterSearch.entity().getState(), Op.EQ); SearchBuilder clusterHost = _hostsDao.createSearchBuilder(); clusterHost.and("clusterId", clusterHost.entity().getClusterId(), Op.EQ); clusterSearch.join("host", clusterHost, clusterSearch.entity().getHostId(), clusterHost.entity().getId(), JoinType.INNER); clusterSearch.done(); + + RunningSearch = createSearchBuilder(); + RunningSearch.and("dc", RunningSearch.entity().getDataCenterId(), Op.EQ); + RunningSearch.and("account", RunningSearch.entity().getAccountId(), Op.EQ); + RunningSearch.and("domainId", RunningSearch.entity().getDomainId(), Op.EQ); + RunningSearch.and("state", RunningSearch.entity().getState(), Op.EQ); + RunningSearch.and("podId", RunningSearch.entity().getPodIdToDeployIn(), Op.EQ); + RunningSearch.done(); } @Override @@ -200,15 +210,17 @@ public class DomainRouterDaoImpl extends GenericDaoBase im } @Override - public List listByPodId(Long podId) { - SearchCriteria sc = AllFieldsSearch.create(); + public List listRunningByPodId(Long podId) { + SearchCriteria sc = RunningSearch.create(); + sc.setParameters("state", State.Running); sc.setParameters("podId", podId); return listBy(sc); } @Override - public List listByClusterId(Long clusterId) { + public List listRunningByClusterId(Long clusterId) { SearchCriteria sc = clusterSearch.create(); + sc.setParameters("state", State.Running); sc.setJoinParameters("host", "clusterId", clusterId); return listBy(sc); } @@ -237,8 +249,9 @@ public class DomainRouterDaoImpl extends GenericDaoBase im } @Override - public List listByDomain(Long domainId) { - SearchCriteria sc = AllFieldsSearch.create(); + public List listRunningByDomain(Long domainId) { + SearchCriteria sc = RunningSearch.create(); + sc.setParameters("state", State.Running); sc.setParameters("domainId", domainId); return listBy(sc); } @@ -379,4 +392,19 @@ public class DomainRouterDaoImpl extends GenericDaoBase im return listBy(sc); } + @Override + public List listRunningByAccountId(long accountId) { + SearchCriteria sc = RunningSearch.create(); + sc.setParameters("state", State.Running); + sc.setParameters("account", accountId); + return listBy(sc); + } + + @Override + public List listRunningByDataCenter(long dcId) { + SearchCriteria sc = RunningSearch.create(); + sc.setParameters("state", State.Running); + sc.setParameters("dc", dcId); + return listBy(sc); + } } diff --git a/engine/schema/src/com/cloud/vm/dao/NicDao.java b/engine/schema/src/com/cloud/vm/dao/NicDao.java index fc934750bf3..a7ad01692cf 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicDao.java +++ b/engine/schema/src/com/cloud/vm/dao/NicDao.java @@ -61,7 +61,7 @@ public interface NicDao extends GenericDao { NicVO findByIp4AddressAndNetworkIdAndInstanceId(long networkId, long instanceId, String ip4Address); - List listByVmIdAndNicId(Long vmId, Long nicId); + List listByVmIdAndNicIdAndNtwkId(long vmId, Long nicId, Long networkId); NicVO findByIp4AddressAndVmId(String ip4Address, long instance); diff --git a/engine/schema/src/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/NicDaoImpl.java index 2ac6a8090ef..c345bb290cf 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/NicDaoImpl.java @@ -244,10 +244,17 @@ public class NicDaoImpl extends GenericDaoBase implements NicDao { } @Override - public List listByVmIdAndNicId(Long vmId, Long nicId) { + public List listByVmIdAndNicIdAndNtwkId(long vmId, Long nicId, Long networkId) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("instance", vmId); - sc.setParameters("nicid", nicId); + + if (nicId != null) { + sc.setParameters("nicid", nicId); + } + + if (networkId != null) { + sc.setParameters("network", networkId); + } return listBy(sc); } diff --git a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java index 78b779f6a02..142de8a6935 100644 --- a/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java +++ b/engine/schema/src/com/cloud/vm/dao/NicSecondaryIpVO.java @@ -33,7 +33,7 @@ import com.cloud.vm.NicSecondaryIp; @Table(name = "nic_secondary_ips") public class NicSecondaryIpVO implements NicSecondaryIp { - public NicSecondaryIpVO(Long nicId, String ipaddr, Long vmId, Long accountId, Long domainId, Long networkId) { + public NicSecondaryIpVO(long nicId, String ipaddr, long vmId, long accountId, long domainId, long networkId) { this.nicId = nicId; this.vmId = vmId; this.ip4Address = ipaddr; @@ -57,7 +57,7 @@ public class NicSecondaryIpVO implements NicSecondaryIp { long domainId; @Column(name = "account_id", updatable = false) - private Long accountId; + private long accountId; @Column(name = "ip4_address") String ip4Address; @@ -75,93 +75,53 @@ public class NicSecondaryIpVO implements NicSecondaryIp { String uuid = UUID.randomUUID().toString(); @Column(name = "vmId") - Long vmId; + long vmId; @Override public long getId() { return id; } - public void setId(long id) { - this.id = id; - } - @Override public long getNicId() { return nicId; } - public void setNicId(long nicId) { - this.nicId = nicId; - } - @Override public long getDomainId() { return domainId; } - public void setDomainId(Long domainId) { - this.domainId = domainId; - } - @Override public long getAccountId() { return accountId; } - public void setAccountId(Long accountId) { - this.accountId = accountId; - } - @Override public String getIp4Address() { return ip4Address; } - public void setIp4Address(String ip4Address) { - this.ip4Address = ip4Address; - } - public String getIp6Address() { return ip6Address; } - public void setIp6Address(String ip6Address) { - this.ip6Address = ip6Address; - } - @Override public long getNetworkId() { return networkId; } - public void setNetworkId(long networkId) { - this.networkId = networkId; - } - public Date getCreated() { return created; } - public void setCreated(Date created) { - this.created = created; - } - @Override public String getUuid() { return uuid; } - public void setUuid(String uuid) { - this.uuid = uuid; - } - @Override public long getVmId() { return vmId; } - - public void setVmId(Long vmId) { - this.vmId = vmId; - } } diff --git a/engine/schema/src/com/cloud/vm/dao/UserVmDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/UserVmDaoImpl.java index 5ea4798cf3d..812e628e1b2 100755 --- a/engine/schema/src/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/UserVmDaoImpl.java @@ -175,6 +175,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountByAccount.and("type", CountByAccount.entity().getType(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); + CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ); CountByAccount.done(); SearchBuilder nicSearch = _nicDao.createSearchBuilder(); @@ -560,6 +561,7 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use sc.setParameters("account", accountId); sc.setParameters("type", VirtualMachine.Type.User); sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + sc.setParameters("displayVm", 1); return customSearch(sc, null).get(0); } diff --git a/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java index a4a0cc9877c..19d608bbdda 100644 --- a/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java +++ b/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java @@ -145,12 +145,6 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject details; @@ -161,9 +155,6 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject computeTags; @@ -491,14 +482,6 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject getComputeTags() { return computeTags; } diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java new file mode 100644 index 00000000000..bdee3c14db0 --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/DiskOfferingDetailVO.java @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resourcedetail; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "disk_offering_details") +public class DiskOfferingDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "offering_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 1024) + private String value; + + @Column(name = "display") + private boolean display; + + public DiskOfferingDetailVO() { + } + + public DiskOfferingDetailVO(long id, String name, String value) { + this.resourceId = id; + this.name = name; + this.value = value; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public boolean isDisplay() { + return display; + } +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/UserDetailVO.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/UserDetailVO.java new file mode 100644 index 00000000000..69f44739a5e --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/UserDetailVO.java @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resourcedetail; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.ResourceDetail; + +@Entity +@Table(name = "user_details") +public class UserDetailVO implements ResourceDetail { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "user_id") + private long resourceId; + + @Column(name = "name") + private String name; + + @Column(name = "value", length = 1024) + private String value; + + @Column(name = "display") + private boolean display; + + public UserDetailVO() { + } + + public UserDetailVO(long id, String name, String value) { + this.resourceId = id; + this.name = name; + this.value = value; + } + + @Override + public long getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public boolean isDisplay() { + return display; + } +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java new file mode 100644 index 00000000000..68a8614c93b --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.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 org.apache.cloudstack.resourcedetail.dao; + +import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface DiskOfferingDetailsDao extends GenericDao, ResourceDetailsDao { + +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java new file mode 100644 index 00000000000..e2fc26e722a --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resourcedetail.dao; + +import javax.ejb.Local; + +import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.springframework.stereotype.Component; + +@Component +@Local(value = {DiskOfferingDetailsDao.class}) +public class DiskOfferingDetailsDaoImpl extends ResourceDetailsDaoBase implements DiskOfferingDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value) { + super.addDetail(new DiskOfferingDetailVO(resourceId, key, value)); + } +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDao.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDao.java new file mode 100644 index 00000000000..2ef3fea503b --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDao.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 org.apache.cloudstack.resourcedetail.dao; + +import org.apache.cloudstack.resourcedetail.UserDetailVO; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + +import com.cloud.utils.db.GenericDao; + +public interface UserDetailsDao extends GenericDao, ResourceDetailsDao { + +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDaoImpl.java b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDaoImpl.java new file mode 100644 index 00000000000..508185297a8 --- /dev/null +++ b/engine/schema/src/org/apache/cloudstack/resourcedetail/dao/UserDetailsDaoImpl.java @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resourcedetail.dao; + +import javax.ejb.Local; + +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; +import org.apache.cloudstack.resourcedetail.UserDetailVO; +import org.springframework.stereotype.Component; + +@Component +@Local(value = {UserDetailsDao.class}) +public class UserDetailsDaoImpl extends ResourceDetailsDaoBase implements UserDetailsDao { + + @Override + public void addDetail(long resourceId, String key, String value) { + super.addDetail(new UserDetailVO(resourceId, key, value)); + } +} \ No newline at end of file diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java index 67ce558ee72..e24c0351e46 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java @@ -49,7 +49,11 @@ public interface SnapshotDataStoreDao extends GenericDao listOnCache(long snapshotId); void updateStoreRoleToCache(long storeId); + + SnapshotDataStoreVO findLatestSnapshotForVolume(Long volumeId, DataStoreRole role); } diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java index e292a20e6d3..ab458895e46 100644 --- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java +++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java @@ -59,6 +59,8 @@ public interface TemplateDataStoreDao extends GenericDao listByTemplate(long templateId); diff --git a/engine/service/src/main/webapp/WEB-INF/beans.xml b/engine/service/src/main/webapp/WEB-INF/beans.xml index 1be2c223e20..33bd4adb13b 100644 --- a/engine/service/src/main/webapp/WEB-INF/beans.xml +++ b/engine/service/src/main/webapp/WEB-INF/beans.xml @@ -42,7 +42,7 @@ - + @@ -51,7 +51,7 @@ - + @@ -61,7 +61,7 @@ - + diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 5c1ff40d32e..fa1dce45719 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -18,6 +18,7 @@ */ package org.apache.cloudstack.storage.motion; +import java.util.HashMap; import java.util.Map; import javax.inject.Inject; @@ -37,6 +38,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.StorageAction; import org.apache.cloudstack.engine.subsystem.api.storage.StorageCacheManager; import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; @@ -191,9 +193,9 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } } - protected DataObject cacheSnapshotChain(SnapshotInfo snapshot) { + protected DataObject cacheSnapshotChain(SnapshotInfo snapshot, Scope scope) { DataObject leafData = null; - DataStore store = cacheMgr.getCacheStorage(snapshot.getDataStore().getScope()); + DataStore store = cacheMgr.getCacheStorage(scope); while (snapshot != null) { DataObject cacheData = cacheMgr.createCacheObject(snapshot, store); if (leafData == null) { @@ -204,6 +206,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return leafData; } + protected void deleteSnapshotCacheChain(SnapshotInfo snapshot) { while (snapshot != null) { cacheMgr.deleteCacheObject(snapshot); @@ -228,7 +231,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { DataObject srcData = snapObj; try { if (!(storTO instanceof NfsTO)) { - srcData = cacheSnapshotChain(snapshot); + // cache snapshot to zone-wide staging store for the volume to be created + srcData = cacheSnapshotChain(snapshot, new ZoneScope(pool.getDataCenterId())); } String value = configDao.getValue(Config.CreateVolumeFromSnapshotWait.toString()); @@ -360,7 +364,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { protected Answer migrateVolumeToPool(DataObject srcData, DataObject destData) { VolumeInfo volume = (VolumeInfo)srcData; StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary); - MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool); + MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool, volume.getAttachedVmName()); EndPoint ep = selector.select(volume.getDataStore()); Answer answer = null; if (ep == null) { @@ -437,8 +441,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { boolean needCache = false; if (needCacheStorage(srcData, destData)) { needCache = true; - SnapshotInfo snapshot = (SnapshotInfo)srcData; - srcData = cacheSnapshotChain(snapshot); + SnapshotInfo snapshot = (SnapshotInfo) srcData; + srcData = cacheSnapshotChain(snapshot, snapshot.getDataStore().getScope()); } EndPoint ep = null; @@ -470,6 +474,14 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { int _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue())); DataObject cacheData = null; + SnapshotInfo snapshotInfo = (SnapshotInfo)srcData; + Object payload = snapshotInfo.getPayload(); + Boolean fullSnapshot = true; + if (payload != null) { + fullSnapshot = (Boolean)payload; + } + Map options = new HashMap(); + options.put("fullSnapshot", fullSnapshot.toString()); Answer answer = null; try { if (needCacheStorage(srcData, destData)) { @@ -478,6 +490,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value()); cmd.setCacheTO(cacheData.getTO()); + cmd.setOptions(options); EndPoint ep = selector.select(srcData, destData); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; @@ -488,7 +501,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } } else { CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait, VirtualMachineManager.ExecuteInSequence.value()); - EndPoint ep = selector.select(srcData, destData); + cmd.setOptions(options); + EndPoint ep = selector.select(srcData, destData, StorageAction.BACKUPSNAPSHOT); if (ep == null) { String errMsg = "No remote endpoint to send command, check if host or ssvm is down?"; s_logger.error(errMsg); @@ -496,12 +510,11 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } else { answer = ep.sendMessage(cmd); } + } - // clean up cache entry in case of failure - if (answer == null || !answer.getResult()) { - if (cacheData != null) { - cacheMgr.deleteCacheObject(cacheData); - } + // clean up cache entry + if (cacheData != null) { + cacheMgr.deleteCacheObject(cacheData); } return answer; } catch (Exception e) { diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index 9e030423a0c..2c829141a1e 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -75,7 +75,6 @@ import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.ResourceAllocationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; -import com.cloud.storage.ScopeType; import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateStorageResourceAssoc; @@ -394,7 +393,7 @@ public class TemplateServiceImpl implements TemplateService { s_logger.info("Template Sync did not find " + uniqueName + " on image store " + storeId + ", may request download based on available hypervisor types"); if (tmpltStore != null) { - if (isRegionStore(store) && tmpltStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED + if (_storeMgr.isRegionStore(store) && tmpltStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED && tmpltStore.getState() == State.Ready && tmpltStore.getInstallPath() == null) { s_logger.info("Keep fake entry in template store table for migration of previous NFS to object store"); @@ -435,7 +434,7 @@ public class TemplateServiceImpl implements TemplateService { // if this is a region store, and there is already an DOWNLOADED entry there without install_path information, which // means that this is a duplicate entry from migration of previous NFS to staging. - if (isRegionStore(store)) { + if (_storeMgr.isRegionStore(store)) { TemplateDataStoreVO tmpltStore = _vmTemplateStoreDao.findByStoreTemplate(storeId, tmplt.getId()); if (tmpltStore != null && tmpltStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED && tmpltStore.getState() == State.Ready && tmpltStore.getInstallPath() == null) { @@ -685,18 +684,14 @@ public class TemplateServiceImpl implements TemplateService { return null; } - private boolean isRegionStore(DataStore store) { - if (store.getScope().getScopeType() == ScopeType.ZONE && store.getScope().getScopeId() == null) - return true; - else - return false; - } - // This routine is used to push templates currently on cache store, but not in region store to region store. // used in migrating existing NFS secondary storage to S3. @Override public void syncTemplateToRegionStore(long templateId, DataStore store) { - if (isRegionStore(store)) { + if (_storeMgr.isRegionStore(store)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Sync template " + templateId + " from cache to object store..."); + } // if template is on region wide object store, check if it is really downloaded there (by checking install_path). Sync template to region // wide store if it is not there physically. TemplateInfo tmplOnStore = _templateFactory.getTemplate(templateId, store); diff --git a/engine/storage/integration-test/pom.xml b/engine/storage/integration-test/pom.xml index 7de60fcfb7b..e002ab3345c 100644 --- a/engine/storage/integration-test/pom.xml +++ b/engine/storage/integration-test/pom.xml @@ -185,38 +185,5 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-antrun-plugin - - [1.7,) - - run - - - - - - - - - - - - diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java index 7fb9e81b593..d277991cbe6 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java @@ -18,6 +18,7 @@ */ package org.apache.cloudstack.storage.test; +import java.util.Map; import java.util.UUID; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; @@ -35,13 +36,27 @@ import org.apache.cloudstack.storage.to.SnapshotObjectTO; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; +import com.cloud.host.Host; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { boolean snapshotResult = true; @Override public ChapInfo getChapInfo(VolumeInfo volumeInfo) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; // To change body of implemented methods, use File | Settings | File Templates. + } + + @Override + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { return false; } + + @Override + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) {} + + @Override + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + return volume.getSize(); } @Override @@ -106,4 +121,10 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { public void resize(DataObject data, AsyncCompletionCallback callback) { //To change body of implemented methods use File | Settings | File Templates. } + + @Override + public Map getCapabilities() { + // TODO Auto-generated method stub + return null; + } } diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java index f1f9383bfce..cac6ea2bbd7 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTest.java @@ -277,8 +277,7 @@ public class SnapshotTest extends CloudStackTestNGBase { List hosts = new ArrayList(); hosts.add(this.host); Mockito.when(resourceMgr.listAllUpAndEnabledHosts((Type)Matchers.any(), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyLong())).thenReturn(hosts); - - remoteEp = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host.getId(), this.host.getPrivateIpAddress(), this.host.getPublicIpAddress()); + remoteEp = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host); Mockito.when(epSelector.select(Matchers.any(DataObject.class), Matchers.any(DataObject.class))).thenReturn(remoteEp); Mockito.when(epSelector.select(Matchers.any(DataObject.class))).thenReturn(remoteEp); Mockito.when(epSelector.select(Matchers.any(DataStore.class))).thenReturn(remoteEp); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeServiceTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeServiceTest.java index 2f5b18b861e..5fe1d395330 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeServiceTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeServiceTest.java @@ -195,7 +195,7 @@ public class VolumeServiceTest extends CloudStackTestNGBase { Mockito.when(hostDao.findById(Matchers.anyLong())).thenReturn(host); Mockito.when(hostDao.findHypervisorHostInCluster(Matchers.anyLong())).thenReturn(results); List eps = new ArrayList(); - eps.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress(), host.getPublicIpAddress())); + eps.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host)); Mockito.when(selector.selectAll(Matchers.any(DataStore.class))).thenReturn(eps); Mockito.when(selector.select(Matchers.any(DataObject.class))).thenReturn(eps.get(0)); Mockito.when(selector.select(Matchers.any(DataObject.class), Matchers.any(DataObject.class))).thenReturn(eps.get(0)); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java index c75b2e75c80..4a6151d55c9 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTest.java @@ -254,7 +254,7 @@ public class VolumeTest extends CloudStackTestNGBase { hosts.add(this.host); Mockito.when(resourceMgr.listAllUpAndEnabledHosts((Type)Matchers.any(), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyLong())).thenReturn(hosts); - RemoteHostEndPoint ep = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host.getId(), this.host.getPrivateIpAddress(), this.host.getPublicIpAddress()); + RemoteHostEndPoint ep = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host); Mockito.when(epSelector.select(Matchers.any(DataObject.class), Matchers.any(DataObject.class))).thenReturn(ep); Mockito.when(epSelector.select(Matchers.any(DataObject.class))).thenReturn(ep); Mockito.when(epSelector.select(Matchers.any(DataStore.class))).thenReturn(ep); diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java index 800bc6b05de..1f3aff04f33 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/VolumeTestVmware.java @@ -256,7 +256,7 @@ public class VolumeTestVmware extends CloudStackTestNGBase { hosts.add(this.host); Mockito.when(resourceMgr.listAllUpAndEnabledHosts((Type)Matchers.any(), Matchers.anyLong(), Matchers.anyLong(), Matchers.anyLong())).thenReturn(hosts); - RemoteHostEndPoint ep = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host.getId(), this.host.getPrivateIpAddress(), this.host.getPublicIpAddress()); + RemoteHostEndPoint ep = RemoteHostEndPoint.getHypervisorHostEndPoint(this.host); Mockito.when(epSelector.select(Matchers.any(DataObject.class), Matchers.any(DataObject.class))).thenReturn(ep); Mockito.when(epSelector.select(Matchers.any(DataObject.class))).thenReturn(ep); Mockito.when(epSelector.select(Matchers.any(DataStore.class))).thenReturn(ep); diff --git a/engine/storage/integration-test/test/resources/StorageAllocatorTestContext.xml b/engine/storage/integration-test/test/resources/StorageAllocatorTestContext.xml index 72e5c84aff0..a981b8e9b4e 100644 --- a/engine/storage/integration-test/test/resources/StorageAllocatorTestContext.xml +++ b/engine/storage/integration-test/test/resources/StorageAllocatorTestContext.xml @@ -1,19 +1,19 @@ - context = new CopySnapshotContext(null, snapshot, snapshotOnImageStore, future); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().copySnapshotAsyncCallback(null, null)).setContext(context); - this.motionSrv.copyAsync(snapshot, snapshotOnImageStore, caller); + motionSrv.copyAsync(snapshot, snapshotOnImageStore, caller); } catch (Exception e) { s_logger.debug("Failed to copy snapshot", e); result.setResult("Failed to copy snapshot:" + e.toString()); @@ -301,7 +315,7 @@ public class SnapshotServiceImpl implements SnapshotService { CopyCmdAnswer answer = (CopyCmdAnswer)result.getAnswer(); destSnapshot.processEvent(Event.OperationSuccessed, result.getAnswer()); srcSnapshot.processEvent(Snapshot.Event.OperationSucceeded); - snapResult = new SnapshotResult(this.snapshotfactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer); + snapResult = new SnapshotResult(_snapshotFactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer); future.complete(snapResult); } catch (Exception e) { s_logger.debug("Failed to update snapshot state", e); @@ -386,7 +400,7 @@ public class SnapshotServiceImpl implements SnapshotService { @Override public boolean revertSnapshot(Long snapshotId) { - SnapshotInfo snapshot = snapshotfactory.getSnapshot(snapshotId, DataStoreRole.Primary); + SnapshotInfo snapshot = _snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Primary); PrimaryDataStore store = (PrimaryDataStore)snapshot.getDataStore(); AsyncCallFuture future = new AsyncCallFuture(); @@ -412,4 +426,92 @@ public class SnapshotServiceImpl implements SnapshotService { return false; } + // This routine is used to push snapshots currently on cache store, but not in region store to region store. + // used in migrating existing NFS secondary storage to S3. We chose to push all volume related snapshots to handle delta snapshots smoothly. + @Override + public void syncVolumeSnapshotsToRegionStore(long volumeId, DataStore store) { + if (dataStoreMgr.isRegionStore(store)) { + // list all backed up snapshots for the given volume + List snapshots = _snapshotDao.listByStatus(volumeId, Snapshot.State.BackedUp); + if (snapshots != null) { + for (SnapshotVO snapshot : snapshots) { + syncSnapshotToRegionStore(snapshot.getId(), store); + } + } + } + } + + // push one individual snapshots currently on cache store to region store if it is not there already + private void syncSnapshotToRegionStore(long snapshotId, DataStore store) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("sync snapshot " + snapshotId + " from cache to object store..."); + } + // if snapshot is already on region wide object store, check if it is really downloaded there (by checking install_path). Sync snapshot to region + // wide store if it is not there physically. + SnapshotInfo snapOnStore = _snapshotFactory.getSnapshot(snapshotId, store); + if (snapOnStore == null) { + throw new CloudRuntimeException("Cannot find an entry in snapshot_store_ref for snapshot " + snapshotId + " on region store: " + store.getName()); + } + if (snapOnStore.getPath() == null || snapOnStore.getPath().length() == 0) { + // snapshot is not on region store yet, sync to region store + SnapshotInfo srcSnapshot = _snapshotFactory.getReadySnapshotOnCache(snapshotId); + if (srcSnapshot == null) { + throw new CloudRuntimeException("Cannot find snapshot " + snapshotId + " on cache store"); + } + AsyncCallFuture future = syncToRegionStoreAsync(srcSnapshot, store); + try { + SnapshotResult result = future.get(); + if (result.isFailed()) { + throw new CloudRuntimeException("sync snapshot from cache to region wide store failed for image store " + store.getName() + ":" + + result.getResult()); + } + _cacheMgr.releaseCacheObject(srcSnapshot); // reduce reference count for template on cache, so it can recycled by schedule + } catch (Exception ex) { + throw new CloudRuntimeException("sync snapshot from cache to region wide store failed for image store " + store.getName()); + } + } + + } + + private AsyncCallFuture syncToRegionStoreAsync(SnapshotInfo snapshot, DataStore store) { + AsyncCallFuture future = new AsyncCallFuture(); + // no need to create entry on snapshot_store_ref here, since entries are already created when updateCloudToUseObjectStore is invoked. + // But we need to set default install path so that sync can be done in the right s3 path + SnapshotInfo snapshotOnStore = _snapshotFactory.getSnapshot(snapshot, store); + String installPath = TemplateConstants.DEFAULT_SNAPSHOT_ROOT_DIR + "/" + + snapshot.getAccountId() + "/" + snapshot.getVolumeId(); + ((SnapshotObject)snapshotOnStore).setPath(installPath); + CopySnapshotContext context = new CopySnapshotContext(null, snapshot, + snapshotOnStore, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher + .create(this); + caller.setCallback(caller.getTarget().syncSnapshotCallBack(null, null)).setContext(context); + motionSrv.copyAsync(snapshot, snapshotOnStore, caller); + return future; + } + + protected Void syncSnapshotCallBack(AsyncCallbackDispatcher callback, + CopySnapshotContext context) { + CopyCommandResult result = callback.getResult(); + SnapshotInfo destSnapshot = context.destSnapshot; + SnapshotResult res = new SnapshotResult(destSnapshot, null); + + AsyncCallFuture future = context.future; + try { + if (result.isFailed()) { + res.setResult(result.getResult()); + // no change to existing snapshot_store_ref, will try to re-sync later if other call triggers this sync operation + } else { + // this will update install path properly, next time it will not sync anymore. + destSnapshot.processEvent(Event.OperationSuccessed, result.getAnswer()); + } + future.complete(res); + } catch (Exception e) { + s_logger.debug("Failed to process sync snapshot callback", e); + res.setResult(e.toString()); + future.complete(res); + } + + return null; + } } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 14fb6180861..ad7e0f3e3d8 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -22,7 +22,6 @@ import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; - 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.ObjectInDataStoreStateMachine.Event; @@ -40,6 +39,8 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.CreateSnapshotPayload; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; @@ -104,29 +105,31 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { // determine full snapshot backup or not - boolean fullBackup = false; - if (parentSnapshot != null) { - int _deltaSnapshotMax = NumbersUtil.parseInt(configDao.getValue("snapshot.delta.max"), SnapshotManager.DELTAMAX); + boolean fullBackup = true; + SnapshotDataStoreVO parentSnapshotOnBackupStore = snapshotStoreDao.findLatestSnapshotForVolume(snapshot.getVolumeId(), DataStoreRole.Image); + HypervisorType hypervisorType = snapshot.getBaseVolume().getHypervisorType(); + if (parentSnapshotOnBackupStore != null && hypervisorType == Hypervisor.HypervisorType.XenServer) { // CS does incremental backup only for XenServer + int _deltaSnapshotMax = NumbersUtil.parseInt(configDao.getValue("snapshot.delta.max"), + SnapshotManager.DELTAMAX); int deltaSnap = _deltaSnapshotMax; - int i; - SnapshotDataStoreVO parentSnapshotOnBackupStore = null; - for (i = 1; i < deltaSnap; i++) { - parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(parentSnapshot.getId(), DataStoreRole.Image); - if (parentSnapshotOnBackupStore == null) { - break; - } - Long prevBackupId = parentSnapshotOnBackupStore.getParentSnapshotId(); + for (i = 1; i < deltaSnap; i++) { + Long prevBackupId = parentSnapshotOnBackupStore.getParentSnapshotId(); if (prevBackupId == 0) { break; } - parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image); + if (parentSnapshotOnBackupStore == null) { + break; + } } + if (i >= deltaSnap) { fullBackup = true; + } else { + fullBackup = false; } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/RemoteHostEndPoint.java b/engine/storage/src/org/apache/cloudstack/storage/RemoteHostEndPoint.java index 98c9db17fff..34f9c9d9068 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/RemoteHostEndPoint.java +++ b/engine/storage/src/org/apache/cloudstack/storage/RemoteHostEndPoint.java @@ -25,7 +25,8 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.apache.log4j.Logger; - +import com.cloud.vm.SecondaryStorageVmVO; +import com.cloud.vm.dao.SecondaryStorageVmDao; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.managed.context.ManagedContextRunnable; @@ -57,21 +58,30 @@ public class RemoteHostEndPoint implements EndPoint { AgentManager agentMgr; @Inject protected HypervisorGuruManager _hvGuruMgr; + @Inject + protected SecondaryStorageVmDao vmDao; private ScheduledExecutorService executor; public RemoteHostEndPoint() { executor = Executors.newScheduledThreadPool(10, new NamedThreadFactory("RemoteHostEndPoint")); } - private void configure(long hostId, String hostAddress, String publicAddress) { - this.hostId = hostId; - this.hostAddress = hostAddress; - this.publicAddress = publicAddress; + private void configure(Host host) { + this.hostId = host.getId(); + this.hostAddress = host.getPrivateIpAddress(); + this.publicAddress = host.getPublicIpAddress(); + if (Host.Type.SecondaryStorageVM == host.getType()) { + String vmName = host.getName(); + SecondaryStorageVmVO ssvm = vmDao.findByInstanceName(vmName); + if (ssvm != null) { + this.publicAddress = ssvm.getPublicIpAddress(); + } + } } - public static RemoteHostEndPoint getHypervisorHostEndPoint(long hostId, String hostAddress, String publicAddress) { + public static RemoteHostEndPoint getHypervisorHostEndPoint(Host host) { RemoteHostEndPoint ep = ComponentContext.inject(RemoteHostEndPoint.class); - ep.configure(hostId, hostAddress, publicAddress); + ep.configure(host); return ep; } diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java index 35737ede5e1..af228107811 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/ClusterScopeStoragePoolAllocator.java @@ -77,14 +77,22 @@ public class ClusterScopeStoragePoolAllocator extends AbstractStoragePoolAllocat } List pools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, dskCh.getTags()); + s_logger.debug("Found pools matching tags: " + pools); // add remaining pools in cluster, that did not match tags, to avoid set List allPools = _storagePoolDao.findPoolsByTags(dcId, podId, clusterId, null); allPools.removeAll(pools); for (StoragePoolVO pool : allPools) { + s_logger.debug("Adding pool " + pool + " to avoid set since it did not match tags"); avoid.addPool(pool.getId()); } + // make sure our matching pool was not in avoid set + for (StoragePoolVO pool : pools) { + s_logger.debug("Removing pool " + pool + " from avoid set, must have been inserted when searching for another disk's tag"); + avoid.removePool(pool.getId()); + } + if (pools.size() == 0) { if (s_logger.isDebugEnabled()) { s_logger.debug("No storage pools available for " + ServiceOffering.StorageType.shared.toString() + " volume allocation, returning"); diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java index 57253a787a3..8fb9c8d87ac 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/ZoneWideStoragePoolAllocator.java @@ -89,6 +89,12 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator { avoid.addPool(pool.getId()); } + // make sure our matching pool was not in avoid set + for (StoragePoolVO pool : storagePoolsByHypervisor) { + s_logger.debug("Removing pool " + pool + " from avoid set, must have been inserted when searching for another disk's tag"); + avoid.removePool(pool.getId()); + } + for (StoragePoolVO storage : storagePools) { if (suitablePools.size() == returnUpTo) { break; diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java index e6b27152048..d44fb42045c 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java @@ -32,6 +32,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.storage.image.datastore.ImageStoreProviderManager; import com.cloud.storage.DataStoreRole; +import com.cloud.storage.ScopeType; import com.cloud.utils.exception.CloudRuntimeException; @Component @@ -82,6 +83,14 @@ public class DataStoreManagerImpl implements DataStoreManager { return stores.get(0); } + @Override + public boolean isRegionStore(DataStore store) { + if (store.getScope().getScopeType() == ScopeType.ZONE && store.getScope().getScopeId() == null) + return true; + else + return false; + } + @Override public DataStore getPrimaryDataStore(long storeId) { return primaryStoreMgr.getPrimaryDataStore(storeId); diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 2eafacc2ae6..82164ee06d8 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -115,6 +115,7 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { ss.setDataStoreId(dataStore.getId()); ss.setRole(dataStore.getRole()); ss.setVolumeId(snapshotInfo.getVolumeId()); + ss.setSize(snapshotInfo.getSize()); // this is the virtual size of snapshot in primary storage. SnapshotDataStoreVO snapshotDataStoreVO = snapshotDataStoreDao.findParent(dataStore.getRole(), dataStore.getId(), snapshotInfo.getVolumeId()); if (snapshotDataStoreVO != null) { ss.setParentSnapshotId(snapshotDataStoreVO.getSnapshotId()); diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java index df1c9ee0c67..51c04bf2fc2 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java +++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java @@ -35,7 +35,10 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.StorageAction; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.LocalHostEndpoint; import org.apache.cloudstack.storage.RemoteHostEndPoint; @@ -43,6 +46,7 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage.TemplateType; @@ -51,6 +55,7 @@ import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; @Component public class DefaultEndPointSelector implements EndPointSelector { @@ -60,6 +65,7 @@ public class DefaultEndPointSelector implements EndPointSelector { private final String findOneHostOnPrimaryStorage = "select h.id from host h, storage_pool_host_ref s where h.status = 'Up' and h.type = 'Routing' and h.resource_state = 'Enabled' and" + " h.id = s.host_id and s.pool_id = ? "; + private String findOneHypervisorHostInScope = "select h.id from host h where h.status = 'Up' and h.hypervisor_type is not null "; protected boolean moveBetweenPrimaryImage(DataStore srcStore, DataStore destStore) { DataStoreRole srcRole = srcStore.getRole(); @@ -140,7 +146,7 @@ public class DefaultEndPointSelector implements EndPointSelector { return null; } - return RemoteHostEndPoint.getHypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress(), host.getPublicIpAddress()); + return RemoteHostEndPoint.getHypervisorHostEndPoint(host); } protected EndPoint findEndPointForImageMove(DataStore srcStore, DataStore destStore) { @@ -204,6 +210,21 @@ public class DefaultEndPointSelector implements EndPointSelector { return null; } + @Override + public EndPoint select(DataObject srcData, DataObject destData, StorageAction action) { + if (action == StorageAction.BACKUPSNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) { + SnapshotInfo srcSnapshot = (SnapshotInfo)srcData; + if (srcSnapshot.getHypervisorType() == Hypervisor.HypervisorType.KVM) { + VolumeInfo volumeInfo = srcSnapshot.getBaseVolume(); + VirtualMachine vm = volumeInfo.getAttachedVM(); + if (vm != null && vm.getState() == VirtualMachine.State.Running) { + return getEndPointFromHostId(vm.getHostId()); + } + } + } + return select(srcData, destData); + } + protected EndPoint findEndpointForPrimaryStorage(DataStore store) { return findEndPointInScope(store.getScope(), findOneHostOnPrimaryStorage, store.getId()); } @@ -223,7 +244,7 @@ public class DefaultEndPointSelector implements EndPointSelector { } Collections.shuffle(ssAHosts); HostVO host = ssAHosts.get(0); - return RemoteHostEndPoint.getHypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress(), host.getPublicIpAddress()); + return RemoteHostEndPoint.getHypervisorHostEndPoint(host); } private List listUpAndConnectingSecondaryStorageVmHost(Long dcId) { @@ -266,6 +287,27 @@ public class DefaultEndPointSelector implements EndPointSelector { } } + private EndPoint getEndPointFromHostId(Long hostId) { + HostVO host = hostDao.findById(hostId); + return RemoteHostEndPoint.getHypervisorHostEndPoint(host); + } + + @Override + public EndPoint select(DataObject object, StorageAction action) { + if (action == StorageAction.TAKESNAPSHOT) { + SnapshotInfo snapshotInfo = (SnapshotInfo)object; + if (snapshotInfo.getHypervisorType() == Hypervisor.HypervisorType.KVM) { + VolumeInfo volumeInfo = snapshotInfo.getBaseVolume(); + VirtualMachine vm = volumeInfo.getAttachedVM(); + if ((vm != null) && (vm.getState() == VirtualMachine.State.Running)) { + Long hostId = vm.getHostId(); + return getEndPointFromHostId(hostId); + } + } + } + return select(object); + } + @Override public EndPoint select(Scope scope, Long storeId) { return findEndPointInScope(scope, findOneHostOnPrimaryStorage, storeId); @@ -276,14 +318,15 @@ public class DefaultEndPointSelector implements EndPointSelector { List endPoints = new ArrayList(); if (store.getScope().getScopeType() == ScopeType.HOST) { HostVO host = hostDao.findById(store.getScope().getScopeId()); - endPoints.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress(), host.getPublicIpAddress())); + + endPoints.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host)); } else if (store.getScope().getScopeType() == ScopeType.CLUSTER) { QueryBuilder sc = QueryBuilder.create(HostVO.class); sc.and(sc.entity().getClusterId(), Op.EQ, store.getScope().getScopeId()); sc.and(sc.entity().getStatus(), Op.EQ, Status.Up); List hosts = sc.list(); for (HostVO host : hosts) { - endPoints.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host.getId(), host.getPrivateIpAddress(), host.getPublicIpAddress())); + endPoints.add(RemoteHostEndPoint.getHypervisorHostEndPoint(host)); } } else { @@ -291,4 +334,51 @@ public class DefaultEndPointSelector implements EndPointSelector { } return endPoints; } + + @Override + public EndPoint selectHypervisorHost(Scope scope) { + StringBuilder sbuilder = new StringBuilder(); + sbuilder.append(findOneHypervisorHostInScope); + if (scope.getScopeType() == ScopeType.ZONE) { + sbuilder.append(" and h.data_center_id = "); + sbuilder.append(scope.getScopeId()); + } else if (scope.getScopeType() == ScopeType.CLUSTER) { + sbuilder.append(" and h.cluster_id = "); + sbuilder.append(scope.getScopeId()); + } + sbuilder.append(" ORDER by rand() limit 1"); + + String sql = sbuilder.toString(); + PreparedStatement pstmt = null; + ResultSet rs = null; + HostVO host = null; + TransactionLegacy txn = TransactionLegacy.currentTxn(); + + try { + pstmt = txn.prepareStatement(sql); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + host = hostDao.findById(id); + } + } catch (SQLException e) { + s_logger.warn("can't find endpoint", e); + } finally { + try { + if (rs != null) { + rs.close(); + } + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + + if (host == null) { + return null; + } + + return RemoteHostEndPoint.getHypervisorHostEndPoint(host); + } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java index 3836c2c3d49..b41c1fa14ac 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/helper/HypervisorHelperImpl.java @@ -111,7 +111,8 @@ public class HypervisorHelperImpl implements HypervisorHelper { String value = configurationDao.getValue("vmsnapshot.create.wait"); int wait = NumbersUtil.parseInt(value, 1800); Long hostId = vmSnapshotHelper.pickRunningHost(virtualMachine.getId()); - VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(1L, UUID.randomUUID().toString(), VMSnapshot.Type.DiskAndMemory, null, null, false, null, true); + VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(1L, UUID.randomUUID().toString(), VMSnapshot.Type.Disk, null, null, false, + null, true); GuestOSVO guestOS = guestOSDao.findById(virtualMachine.getGuestOSId()); List volumeTOs = vmSnapshotHelper.getVolumeTOList(virtualMachine.getId()); CreateVMSnapshotCommand ccmd = diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index e40cfd00b62..28d65981065 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -54,8 +54,13 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase snapshotSearch; private SearchBuilder storeSnapshotSearch; private SearchBuilder snapshotIdSearch; + private final String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? " + " and store_role = ? and volume_id = ? and state = 'Ready'" + " order by created DESC " + " limit 1"; + private final String findLatestSnapshot = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where " + + " store_role = ? and volume_id = ? and state = 'Ready'" + + " order by created DESC " + + " limit 1"; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -98,6 +103,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = storeSnapshotSearch.create(); + sc.setParameters("snapshot_id", snapshotId); + sc.setParameters("store_role", DataStoreRole.ImageCache); + sc.setParameters("state", ObjectInDataStoreStateMachine.State.Ready); + return findOneIncludingRemovedBy(sc); + } + @Override public List listOnCache(long snapshotId) { SearchCriteria sc = storeSnapshotSearch.create(); diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java index be0262e0a53..d4b0445be7e 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/TemplateDataStoreDaoImpl.java @@ -350,15 +350,20 @@ public class TemplateDataStoreDaoImpl extends GenericDaoBase sc = templateRoleSearch.create(); sc.setParameters("template_id", templateId); - sc.setParameters("store_role", DataStoreRole.ImageCache); + sc.setParameters("store_role", role); sc.setParameters("destroyed", false); sc.setParameters("state", ObjectInDataStoreStateMachine.State.Ready); return findOneIncludingRemovedBy(sc); } + @Override + public TemplateDataStoreVO findReadyOnCache(long templateId) { + return findReadyByTemplate(templateId, DataStoreRole.ImageCache); + } + @Override public List listOnCache(long templateId) { SearchCriteria sc = templateRoleSearch.create(); diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index 3b5d938f8a6..fd0f2c1e19d 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -22,6 +22,9 @@ import javax.inject.Inject; import org.apache.log4j.Logger; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -40,11 +43,9 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering.DiskCacheMode; import com.cloud.storage.DataStoreRole; -import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; @@ -100,6 +101,15 @@ public class VolumeObject implements VolumeInfo { return null; } + @Override + public VirtualMachine getAttachedVM() { + Long vmId = this.volumeVO.getInstanceId(); + if (vmId != null) { + VMInstanceVO vm = vmInstanceDao.findById(vmId); + return vm; + } + return null; + } @Override public String getUuid() { return volumeVO.getUuid(); @@ -143,7 +153,6 @@ public class VolumeObject implements VolumeInfo { return volumeVO.getMaxIops(); } - @Override public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { volumeVO.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index c9d7fabbaba..ac507cf4629 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -158,6 +158,24 @@ public class VolumeServiceImpl implements VolumeService { return null; } + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { + DataStoreDriver dataStoreDriver = dataStore.getDriver(); + + if (dataStoreDriver instanceof PrimaryDataStoreDriver) { + return ((PrimaryDataStoreDriver)dataStoreDriver).connectVolumeToHost(volumeInfo, host, dataStore); + } + + return false; + } + + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { + DataStoreDriver dataStoreDriver = dataStore.getDriver(); + + if (dataStoreDriver instanceof PrimaryDataStoreDriver) { + ((PrimaryDataStoreDriver)dataStoreDriver).disconnectVolumeFromHost(volumeInfo, host, dataStore); + } + } + @Override public AsyncCallFuture createVolumeAsync(VolumeInfo volume, DataStore dataStore) { AsyncCallFuture future = new AsyncCallFuture(); diff --git a/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java b/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java index c7c371090b9..ca0b031a5c2 100644 --- a/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java +++ b/framework/cluster/src/com/cloud/cluster/ClusterManagerImpl.java @@ -23,6 +23,7 @@ import java.nio.channels.SocketChannel; import java.rmi.RemoteException; import java.sql.Connection; import java.sql.SQLException; +import java.sql.SQLNonTransientException; import java.sql.SQLRecoverableException; import java.util.ArrayList; import java.util.Date; @@ -576,21 +577,15 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C } if (isRootCauseConnectionRelated(e.getCause())) { - s_logger.error("DB communication problem detected, fence it"); - queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); + invalidHeartbeatConnection(); } - - invalidHeartbeatConnection(); } catch (ActiveFencingException e) { queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); } catch (Throwable e) { s_logger.error("Unexpected exception in cluster heartbeat", e); if (isRootCauseConnectionRelated(e.getCause())) { - s_logger.error("DB communication problem detected, fence it"); - queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); + invalidHeartbeatConnection(); } - - invalidHeartbeatConnection(); } finally { txn.transitToAutoManagedConnection(TransactionLegacy.CLOUD_DB); txn.close("ClusterHeartbeat"); @@ -601,8 +596,8 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C private boolean isRootCauseConnectionRelated(Throwable e) { while (e != null) { - if (e instanceof SQLRecoverableException) { - return true; + if (e instanceof SQLRecoverableException || e instanceof SQLNonTransientException) { + return true; } e = e.getCause(); @@ -625,6 +620,9 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C Connection conn = TransactionLegacy.getStandaloneConnection(); if (conn != null) { _heartbeatConnection.reset(conn); + } else { + s_logger.error("DB communication problem detected, fence it"); + queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeIsolated)); } // The stand-alone connection does not have to be closed here because there will be another reference to it. // As a matter of fact, it will be assigned to the connection instance variable in the ConnectionConcierge class. diff --git a/framework/config/src/org/apache/cloudstack/framework/config/ConfigDepot.java b/framework/config/src/org/apache/cloudstack/framework/config/ConfigDepot.java index 8592745c7e7..7df00493d45 100644 --- a/framework/config/src/org/apache/cloudstack/framework/config/ConfigDepot.java +++ b/framework/config/src/org/apache/cloudstack/framework/config/ConfigDepot.java @@ -16,7 +16,7 @@ // under the License. package org.apache.cloudstack.framework.config; -import java.util.List; +import java.util.Set; /** * ConfigDepot is a repository of configurations. @@ -26,5 +26,5 @@ public interface ConfigDepot { ConfigKey get(String paramName); - List> getConfigListByScope(String scope); + Set> getConfigListByScope(String scope); } diff --git a/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java b/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java index 15933de60ff..2f6e524e860 100644 --- a/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java +++ b/framework/config/src/org/apache/cloudstack/framework/config/impl/ConfigDepotImpl.java @@ -77,14 +77,14 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin { HashMap>> _allKeys = new HashMap>>(1007); - HashMap>> _scopeLevelConfigsMap = new HashMap>>(); + HashMap>> _scopeLevelConfigsMap = new HashMap>>(); public ConfigDepotImpl() { ConfigKey.init(this); - _scopeLevelConfigsMap.put(ConfigKey.Scope.Zone, new ArrayList>()); - _scopeLevelConfigsMap.put(ConfigKey.Scope.Cluster, new ArrayList>()); - _scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new ArrayList>()); - _scopeLevelConfigsMap.put(ConfigKey.Scope.Account, new ArrayList>()); + _scopeLevelConfigsMap.put(ConfigKey.Scope.Zone, new HashSet>()); + _scopeLevelConfigsMap.put(ConfigKey.Scope.Cluster, new HashSet>()); + _scopeLevelConfigsMap.put(ConfigKey.Scope.StoragePool, new HashSet>()); + _scopeLevelConfigsMap.put(ConfigKey.Scope.Account, new HashSet>()); } @Override @@ -123,16 +123,18 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin { _configDao.persist(vo); } else { if (vo.isDynamic() != key.isDynamic() || !ObjectUtils.equals(vo.getDescription(), key.description()) || - !ObjectUtils.equals(vo.getDefaultValue(), key.defaultValue())) { + !ObjectUtils.equals(vo.getDefaultValue(), key.defaultValue()) || + !ObjectUtils.equals(vo.getScope(), key.scope().toString())) { vo.setDynamic(key.isDynamic()); vo.setDescription(key.description()); vo.setDefaultValue(key.defaultValue()); + vo.setScope(key.scope().toString()); vo.setUpdated(date); _configDao.persist(vo); } } - if (key.scope() != ConfigKey.Scope.Global) { - List> currentConfigs = _scopeLevelConfigsMap.get(key.scope()); + if ((key.scope() != null) && (key.scope() != ConfigKey.Scope.Global)) { + Set> currentConfigs = _scopeLevelConfigsMap.get(key.scope()); currentConfigs.add(key); } } @@ -183,7 +185,7 @@ public class ConfigDepotImpl implements ConfigDepot, ConfigDepotAdmin { } @Override - public List> getConfigListByScope(String scope) { + public Set> getConfigListByScope(String scope) { return _scopeLevelConfigsMap.get(ConfigKey.Scope.valueOf(scope)); } diff --git a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java index b36c02746a6..ac0ea21d1f1 100755 --- a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java +++ b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java @@ -769,7 +769,7 @@ public class TransactionLegacy { try { // we should only close db connection when it is not user managed - if (this._dbId != CONNECTED_DB) { + if (_dbId != CONNECTED_DB) { if (s_connLogger.isTraceEnabled()) { s_connLogger.trace("Closing DB connection: dbconn" + System.identityHashCode(_conn)); } @@ -1212,6 +1212,26 @@ public class TransactionLegacy { * @param conn */ protected void setConnection(Connection conn) { - this._conn = conn; + _conn = conn; } + + /** + * Receives a list of {@link PreparedStatement} and quietly closes all of them, which + * triggers also closing their dependent objects, like a {@link ResultSet} + * + * @param pstmt2Close + */ + public static void closePstmts(List pstmt2Close) { + for (PreparedStatement pstmt : pstmt2Close) { + try { + if (pstmt != null && !pstmt.isClosed()) { + pstmt.close(); + } + } catch (SQLException e) { + // It's not possible to recover from this and we need to continue closing + e.printStackTrace(); + } + } + } + } diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobExecutionContext.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobExecutionContext.java index 31fd827caf6..f558e013712 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobExecutionContext.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobExecutionContext.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.framework.jobs; import org.apache.log4j.Logger; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.jobs.dao.AsyncJobJoinMapDao; import org.apache.cloudstack.framework.jobs.impl.AsyncJobJoinMapVO; import org.apache.cloudstack.framework.jobs.impl.JobSerializerHelper; @@ -103,17 +104,19 @@ public class AsyncJobExecutionContext { s_jobMgr.joinJob(_job.getId(), joinJobId); } - public void joinJob(long joinJobId, String wakeupHandler, String wakeupDispatcher, String[] wakeupTopcisOnMessageBus, long wakeupIntervalInMilliSeconds, - long timeoutInMilliSeconds) { + public void joinJob(long joinJobId, String wakeupHandler, String wakeupDispatcher, + String[] wakeupTopcisOnMessageBus, long wakeupIntervalInMilliSeconds, long timeoutInMilliSeconds) { assert (_job != null); - s_jobMgr.joinJob(_job.getId(), joinJobId, wakeupHandler, wakeupDispatcher, wakeupTopcisOnMessageBus, wakeupIntervalInMilliSeconds, timeoutInMilliSeconds); + s_jobMgr.joinJob(_job.getId(), joinJobId, wakeupHandler, wakeupDispatcher, wakeupTopcisOnMessageBus, + wakeupIntervalInMilliSeconds, timeoutInMilliSeconds); } // // check failure exception before we disjoin the worker job // TODO : it is ugly and this will become unnecessary after we switch to full-async mode // - public void disjoinJob(long joinedJobId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { + public void disjoinJob(long joinedJobId) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException { assert (_job != null); AsyncJobJoinMapVO record = s_joinMapDao.getJoinRecord(_job.getId(), joinedJobId); @@ -147,6 +150,10 @@ public class AsyncJobExecutionContext { public static AsyncJobExecutionContext getCurrentExecutionContext() { AsyncJobExecutionContext context = s_currentExectionContext.get(); + if (context == null) { + context = registerPseudoExecutionContext(CallContext.current().getCallingAccountId(), + CallContext.current().getCallingUserId()); + } return context; } @@ -171,4 +178,8 @@ public class AsyncJobExecutionContext { public static void setCurrentExecutionContext(AsyncJobExecutionContext currentContext) { s_currentExectionContext.set(currentContext); } + + public static String getOriginJobContextId() { + return String.valueOf(CallContext.current().getContextId()); + } } diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobManager.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobManager.java index d1a4a9d5930..67733ed1c90 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobManager.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/AsyncJobManager.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.framework.jobs; +import java.io.Serializable; import java.util.List; import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; @@ -41,8 +42,8 @@ public interface AsyncJobManager extends Manager { void updateAsyncJobStatus(long jobId, int processStatus, String resultObject); void updateAsyncJobAttachment(long jobId, String instanceType, Long instanceId); - - void logJobJournal(long jobId, AsyncJob.JournalType journalType, String journalText, String journalObjJson); + void logJobJournal(long jobId, AsyncJob.JournalType journalType, String + journalText, String journalObjJson); /** * A running thread inside management server can have a 1:1 linked pseudo job. @@ -81,8 +82,8 @@ public interface AsyncJobManager extends Manager { * @param wakeupIntervalInMilliSeconds * @param timeoutInMilliSeconds */ - void joinJob(long jobId, long joinJobId, String wakeupHandler, String wakupDispatcher, String[] wakeupTopicsOnMessageBus, long wakeupIntervalInMilliSeconds, - long timeoutInMilliSeconds); + void joinJob(long jobId, long joinJobId, String wakeupHandler, String wakupDispatcher, + String[] wakeupTopicsOnMessageBus, long wakeupIntervalInMilliSeconds, long timeoutInMilliSeconds); /** * Dis-join two related jobs @@ -124,4 +125,7 @@ public interface AsyncJobManager extends Manager { AsyncJob queryJob(long jobId, boolean updatePollTime); + String marshallResultObject(Serializable obj); + + Object unmarshallResultObject(AsyncJob job); } diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java index d98d8329cc2..bfd9f18caf9 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImpl.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.framework.jobs.impl; +import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.Date; @@ -240,6 +241,8 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, if (resultObject != null) { job.setResult(resultObject); + } else { + job.setResult(null); } job.setLastUpdated(DateUtil.currentGMTTime()); @@ -611,6 +614,21 @@ public class AsyncJobManagerImpl extends ManagerBase implements AsyncJobManager, return false; } + @Override + public String marshallResultObject(Serializable obj) { + if (obj != null) + return JobSerializerHelper.toObjectSerializedString(obj); + + return null; + } + + @Override + public Object unmarshallResultObject(AsyncJob job) { + if(job.getResult() != null) + return JobSerializerHelper.fromObjectSerializedString(job.getResult()); + return null; + } + private void checkQueue(long queueId) { while (true) { try { diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/OutcomeImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/OutcomeImpl.java index c65d9c5b3b3..bf7d54a3153 100644 --- a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/OutcomeImpl.java +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/OutcomeImpl.java @@ -51,7 +51,9 @@ public class OutcomeImpl implements Outcome { @Override public AsyncJob getJob() { - return _job; + // always reload job so that we retrieve the latest job result + AsyncJob job = s_jobMgr.getAsyncJob(_job.getId()); + return job; } @Override @@ -110,13 +112,11 @@ public class OutcomeImpl implements Outcome { @Override public void execute(Task task) { // TODO Auto-generated method stub - } @Override public void execute(Task task, long wait, TimeUnit unit) { // TODO Auto-generated method stub - } public Predicate getPredicate() { diff --git a/framework/pom.xml b/framework/pom.xml index c7e61dffade..505c49e99c4 100644 --- a/framework/pom.xml +++ b/framework/pom.xml @@ -40,5 +40,6 @@ managed-context spring/lifecycle spring/module + security diff --git a/framework/security/pom.xml b/framework/security/pom.xml new file mode 100644 index 00000000000..a5fa5f07a51 --- /dev/null +++ b/framework/security/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + cloud-framework-security + Apache CloudStack Framework - Security + + org.apache.cloudstack + cloudstack-framework + 4.4.0-SNAPSHOT + ../pom.xml + + + + org.apache.cloudstack + cloud-utils + ${project.version} + + + org.apache.cloudstack + cloud-api + ${project.version} + + + org.apache.cloudstack + cloud-framework-ipc + ${project.version} + + + org.apache.cloudstack + cloud-framework-db + ${project.version} + + + org.apache.cloudstack + cloud-framework-config + ${project.version} + + + diff --git a/framework/security/resources/META-INF/cloudstack/core/spring-framework-security-core-context.xml b/framework/security/resources/META-INF/cloudstack/core/spring-framework-security-core-context.xml new file mode 100644 index 00000000000..3775565aeea --- /dev/null +++ b/framework/security/resources/META-INF/cloudstack/core/spring-framework-security-core-context.xml @@ -0,0 +1,31 @@ + + + + + diff --git a/engine/schema/src/com/cloud/keystore/KeystoreDao.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java similarity index 95% rename from engine/schema/src/com/cloud/keystore/KeystoreDao.java rename to framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java index 333eb249ec3..e60e4b004c6 100644 --- a/engine/schema/src/com/cloud/keystore/KeystoreDao.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDao.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.keystore; +package org.apache.cloudstack.framework.security.keystore; import java.util.List; diff --git a/engine/schema/src/com/cloud/keystore/KeystoreDaoImpl.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java similarity index 98% rename from engine/schema/src/com/cloud/keystore/KeystoreDaoImpl.java rename to framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java index 6c51a26713d..cd24611a86e 100644 --- a/engine/schema/src/com/cloud/keystore/KeystoreDaoImpl.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreDaoImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.keystore; +package org.apache.cloudstack.framework.security.keystore; import java.sql.PreparedStatement; import java.util.Collections; diff --git a/server/src/com/cloud/keystore/KeystoreManager.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java similarity index 60% rename from server/src/com/cloud/keystore/KeystoreManager.java rename to framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java index 8a7d553bef5..3b99947c4b5 100644 --- a/server/src/com/cloud/keystore/KeystoreManager.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManager.java @@ -14,12 +14,44 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.keystore; +package org.apache.cloudstack.framework.security.keystore; -import com.cloud.agent.api.SecStorageSetupCommand.Certificates; +import com.cloud.agent.api.LogLevel; +import com.cloud.agent.api.LogLevel.Log4jLevel; import com.cloud.utils.component.Manager; public interface KeystoreManager extends Manager { + public static class Certificates { + @LogLevel(Log4jLevel.Off) + private String privKey; + @LogLevel(Log4jLevel.Off) + private String privCert; + @LogLevel(Log4jLevel.Off) + private String certChain; + + public Certificates() { + + } + + public Certificates(String prvKey, String privCert, String certChain) { + privKey = prvKey; + this.privCert = privCert; + this.certChain = certChain; + } + + public String getPrivKey() { + return privKey; + } + + public String getPrivCert() { + return privCert; + } + + public String getCertChain() { + return certChain; + } + } + boolean validateCertificate(String certificate, String key, String domainSuffix); void saveCertificate(String name, String certificate, String key, String domainSuffix); diff --git a/server/src/com/cloud/keystore/KeystoreManagerImpl.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java similarity index 95% rename from server/src/com/cloud/keystore/KeystoreManagerImpl.java rename to framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java index 2b798fe094c..306083492af 100644 --- a/server/src/com/cloud/keystore/KeystoreManagerImpl.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreManagerImpl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.keystore; +package org.apache.cloudstack.framework.security.keystore; import java.io.IOException; import java.security.KeyStore; @@ -33,7 +33,6 @@ import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import com.cloud.agent.api.SecStorageSetupCommand; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.exception.CloudRuntimeException; @@ -123,7 +122,7 @@ public class KeystoreManagerImpl extends ManagerBase implements KeystoreManager } @Override - public SecStorageSetupCommand.Certificates getCertificates(String name) { + public Certificates getCertificates(String name) { KeystoreVO ksVo = _ksDao.findByName(name); if (ksVo == null) { return null; @@ -140,7 +139,7 @@ public class KeystoreManagerImpl extends ManagerBase implements KeystoreManager } certChain = chains.toString(); } - SecStorageSetupCommand.Certificates certs = new SecStorageSetupCommand.Certificates(prvKey, prvCert, certChain); + Certificates certs = new Certificates(prvKey, prvCert, certChain); return certs; } diff --git a/engine/schema/src/com/cloud/keystore/KeystoreVO.java b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreVO.java similarity index 97% rename from engine/schema/src/com/cloud/keystore/KeystoreVO.java rename to framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreVO.java index 66dfac10058..2e4b9febd9d 100644 --- a/engine/schema/src/com/cloud/keystore/KeystoreVO.java +++ b/framework/security/src/org/apache/cloudstack/framework/security/keystore/KeystoreVO.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.keystore; +package org.apache.cloudstack.framework.security.keystore; import javax.persistence.Column; import javax.persistence.Entity; diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java index 6927d2f5fda..321e365e2c1 100644 --- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/ExtensionRegistry.java @@ -159,7 +159,6 @@ public class ExtensionRegistry implements Registry, Configurable, BeanNa if (name == null) { for (String part : beanName.replaceAll("([A-Z])", " $1").split("\\s+")) { part = StringUtils.capitalize(part.toLowerCase()); - ; name = name == null ? part : name + " " + part; } @@ -219,7 +218,7 @@ public class ExtensionRegistry implements Registry, Configurable, BeanNa @Override public void setBeanName(String name) { - this.beanName = name; + beanName = name; } public List getPreRegistered() { diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index 893628d55d2..7132d4f2cc4 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -125,6 +125,8 @@ Requires: jakarta-commons-daemon Requires: jakarta-commons-daemon-jsvc Requires: perl Requires: libvirt-python +Requires: qemu-img +Requires: qemu-kvm Provides: cloud-agent Obsoletes: cloud-agent < 4.1.0 Obsoletes: cloud-agent-libs < 4.1.0 diff --git a/plugins/event-bus/inmemory/pom.xml b/plugins/event-bus/inmemory/pom.xml new file mode 100644 index 00000000000..1bde8b8d843 --- /dev/null +++ b/plugins/event-bus/inmemory/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + cloud-mom-inmemory + Apache CloudStack Plugin - In Memory Event Bus + + org.apache.cloudstack + cloudstack-plugins + 4.4.0-SNAPSHOT + ../../pom.xml + + + + org.apache.cloudstack + cloud-framework-events + ${project.version} + + + + install + + diff --git a/plugins/event-bus/inmemory/src/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java b/plugins/event-bus/inmemory/src/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java new file mode 100644 index 00000000000..99d0a12eb3b --- /dev/null +++ b/plugins/event-bus/inmemory/src/org/apache/cloudstack/mom/inmemory/InMemoryEventBus.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cloudstack.mom.inmemory; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import com.cloud.utils.Pair; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.framework.events.Event; +import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.cloudstack.framework.events.EventSubscriber; +import org.apache.cloudstack.framework.events.EventTopic; + +import com.cloud.utils.component.ManagerBase; + +@Local(value = EventBus.class) +public class InMemoryEventBus extends ManagerBase implements EventBus { + + private String name; + private static final Logger s_logger = Logger.getLogger(InMemoryEventBus.class); + private static ConcurrentHashMap> s_subscribers; + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + s_subscribers = new ConcurrentHashMap>(); + return true; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException { + if (subscriber == null || topic == null) { + throw new EventBusException("Invalid EventSubscriber/EventTopic object passed."); + } + UUID subscriberId = UUID.randomUUID(); + + s_subscribers.put(subscriberId, new Pair(topic, subscriber)); + return subscriberId; + } + + @Override + public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException { + if (s_subscribers != null && s_subscribers.isEmpty()) { + throw new EventBusException("There are no registered subscribers to unregister."); + } + if (s_subscribers.get(subscriberId) == null) { + throw new EventBusException("No subscriber found with subscriber id " + subscriberId); + } + s_subscribers.remove(subscriberId); + } + + @Override + public void publish(Event event) throws EventBusException { + if (s_subscribers == null || s_subscribers.isEmpty()) { + return; // no subscriber to publish to, so just return + } + + for (UUID subscriberId : s_subscribers.keySet()) { + Pair subscriberDetails = s_subscribers.get(subscriberId); + // if the event matches subscribers interested event topic then call back the subscriber with the event + if (isEventMatchesTopic(event, subscriberDetails.first())) { + EventSubscriber subscriber = subscriberDetails.second(); + subscriber.onEvent(event); + } + } + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + private String replaceNullWithWildcard(String key) { + if (key == null || key.isEmpty()) { + return "*"; + } else { + return key; + } + } + + private boolean isEventMatchesTopic(Event event, EventTopic topic) { + + String eventTopicSource = replaceNullWithWildcard(topic.getEventSource()); + eventTopicSource = eventTopicSource.replace(".", "-"); + String eventSource = replaceNullWithWildcard(event.getEventSource()); + eventSource = eventSource.replace(".", "-"); + if (eventTopicSource != "*" && eventSource != "*" && !eventTopicSource.equalsIgnoreCase(eventSource)) { + return false; + } + + String eventTopicCategory = replaceNullWithWildcard(topic.getEventCategory()); + eventTopicCategory = eventTopicCategory.replace(".", "-"); + String eventCategory = replaceNullWithWildcard(event.getEventCategory()); + eventCategory = eventCategory.replace(".", "-"); + if (eventTopicCategory != "*" && eventCategory != "*" && !eventTopicCategory.equalsIgnoreCase(eventCategory)) { + return false; + } + + String eventTopicType = replaceNullWithWildcard(topic.getEventType()); + eventTopicType = eventTopicType.replace(".", "-"); + String eventType = replaceNullWithWildcard(event.getEventType()); + eventType = eventType.replace(".", "-"); + if (eventTopicType != "*" && eventType != "*" && !eventTopicType.equalsIgnoreCase(eventType)) { + return false; + } + + String eventTopicResourceType = replaceNullWithWildcard(topic.getResourceType()); + eventTopicResourceType = eventTopicResourceType.replace(".", "-"); + String resourceType = replaceNullWithWildcard(event.getResourceType()); + resourceType = resourceType.replace(".", "-"); + if (eventTopicResourceType != "*" && resourceType != "*" && !eventTopicResourceType.equalsIgnoreCase(resourceType)) { + return false; + } + + String resourceUuid = replaceNullWithWildcard(event.getResourceUUID()); + resourceUuid = resourceUuid.replace(".", "-"); + String eventTopicresourceUuid = replaceNullWithWildcard(topic.getResourceUUID()); + eventTopicresourceUuid = eventTopicresourceUuid.replace(".", "-"); + if (resourceUuid != "*" && eventTopicresourceUuid != "*" && !resourceUuid.equalsIgnoreCase(eventTopicresourceUuid)) { + return false; + } + + return true; + } +} diff --git a/plugins/ha-planners/skip-heurestics/pom.xml b/plugins/ha-planners/skip-heurestics/pom.xml new file mode 100644 index 00000000000..223789a287f --- /dev/null +++ b/plugins/ha-planners/skip-heurestics/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-planner-skip-heurestics + Apache CloudStack Plugin - Skip Heurestics Planner + + org.apache.cloudstack + cloudstack-plugins + 4.4.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/module.properties b/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/module.properties new file mode 100644 index 00000000000..dfe0641ba4a --- /dev/null +++ b/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/module.properties @@ -0,0 +1,18 @@ +# 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. +name=skip-heurestics +parent=planner diff --git a/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/spring-skip-heurestics-context.xml b/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/spring-skip-heurestics-context.xml new file mode 100644 index 00000000000..93a015874af --- /dev/null +++ b/plugins/ha-planners/skip-heurestics/resources/META-INF/cloudstack/skip-heurestics/spring-skip-heurestics-context.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/plugins/ha-planners/skip-heurestics/src/com/cloud/deploy/SkipHeuresticsPlanner.java b/plugins/ha-planners/skip-heurestics/src/com/cloud/deploy/SkipHeuresticsPlanner.java new file mode 100644 index 00000000000..b67d112f825 --- /dev/null +++ b/plugins/ha-planners/skip-heurestics/src/com/cloud/deploy/SkipHeuresticsPlanner.java @@ -0,0 +1,61 @@ +// 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.deploy; + +import com.cloud.vm.VirtualMachineProfile; +import org.apache.log4j.Logger; + + +import javax.ejb.Local; +import javax.naming.ConfigurationException; +import java.util.List; +import java.util.Map; + +@Local(value=HAPlanner.class) +public class SkipHeuresticsPlanner extends FirstFitPlanner implements HAPlanner { + private static final Logger s_logger = Logger.getLogger(SkipHeuresticsPlanner.class); + + + /** + * This method should remove the clusters crossing capacity threshold + * to avoid further vm allocation on it. + * + * In case of HA, we shouldn't consider this threshold as we have reserved the capacity for such emergencies. + */ + @Override + protected void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, + VirtualMachineProfile vmProfile, DeploymentPlan plan){ + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deploying vm during HA process, so skipping disable threshold check"); + } + return; + } + + @Override + public boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) { + return true; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + super.configure(name, params); + + return true; + + } + +} \ No newline at end of file diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs index 5e58211d60f..7545644fb70 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/Program.cs @@ -29,7 +29,13 @@ namespace CloudStack.Plugin.AgentShell static class Program { private static ILog logger = LogManager.GetLogger(typeof(Program)); - public static string serviceName = "CloudStack Hyper-V Agent"; + public const string serviceName = "CloudStack Hyper-V Agent"; + private static string option = null; + private static string user = null; + private static string password = null; + private const string install = "--install"; + private const string uninstall = "--uninstall"; + private const string console = "--console"; /// /// Application entry point allows service to run in console application or as a Windows service. @@ -43,22 +49,21 @@ namespace CloudStack.Plugin.AgentShell ServiceBase[] ServicesToRun = new ServiceBase[] { new AgentService() }; ServiceBase.Run(ServicesToRun); } - else if (args.Length == 1) + else if (ParseArguments(args)) { - logger.DebugFormat(serviceName + " arg is ", args[0]); - switch (args[0]) + switch (option) { - case "--install": + case install: logger.InfoFormat("Installing and running " + serviceName); InstallService(); StartService(); break; - case "--uninstall": + case uninstall: logger.InfoFormat("Stopping and uninstalling " + serviceName); StopService(); UninstallService(); break; - case "--console": + case console: logger.InfoFormat(serviceName + " is running as console application"); new AgentService().RunConsole(args); break; @@ -66,6 +71,74 @@ namespace CloudStack.Plugin.AgentShell throw new NotImplementedException(); } } + else + { + string argumentExample = "--(install/uninstall/console) [-u ] [-p ].\n" + + " For example:\n " + + " --install -u domain1\\user1 -p mypwd\n"+ + " --uninstall"; + logger.Error("Invalid arguments passed for installing\\uninstalling the service. \n" + argumentExample); + } + } + + static private Boolean ParseArguments(string[] args) + { + logger.DebugFormat(serviceName + " arg is ", args[0]); + int argIndex = 0; + while (argIndex < args.Length) + { + switch (args[argIndex]) + { + case install: + case uninstall: + case console: + option = args[argIndex]; + argIndex++; + break; + case "-u": + user = args[argIndex+1]; + argIndex+=2; + break; + case "-p": + password = args[argIndex+1]; + argIndex+=2; + break; + default: + argIndex++; + // Unrecognised argument. Do nothing; + break; + } + } + + // Validate arguments + return ValidateArguments(); + } + + private static Boolean ValidateArguments() + { + Boolean argsValid = false; + switch (option) + { + case uninstall: + case install: + case console: + argsValid = true; + break; + default: + break; + } + + return argsValid; + } + + public static string GetPassword() + { + return password; + } + + public static string GetUser() + { + return user; } private static bool IsInstalled() diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/ProjectInstaller.Designer.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/ProjectInstaller.Designer.cs index d858192f63a..2c6ffe24462 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/ProjectInstaller.Designer.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/ProjectInstaller.Designer.cs @@ -46,22 +46,35 @@ namespace CloudStack.Plugin.AgentShell { this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); this.serviceInstaller = new System.ServiceProcess.ServiceInstaller(); - // + // // serviceProcessInstaller - // - this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; - this.serviceProcessInstaller.Password = null; - this.serviceProcessInstaller.Username = null; - // + // + string user = Program.GetUser(); + string password = Program.GetPassword(); + + if (string.IsNullOrEmpty(user)) + { + this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; + this.serviceProcessInstaller.Password = null; + this.serviceProcessInstaller.Username = null; + } + else + { + this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.User; + this.serviceProcessInstaller.Password = password; + this.serviceProcessInstaller.Username = user; + } + + // // serviceInstaller - // + // this.serviceInstaller.Description = "CloudStack agent for managing a hyper-v host"; this.serviceInstaller.DisplayName = Program.serviceName; this.serviceInstaller.ServiceName = Program.serviceName; this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; - // + // // ProjectInstaller - // + // this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.serviceProcessInstaller, this.serviceInstaller}); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config index 1bf17d4791f..0e75ef9b5f4 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/App.config @@ -24,7 +24,7 @@ 8 - 10.1.1.1 + 0.0.0.0 Routing @@ -45,13 +45,13 @@ 34359738368 - camldonall01.citrite.net + 0.0.0.0 1 - 10.70.176.1 + 0.0.0.0 2 diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index f4d25ecfb6c..89f0814214e 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -153,17 +153,48 @@ namespace HypervResource get { string fileName = null; - if (this.primaryDataStore.isLocal) + if (this.primaryDataStore != null) { - fileName = Path.Combine(this.primaryDataStore.Path, this.name); + PrimaryDataStoreTO store = this.primaryDataStore; + if (store.isLocal) + { + fileName = Path.Combine(store.Path, this.name); + } + else + { + fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" + this.name; + fileName = Utils.NormalizePath(fileName); + } + } + else if (this.nfsDataStore != null) + { + if (this.path != null && File.Exists(this.path)) + { + fileName = this.path; + } + else + { + fileName = this.nfsDataStore.UncPath; + if (this.path != null) + { + fileName += @"\" + this.path; + } + + fileName = Utils.NormalizePath(fileName); + if (Directory.Exists(fileName)) + { + fileName = Utils.NormalizePath(fileName + @"\" + this.name); + } + } } else { - fileName = @"\\" + this.primaryDataStore.uri.Host + this.primaryDataStore.uri.LocalPath + @"\" + this.name; - fileName = Utils.NormalizePath(fileName); + String errMsg = "Invalid dataStore in VolumeObjectTO spec"; + logger.Error(errMsg); + throw new InvalidDataException(errMsg); } - if (this.format != null) + if (fileName != null && !Path.HasExtension(fileName) && this.format != null) { fileName = fileName + "." + this.format.ToLowerInvariant(); } @@ -175,9 +206,11 @@ namespace HypervResource public dynamic dataStore; public string format; public string name; + public string path; public string uuid; public ulong size; public PrimaryDataStoreTO primaryDataStore; + public NFSTO nfsDataStore; public static VolumeObjectTO ParseJson(dynamic json) { @@ -196,15 +229,17 @@ namespace HypervResource dataStore = volumeObjectTOJson.dataStore, format = ((string)volumeObjectTOJson.format), name = (string)volumeObjectTOJson.name, + path = volumeObjectTOJson.path, uuid = (string)volumeObjectTOJson.uuid, size = (ulong)volumeObjectTOJson.size }; result.primaryDataStore = PrimaryDataStoreTO.ParseJson(volumeObjectTOJson.dataStore); + result.nfsDataStore = NFSTO.ParseJson(volumeObjectTOJson.dataStore); // Assert - if (result.dataStore == null || result.primaryDataStore == null) + if (result.dataStore == null || (result.primaryDataStore == null && result.nfsDataStore == null)) { - String errMsg = "VolumeObjectTO missing primary dataStore in spec " + volumeObjectTOJson.ToString(); + String errMsg = "VolumeObjectTO missing dataStore in spec " + volumeObjectTOJson.ToString(); logger.Error(errMsg); throw new ArgumentNullException(errMsg); } @@ -220,22 +255,48 @@ namespace HypervResource { logger.Info("No image format in VolumeObjectTO, going to use format from first file that matches " + volInfo.FullFileName); - string path = volInfo.primaryDataStore.Path; - if (!volInfo.primaryDataStore.isLocal) + string path = null; + if (volInfo.primaryDataStore != null) { - path = volInfo.primaryDataStore.UncPath; + if (volInfo.primaryDataStore.isLocal) + { + path = volInfo.primaryDataStore.Path; + } + else + { + path = volInfo.primaryDataStore.UncPath; + } } - - string[] choices = choices = Directory.GetFiles(path, volInfo.name + ".vhd*"); - if (choices.Length != 1) + else if (volInfo.nfsDataStore != null) { - String errMsg = "Tried to guess file extension, but cannot find file corresponding to " + Path.Combine(volInfo.primaryDataStore.Path, volInfo.name); // format being guessed. - logger.Debug(errMsg); + path = volInfo.nfsDataStore.UncPath; + if (volInfo.path != null) + { + path += @"\" + volInfo.path; + } } else { - string[] splitFileName = choices[0].Split(new char[] { '.' }); - volInfo.format = splitFileName[splitFileName.Length - 1]; + String errMsg = "VolumeObjectTO missing dataStore in spec " + volInfo.ToString(); + logger.Error(errMsg); + throw new ArgumentNullException(errMsg); + } + + path = Utils.NormalizePath(path); + if (Directory.Exists(path)) + { + string[] choices = choices = Directory.GetFiles(path, volInfo.name + ".vhd*"); + if (choices.Length != 1) + { + String errMsg = "Tried to guess file extension, but cannot find file corresponding to " + + Path.Combine(volInfo.primaryDataStore.Path, volInfo.name); + logger.Debug(errMsg); + } + else + { + string[] splitFileName = choices[0].Split(new char[] { '.' }); + volInfo.format = splitFileName[splitFileName.Length - 1]; + } } logger.Debug("Going to use file " + volInfo.FullFileName); } @@ -252,20 +313,34 @@ namespace HypervResource { get { - if (String.IsNullOrEmpty(this.path)) + string fileName = null; + if (this.primaryDataStore != null) { - string fileName = null; - if (this.primaryDataStore.isLocal) + PrimaryDataStoreTO store = this.primaryDataStore; + if (store.isLocal) { - fileName = Path.Combine(this.primaryDataStore.Path, this.name); + fileName = Path.Combine(store.Path, this.name); } else { - fileName = @"\\" + this.primaryDataStore.uri.Host + this.primaryDataStore.uri.LocalPath + @"\" + this.name; + fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" + this.name; } - return fileName +'.' + this.format.ToLowerInvariant(); + fileName = fileName + '.' + this.format.ToLowerInvariant(); } - return this.path; + else if (this.nfsDataStoreTO != null) + { + NFSTO store = this.nfsDataStoreTO; + fileName = store.UncPath + @"\" + this.path + @"\" + this.name; + if (!this.format.Equals("RAW")) + { + fileName = fileName + '.' + this.format.ToLowerInvariant(); + } + } + else + { + fileName = this.path; + } + return Utils.NormalizePath(fileName); } } @@ -278,6 +353,8 @@ namespace HypervResource public PrimaryDataStoreTO primaryDataStore = null; public string path; public string checksum; + public string size; + public string id; public static TemplateObjectTO ParseJson(dynamic json) { @@ -292,7 +369,9 @@ namespace HypervResource name = (string)templateObjectTOJson.name, uuid = (string)templateObjectTOJson.uuid, path = (string)templateObjectTOJson.path, - checksum = (string)templateObjectTOJson.checksum + checksum = (string)templateObjectTOJson.checksum, + size = (string)templateObjectTOJson.size, + id = (string)templateObjectTOJson.id }; result.s3DataStoreTO = S3TO.ParseJson(templateObjectTOJson.imageDataStore); result.nfsDataStoreTO = NFSTO.ParseJson(templateObjectTOJson.imageDataStore); @@ -406,7 +485,9 @@ namespace HypervResource public class DiskTO { public string type; + public string diskSequence = null; public TemplateObjectTO templateObjectTO = null; + public VolumeObjectTO volumeObjectTO = null; public static DiskTO ParseJson(dynamic json) { @@ -416,7 +497,9 @@ namespace HypervResource result = new DiskTO() { templateObjectTO = TemplateObjectTO.ParseJson(json.data), + volumeObjectTO = VolumeObjectTO.ParseJson(json.data), type = (string)json.type, + diskSequence = json.diskSeq }; } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index b283d976c51..18f3158da46 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -31,6 +31,7 @@ using System.IO; using System.Linq; using System.Net; using System.Net.Http; +using System.Text; using System.Security.Cryptography; using System.Security.Principal; using System.Web.Http; @@ -215,18 +216,32 @@ namespace HypervResource { string vmName = (string)cmd.vmName; DiskTO disk = DiskTO.ParseJson(cmd.disk); - TemplateObjectTO dataStore = disk.templateObjectTO; - if (dataStore.nfsDataStoreTO != null) + if (disk.type.Equals("ISO")) { + TemplateObjectTO dataStore = disk.templateObjectTO; NFSTO share = dataStore.nfsDataStoreTO; Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); - - // The share is mapped, now attach the iso - string isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); - wmiCallsV2.AttachIso(vmName, isoPath); + string diskPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); + wmiCallsV2.AttachIso(vmName, diskPath); result = true; } + else if (disk.type.Equals("DATADISK")) + { + VolumeObjectTO volume = disk.volumeObjectTO; + PrimaryDataStoreTO primary = volume.primaryDataStore; + if (!primary.isLocal) + { + Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + } + string diskPath = Utils.NormalizePath(volume.FullFileName); + wmiCallsV2.AttachDisk(vmName, diskPath, disk.diskSequence); + result = true; + } + else + { + details = "Invalid disk type to be attached to vm " + vmName; + } } catch (Exception sysEx) { @@ -238,6 +253,7 @@ namespace HypervResource { result = result, details = details, + disk = cmd.disk, contextMap = contextMap }; @@ -261,16 +277,27 @@ namespace HypervResource { string vmName = (string)cmd.vmName; DiskTO disk = DiskTO.ParseJson(cmd.disk); - TemplateObjectTO dataStore = disk.templateObjectTO; - if (dataStore.nfsDataStoreTO != null) + if (disk.type.Equals("ISO")) { + TemplateObjectTO dataStore = disk.templateObjectTO; NFSTO share = dataStore.nfsDataStoreTO; - // The share is mapped, now attach the iso - string isoPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); - wmiCallsV2.DetachDisk(vmName, isoPath); + string diskPath = Utils.NormalizePath(Path.Combine(share.UncPath, dataStore.path)); + wmiCallsV2.DetachDisk(vmName, diskPath); result = true; } + else if (disk.type.Equals("DATADISK")) + { + VolumeObjectTO volume = disk.volumeObjectTO; + PrimaryDataStoreTO primary = volume.primaryDataStore; + string diskPath = Utils.NormalizePath(volume.FullFileName); + wmiCallsV2.DetachDisk(vmName, diskPath); + result = true; + } + else + { + details = "Invalid disk type to be dettached from vm " + vmName; + } } catch (Exception sysEx) { @@ -396,6 +423,64 @@ namespace HypervResource } } + // POST api/HypervResource/DeleteCommand + [HttpPost] + [ActionName(CloudStackTypes.DeleteCommand)] + public JContainer DeleteCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.DestroyCommand + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + // Assert + String errMsg = "No 'volume' details in " + CloudStackTypes.DestroyCommand + " " + cmd.ToString(); + VolumeObjectTO destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.data); + + if (destVolumeObjectTO.name == null) + { + logger.Error(errMsg); + throw new ArgumentException(errMsg); + } + + String path = destVolumeObjectTO.FullFileName; + if (!File.Exists(path)) + { + logger.Info(CloudStackTypes.DestroyCommand + ", but volume at pass already deleted " + path); + } + + string vmName = (string)cmd.vmName; + if (!string.IsNullOrEmpty(vmName) && File.Exists(path)) + { + // Make sure that this resource is removed from the VM + wmiCallsV2.DetachDisk(vmName, path); + } + + File.Delete(path); + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.DestroyCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + contextMap = contextMap + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); + } + } + + private static JArray ReturnCloudStackTypedJArray(object ansContent, string ansType) { JObject ansObj = Utils.CreateCloudStackObject(ansType, ansContent); @@ -665,7 +750,6 @@ namespace HypervResource } // POST api/HypervResource/CheckHealthCommand - // TODO: create test [HttpPost] [ActionName(CloudStackTypes.CheckHealthCommand)] public JContainer CheckHealthCommand([FromBody]dynamic cmd) @@ -683,6 +767,24 @@ namespace HypervResource } } + // POST api/HypervResource/CheckOnHostCommand + [HttpPost] + [ActionName(CloudStackTypes.CheckOnHostCommand)] + public JContainer CheckOnHostCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.CheckOnHostCommand + cmd.ToString()); + object ansContent = new + { + result = true, + details = "resource is alive", + contextMap = contextMap + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CheckOnHostAnswer); + } + } + // POST api/HypervResource/CheckSshCommand // TODO: create test [HttpPost] @@ -808,18 +910,23 @@ namespace HypervResource var tInfo = new Dictionary(); long capacityBytes = 0; long availableBytes = 0; + string hostPath = null; if (poolType == StoragePoolType.Filesystem) { GetCapacityForLocalPath(localPath, out capacityBytes, out availableBytes); + hostPath = localPath; } else if (poolType == StoragePoolType.NetworkFilesystem) { NFSTO share = new NFSTO(); String uriStr = "cifs://" + (string)cmd.pool.host + (string)cmd.pool.path; share.uri = new Uri(uriStr); + hostPath = Utils.NormalizePath(share.UncPath); + // Check access to share. Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); Utils.GetShareDetails(share.UncPath, out capacityBytes, out availableBytes); + config.setPrimaryStorage((string)cmd.pool.uuid, hostPath); } else { @@ -831,8 +938,8 @@ namespace HypervResource { uuid = uuid, host = cmd.pool.host, - localPath = cmd.pool.host, - hostPath = cmd.localPath, + hostPath = cmd.pool.path, + localPath = hostPath, poolType = cmd.pool.type, capacityBytes = capacityBytes, availableBytes = availableBytes @@ -842,6 +949,7 @@ namespace HypervResource { result = result, details = details, + localPath = hostPath, templateInfo = tInfo, poolInfo = poolInfo, contextMap = contextMap @@ -1046,7 +1154,7 @@ namespace HypervResource VolumeObjectTO volume = VolumeObjectTO.ParseJson(cmd.data); PrimaryDataStoreTO primary = volume.primaryDataStore; ulong volumeSize = volume.size; - string volumeName = volume.name + ".vhdx"; + string volumeName = volume.name + ".vhd"; string volumePath = null; if (primary.isLocal) @@ -1102,6 +1210,7 @@ namespace HypervResource object ansContent = new { result = true, + willMigrate = true, details = "success - NOP for MaintainCommand", _reconnect = false, contextMap = contextMap @@ -1206,29 +1315,42 @@ namespace HypervResource using (log4net.NDC.Push(Guid.NewGuid().ToString())) { // Log command *after* we've removed security details from the command. + logger.Info(CloudStackTypes.CopyCommand + cmd.ToString()); bool result = false; string details = null; object newData = null; + TemplateObjectTO destTemplateObjectTO = null; + VolumeObjectTO destVolumeObjectTO = null; + VolumeObjectTO srcVolumeObjectTO = null; + TemplateObjectTO srcTemplateObjectTO = null; try { dynamic timeout = cmd.wait; // TODO: Useful? - TemplateObjectTO srcTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.srcTO); - TemplateObjectTO destTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.destTO); - VolumeObjectTO destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.destTO); - - logger.Info(CloudStackTypes.CopyCommand + cmd.ToString()); + srcTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.srcTO); + destTemplateObjectTO = TemplateObjectTO.ParseJson(cmd.destTO); + srcVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.srcTO); + destVolumeObjectTO = VolumeObjectTO.ParseJson(cmd.destTO); string destFile = null; - if (destTemplateObjectTO != null && destTemplateObjectTO.primaryDataStore != null) + if (destTemplateObjectTO != null) { - destFile = destTemplateObjectTO.FullFileName; - if (!destTemplateObjectTO.primaryDataStore.isLocal) + if (destTemplateObjectTO.primaryDataStore != null) { - PrimaryDataStoreTO primary = destTemplateObjectTO.primaryDataStore; - Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + destFile = destTemplateObjectTO.FullFileName; + if (!destTemplateObjectTO.primaryDataStore.isLocal) + { + PrimaryDataStoreTO primary = destTemplateObjectTO.primaryDataStore; + Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User, primary.Password); + } + } + else if (destTemplateObjectTO.nfsDataStoreTO != null) + { + destFile = destTemplateObjectTO.FullFileName; + NFSTO store = destTemplateObjectTO.nfsDataStoreTO; + Utils.ConnectToRemote(store.UncPath, store.Domain, store.User, store.Password); } } @@ -1352,7 +1474,97 @@ namespace HypervResource { // TODO: thin provision instead of copying the full file. File.Copy(srcFile, destFile); - newData = cmd.destTO; + VolumeObjectTO volume = new VolumeObjectTO(); + volume.path = destFile; + volume.dataStore = destVolumeObjectTO.dataStore; + volume.name = destVolumeObjectTO.name; + volume.size = ulong.Parse(destVolumeObjectTO.size.ToString()); + volume.format = destVolumeObjectTO.format; + volume.nfsDataStore = destVolumeObjectTO.nfsDataStore; + volume.primaryDataStore = destVolumeObjectTO.primaryDataStore; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, volume); + newData = ansObj; + result = true; + } + } + else if (srcVolumeObjectTO != null && destVolumeObjectTO != null) + { + var guessedDestFile = destVolumeObjectTO.FullFileName; + if (File.Exists(guessedDestFile)) + { + logger.Info("Deleting existing file " + guessedDestFile); + File.Delete(guessedDestFile); + } + + destVolumeObjectTO.format = srcVolumeObjectTO.format; + destFile = destVolumeObjectTO.FullFileName; + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + string srcFile = srcVolumeObjectTO.FullFileName; + if (!File.Exists(srcFile)) + { + details = "Local template file missing from " + srcFile; + } + else + { + // Create the directory before copying the files. CreateDirectory + // doesn't do anything if the directory is already present. + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(srcFile, destFile); + // Create volumeto object deserialize and send it + destVolumeObjectTO.path = destFile; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO, destVolumeObjectTO); + newData = ansObj; + result = true; + } + } + else if (srcVolumeObjectTO != null && destTemplateObjectTO != null) + { + var guessedDestFile = destTemplateObjectTO.FullFileName; + if (File.Exists(guessedDestFile)) + { + logger.Info("Deleting existing file " + guessedDestFile); + File.Delete(guessedDestFile); + } + + destTemplateObjectTO.format = srcVolumeObjectTO.format; + destFile = destTemplateObjectTO.FullFileName; + if (File.Exists(destFile)) + { + logger.Info("Deleting existing file " + destFile); + File.Delete(destFile); + } + + string srcFile = srcVolumeObjectTO.FullFileName; + if (!File.Exists(srcFile)) + { + details = "Local template file missing from " + srcFile; + } + else + { + // Create the directory before copying the files. CreateDirectory + // doesn't do anything if the directory is already present. + Directory.CreateDirectory(Path.GetDirectoryName(destFile)); + File.Copy(srcFile, destFile); + + FileInfo destFileInfo = new FileInfo(destFile); + // Write the template.properties file + PostCreateTemplate(Path.GetDirectoryName(destFile), destTemplateObjectTO.id, destTemplateObjectTO.name, + destFileInfo.Length.ToString(), srcVolumeObjectTO.size.ToString(), destTemplateObjectTO.format); + + TemplateObjectTO destTemplateObject = new TemplateObjectTO(); + destTemplateObject.size = srcVolumeObjectTO.size.ToString(); + destTemplateObject.format = srcVolumeObjectTO.format; + destTemplateObject.path = destFile; + destTemplateObject.nfsDataStoreTO = destTemplateObjectTO.nfsDataStoreTO; + destTemplateObject.checksum = destTemplateObjectTO.checksum; + newData = destTemplateObject; + JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.TemplateObjectTO, destTemplateObject); + newData = ansObj; result = true; } } @@ -1373,13 +1585,32 @@ namespace HypervResource { result = result, details = details, - newData = cmd.destTO, + newData = newData, contextMap = contextMap }; return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.CopyCmdAnswer); } } + private static void PostCreateTemplate(string path, string templateId, string templateUuid, string physicalSize, string virtualSize, string format) + { + string templatePropFile = Path.Combine(path, "template.properties"); + using (StreamWriter sw = new StreamWriter(File.Open(templatePropFile, FileMode.Create), Encoding.GetEncoding("iso-8859-1"))) + { + sw.NewLine = "\n"; + sw.WriteLine("id=" + templateId); + sw.WriteLine("filename=" + templateUuid + "." + format); + sw.WriteLine(format + ".filename=" + templateUuid + "." + format); + sw.WriteLine("uniquename=" + templateUuid); + sw.WriteLine(format + "=true"); + sw.WriteLine("virtualsize=" + virtualSize); + sw.WriteLine(format + ".virtualsize=" + virtualSize); + sw.WriteLine("size=" + physicalSize); + sw.WriteLine("vhd.size=" + physicalSize); + sw.WriteLine("public=false"); + } + } + private static bool VerifyChecksum(string destFile, string checksum) { string localChecksum = BitConverter.ToString(CalcFileChecksum(destFile)).Replace("-", "").ToLower(); @@ -1470,11 +1701,42 @@ namespace HypervResource long used = 0; try { - string localPath = (string)cmd.localPath; - GetCapacityForLocalPath(localPath, out capacity, out available); - used = capacity - available; - result = true; - logger.Debug(CloudStackTypes.GetStorageStatsCommand + " set used bytes to " + used); + StoragePoolType poolType; + string poolId = (string)cmd.id; + string hostPath = null; + if (!Enum.TryParse((string)cmd.pooltype, out poolType)) + { + details = "Request to get unsupported pool type: " + ((string)cmd.pooltype == null ? "NULL" : (string)cmd.pooltype) + "in cmd " + + JsonConvert.SerializeObject(cmd); + logger.Error(details); + } + else if (poolType == StoragePoolType.Filesystem) + { + hostPath = (string)cmd.localPath;; + GetCapacityForLocalPath(hostPath, out capacity, out available); + used = capacity - available; + result = true; + } + else if (poolType == StoragePoolType.NetworkFilesystem) + { + string sharePath = config.getPrimaryStorage((string)cmd.id); + if (sharePath != null) + { + hostPath = sharePath; + Utils.GetShareDetails(sharePath, out capacity, out available); + used = capacity - available; + result = true; + } + } + else + { + result = false; + } + + if (result) + { + logger.Debug(CloudStackTypes.GetStorageStatsCommand + " set used bytes for " + hostPath + " to " + used); + } } catch (Exception ex) { @@ -1639,22 +1901,38 @@ namespace HypervResource dynamic strtRouteCmd = cmdArray[0][CloudStackTypes.StartupRoutingCommand]; // Insert networking details - strtRouteCmd.privateIpAddress = config.PrivateIpAddress; - strtRouteCmd.privateNetmask = config.PrivateNetmask; - strtRouteCmd.privateMacAddress = config.PrivateMacAddress; - strtRouteCmd.storageIpAddress = config.PrivateIpAddress; - strtRouteCmd.storageNetmask = config.PrivateNetmask; - strtRouteCmd.storageMacAddress = config.PrivateMacAddress; - strtRouteCmd.gatewayIpAddress = config.GatewayIpAddress; - strtRouteCmd.hypervisorVersion = System.Environment.OSVersion.Version.ToString(); + string privateIpAddress = strtRouteCmd.privateIpAddress; + string subnet; + System.Net.NetworkInformation.NetworkInterface privateNic = GetNicInfoFromIpAddress(privateIpAddress, out subnet); + strtRouteCmd.privateIpAddress = privateIpAddress; + strtRouteCmd.privateNetmask = subnet; + strtRouteCmd.privateMacAddress = privateNic.GetPhysicalAddress().ToString(); + string storageip = strtRouteCmd.storageIpAddress; + System.Net.NetworkInformation.NetworkInterface storageNic = GetNicInfoFromIpAddress(storageip, out subnet); + + strtRouteCmd.storageIpAddress = storageip; + strtRouteCmd.storageNetmask = subnet; + strtRouteCmd.storageMacAddress = storageNic.GetPhysicalAddress().ToString(); + strtRouteCmd.gatewayIpAddress = storageNic.GetPhysicalAddress().ToString(); + strtRouteCmd.caps = "hvm"; + dynamic details = strtRouteCmd.hostDetails; + if (details != null) + { + string productVersion = System.Environment.OSVersion.Version.Major.ToString() + "." + + System.Environment.OSVersion.Version.Minor.ToString(); + details.Add("product_version", productVersion); + } + // Detect CPUs, speed, memory uint cores; uint mhz; - wmiCallsV2.GetProcessorResources(out cores, out mhz); + uint sockets; + wmiCallsV2.GetProcessorResources(out sockets, out cores, out mhz); strtRouteCmd.cpus = cores; strtRouteCmd.speed = mhz; + strtRouteCmd.cpuSockets = sockets; ulong memoryKBs; ulong freeMemoryKBs; wmiCallsV2.GetMemoryResources(out memoryKBs, out freeMemoryKBs); @@ -1727,9 +2005,11 @@ namespace HypervResource public static System.Net.NetworkInformation.NetworkInterface GetNicInfoFromIpAddress(string ipAddress, out string subnet) { System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); + System.Net.NetworkInformation.NetworkInterface defaultnic = null; foreach (var nic in nics) { subnet = null; + defaultnic = nic; // TODO: use to remove NETMASK and MAC from the config file, and to validate the IPAddress. var nicProps = nic.GetIPProperties(); bool found = false; @@ -1747,7 +2027,9 @@ namespace HypervResource } return nic; } - throw new ArgumentException("No NIC for ipAddress " + ipAddress); + var defaultSubnet = defaultnic.GetIPProperties().UnicastAddresses[0]; + subnet = defaultSubnet.IPv4Mask.ToString(); + return defaultnic; } public static void GetCapacityForLocalPath(string localStoragePath, out long capacityBytes, out long availableBytes) diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 99ce35276b7..5f814c59535 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -27,9 +27,10 @@ namespace HypervResource { public interface IWmiCallsV2 { - System.Management.ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType); + System.Management.ManagementPath AddDiskDriveToIdeController(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType); ComputerSystem AddUserData(ComputerSystem vm, string userData); void AttachIso(string displayName, string iso); + void AttachDisk(string vmName, string diskPath, string addressOnController); void CreateDynamicVirtualHardDisk(ulong MaxInternalSize, string Path); SyntheticEthernetPortSettingData CreateNICforVm(ComputerSystem vm, string mac); ComputerSystem CreateVM(string name, long memory_mb, int vcpus); @@ -51,7 +52,7 @@ namespace HypervResource KvpExchangeComponentSettingData GetKvpSettings(VirtualSystemSettingData vmSettings); void GetMemoryResources(out ulong physicalRamKBs, out ulong freeMemoryKBs); MemorySettingData GetMemSettings(VirtualSystemSettingData vmSettings); - void GetProcessorResources(out uint cores, out uint mhz); + void GetProcessorResources(out uint sockets, out uint cores, out uint mhz); void GetProcessorUsageInfo(out double cpuUtilization); ProcessorSettingData GetProcSettings(VirtualSystemSettingData vmSettings); ResourceAllocationSettingData.ResourceAllocationSettingDataCollection GetResourceAllocationSettings(VirtualSystemSettingData vmSettings); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index c89f837a7a6..2e3aca59675 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -207,14 +207,14 @@ namespace HypervResource return new SyntheticEthernetPortSettingData(newResourcePaths[0]); } - public const string IDE_HARDDISK_CONTROLLER = "Microsoft:Hyper-V:Emulated IDE Controller"; + public const string IDE_CONTROLLER = "Microsoft:Hyper-V:Emulated IDE Controller"; public const string SCSI_CONTROLLER = "Microsoft:Hyper-V:Synthetic SCSI Controller"; - public const string IDE_HARDDISK_DRIVE = "Microsoft:Hyper-V:Synthetic Disk Drive"; - public const string IDE_ISO_DRIVE = "Microsoft:Hyper-V:Synthetic DVD Drive"; + public const string HARDDISK_DRIVE = "Microsoft:Hyper-V:Synthetic Disk Drive"; + public const string ISO_DRIVE = "Microsoft:Hyper-V:Synthetic DVD Drive"; // TODO: names harvested from Msvm_ResourcePool, not clear how to create new instances - public const string IDE_ISO_DISK = "Microsoft:Hyper-V:Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE - public const string IDE_HARDDISK_DISK = "Microsoft:Hyper-V:Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE + public const string ISO_DISK = "Microsoft:Hyper-V:Virtual CD/DVD Disk"; // For IDE_ISO_DRIVE + public const string HARDDISK_DISK = "Microsoft:Hyper-V:Virtual Hard Disk"; // For IDE_HARDDISK_DRIVE /// /// Create new VM. By default we start it. @@ -280,7 +280,7 @@ namespace HypervResource var newVm = CreateVM(vmName, memSize, vcpus); // Add a SCSI controller for attaching/detaching data volumes. - AddScsiControllerToVm(newVm); + AddScsiController(newVm); foreach (var diskDrive in diskDrives) { @@ -339,17 +339,18 @@ namespace HypervResource } string driveType = diskDrive.type; - string ideCtrllr = "0"; string driveResourceType = null; switch (driveType) { case "ROOT": ideCtrllr = "0"; - driveResourceType = IDE_HARDDISK_DRIVE; + driveResourceType = HARDDISK_DRIVE; break; case "ISO": ideCtrllr = "1"; - driveResourceType = IDE_ISO_DRIVE; + driveResourceType = ISO_DRIVE; + break; + case "DATADISK": break; default: // TODO: double check exception type @@ -360,12 +361,20 @@ namespace HypervResource logger.Error(errMsg, ex); throw ex; } - logger.DebugFormat("Create disk type {1} (Named: {0}), on vm {2} {3}", diskName, driveResourceType, vmName, - string.IsNullOrEmpty(vhdFile) ? " no disk to insert" : ", inserting disk" +vhdFile ); - AddDiskDriveToVm(newVm, vhdFile, ideCtrllr, driveResourceType); - if (isoPath != null) + + logger.DebugFormat("Create disk type {1} (Named: {0}), on vm {2} {3}", diskName, driveResourceType, vmName, + string.IsNullOrEmpty(vhdFile) ? " no disk to insert" : ", inserting disk" + vhdFile); + if (driveType.Equals("DATADISK")) { - AttachIso(vmName, isoPath); + AttachDisk(vmName, vhdFile, (string)diskDrive.diskSeq); + } + else + { + AddDiskDriveToIdeController(newVm, vhdFile, ideCtrllr, driveResourceType); + if (isoPath != null) + { + AttachIso(vmName, isoPath); + } } } @@ -381,9 +390,17 @@ namespace HypervResource string mac = nic.mac; string vlan = null; string isolationUri = nic.isolationUri; - if (isolationUri != null && isolationUri.StartsWith("vlan://") && !isolationUri.Equals("vlan://untagged")) + string broadcastUri = nic.broadcastUri; + if ( (broadcastUri != null ) || (isolationUri != null && isolationUri.StartsWith("vlan://")) && !isolationUri.Equals("vlan://untagged")) { - vlan = isolationUri.Substring("vlan://".Length); + if (broadcastUri != null && broadcastUri.StartsWith("storage")) + { + vlan = broadcastUri.Substring("storage://".Length); + } + else + { + vlan = isolationUri.Substring("vlan://".Length); + } int tmp; if (!int.TryParse(vlan, out tmp)) { @@ -436,13 +453,6 @@ namespace HypervResource String bootargs = bootArgs; AddUserData(vm, bootargs); - - // Get existing KVP - //var vmSettings = GetVmSettings(vm); - //var kvpInfo = GetKvpSettings(vmSettings); - //logger.DebugFormat("Boot Args presisted on the VM are ", kvpInfo); - //AddUserData(vm, bootargs); - // Verify key added to subsystem //kvpInfo = GetKvpSettings(vmSettings); @@ -466,17 +476,16 @@ namespace HypervResource if (vmName.StartsWith("r-") || vmName.StartsWith("s-") || vmName.StartsWith("v-")) { System.Threading.Thread.Sleep(90000); - SetState(newVm, RequiredState.Reset); // wait for the second boot and then return with sucesss //if publicIPAddress is empty or null don't ping the ip - if (publicIpAddress.Equals("") == true) + /*if (publicIpAddress.Equals("") == true) { System.Threading.Thread.Sleep(90000); } else { pingResource(publicIpAddress); - } + }*/ } logger.InfoFormat("Started VM {0}", vmName); return newVm; @@ -488,7 +497,7 @@ namespace HypervResource PingReply pingReply = null; IPAddress ipAddress = null; Ping pingSender = new Ping(); - int numberOfPings = 4; + int numberOfPings = 6; int pingTimeout = 1000; int byteSize = 32; byte[] buffer = new byte[byteSize]; @@ -566,16 +575,36 @@ namespace HypervResource public void patchSystemVmIso(String vmName, String systemVmIso) { ComputerSystem vmObject = GetComputerSystem(vmName); - AddDiskDriveToVm(vmObject, "", "1", IDE_ISO_DRIVE); + AddDiskDriveToIdeController(vmObject, "", "1", ISO_DRIVE); AttachIso(vmName, systemVmIso); } + public void AttachDisk(string vmName, string diskPath, string addressOnController) + { + logger.DebugFormat("Got request to attach disk {0} to vm {1}", diskPath, vmName); + + ComputerSystem vm = GetComputerSystem(vmName); + if (vm == null) + { + logger.DebugFormat("VM {0} not found", vmName); + return; + } + else + { + ManagementPath newDrivePath = GetDiskDriveOnScsiController(vm, addressOnController); + if (newDrivePath == null) + { + newDrivePath = AttachDiskDriveToScsiController(vm, addressOnController); + } + InsertDiskImage(vm, diskPath, HARDDISK_DISK, newDrivePath); + } + } /// /// /// /// IDE_HARDDISK_DRIVE or IDE_ISO_DRIVE - public ManagementPath AddDiskDriveToVm(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType) + public ManagementPath AddDiskDriveToIdeController(ComputerSystem vm, string vhdfile, string cntrllerAddr, string driveResourceType) { logger.DebugFormat("Creating DISK for VM {0} (GUID {1}) by attaching {2}", vm.ElementName, @@ -585,11 +614,11 @@ namespace HypervResource // Determine disk type for drive and assert drive type valid string diskResourceSubType = null; switch(driveResourceType) { - case IDE_HARDDISK_DRIVE: - diskResourceSubType = IDE_HARDDISK_DISK; + case HARDDISK_DRIVE: + diskResourceSubType = HARDDISK_DISK; break; - case IDE_ISO_DRIVE: - diskResourceSubType = IDE_ISO_DISK; + case ISO_DRIVE: + diskResourceSubType = ISO_DISK; break; default: var errMsg = string.Format( @@ -602,7 +631,7 @@ namespace HypervResource throw ex; } - ManagementPath newDrivePath = AttachNewDriveToVm(vm, cntrllerAddr, driveResourceType); + ManagementPath newDrivePath = AttachNewDrive(vm, cntrllerAddr, driveResourceType); // If there's not disk to insert, we are done. if (String.IsNullOrEmpty(vhdfile)) @@ -629,7 +658,7 @@ namespace HypervResource } else { - RemoveStorageImageFromVm(vm, diskFileName); + RemoveStorageImage(vm, diskFileName); } } @@ -638,7 +667,7 @@ namespace HypervResource /// /// /// - private void RemoveStorageImageFromVm(ComputerSystem vm, string diskFileName) + private void RemoveStorageImage(ComputerSystem vm, string diskFileName) { // Obtain StorageAllocationSettingData for disk StorageAllocationSettingData.StorageAllocationSettingDataCollection storageSettingsObjs = StorageAllocationSettingData.GetInstances(); @@ -674,13 +703,13 @@ namespace HypervResource RemoveStorageResource(imageToRemove.Path, vm); - logger.InfoFormat("REmoved disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", + logger.InfoFormat("Removed disk image {0} from VM {1} (GUID {2}): the disk image is not attached.", diskFileName, vm.ElementName, vm.Name); } - private ManagementPath AttachNewDriveToVm(ComputerSystem vm, string cntrllerAddr, string driveType) + private ManagementPath AttachNewDrive(ComputerSystem vm, string cntrllerAddr, string driveType) { // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it. VirtualSystemSettingData vmSettings = GetVmSettings(vm); @@ -722,7 +751,7 @@ namespace HypervResource return newDrivePaths[0]; } - private ManagementPath AddScsiControllerToVm(ComputerSystem vm) + private ManagementPath AddScsiController(ComputerSystem vm) { // A description of the controller is created by modifying a clone of the default ResourceAllocationSettingData for scsi controller string scsiQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", SCSI_CONTROLLER); @@ -754,6 +783,66 @@ namespace HypervResource return newResourcePaths[0]; } + private ManagementPath GetDiskDriveOnScsiController(ComputerSystem vm, string addrOnController) + { + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + var wmiObjCollection = GetResourceAllocationSettings(vmSettings); + foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) + { + if (wmiObj.ResourceSubType == HARDDISK_DRIVE) + { + ResourceAllocationSettingData parent = new ResourceAllocationSettingData(new ManagementObject(wmiObj.Parent)); + if (parent.ResourceSubType == SCSI_CONTROLLER && wmiObj.AddressOnParent == addrOnController) + { + return wmiObj.Path; + } + } + } + return null; + } + + private ManagementPath AttachDiskDriveToScsiController(ComputerSystem vm, string addrOnController) + { + // Disk drives are attached to a 'Parent' Scsi controller. + VirtualSystemSettingData vmSettings = GetVmSettings(vm); + var ctrller = GetScsiControllerSettings(vmSettings); + + // A description of the drive is created by modifying a clone of the default ResourceAllocationSettingData for that drive type + string defaultDriveQuery = String.Format("ResourceSubType LIKE \"{0}\" AND InstanceID LIKE \"%Default\"", HARDDISK_DRIVE); + var newDiskDriveSettings = CloneResourceAllocationSetting(defaultDriveQuery); + + // Set IDE controller and address on the controller for the new drive + newDiskDriveSettings.LateBoundObject["Parent"] = ctrller.Path.ToString(); + newDiskDriveSettings.LateBoundObject["AddressOnParent"] = addrOnController; + newDiskDriveSettings.CommitObject(); + + // Add this new disk drive to the VM + logger.DebugFormat("Creating disk drive type {0}, parent IDE controller is {1} and address on controller is {2}", + newDiskDriveSettings.ResourceSubType, + newDiskDriveSettings.Parent, + newDiskDriveSettings.AddressOnParent); + string[] newDriveResource = new string[] { newDiskDriveSettings.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20) }; + ManagementPath[] newDrivePaths = AddVirtualResource(newDriveResource, vm); + + // assert + if (newDrivePaths.Length != 1) + { + var errMsg = string.Format( + "Failed to add disk drive type {3} to VM {0} (GUID {1}): number of resource created {2}", + vm.ElementName, + vm.Name, + newDrivePaths.Length, + HARDDISK_DRIVE); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + logger.DebugFormat("New disk drive type {0} WMI path is {1}s", + newDiskDriveSettings.ResourceSubType, + newDrivePaths[0].Path); + return newDrivePaths[0]; + } + private void InsertDiskImage(ComputerSystem vm, string diskImagePath, string diskResourceSubType, ManagementPath drivePath) { @@ -795,16 +884,14 @@ namespace HypervResource /// Create Msvm_StorageAllocationSettingData corresponding to the ISO image, and /// associate this with the VM's DVD drive. /// - private void AttachIsoToVm(ComputerSystem vm, string isoPath) + private void AttachIso(ComputerSystem vm, string isoPath) { // Disk drives are attached to a 'Parent' IDE controller. We IDE Controller's settings for the 'Path', which our new Disk drive will use to reference it. VirtualSystemSettingData vmSettings = GetVmSettings(vm); var driveWmiObj = GetDvdDriveSettings(vmSettings); - InsertDiskImage(vm, isoPath, IDE_ISO_DISK, driveWmiObj.Path); + InsertDiskImage(vm, isoPath, ISO_DISK, driveWmiObj.Path); } - - private static ResourceAllocationSettingData CloneResourceAllocationSetting(string wmiQuery) { var defaultDiskDriveSettingsObjs = ResourceAllocationSettingData.GetInstances(wmiQuery); @@ -834,7 +921,7 @@ namespace HypervResource } else { - AttachIsoToVm(vm, iso); + AttachIso(vm, iso); } } @@ -1481,7 +1568,7 @@ namespace HypervResource // Is there a template we can use to fill in the settings? var newVirtHDSettings = VirtualHardDiskSettingData.CreateInstance(); newVirtHDSettings.LateBoundObject["Type"] = 3; // Dynamic - newVirtHDSettings.LateBoundObject["Format"] = 3; // VHDX + newVirtHDSettings.LateBoundObject["Format"] = 2; // VHD newVirtHDSettings.LateBoundObject["Path"] = Path; newVirtHDSettings.LateBoundObject["MaxInternalSize"] = MaxInternalSize; newVirtHDSettings.LateBoundObject["BlockSize"] = 0; // Use defaults @@ -1651,16 +1738,18 @@ namespace HypervResource } } - public void GetProcessorResources(out uint cores, out uint mhz) + public void GetProcessorResources(out uint sockets, out uint cores, out uint mhz) { // Processor processors cores = 0; mhz = 0; + sockets = 0; Processor.ProcessorCollection procCol = Processor.GetInstances(); foreach (Processor procInfo in procCol) { cores += procInfo.NumberOfCores; mhz = procInfo.MaxClockSpeed; + sockets++; } } @@ -1827,7 +1916,7 @@ namespace HypervResource foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) { - if (wmiObj.ResourceSubType == IDE_HARDDISK_CONTROLLER && wmiObj.Address == cntrllerAddr) + if (wmiObj.ResourceSubType == IDE_CONTROLLER && wmiObj.Address == cntrllerAddr) { return wmiObj; } @@ -1842,6 +1931,26 @@ namespace HypervResource throw ex; } + public ResourceAllocationSettingData GetScsiControllerSettings(VirtualSystemSettingData vmSettings) + { + var wmiObjCollection = GetResourceAllocationSettings(vmSettings); + + foreach (ResourceAllocationSettingData wmiObj in wmiObjCollection) + { + if (wmiObj.ResourceSubType == SCSI_CONTROLLER) + { + return wmiObj; + } + } + + var errMsg = string.Format( + "Cannot find the Microsoft Synthetic SCSI Controller in VirtualSystemSettingData {1}", + vmSettings.Path.Path); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + /// /// VM resources, typically hardware a described by a generic MSVM_ResourceAllocationSettingData object. The hardware type being /// described is identified in two ways: in general terms using an enum in the ResourceType field, and in terms of the implementation diff --git a/plugins/hypervisors/hyperv/resources/META-INF/cloudstack/hyperv-compute/spring-hyperv-compute-context.xml b/plugins/hypervisors/hyperv/resources/META-INF/cloudstack/hyperv-compute/spring-hyperv-compute-context.xml index 63521618df5..a128fbc8876 100644 --- a/plugins/hypervisors/hyperv/resources/META-INF/cloudstack/hyperv-compute/spring-hyperv-compute-context.xml +++ b/plugins/hypervisors/hyperv/resources/META-INF/cloudstack/hyperv-compute/spring-hyperv-compute-context.xml @@ -20,5 +20,7 @@ - + + + diff --git a/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java b/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java new file mode 100644 index 00000000000..cebfb7ac70c --- /dev/null +++ b/plugins/hypervisors/hyperv/src/com/cloud/ha/HypervInvestigator.java @@ -0,0 +1,77 @@ +/* + * 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.ha; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.CheckOnHostCommand; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.resource.ResourceManager; +import com.cloud.utils.component.AdapterBase; +import org.apache.log4j.Logger; + +import javax.ejb.Local; +import javax.inject.Inject; +import java.util.List; + +@Local(value=Investigator.class) +public class HypervInvestigator extends AdapterBase implements Investigator { + private final static Logger s_logger = Logger.getLogger(HypervInvestigator.class); + @Inject HostDao _hostDao; + @Inject AgentManager _agentMgr; + @Inject ResourceManager _resourceMgr; + + @Override + public Boolean isVmAlive(com.cloud.vm.VirtualMachine vm, Host host) { + Status status = isAgentAlive(host); + if (status == null) { + return null; + } + return status == Status.Up ? true : null; + } + + @Override + public Status isAgentAlive(Host agent) { + if (agent.getHypervisorType() != Hypervisor.HypervisorType.Hyperv) { + return null; + } + CheckOnHostCommand cmd = new CheckOnHostCommand(agent); + List neighbors = _resourceMgr.listHostsInClusterByStatus(agent.getClusterId(), Status.Up); + for (HostVO neighbor : neighbors) { + if (neighbor.getId() == agent.getId() || neighbor.getHypervisorType() != Hypervisor.HypervisorType.Hyperv) { + continue; + } + + try { + Answer answer = _agentMgr.easySend(neighbor.getId(), cmd); + if (answer != null) { + return answer.getResult() ? Status.Down : Status.Up; + } + } catch (Exception e) { + s_logger.debug("Failed to send command to host: " + neighbor.getId()); + } + } + + return null; + } +} diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/manager/HypervManagerImpl.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/manager/HypervManagerImpl.java index e6d8e1ad4fe..a30eb7df005 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/manager/HypervManagerImpl.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/manager/HypervManagerImpl.java @@ -230,11 +230,15 @@ public class HypervManagerImpl implements HypervManager { Script script = null; String result = null; if (scheme.equals("cifs")) { + String user = System.getProperty("user.name"); Script command = new Script(true, "mount", _timeout, s_logger); command.add("-t", "cifs"); command.add(path); command.add(mountPoint); - command.add("-o", "uid=`whoami`,gid=`whoami`"); + + if (user != null) { + command.add("-o", "uid=" + user + ",gid=" + user); + } if (query != null) { query = query.replace('&', ','); diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index 5de3d7012fd..72a59216c2f 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -19,10 +19,12 @@ package com.cloud.hypervisor.hyperv.resource; import java.io.File; import java.io.IOException; import java.net.ConnectException; +import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.SocketChannel; +import java.rmi.RemoteException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -76,9 +78,11 @@ import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetFirewallRulesAnswer; import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetMonitorServiceCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetSourceNatAnswer; @@ -89,6 +93,7 @@ import com.cloud.agent.api.routing.SetStaticRouteAnswer; import com.cloud.agent.api.routing.SetStaticRouteCommand; import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand; import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.api.routing.VpnUsersCfgCommand; import com.cloud.agent.api.to.DhcpTO; import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; @@ -183,8 +188,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S s_logger.debug("Generated StartupRoutingCommand for _agentIp \"" + _agentIp + "\""); - // TODO: does version need to be hard coded. - defaultStartRoutCmd.setVersion("4.2.0"); + defaultStartRoutCmd.setVersion(this.getClass().getPackage().getImplementationVersion()); // Specifics of the host's resource capacity and network configuration // comes from the host itself. CloudStack sanity checks network @@ -380,9 +384,15 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } else if (clazz == Site2SiteVpnCfgCommand.class) { answer = execute((Site2SiteVpnCfgCommand)cmd); } else if (clazz == CheckS2SVpnConnectionsCommand.class) { - answer = execute((CheckS2SVpnConnectionsCommand)cmd); + answer = execute((CheckS2SVpnConnectionsCommand) cmd); + } else if (clazz == RemoteAccessVpnCfgCommand.class) { + answer = execute((RemoteAccessVpnCfgCommand) cmd); + } else if (clazz == VpnUsersCfgCommand.class) { + answer = execute((VpnUsersCfgCommand) cmd); } else if (clazz == SetStaticRouteCommand.class) { - answer = execute((SetStaticRouteCommand)cmd); + answer = execute((SetStaticRouteCommand) cmd); + } else if (clazz == SetMonitorServiceCommand.class) { + answer = execute((SetMonitorServiceCommand) cmd); } else { if (clazz == StartCommand.class) { VirtualMachineTO vmSpec = ((StartCommand)cmd).getVirtualMachine(); @@ -413,7 +423,87 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } return answer; } + protected Answer execute(final RemoteAccessVpnCfgCommand cmd) { + String controlIp = getRouterSshControlIp(cmd); + StringBuffer argsBuf = new StringBuffer(); + if (cmd.isCreate()) { + argsBuf.append(" -r ").append(cmd.getIpRange()).append(" -p ").append(cmd.getPresharedKey()).append(" -s ").append(cmd.getVpnServerIp()).append(" -l ").append(cmd.getLocalIp()) + .append(" -c "); + } else { + argsBuf.append(" -d ").append(" -s ").append(cmd.getVpnServerIp()); + } + argsBuf.append(" -C ").append(cmd.getLocalCidr()); + argsBuf.append(" -i ").append(cmd.getPublicInterface()); + + try { + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Executing /opt/cloud/bin/vpn_lt2p.sh "); + } + + Pair result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/vpn_l2tp.sh " + argsBuf.toString()); + + if (!result.first()) { + s_logger.error("RemoteAccessVpnCfg command on domR failed, message: " + result.second()); + + return new Answer(cmd, false, "RemoteAccessVpnCfg command failed due to " + result.second()); + } + + if (s_logger.isInfoEnabled()) { + s_logger.info("RemoteAccessVpnCfg command on domain router " + argsBuf.toString() + " completed"); + } + + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn(e.getMessage()); + } + + String msg = "RemoteAccessVpnCfg command failed due to " + e.getMessage(); + s_logger.error(msg, e); + return new Answer(cmd, false, msg); + } + + return new Answer(cmd); + } + + protected Answer execute(final VpnUsersCfgCommand cmd) { + + String controlIp = getRouterSshControlIp(cmd); + for (VpnUsersCfgCommand.UsernamePassword userpwd : cmd.getUserpwds()) { + StringBuffer argsBuf = new StringBuffer(); + if (!userpwd.isAdd()) { + argsBuf.append(" -U ").append(userpwd.getUsername()); + } else { + argsBuf.append(" -u ").append(userpwd.getUsernamePassword()); + } + + try { + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Executing /opt/cloud/bin/vpn_lt2p.sh "); + } + + Pair result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/vpn_l2tp.sh " + argsBuf.toString()); + + if (!result.first()) { + s_logger.error("VpnUserCfg command on domR failed, message: " + result.second()); + + return new Answer(cmd, false, "VpnUserCfg command failed due to " + result.second()); + } + } catch (Throwable e) { + if (e instanceof RemoteException) { + s_logger.warn(e.getMessage()); + } + + String msg = "VpnUserCfg command failed due to " + e.getMessage(); + s_logger.error(msg, e); + return new Answer(cmd, false, msg); + } + } + + return new Answer(cmd); + } private SetStaticRouteAnswer execute(SetStaticRouteCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource SetStaticRouteCommand: " + s_gson.toJson(cmd)); @@ -1451,6 +1541,35 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S return stats; } + protected Answer execute(SetMonitorServiceCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource SetMonitorServiceCommand: " + s_gson.toJson(cmd)); + } + + String controlIp = getRouterSshControlIp(cmd); + String config = cmd.getConfiguration(); + + String args = ""; + + args += " -c " + config; + + try { + Pair result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, "/opt/cloud/bin/monitor_service.sh " + args); + + if (!result.first()) { + String msg= "monitor_service.sh failed on domain router " + controlIp + " failed " + result.second(); + s_logger.error(msg); + return new Answer(cmd, false, msg); + } + + return new Answer(cmd); + + } catch (Throwable e) { + s_logger.error("Unexpected exception: " + e.toString(), e); + return new Answer(cmd, false, "SetMonitorServiceCommand failed due to " + e); + } + } + protected CheckSshAnswer execute(CheckSshCommand cmd) { String vmName = cmd.getName(); String privateIp = cmd.getIp(); @@ -1667,10 +1786,9 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S sch = SocketChannel.open(); sch.configureBlocking(true); sch.socket().setSoTimeout(5000); - // we need to connect to the public ip address to check the status of the VM - /* + // we need to connect to the control ip address to check the status of the system vm InetSocketAddress addr = new InetSocketAddress(ipAddress, port); - sch.connect(addr);*/ + sch.connect(addr); return null; } catch (IOException e) { s_logger.info("Could not connect to " + ipAddress + " due to " + e.toString()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java index b3bd3b3fcfd..57b111e6e34 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java @@ -120,7 +120,8 @@ public class BridgeVifDriver extends VifDriverBase { intf.defBridgeNet(_bridges.get("linklocal"), null, nic.getMac(), getGuestNicModel(guestOsType)); } else if (nic.getType() == Networks.TrafficType.Public) { Integer networkRateKBps = (nic.getNetworkRateMbps() != null && nic.getNetworkRateMbps().intValue() != -1) ? nic.getNetworkRateMbps().intValue() * 128 : 0; - if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan && !vNetId.equalsIgnoreCase("untagged")) { + if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan && !vNetId.equalsIgnoreCase("untagged") || + nic.getBroadcastType() == Networks.BroadcastDomainType.Vxlan) { if (trafficLabel != null && !trafficLabel.isEmpty()) { s_logger.debug("creating a vNet dev and bridge for public traffic per traffic label " + trafficLabel); String brName = createVnetBr(vNetId, trafficLabel, protocol); 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 7a02f3b5bdc..71c8e979bc5 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 @@ -356,7 +356,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } protected static final MessageFormat SnapshotXML = new MessageFormat(" " + " {0}" + " " - + " {1}" + " " + " "); + + " {1}" + " " + " "); protected HypervisorType _hypervisorType; protected String _hypervisorURI; @@ -745,7 +745,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv /* Does node support HVM guest? If not, exit */ if (!IsHVMEnabled(conn)) { throw new ConfigurationException("NO HVM support on this machine, please make sure: " + "1. VT/SVM is supported by your CPU, or is enabled in BIOS. " - + "2. kvm modules are loaded (kvm, kvm_amd|kvm_intel)"); + + "2. kvm modules are loaded (kvm, kvm_amd|kvm_intel)"); } } @@ -765,7 +765,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (_hypervisorLibvirtVersion < (9 * 1000 + 10)) { s_logger.warn("LibVirt version 0.9.10 required for guest cpu mode, but version " + prettyVersion(_hypervisorLibvirtVersion) + - " detected, so it will be disabled"); + " detected, so it will be disabled"); _guestCpuMode = ""; _guestCpuModel = ""; } @@ -796,13 +796,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } switch (_bridgeType) { - case OPENVSWITCH: - getOvsPifs(); - break; - case NATIVE: - default: - getPifs(); - break; + case OPENVSWITCH: + getOvsPifs(); + break; + case NATIVE: + default: + getPifs(); + break; } if (_pifs.get("private") == null) { @@ -989,6 +989,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } + // public creates bridges on a pif, if private bridge not found try pif direct + // This addresses the unnecessary requirement of someone to create an unused bridge just for traffic label + if (_pifs.get("public") == null) { + s_logger.debug("public traffic label '" + _publicBridgeName+ "' not found as bridge, looking for physical interface"); + File dev = new File("/sys/class/net/" + _publicBridgeName); + if (dev.exists()) { + s_logger.debug("public traffic label '" + _publicBridgeName + "' found as a physical device"); + _pifs.put("public", _publicBridgeName); + } + } + s_logger.debug("done looking for pifs, no more bridges"); } @@ -1042,8 +1053,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv for (int i = 0; i < interfaces.length; i++) { String fname = interfaces[i].getName(); s_logger.debug("matchPifFileInDirectory: file name '" + fname + "'"); - if (fname.startsWith("eth") || fname.startsWith("bond") || fname.startsWith("vlan") || fname.startsWith("vxlan") || fname.startsWith("em") || - fname.matches("^p\\d+p\\d+.*")) { + if (fname.startsWith("eth") || fname.startsWith("bond") || fname.startsWith("vlan") || fname.startsWith("vx") || fname.startsWith("em") || + fname.matches("^p\\d+p\\d+.*")) { return fname; } } @@ -1088,7 +1099,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv command.add("-c"); command.add("ovs-vsctl br-exists " + networkName); String result = command.execute(null); - if ("Ok".equals(result)) { + if ("0".equals(result)) { return true; } else { return false; @@ -1130,7 +1141,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv This also makes sure we never have any old "garbage" defined in libvirt which might haunt us. - */ + */ // check for existing inactive vm definition and remove it // this can sometimes happen during crashes, etc @@ -1311,12 +1322,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv String mask = Script.runSimpleBashScript("ifconfig " + label + " | grep 'inet addr:' | cut -d: -f4"); String mac = Script.runSimpleBashScript("ifconfig " + label + " | grep HWaddr | awk -F \" \" '{print $5}'"); return new OvsFetchInterfaceAnswer(cmd, true, "Interface " + label - + " retrieved successfully", ipadd, mask, mac); + + " retrieved successfully", ipadd, mask, mac); } catch (Exception e) { s_logger.warn("Caught execption when fetching interface", e); return new OvsFetchInterfaceAnswer(cmd, false, "EXCEPTION:" - + e.getMessage()); + + e.getMessage()); } } @@ -1324,7 +1335,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv private Answer execute(OvsSetupBridgeCommand cmd) { findOrCreateTunnelNetwork(cmd.getKey()); configureTunnelNetwork(cmd.getNetworkId(), cmd.getHostId(), - cmd.getKey()); + cmd.getKey()); s_logger.debug("OVS Bridge configured"); return new Answer(cmd, true, null); } @@ -1367,8 +1378,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv Map otherConfig = new HashMap(); otherConfig.put("ovs-host-setup", ""); Script.runSimpleBashScript("ovs-vsctl -- --may-exist add-br " - + nwName + " -- set bridge " + nwName - + " other_config:ovs_host_setup='-1'"); + + nwName + " -- set bridge " + nwName + + " other_config:ovs_host_setup='-1'"); s_logger.debug("### KVM network for tunnels created:" + nwName); } catch (Exception e) { s_logger.warn("createTunnelNetwork failed", e); @@ -1377,13 +1388,13 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private synchronized boolean configureTunnelNetwork(long networkId, - long hostId, int key) { + long hostId, int key) { try { findOrCreateTunnelNetwork(key); String nwName = "OVSTunnel" + key; String configuredHosts = Script - .runSimpleBashScript("ovs-vsctl get bridge " + nwName - + " other_config:ovs_host_setup"); + .runSimpleBashScript("ovs-vsctl get bridge " + nwName + + " other_config:ovs_host_setup"); boolean configured = false; if (configuredHosts != null) { String hostIdsStr[] = configuredHosts.split(","); @@ -1403,7 +1414,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv String result = cmd.execute(); if (result != null) { throw new CloudRuntimeException( - "Unable to pre-configure OVS bridge " + nwName + "Unable to pre-configure OVS bridge " + nwName + " for network ID:" + networkId); } } @@ -1420,11 +1431,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (!findOrCreateTunnelNetwork(cmd.getKey())) { s_logger.debug("Error during bridge setup"); return new OvsCreateTunnelAnswer(cmd, false, - "Cannot create network", bridge); + "Cannot create network", bridge); } configureTunnelNetwork(cmd.getNetworkId(), cmd.getFrom(), - cmd.getKey()); + cmd.getKey()); Script command = new Script(_ovsTunnelPath, _timeout, s_logger); command.add("create_tunnel"); command.add("--bridge", bridge); @@ -1436,7 +1447,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv String result = command.execute(); if (result != null) { return new OvsCreateTunnelAnswer(cmd, true, result, null, - bridge); + bridge); } else { return new OvsCreateTunnelAnswer(cmd, false, result, bridge); } @@ -1451,7 +1462,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv try { if (!findOrCreateTunnelNetwork(cmd.getKey())) { s_logger.warn("Unable to find tunnel network for GRE key:" - + cmd.getKey()); + + cmd.getKey()); return new Answer(cmd, false, "No network found"); } @@ -1502,7 +1513,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv It COULD also do it the other way around, but the code in the ManagementServerImpl shows that it always sets copyToSecondary to true - */ + */ boolean copyToSecondary = cmd.toSecondaryStorage(); String volumePath = cmd.getVolumePath(); StorageFilerTO pool = cmd.getPool(); @@ -1515,8 +1526,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } catch (CloudRuntimeException e) { if (e.getMessage().contains("not found")) { primaryPool = - _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), - cmd.getPool().getUserInfo(), cmd.getPool().getType()); + _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), + cmd.getPool().getUserInfo(), cmd.getPool().getType()); } else { return new CopyVolumeAnswer(cmd, false, e.getMessage(), null, null); } @@ -1630,7 +1641,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv vol = primaryPool.createPhysicalDisk(dskch.getPath(), dskch.getSize()); } VolumeTO volume = - new VolumeTO(cmd.getVolumeId(), dskch.getType(), pool.getType(), pool.getUuid(), pool.getPath(), vol.getName(), vol.getName(), disksize, null); + new VolumeTO(cmd.getVolumeId(), dskch.getType(), pool.getType(), pool.getUuid(), pool.getPath(), vol.getName(), vol.getName(), disksize, null); volume.setBytesReadRate(dskch.getBytesReadRate()); volume.setBytesWriteRate(dskch.getBytesWriteRate()); volume.setIopsReadRate(dskch.getIopsReadRate()); @@ -1699,7 +1710,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (pool.getType() == StoragePoolType.CLVM && volFormat == PhysicalDiskFormat.RAW) { return "CLVM"; } else if ((poolType == StoragePoolType.NetworkFilesystem || poolType == StoragePoolType.SharedMountPoint || poolType == StoragePoolType.Filesystem) && - volFormat == PhysicalDiskFormat.QCOW2) { + volFormat == PhysicalDiskFormat.QCOW2) { return "QCOW2"; } return null; @@ -1759,7 +1770,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } s_logger.debug("got to the stage where we execute the volume resize, params:" + path + "," + currentSize + "," + newSize + "," + type + "," + - vmInstanceName + "," + shrinkOk); + vmInstanceName + "," + shrinkOk); final Script resizecmd = new Script(_resizeVolumePath, _cmdsTimeout, s_logger); resizecmd.add("-s", String.valueOf(newSize)); resizecmd.add("-c", String.valueOf(currentSize)); @@ -1804,11 +1815,20 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv String pif = matchPifFileInDirectory(brName); Pattern pattern = Pattern.compile("(\\D+)(\\d+)(\\D*)(\\d*)"); Matcher matcher = pattern.matcher(pif); - if (matcher.find()) { - if (brName.startsWith("brvx")) { + s_logger.debug("getting broadcast uri for pif " + pif + " and bridge " + brName); + if(matcher.find()) { + if (brName.startsWith("brvx")){ return BroadcastDomainType.Vxlan.toUri(matcher.group(2)).toString(); - } else { - return BroadcastDomainType.Vlan.toUri(matcher.group(4)).toString(); + } + else{ + if (!matcher.group(4).isEmpty()) { + return BroadcastDomainType.Vlan.toUri(matcher.group(4)).toString(); + } else { + //untagged or not matching (eth|bond)#.# + s_logger.debug("failed to get vNet id from bridge " + brName + + "attached to physical interface" + pif + ", perhaps untagged interface"); + return ""; + } } } else { s_logger.debug("failed to get vNet id from bridge " + brName + "attached to physical interface" + pif); @@ -1842,7 +1862,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv List ifaces = getInterfaces(conn, dhcpName); InterfaceDef guestNic = ifaces.get(0); script.add(opr, "-b", _guestBridgeName, "-p", primaryPvlan, "-i", isolatedPvlan, "-n", dhcpName, "-d", dhcpIp, "-m", dhcpMac, "-I", - guestNic.getDevName()); + guestNic.getDevName()); } else { script.add(opr, "-b", _guestBridgeName, "-p", primaryPvlan, "-i", isolatedPvlan, "-n", dhcpName, "-d", dhcpIp, "-m", dhcpMac); } @@ -2083,7 +2103,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } else if (pluggedVlanId == null) { /*this should only be true in the case of link local bridge*/ return new SetSourceNatAnswer(cmd, false, "unable to find the vlan id for bridge " + pluggedVlanBr + " when attempting to set up" + pubVlan + - " on router " + routerName); + " on router " + routerName); } else if (pluggedVlanId.equals(pubVlan)) { break; } @@ -2123,7 +2143,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv if (pluggedVlan.equalsIgnoreCase(_linkLocalBridgeName)) { broadcastUriToNicNum.put("LinkLocal", devNum); } else if (pluggedVlan.equalsIgnoreCase(_publicBridgeName) || pluggedVlan.equalsIgnoreCase(_privBridgeName) || - pluggedVlan.equalsIgnoreCase(_guestBridgeName)) { + pluggedVlan.equalsIgnoreCase(_guestBridgeName)) { broadcastUriToNicNum.put(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED).toString(), devNum); } else { broadcastUriToNicNum.put(getBroadcastUriFromBridge(pluggedVlan), devNum); @@ -2168,7 +2188,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv broadcastUriAllocatedToVM.put("LinkLocal", nicPos); } else { if (nic.getBrName().equalsIgnoreCase(_publicBridgeName) || nic.getBrName().equalsIgnoreCase(_privBridgeName) || - nic.getBrName().equalsIgnoreCase(_guestBridgeName)) { + nic.getBrName().equalsIgnoreCase(_guestBridgeName)) { broadcastUriAllocatedToVM.put(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED).toString(), nicPos); } else { String broadcastUri = getBroadcastUriFromBridge(nic.getBrName()); @@ -2192,8 +2212,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv nicNum = broadcastUriAllocatedToVM.get(ip.getBroadcastUri()); networkUsage(routerIp, "addVif", "eth" + nicNum); result = - _virtRouterResource.assignPublicIpAddress(routerName, routerIp, ip.getPublicIp(), ip.isAdd(), ip.isFirstIP(), ip.isSourceNat(), ip.getBroadcastUri(), - ip.getVlanGateway(), ip.getVlanNetmask(), ip.getVifMacAddress(), nicNum, newNic); + _virtRouterResource.assignPublicIpAddress(routerName, routerIp, ip.getPublicIp(), ip.isAdd(), ip.isFirstIP(), ip.isSourceNat(), ip.getBroadcastUri(), + ip.getVlanGateway(), ip.getVlanNetmask(), ip.getVifMacAddress(), nicNum, newNic); if (result == null) { results[i++] = ip.getPublicIp() + " - success"; @@ -2358,8 +2378,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv Rbd rbd = new Rbd(io); RbdImage image = rbd.open(snapshotDisk.getName(), snapshotName); - long startTime = System.currentTimeMillis() / 1000; - File fh = new File(snapshotDestPath); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fh)); int chunkSize = 4194304; @@ -2562,8 +2580,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } catch (CloudRuntimeException e) { if (e.getMessage().contains("not found")) { primary = - _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), - cmd.getPool().getUserInfo(), cmd.getPool().getType()); + _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), + cmd.getPool().getUserInfo(), cmd.getPool().getType()); } else { return new CreatePrivateTemplateAnswer(cmd, false, e.getMessage()); } @@ -2589,8 +2607,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName()); QemuImgFile srcFile = - new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(), - primary.getAuthSecret(), disk.getPath())); + new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(), + primary.getAuthSecret(), disk.getPath())); srcFile.setFormat(PhysicalDiskFormat.RAW); QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + cmd.getUniqueName() + ".qcow2"); @@ -2601,7 +2619,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv q.convert(srcFile, destFile); } catch (QemuImgException e) { s_logger.error("Failed to create new template while converting " + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " + - e.getMessage()); + e.getMessage()); } File templateProp = new File(tmpltPath + "/template.properties"); @@ -2635,7 +2653,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv loc.save(); return new CreatePrivateTemplateAnswer(cmd, true, null, templateInstallFolder + cmd.getUniqueName() + ".qcow2", info.virtualSize, info.size, - cmd.getUniqueName(), ImageFormat.QCOW2); + cmd.getUniqueName(), ImageFormat.QCOW2); } catch (InternalErrorException e) { return new CreatePrivateTemplateAnswer(cmd, false, e.toString()); } catch (IOException e) { @@ -2706,8 +2724,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv protected Answer execute(ModifyStoragePoolCommand cmd) { KVMStoragePool storagepool = - _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), cmd.getPool() - .getUserInfo(), cmd.getPool().getType()); + _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(), cmd.getPool() + .getUserInfo(), cmd.getPool().getType()); if (storagepool == null) { return new Answer(cmd, false, " Failed to create storage pool"); } @@ -2731,15 +2749,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } boolean result = - add_network_rules(cmd.getVmName(), Long.toString(cmd.getVmId()), cmd.getGuestIp(), cmd.getSignature(), Long.toString(cmd.getSeqNum()), cmd.getGuestMac(), - cmd.stringifyRules(), vif, brname, cmd.getSecIpsString()); + add_network_rules(cmd.getVmName(), Long.toString(cmd.getVmId()), cmd.getGuestIp(), cmd.getSignature(), Long.toString(cmd.getSeqNum()), cmd.getGuestMac(), + cmd.stringifyRules(), vif, brname, cmd.getSecIpsString()); if (!result) { s_logger.warn("Failed to program network rules for vm " + cmd.getVmName()); return new SecurityGroupRuleAnswer(cmd, false, "programming network rules failed"); } else { s_logger.debug("Programmed network rules for vm " + cmd.getVmName() + " guestIp=" + cmd.getGuestIp() + ",ingress numrules=" + cmd.getIngressRuleSet().length + - ",egress numrules=" + cmd.getEgressRuleSet().length); + ",egress numrules=" + cmd.getEgressRuleSet().length); return new SecurityGroupRuleAnswer(cmd); } } @@ -2830,8 +2848,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv KVMStoragePool primary = _storagePoolMgr.getStoragePool(cmd.getPooltype(), cmd.getPoolUuid()); KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath()); attachOrDetachDisk(conn, cmd.getAttach(), cmd.getVmName(), disk, - cmd.getDeviceId().intValue(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), - cmd.getCacheMode()); + cmd.getDeviceId().intValue(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), + cmd.getCacheMode()); } catch (LibvirtException e) { return new AttachVolumeAnswer(cmd, e.toString()); } catch (InternalErrorException e) { @@ -2965,7 +2983,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv description for the instance to be used on the target host. This is supported by libvirt-java from version 0.50.0 - */ + */ xmlDesc = dm.getXMLDesc(0).replace(_privateIp, cmd.getDestinationIp()); dconn = new Connect("qemu+tcp://" + cmd.getDestinationIp() + "/system"); @@ -3477,12 +3495,21 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } else { grd.setMemorySize(vmTO.getMaxRam() / 1024); } - grd.setVcpuNum(vmTO.getCpus()); + int vcpus = vmTO.getCpus(); + grd.setVcpuNum(vcpus); vm.addComp(grd); CpuModeDef cmd = new CpuModeDef(); cmd.setMode(_guestCpuMode); cmd.setModel(_guestCpuModel); + // multi cores per socket, for larger core configs + if (vcpus % 6 == 0) { + int sockets = vcpus / 6; + cmd.setTopology(6, sockets); + } else if (vcpus % 4 == 0) { + int sockets = vcpus / 4; + cmd.setTopology(4, sockets); + } vm.addComp(cmd); if (_hypervisorLibvirtVersion >= 9000) { @@ -3739,7 +3766,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv we pass the pool's UUID as the authSecret */ disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(), pool.getAuthUserName(), - pool.getUuid(), devId, diskBusType, diskProtocol.RBD); + pool.getUuid(), devId, diskBusType, diskProtocol.RBD); } else if (pool.getType() == StoragePoolType.CLVM || physicalDisk.getFormat() == PhysicalDiskFormat.RAW) { disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusType); } else { @@ -3835,7 +3862,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } protected synchronized String attachOrDetachISO(Connect conn, String vmName, String isoPath, boolean isAttach) throws LibvirtException, URISyntaxException, - InternalErrorException { + InternalErrorException { String isoXml = null; if (isoPath != null && isAttach) { int index = isoPath.lastIndexOf("/"); @@ -3868,8 +3895,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } protected synchronized String attachOrDetachDisk(Connect conn, - boolean attach, String vmName, KVMPhysicalDisk attachingDisk, - int devId, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, String cacheMode) throws LibvirtException, InternalErrorException { + boolean attach, String vmName, KVMPhysicalDisk attachingDisk, + int devId, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, String cacheMode) throws LibvirtException, InternalErrorException { List disks = null; Domain dm = null; DiskDef diskdef = null; @@ -3896,7 +3923,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv diskdef = new DiskDef(); if (attachingPool.getType() == StoragePoolType.RBD) { diskdef.defNetworkBasedDisk(attachingDisk.getPath(), attachingPool.getSourceHost(), attachingPool.getSourcePort(), attachingPool.getAuthUserName(), - attachingPool.getUuid(), devId, DiskDef.diskBus.VIRTIO, diskProtocol.RBD); + attachingPool.getUuid(), devId, DiskDef.diskBus.VIRTIO, diskProtocol.RBD); } else if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) { diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, DiskDef.diskBus.VIRTIO, DiskDef.diskFmtType.QCOW2); } else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) { @@ -3996,8 +4023,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv final List info = getHostInfo(); final StartupRoutingCommand cmd = - new StartupRoutingCommand((Integer)info.get(0), (Long)info.get(1), (Long)info.get(2), (Long)info.get(4), (String)info.get(3), _hypervisorType, - RouterPrivateIpStrategy.HostLocal); + new StartupRoutingCommand((Integer)info.get(0), (Long)info.get(1), (Long)info.get(2), (Long)info.get(4), (String)info.get(3), _hypervisorType, + RouterPrivateIpStrategy.HostLocal); cmd.setStateChanges(changes); cmd.setCpuSockets((Integer)info.get(5)); fillNetworkInformation(cmd); @@ -4007,14 +4034,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv cmd.setCluster(_clusterId); cmd.setGatewayIpAddress(_localGateway); cmd.setHostVmStateReport(getHostVmStateReport()); + cmd.setIqn(getIqn()); StartupStorageCommand sscmd = null; try { KVMStoragePool localStoragePool = _storagePoolMgr.createStoragePool(_localStorageUUID, "localhost", -1, _localStoragePath, "", StoragePoolType.Filesystem); com.cloud.agent.api.StoragePoolInfo pi = - new com.cloud.agent.api.StoragePoolInfo(localStoragePool.getUuid(), cmd.getPrivateIpAddress(), _localStoragePath, _localStoragePath, - StoragePoolType.Filesystem, localStoragePool.getCapacity(), localStoragePool.getAvailable()); + new com.cloud.agent.api.StoragePoolInfo(localStoragePool.getUuid(), cmd.getPrivateIpAddress(), _localStoragePath, _localStoragePath, + StoragePoolType.Filesystem, localStoragePool.getCapacity(), localStoragePool.getAvailable()); sscmd = new StartupStorageCommand(); sscmd.setPoolInfo(pi); @@ -4032,6 +4060,32 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } } + private String getIqn() { + try { + final String textToFind = "InitiatorName="; + + Script iScsiAdmCmd = new Script(true, "grep", 0, s_logger); + + iScsiAdmCmd.add(textToFind); + iScsiAdmCmd.add("/etc/iscsi/initiatorname.iscsi"); + + OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser(); + + String result = iScsiAdmCmd.execute(parser); + + if (result != null) { + return null; + } + + String textFound = parser.getLine().trim(); + + return textFound.substring(textToFind.length()); + } + catch (Exception ex) { + return null; + } + } + protected HashMap sync() { HashMap newStates; HashMap oldStates = null; @@ -4694,11 +4748,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } String guestOSName = KVMGuestOsMapper.getGuestOsName(guestOS); if (guestOS.startsWith("Ubuntu") || guestOSName.startsWith("Fedora 13") || guestOSName.startsWith("Fedora 12") || guestOSName.startsWith("Fedora 11") || - guestOSName.startsWith("Fedora 10") || guestOSName.startsWith("Fedora 9") || guestOSName.startsWith("CentOS 5.3") || guestOSName.startsWith("CentOS 5.4") || - guestOSName.startsWith("CentOS 5.5") || guestOS.startsWith("CentOS") || guestOS.startsWith("Fedora") || - guestOSName.startsWith("Red Hat Enterprise Linux 5.3") || guestOSName.startsWith("Red Hat Enterprise Linux 5.4") || - guestOSName.startsWith("Red Hat Enterprise Linux 5.5") || guestOSName.startsWith("Red Hat Enterprise Linux 6") || guestOS.startsWith("Debian GNU/Linux") || - guestOSName.startsWith("Other PV")) { + guestOSName.startsWith("Fedora 10") || guestOSName.startsWith("Fedora 9") || guestOSName.startsWith("CentOS 5.3") || guestOSName.startsWith("CentOS 5.4") || + guestOSName.startsWith("CentOS 5.5") || guestOS.startsWith("CentOS") || guestOS.startsWith("Fedora") || + guestOSName.startsWith("Red Hat Enterprise Linux 5.3") || guestOSName.startsWith("Red Hat Enterprise Linux 5.4") || + guestOSName.startsWith("Red Hat Enterprise Linux 5.5") || guestOSName.startsWith("Red Hat Enterprise Linux 6") || guestOS.startsWith("Debian GNU/Linux") || + guestOSName.startsWith("Other PV")) { return true; } else { return false; @@ -5044,7 +5098,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv } private boolean add_network_rules(String vmName, String vmId, String guestIP, String sig, String seq, String mac, String rules, String vif, String brname, - String secIps) { + String secIps) { if (!_canBridgeFirewall) { return false; } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index 3e44e941490..9fd058fa3eb 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -949,6 +949,8 @@ public class LibvirtVMDef { public static class CpuModeDef { private String _mode; private String _model; + private int _coresPerSocket = -1; + private int _sockets = -1; public void setMode(String mode) { _mode = mode; @@ -958,17 +960,34 @@ public class LibvirtVMDef { _model = model; } + public void setTopology(int coresPerSocket, int sockets) { + _coresPerSocket = coresPerSocket; + _sockets = sockets; + } + @Override public String toString() { - StringBuilder modeBuidler = new StringBuilder(); - if ("custom".equalsIgnoreCase(_mode) && _model != null) { - modeBuidler.append("" + _model + ""); + StringBuilder modeBuilder = new StringBuilder(); + + // start cpu def, adding mode, model + if ("custom".equalsIgnoreCase(_mode) && _model != null){ + modeBuilder.append("" + _model + ""); } else if ("host-model".equals(_mode)) { - modeBuidler.append(""); + modeBuilder.append(""); } else if ("host-passthrough".equals(_mode)) { - modeBuidler.append(""); + modeBuilder.append(""); + } else { + modeBuilder.append(""); } - return modeBuidler.toString(); + + // add topology + if (_sockets > 0 && _coresPerSocket > 0) { + modeBuilder.append(""); + } + + // close cpu def + modeBuilder.append(""); + return modeBuilder.toString(); } } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java index dc60f077ac1..c64a472ade2 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java @@ -74,7 +74,7 @@ public class OvsVifDriver extends VifDriverBase { if (nic.getType() == Networks.TrafficType.Guest) { Integer networkRateKBps = (nic.getNetworkRateMbps() != null && nic.getNetworkRateMbps().intValue() != -1) ? nic.getNetworkRateMbps().intValue() * 128 : 0; if ((nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan || nic.getBroadcastType() == Networks.BroadcastDomainType.Pvlan) && - !vlanId.equalsIgnoreCase("untagged")) { + !vlanId.equalsIgnoreCase("untagged")) { if (trafficLabel != null && !trafficLabel.isEmpty()) { s_logger.debug("creating a vlan dev and bridge for guest traffic per traffic label " + trafficLabel); intf.defBridgeNet(_pifs.get(trafficLabel), null, nic.getMac(), getGuestNicModel(guestOsType), networkRateKBps); @@ -83,7 +83,7 @@ public class OvsVifDriver extends VifDriverBase { intf.defBridgeNet(_pifs.get("private"), null, nic.getMac(), getGuestNicModel(guestOsType), networkRateKBps); intf.setVlanTag(Integer.parseInt(vlanId)); } - } else if (nic.getBroadcastType() == Networks.BroadcastDomainType.Lswitch) { + } else if (nic.getBroadcastType() == Networks.BroadcastDomainType.Lswitch || nic.getBroadcastType() == Networks.BroadcastDomainType.OpenDaylight) { s_logger.debug("nic " + nic + " needs to be connected to LogicalSwitch " + logicalSwitchUuid); intf.setVirtualPortInterfaceId(nic.getUuid()); String brName = (trafficLabel != null && !trafficLabel.isEmpty()) ? _pifs.get(trafficLabel) : _pifs.get("private"); @@ -143,7 +143,7 @@ public class OvsVifDriver extends VifDriverBase { } if (!foundLinkLocalBr) { Script.runSimpleBashScript("ifconfig " + linkLocalBr + " 169.254.0.1;" + "ip route add " + NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " + - NetUtils.getLinkLocalGateway()); + NetUtils.getLinkLocalGateway()); } } @@ -159,7 +159,7 @@ public class OvsVifDriver extends VifDriverBase { command.add("-c"); command.add("ovs-vsctl br-exists " + bridgeName); String result = command.execute(null); - if ("Ok".equals(result)) { + if ("0".equals(result)) { return true; } else { return false; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index fefb29cad61..9e07e4bea29 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -171,6 +171,11 @@ public class KVMStoragePoolManager { KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid()); + if (pool == null) { + s_logger.error("Pool " + store.getUuid() + " of type " + store.getPoolType() + " was not found, skipping disconnect logic"); + continue; + } + StorageAdaptor adaptor = getStorageAdaptor(pool.getType()); // if a disk fails to disconnect, still try to disconnect remaining diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index d854ca57e38..7d5d3351220 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -189,6 +189,7 @@ public class KVMStorageProcessor implements StorageProcessor { } /* Copy volume to primary storage */ + s_logger.debug("Copying template to primary storage, template format is " + tmplVol.getFormat() ); KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); KVMPhysicalDisk primaryVol = null; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 82d0f8d41cf..fafbee08e36 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -684,10 +684,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { @Override public boolean disconnectPhysicalDiskByPath(String localPath) { // we've only ever cleaned up ISOs that are NFS mounted - String poolUuid = null; - - if (localPath != null && localPath.startsWith(_mountPoint)) { + if (localPath != null && localPath.startsWith(_mountPoint) && localPath.endsWith(".iso")) { String[] token = localPath.split("/"); if (token.length > 3) { @@ -978,6 +976,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { String sourcePath = disk.getPath(); KVMPhysicalDisk newDisk; + s_logger.debug("copyPhysicalDisk: disk size:" + disk.getSize() + ", virtualsize:" + disk.getVirtualSize()+" format:"+disk.getFormat()); if (destPool.getType() != StoragePoolType.RBD) { if (disk.getFormat() == PhysicalDiskFormat.TAR) { newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, disk.getVirtualSize()); @@ -1015,7 +1014,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { try { Map info = qemu.info(srcFile); String backingFile = info.get(new String("backing_file")); - if (sourceFormat.equals(destFormat) && backingFile == null) { + // qcow2 templates can just be copied into place + if (sourceFormat.equals(destFormat) && backingFile == null && sourcePath.endsWith(".qcow2")) { String result = Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath, timeout); if (result != null) { throw new CloudRuntimeException("Failed to create disk: " + result); diff --git a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java index 8c23f1ee802..a948ca1899f 100644 --- a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -184,8 +184,9 @@ public class QemuImg { public void convert(QemuImgFile srcFile, QemuImgFile destFile, Map options) throws QemuImgException { Script s = new Script(_qemuImgPath, timeout); s.add("convert"); - s.add("-f"); - s.add(srcFile.getFormat().toString()); + // autodetect source format. Sometime int he future we may teach KVMPhysicalDisk about more formats, then we can explicitly pass them if necessary + //s.add("-f"); + //s.add(srcFile.getFormat().toString()); s.add("-O"); s.add(destFile.getFormat().toString()); @@ -350,4 +351,4 @@ public class QemuImg { public void resize(QemuImgFile file, long size) throws QemuImgException { this.resize(file, size, false); } -} \ No newline at end of file +} diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 7b1eade7c47..c8af7f8b82b 100644 --- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -69,7 +69,7 @@ public class LibvirtComputingResourceTest { int id = _random.nextInt(65534); String name = "test-instance-1"; - int cpus = _random.nextInt(7) + 1; + int cpus = _random.nextInt(2) + 1; int speed = 1024; int minRam = 256 * 1024; int maxRam = 512 * 1024; @@ -124,6 +124,7 @@ public class LibvirtComputingResourceTest { //vmStr += "\n"; //vmStr += "" + (cpus * speed) + "\n"; //vmStr += "\n"; + vmStr += ""; vmStr += "restart\n"; vmStr += "destroy\n"; vmStr += "destroy\n"; @@ -131,6 +132,146 @@ public class LibvirtComputingResourceTest { assertEquals(vmStr, vm.toString()); } + /** + This test verifies that CPU topology is properly set for hex-core + */ + @Test + public void testCreateVMFromSpecWithTopology6() { + int id = _random.nextInt(65534); + String name = "test-instance-1"; + + int cpus = 12; + int minSpeed = 1024; + int maxSpeed = 2048; + int minRam = 256 * 1024; + int maxRam = 512 * 1024; + + String os = "Ubuntu"; + boolean haEnabled = false; + boolean limitCpuUse = false; + + String vncAddr = ""; + String vncPassword = "mySuperSecretPassword"; + + LibvirtComputingResource lcr = new LibvirtComputingResource(); + VirtualMachineTO to = new VirtualMachineTO(id, name, VirtualMachine.Type.User, cpus, minSpeed, maxSpeed, minRam, maxRam, BootloaderType.HVM, os, false, false, vncPassword); + to.setVncAddr(vncAddr); + to.setUuid("b0f0a72d-7efb-3cad-a8ff-70ebf30b3af9"); + + LibvirtVMDef vm = lcr.createVMFromSpec(to); + vm.setHvsType(_hyperVisorType); + + String vmStr = "\n"; + vmStr += "" + name + "\n"; + vmStr += "b0f0a72d-7efb-3cad-a8ff-70ebf30b3af9\n"; + vmStr += "" + os + "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "" + maxRam / 1024 + "\n"; + vmStr += "" + minRam / 1024 + "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "" + cpus + "\n"; + vmStr += "\n"; + vmStr += "hvm\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += ""; + vmStr += "restart\n"; + vmStr += "destroy\n"; + vmStr += "destroy\n"; + vmStr += "\n"; + + assertEquals(vmStr, vm.toString()); + } + + /** + This test verifies that CPU topology is properly set for quad-core + */ + @Test + public void testCreateVMFromSpecWithTopology4() { + int id = _random.nextInt(65534); + String name = "test-instance-1"; + + int cpus = 8; + int minSpeed = 1024; + int maxSpeed = 2048; + int minRam = 256 * 1024; + int maxRam = 512 * 1024; + + String os = "Ubuntu"; + boolean haEnabled = false; + boolean limitCpuUse = false; + + String vncAddr = ""; + String vncPassword = "mySuperSecretPassword"; + + LibvirtComputingResource lcr = new LibvirtComputingResource(); + VirtualMachineTO to = new VirtualMachineTO(id, name, VirtualMachine.Type.User, cpus, minSpeed, maxSpeed, minRam, maxRam, BootloaderType.HVM, os, false, false, vncPassword); + to.setVncAddr(vncAddr); + to.setUuid("b0f0a72d-7efb-3cad-a8ff-70ebf30b3af9"); + + LibvirtVMDef vm = lcr.createVMFromSpec(to); + vm.setHvsType(_hyperVisorType); + + String vmStr = "\n"; + vmStr += "" + name + "\n"; + vmStr += "b0f0a72d-7efb-3cad-a8ff-70ebf30b3af9\n"; + vmStr += "" + os + "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "" + maxRam / 1024 + "\n"; + vmStr += "" + minRam / 1024 + "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "" + cpus + "\n"; + vmStr += "\n"; + vmStr += "hvm\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += "\n"; + vmStr += ""; + vmStr += "restart\n"; + vmStr += "destroy\n"; + vmStr += "destroy\n"; + vmStr += "\n"; + + assertEquals(vmStr, vm.toString()); + } + /** This test tests if the Agent can handle a vmSpec coming from a >4.1 management server. @@ -143,7 +284,7 @@ public class LibvirtComputingResourceTest { int id = _random.nextInt(65534); String name = "test-instance-1"; - int cpus = _random.nextInt(7) + 1; + int cpus = _random.nextInt(2) + 1; int minSpeed = 1024; int maxSpeed = 2048; int minRam = 256 * 1024; @@ -200,6 +341,7 @@ public class LibvirtComputingResourceTest { //vmStr += "\n"; //vmStr += "" + (cpus * minSpeed) + "\n"; //vmStr += "\n"; + vmStr += ""; vmStr += "restart\n"; vmStr += "destroy\n"; vmStr += "destroy\n"; 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 b326c54d69b..d72787ca86e 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/guru/VMwareGuru.java @@ -203,7 +203,8 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co break; } } - long clusterId = _hostDao.findById(_vmDao.findById(vm.getId()).getHostId()).getClusterId(); + + long clusterId = this.getClusterId(vm.getId()); details.put(Config.VmwareReserveCpu.key(), VmwareReserveCpu.valueIn(clusterId).toString()); details.put(Config.VmwareReserveMem.key(), VmwareReserveMemory.valueIn(clusterId).toString()); to.setDetails(details); @@ -298,6 +299,20 @@ public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, Co return to; } + private long getClusterId(long vmId) { + long clusterId; + Long hostId; + + hostId = _vmDao.findById(vmId).getHostId(); + if (hostId == null) { + // If VM is in stopped state then hostId would be undefined. Hence read last host's Id instead. + hostId = _vmDao.findById(vmId).getLastHostId(); + } + clusterId = _hostDao.findById(hostId).getClusterId(); + + return clusterId; + } + private NicTO[] sortNicsByDeviceId(NicTO[] nics) { List listForSort = new ArrayList(); 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 fe91cae47fc..f78f370da31 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 @@ -16,6 +16,8 @@ // under the License. package com.cloud.hypervisor.vmware.manager; +import org.apache.cloudstack.storage.to.TemplateObjectTO; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; @@ -50,4 +52,6 @@ public interface VmwareStorageManager { boolean execute(VmwareHostService hostService, CreateEntityDownloadURLCommand cmd); public void createOva(String path, String name); + + public String createOvaForTemplate(TemplateObjectTO template); } 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 040a4cfd8f3..a7089cedda2 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 @@ -80,7 +80,7 @@ import com.cloud.storage.JavaStorageLayer; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.storage.Volume; -import com.cloud.storage.template.VmdkProcessor; +import com.cloud.storage.template.OVAProcessor; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; @@ -144,6 +144,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { _timeout = NumbersUtil.parseInt(value, 1440) * 1000; } + @Override public String createOvaForTemplate(TemplateObjectTO template) { DataStoreTO storeTO = template.getDataStore(); if (!(storeTO instanceof NfsTO)) { @@ -649,10 +650,10 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, true, false); long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); - VmdkProcessor processor = new VmdkProcessor(); + OVAProcessor processor = new OVAProcessor(); Map params = new HashMap(); params.put(StorageLayer.InstanceConfigKey, _storage); - processor.configure("VMDK Processor", params); + processor.configure("OVA Processor", params); long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); @@ -771,11 +772,11 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } long physicalSize = new File(installFullPath + "/" + templateVMDKName).length(); - VmdkProcessor processor = new VmdkProcessor(); + OVAProcessor processor = new OVAProcessor(); // long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); Map params = new HashMap(); params.put(StorageLayer.InstanceConfigKey, _storage); - processor.configure("VMDK Processor", params); + processor.configure("OVA Processor", params); long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); @@ -1183,9 +1184,10 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { return "snapshots/" + accountId + "/" + volumeId; } - private long getVMSnapshotChainSize(VmwareContext context, VmwareHypervisorHost hyperHost, String fileName, String poolUuid, String exceptFileName) throws Exception { + private long getVMSnapshotChainSize(VmwareContext context, VmwareHypervisorHost hyperHost, + String fileName, ManagedObjectReference morDs, String exceptFileName) + throws Exception{ long size = 0; - ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, poolUuid); DatastoreMO dsMo = new DatastoreMO(context, morDs); HostDatastoreBrowserMO browserMo = dsMo.getHostDatastoreBrowserMO(); String datastorePath = "[" + dsMo.getName() + "]"; @@ -1244,15 +1246,17 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { boolean quiescevm = cmd.getTarget().getQuiescevm(); VirtualMachineMO vmMo = null; VmwareContext context = hostService.getServiceContext(cmd); - Map mapNewDisk = new HashMap(); + try { VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); // wait if there are already VM snapshot task running ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager(); List tasks = (ArrayList)context.getVimClient().getDynamicProperty(taskmgr, "recentTask"); + for (ManagedObjectReference taskMor : tasks) { TaskInfo info = (TaskInfo)(context.getVimClient().getDynamicProperty(taskMor, "info")); + if (info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("CreateSnapshot_Task")) { s_logger.debug("There is already a VM snapshot task running, wait for it"); context.getVimClient().waitForTask(taskMor); @@ -1260,12 +1264,15 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { vmMo = hyperHost.findVmOnPeerHyperHost(vmName); } + if (vmMo == null) { String msg = "Unable to find VM for CreateVMSnapshotCommand"; s_logger.debug(msg); + return new CreateVMSnapshotAnswer(cmd, false, msg); } else { if (vmMo.getSnapshotMor(vmSnapshotName) != null) { @@ -1273,66 +1280,145 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { } else if (!vmMo.createSnapshot(vmSnapshotName, vmSnapshotDesc, snapshotMemory, quiescevm)) { return new CreateVMSnapshotAnswer(cmd, false, "Unable to create snapshot due to esxi internal failed"); } - // find VM disk file path after creating snapshot - VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); - for (int i = 0; i < vdisks.length; i++) { - List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); - for (Pair fileItem : vmdkFiles) { - String vmdkName = fileItem.first().split(" ")[1]; - if (vmdkName.endsWith(".vmdk")) { - vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); - } - String baseName = extractSnapshotBaseFileName(vmdkName); - mapNewDisk.put(baseName, vmdkName); - } - } - for (VolumeObjectTO volumeTO : volumeTOs) { - String baseName = extractSnapshotBaseFileName(volumeTO.getPath()); - String newPath = mapNewDisk.get(baseName); - // get volume's chain size for this VM snapshot, exclude current volume vdisk - DataStoreTO store = volumeTO.getDataStore(); - long size = getVMSnapshotChainSize(context, hyperHost, baseName + "*.vmdk", store.getUuid(), newPath); - if (volumeTO.getVolumeType() == Volume.Type.ROOT) { - // add memory snapshot size - size = size + getVMSnapshotChainSize(context, hyperHost, cmd.getVmName() + "*.vmsn", store.getUuid(), null); - } + Map mapNewDisk = getNewDiskMap(vmMo); + + setVolumeToPathAndSize(volumeTOs, mapNewDisk, context, hyperHost, cmd.getVmName()); - volumeTO.setSize(size); - volumeTO.setPath(newPath); - } return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs); } } catch (Exception e) { String msg = e.getMessage(); s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg); + try { if (vmMo.getSnapshotMor(vmSnapshotName) != null) { vmMo.removeSnapshot(vmSnapshotName, false); } } catch (Exception e1) { } + return new CreateVMSnapshotAnswer(cmd, false, e.getMessage()); } } + private Map getNewDiskMap(VirtualMachineMO vmMo) throws Exception { + Map mapNewDisk = new HashMap(); + + // find VM disk file path after creating snapshot + VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); + + for (int i = 0; i < vdisks.length; i++) { + List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); + + for (Pair fileItem : vmdkFiles) { + String fullPath = fileItem.first(); + String baseName = null; + String vmdkName = null; + + // if this is managed storage + if (fullPath.startsWith("[-iqn.")) { // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] -iqn.2010-01.com.company:3y8w.vol-10.64-0-000001.vmdk + baseName = fullPath.split(" ")[0]; // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] + + // remove '[' and ']' + baseName = baseName.substring(1, baseName.length() - 1); + + vmdkName = fullPath; // for managed storage, vmdkName == fullPath + } + else { + vmdkName = fullPath.split(" ")[1]; + + if (vmdkName.endsWith(".vmdk")) { + vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); + } + + String token = "/"; + + if (vmdkName.contains(token)) { + vmdkName = vmdkName.substring(vmdkName.indexOf(token) + token.length()); + } + + baseName = extractSnapshotBaseFileName(vmdkName); + } + + mapNewDisk.put(baseName, vmdkName); + } + } + + return mapNewDisk; + } + + private void setVolumeToPathAndSize(List volumeTOs, Map mapNewDisk, VmwareContext context, + VmwareHypervisorHost hyperHost, String vmName) throws Exception { + for (VolumeObjectTO volumeTO : volumeTOs) { + String oldPath = volumeTO.getPath(); + + final String baseName; + + // if this is managed storage + if (oldPath.startsWith("[-iqn.")) { // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] -iqn.2010-01.com.company:3y8w.vol-10.64-0-000001.vmdk + oldPath = oldPath.split(" ")[0]; // ex. [-iqn.2010-01.com.company:3y8w.vol-10.64-0] + + // remove '[' and ']' + baseName = oldPath.substring(1, oldPath.length() - 1); + } + else { + baseName = extractSnapshotBaseFileName(volumeTO.getPath()); + } + + String newPath = mapNewDisk.get(baseName); + + // get volume's chain size for this VM snapshot; exclude current volume vdisk + DataStoreTO store = volumeTO.getDataStore(); + ManagedObjectReference morDs = getDatastoreAsManagedObjectReference(baseName, hyperHost, store); + long size = getVMSnapshotChainSize(context, hyperHost, baseName + "*.vmdk", morDs, newPath); + + if (volumeTO.getVolumeType()== Volume.Type.ROOT) { + // add memory snapshot size + size += getVMSnapshotChainSize(context, hyperHost, vmName + "*.vmsn", morDs, null); + } + + volumeTO.setSize(size); + volumeTO.setPath(newPath); + } + } + + private ManagedObjectReference getDatastoreAsManagedObjectReference(String baseName, VmwareHypervisorHost hyperHost, DataStoreTO store) throws Exception { + try { + // if baseName equates to a datastore name, this should be managed storage + ManagedObjectReference morDs = hyperHost.findDatastoreByName(baseName); + + if (morDs != null) { + return morDs; + } + } + catch (Exception ex) { + } + + // not managed storage, so use the standard way of getting a ManagedObjectReference for a datastore + return HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, store.getUuid()); + } + @Override public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) { List listVolumeTo = cmd.getVolumeTOs(); VirtualMachineMO vmMo = null; VmwareContext context = hostService.getServiceContext(cmd); - Map mapNewDisk = new HashMap(); String vmName = cmd.getVmName(); String vmSnapshotName = cmd.getTarget().getSnapshotName(); + try { VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { vmMo = hyperHost.findVmOnPeerHyperHost(vmName); } + if (vmMo == null) { String msg = "Unable to find VM for RevertToVMSnapshotCommand"; s_logger.debug(msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); } else { if (vmMo.getSnapshotMor(vmSnapshotName) == null) { @@ -1341,41 +1427,25 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { if (!vmMo.removeSnapshot(vmSnapshotName, false)) { String msg = "delete vm snapshot " + vmSnapshotName + " due to error occured in vmware"; s_logger.error(msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); } } + s_logger.debug("snapshot: " + vmSnapshotName + " is removed"); + // after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager - VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); - for (int i = 0; i < vdisks.length; i++) { - @SuppressWarnings("deprecation") - List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); - for (Pair fileItem : vmdkFiles) { - String vmdkName = fileItem.first().split(" ")[1]; - if (vmdkName.endsWith(".vmdk")) { - vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); - } - String baseName = extractSnapshotBaseFileName(vmdkName); - mapNewDisk.put(baseName, vmdkName); - } - } - for (VolumeObjectTO volumeTo : listVolumeTo) { - String baseName = extractSnapshotBaseFileName(volumeTo.getPath()); - String newPath = mapNewDisk.get(baseName); - DataStoreTO store = volumeTo.getDataStore(); - long size = getVMSnapshotChainSize(context, hyperHost, baseName + "*.vmdk", store.getUuid(), newPath); - if (volumeTo.getVolumeType() == Volume.Type.ROOT) { - // add memory snapshot size - size = size + getVMSnapshotChainSize(context, hyperHost, cmd.getVmName() + "*.vmsn", store.getUuid(), null); - } - volumeTo.setSize(size); - volumeTo.setPath(newPath); - } + + Map mapNewDisk = getNewDiskMap(vmMo); + + setVolumeToPathAndSize(listVolumeTo, mapNewDisk, context, hyperHost, cmd.getVmName()); + return new DeleteVMSnapshotAnswer(cmd, listVolumeTo); } } catch (Exception e) { String msg = e.getMessage(); s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg); + return new DeleteVMSnapshotAnswer(cmd, false, msg); } } @@ -1389,15 +1459,17 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { VirtualMachine.State vmState = VirtualMachine.State.Running; VirtualMachineMO vmMo = null; VmwareContext context = hostService.getServiceContext(cmd); - Map mapNewDisk = new HashMap(); + try { VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd); // wait if there are already VM revert task running ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager(); List tasks = (ArrayList)context.getVimClient().getDynamicProperty(taskmgr, "recentTask"); + for (ManagedObjectReference taskMor : tasks) { TaskInfo info = (TaskInfo)(context.getVimClient().getDynamicProperty(taskMor, "info")); + if (info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("RevertToSnapshot_Task")) { s_logger.debug("There is already a VM snapshot task running, wait for it"); context.getVimClient().waitForTask(taskMor); @@ -1406,58 +1478,49 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { HostMO hostMo = (HostMO)hyperHost; vmMo = hyperHost.findVmOnHyperHost(vmName); + if (vmMo == null) { vmMo = hyperHost.findVmOnPeerHyperHost(vmName); } + if (vmMo == null) { String msg = "Unable to find VM for RevertToVMSnapshotCommand"; s_logger.debug(msg); + return new RevertToVMSnapshotAnswer(cmd, false, msg); } else { if (cmd.isReloadVm()) { vmMo.reload(); } + boolean result = false; + if (snapshotName != null) { ManagedObjectReference morSnapshot = vmMo.getSnapshotMor(snapshotName); + result = hostMo.revertToSnapshot(morSnapshot); } else { return new RevertToVMSnapshotAnswer(cmd, false, "Unable to find the snapshot by name " + snapshotName); } if (result) { - VirtualDisk[] vdisks = vmMo.getAllDiskDevice(); - // build a map - for (int i = 0; i < vdisks.length; i++) { - @SuppressWarnings("deprecation") - List> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false); - for (Pair fileItem : vmdkFiles) { - String vmdkName = fileItem.first().split(" ")[1]; - if (vmdkName.endsWith(".vmdk")) { - vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length()); - } - String[] s = vmdkName.split("-"); - mapNewDisk.put(s[0], vmdkName); - } - } - String key = null; - for (VolumeObjectTO volumeTo : listVolumeTo) { - String parentUUID = volumeTo.getPath(); - String[] s = parentUUID.split("-"); - key = s[0]; - volumeTo.setPath(mapNewDisk.get(key)); - } + Map mapNewDisk = getNewDiskMap(vmMo); + + setVolumeToPathAndSize(listVolumeTo, mapNewDisk, context, hyperHost, cmd.getVmName()); + if (!snapshotMemory) { vmState = VirtualMachine.State.Stopped; } + return new RevertToVMSnapshotAnswer(cmd, listVolumeTo, vmState); } else { - return new RevertToVMSnapshotAnswer(cmd, false, "Error while reverting to snapshot due to execute in esxi"); + return new RevertToVMSnapshotAnswer(cmd, false, "Error while reverting to snapshot due to execute in ESXi"); } } } catch (Exception e) { String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage(); s_logger.error(msg); + return new RevertToVMSnapshotAnswer(cmd, false, msg); } } 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 ade46d2876a..4aa6f27991a 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 @@ -42,7 +42,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; @@ -104,7 +103,6 @@ import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator; import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec; -import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.to.TemplateObjectTO; @@ -339,9 +337,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa protected final int _shutdownWaitMs = 300000; // wait up to 5 minutes for shutdown - @Inject - protected VolumeOrchestrationService volMgr; - // out an operation protected final int _retry = 24; protected final int _sleep = 10000; @@ -4360,7 +4355,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa s_logger.info("Executing resource MigrateVolumeCommand: " + _gson.toJson(cmd)); } - final String vmName = volMgr.getVmNameFromVolumeId(cmd.getVolumeId()); + final String vmName = cmd.getAttachedVmName(); VirtualMachineMO vmMo = null; VmwareHypervisorHost srcHyperHost = null; @@ -4372,13 +4367,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VirtualMachineRelocateSpecDiskLocator diskLocator = null; String srcDiskName = ""; - String srcDsName = ""; String tgtDsName = ""; try { srcHyperHost = getHyperHost(getServiceContext()); morDc = srcHyperHost.getHyperHostDatacenter(); - srcDsName = volMgr.getStoragePoolOfVolume(cmd.getVolumeId()); tgtDsName = poolTo.getUuid().replace("-", ""); // find VM in this datacenter not just in this cluster. @@ -4807,25 +4800,27 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa target.setPort(storagePortNumber); target.setIScsiName(iqn); - HostInternetScsiHbaAuthenticationProperties auth = new HostInternetScsiHbaAuthenticationProperties(); + if (StringUtils.isNotBlank(chapName) && StringUtils.isNotBlank(chapSecret)) { + HostInternetScsiHbaAuthenticationProperties auth = new HostInternetScsiHbaAuthenticationProperties(); - String strAuthType = "chapRequired"; + String strAuthType = "chapRequired"; - auth.setChapAuthEnabled(true); - auth.setChapInherited(false); - auth.setChapAuthenticationType(strAuthType); - auth.setChapName(chapName); - auth.setChapSecret(chapSecret); + auth.setChapAuthEnabled(true); + auth.setChapInherited(false); + auth.setChapAuthenticationType(strAuthType); + auth.setChapName(chapName); + auth.setChapSecret(chapSecret); - if (StringUtils.isNotBlank(mutualChapName) && StringUtils.isNotBlank(mutualChapSecret)) { - auth.setMutualChapInherited(false); - auth.setMutualChapAuthenticationType(strAuthType); - auth.setMutualChapName(mutualChapName); - auth.setMutualChapSecret(mutualChapSecret); + if (StringUtils.isNotBlank(mutualChapName) && StringUtils.isNotBlank(mutualChapSecret)) { + auth.setMutualChapInherited(false); + auth.setMutualChapAuthenticationType(strAuthType); + auth.setMutualChapName(mutualChapName); + auth.setMutualChapSecret(mutualChapSecret); + } + + target.setAuthenticationProperties(auth); } - target.setAuthenticationProperties(auth); - final List lstTargets = new ArrayList(); lstTargets.add(target); @@ -4945,9 +4940,12 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if (cmd.isAttach()) { vmMo.mountToolsInstaller(); } else { - try { - vmMo.unmountToolsInstaller(); - } catch (Throwable e) { + try{ + if (!vmMo.unmountToolsInstaller()) { + return new Answer(cmd, false, + "Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); + } + }catch(Throwable e){ vmMo.detachIso(null); } } @@ -6058,11 +6056,34 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa cmd.setName(_url); cmd.setGuid(_guid); cmd.setDataCenter(_dcId); + cmd.setIqn(getIqn()); cmd.setPod(_pod); cmd.setCluster(_cluster); cmd.setVersion(VmwareResource.class.getPackage().getImplementationVersion()); } + private String getIqn() { + try { + VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext()); + + if (hyperHost instanceof HostMO) { + HostMO host = (HostMO)hyperHost; + HostStorageSystemMO hostStorageSystem = host.getHostStorageSystemMO(); + + for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) { + if (hba instanceof HostInternetScsiHba) { + return ((HostInternetScsiHba)hba).getIScsiName(); + } + } + } + } + catch (Exception ex) { + s_logger.info("Could not locate an IQN for this host."); + } + + return null; + } + private void fillHostHardwareInfo(VmwareContext serviceContext, StartupRoutingCommand cmd) throws RuntimeFaultFaultMsg, RemoteException, Exception { VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext()); diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java index 7c3e73a0a85..70c8c885817 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -78,7 +78,7 @@ import com.cloud.storage.JavaStorageLayer; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.StorageLayer; import com.cloud.storage.Volume; -import com.cloud.storage.template.VmdkProcessor; +import com.cloud.storage.template.OVAProcessor; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.script.Script; @@ -253,16 +253,6 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.error(msg); throw new Exception(msg); } - - s_logger.info("Move volume out of volume-wrapper VM "); - String[] vmwareLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkName, VmwareStorageLayoutType.VMWARE, true); - String[] legacyCloudStackLayoutFilePair = - VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, true); - - dsMo.moveDatastoreFile(vmwareLayoutFilePair[0], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[0], dcMo.getMor(), true); - - dsMo.moveDatastoreFile(vmwareLayoutFilePair[1], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[1], dcMo.getMor(), true); - return true; } @@ -275,15 +265,6 @@ public class VmwareStorageProcessor implements StorageProcessor { s_logger.error(msg); throw new Exception(msg); } - - s_logger.info("Move volume out of volume-wrapper VM "); - String[] vmwareLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkName, VmwareStorageLayoutType.VMWARE, false); - String[] legacyCloudStackLayoutFilePair = - VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, false); - - dsMo.moveDatastoreFile(vmwareLayoutFilePair[0], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[0], dcMo.getMor(), true); - - dsMo.moveDatastoreFile(vmwareLayoutFilePair[1], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[1], dcMo.getMor(), true); return true; } @@ -309,6 +290,7 @@ public class VmwareStorageProcessor implements StorageProcessor { DatastoreMO dsMo = new DatastoreMO(context, morDatastore); String vmdkName = volume.getName(); + String vmdkFileBaseName = null; if (srcStore == null) { // create a root volume for blank VM (created from ISO) String dummyVmName = this.hostService.getWorkerName(context, cmd, 0); @@ -319,8 +301,9 @@ public class VmwareStorageProcessor implements StorageProcessor { throw new Exception("Unable to create a dummy VM for volume creation"); } - String vmdkFilePair[] = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, null, vmdkName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, true // we only use the first file in the pair, linked or not will not matter - ); + vmdkFileBaseName = vmMo.getVmdkFileBaseNames().get(0); + // we only use the first file in the pair, linked or not will not matter + String vmdkFilePair[] = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, null, vmdkFileBaseName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, true); String volumeDatastorePath = vmdkFilePair[0]; synchronized (this) { s_logger.info("Delete file if exists in datastore to clear the way for creating the volume. file: " + volumeDatastorePath); @@ -353,6 +336,14 @@ public class VmwareStorageProcessor implements StorageProcessor { vmMo = new ClusterMO(context, morCluster).findVmOnHyperHost(vmdkName); assert (vmMo != null); + vmdkFileBaseName = vmMo.getVmdkFileBaseNames().get(0); // TO-DO: Support for base template containing multiple disks + s_logger.info("Move volume out of volume-wrapper VM "); + String[] vmwareLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkFileBaseName, VmwareStorageLayoutType.VMWARE, !_fullCloneFlag); + String[] legacyCloudStackLayoutFilePair = VmwareStorageLayoutHelper.getVmdkFilePairDatastorePath(dsMo, vmdkName, vmdkFileBaseName, VmwareStorageLayoutType.CLOUDSTACK_LEGACY, !_fullCloneFlag); + + dsMo.moveDatastoreFile(vmwareLayoutFilePair[0], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[0], dcMo.getMor(), true); + dsMo.moveDatastoreFile(vmwareLayoutFilePair[1], dcMo.getMor(), dsMo.getMor(), legacyCloudStackLayoutFilePair[1], dcMo.getMor(), true); + s_logger.info("detach disks from volume-wrapper VM " + vmdkName); vmMo.detachAllDisks(); @@ -365,11 +356,11 @@ public class VmwareStorageProcessor implements StorageProcessor { // restoreVM - move the new ROOT disk into corresponding VM folder String vmInternalCSName = volume.getVmName(); if (dsMo.folderExists(String.format("[%s]", dsMo.getName()), vmInternalCSName)) { - VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dcMo, vmInternalCSName, dsMo, vmdkName); + VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dcMo, vmInternalCSName, dsMo, vmdkFileBaseName); } VolumeObjectTO newVol = new VolumeObjectTO(); - newVol.setPath(vmdkName); + newVol.setPath(vmdkFileBaseName); newVol.setSize(volume.getSize()); return new CopyCmdAnswer(newVol); } catch (Throwable e) { @@ -623,16 +614,31 @@ public class VmwareStorageProcessor implements StorageProcessor { vmMo.cloneFromCurrentSnapshot(workerVmName, 0, 4, volumeDeviceInfo.second(), VmwareHelper.getDiskDeviceDatastore(volumeDeviceInfo.first())); clonedVm = cloneResult.first(); - clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, true, false); + clonedVm.exportVm(secondaryMountPoint + "/" + installPath, templateUniqueName, false, false); + + // Get VMDK filename + String templateVMDKName = ""; + File[] files = new File(installFullPath).listFiles(); + if(files != null) { + for(File file : files) { + String fileName = file.getName(); + if(fileName.toLowerCase().startsWith(templateUniqueName) && fileName.toLowerCase().endsWith(".vmdk")) { + templateVMDKName += fileName; + break; + } + } + } + + long physicalSize = new File(installFullPath + "/" + templateVMDKName).length(); + OVAProcessor processor = new OVAProcessor(); - long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); - VmdkProcessor processor = new VmdkProcessor(); Map params = new HashMap(); params.put(StorageLayer.InstanceConfigKey, _storage); - processor.configure("VMDK Processor", params); + processor.configure("OVA Processor", params); long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); + writeMetaOvaForTemplate(installFullPath, templateUniqueName + ".ovf", templateVMDKName, templateUniqueName, physicalSize); return new Ternary(installPath + "/" + templateUniqueName + ".ova", physicalSize, virtualSize); } finally { @@ -850,11 +856,11 @@ public class VmwareStorageProcessor implements StorageProcessor { } long physicalSize = new File(installFullPath + "/" + templateVMDKName).length(); - VmdkProcessor processor = new VmdkProcessor(); + OVAProcessor processor = new OVAProcessor(); // long physicalSize = new File(installFullPath + "/" + templateUniqueName + ".ova").length(); Map params = new HashMap(); params.put(StorageLayer.InstanceConfigKey, _storage); - processor.configure("VMDK Processor", params); + processor.configure("OVA Processor", params); long virtualSize = processor.getTemplateVirtualSize(installFullPath, templateUniqueName); postCreatePrivateTemplate(installFullPath, templateId, templateUniqueName, physicalSize, virtualSize); @@ -994,32 +1000,26 @@ public class VmwareStorageProcessor implements StorageProcessor { CopyCmdAnswer answer = null; try { - vmMo = hyperHost.findVmOnHyperHost(vmName); - if (vmMo == null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to find owner VM for BackupSnapshotCommand on host " + hyperHost.getHyperHostName() + ", will try within datacenter"); - } - - vmMo = hyperHost.findVmOnPeerHyperHost(vmName); + if(vmName != null) { + vmMo = hyperHost.findVmOnHyperHost(vmName); if (vmMo == null) { - dsMo = new DatastoreMO(hyperHost.getContext(), morDs); - - workerVMName = hostService.getWorkerName(context, cmd, 0); - - vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVMName); - - if (vmMo == null) { - throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName); + if(s_logger.isDebugEnabled()) { + s_logger.debug("Unable to find owner VM for BackupSnapshotCommand on host " + hyperHost.getHyperHostName() + ", will try within datacenter"); } - workerVm = vmMo; - - // attach volume to worker VM - String datastoreVolumePath = dsMo.getDatastorePath(volumePath + ".vmdk"); - vmMo.attachDisk(new String[] {datastoreVolumePath}, morDs); - } else { - s_logger.info("Using owner VM " + vmName + " for snapshot operation"); - hasOwnerVm = true; + vmMo = hyperHost.findVmOnPeerHyperHost(vmName); } + } + if(vmMo == null) { + dsMo = new DatastoreMO(hyperHost.getContext(), morDs); + workerVMName = hostService.getWorkerName(context, cmd, 0); + vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVMName); + if (vmMo == null) { + throw new Exception("Failed to find the newly create or relocated VM. vmName: " + workerVMName); + } + workerVm = vmMo; + // attach volume to worker VM + String datastoreVolumePath = dsMo.getDatastorePath(volumePath + ".vmdk"); + vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs); } else { s_logger.info("Using owner VM " + vmName + " for snapshot operation"); hasOwnerVm = true; @@ -1272,9 +1272,11 @@ public class VmwareStorageProcessor implements StorageProcessor { if (isAttach) { vmMo.mountToolsInstaller(); } else { - try { - vmMo.unmountToolsInstaller(); - } catch (Throwable e) { + try{ + if (!vmMo.unmountToolsInstaller()) { + return new AttachAnswer("Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); + } + } catch(Throwable e){ vmMo.detachIso(null); } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java index 8391c6416d8..39c353931d8 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageSubsystemCommandHandler.java @@ -89,6 +89,9 @@ public class VmwareStorageSubsystemCommandHandler extends StorageSubsystemComman String name = path.substring(index + 1); storageManager.createOva(parentPath + File.separator + path, name); vol.setPath(path + File.separator + name + ".ova"); + } else if (srcData.getObjectType() == DataObjectType.TEMPLATE) { + // sync template from NFS cache to S3 in NFS migration to S3 case + storageManager.createOvaForTemplate((TemplateObjectTO)srcData); } else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && destData.getObjectType() == DataObjectType.TEMPLATE) { //create template from snapshot on src at first, then copy it to s3 TemplateObjectTO cacheTemplate = (TemplateObjectTO)destData; diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java index bd10cb013b1..4f21a021b75 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/XenServerGuru.java @@ -16,20 +16,56 @@ // under the License. package com.cloud.hypervisor; +import java.util.ArrayList; +import java.util.List; + import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.DiskTO; +import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.host.HostInfo; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.GuestOSDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.template.VirtualMachineTemplate.BootloaderType; +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.command.DettachCommand; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; + @Local(value = HypervisorGuru.class) public class XenServerGuru extends HypervisorGuruBase implements HypervisorGuru { @Inject GuestOSDao _guestOsDao; + @Inject + EndPointSelector endPointSelector; + @Inject + HostDao hostDao; + @Inject + VolumeDao _volumeDao; + @Inject + PrimaryDataStoreDao _storagePoolDao; + @Inject + VolumeDataFactory _volFactory; protected XenServerGuru() { super(); @@ -60,4 +96,62 @@ public class XenServerGuru extends HypervisorGuruBase implements HypervisorGuru public boolean trackVmHostChange() { return true; } + + @Override + public List finalizeExpungeVolumes(VirtualMachine vm) { + List commands = new ArrayList(); + + List volumes = _volumeDao.findByInstance(vm.getId()); + + if (volumes != null) { + for (VolumeVO volume : volumes) { + if (volume.getVolumeType() == Volume.Type.DATADISK) { + StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); + + if (storagePool.isManaged()) { + DataTO volTO = _volFactory.getVolume(volume.getId()).getTO(); + DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getPath(), volume.getVolumeType()); + + DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName()); + + cmd.setManaged(true); + + cmd.setStorageHost(storagePool.getHostAddress()); + cmd.setStoragePort(storagePool.getPort()); + + cmd.set_iScsiName(volume.get_iScsiName()); + + commands.add(cmd); + } + } + } + } + + return commands; + } + + @Override + public Pair getCommandHostDelegation(long hostId, Command cmd) { + if (cmd instanceof CopyCommand) { + CopyCommand cpyCommand = (CopyCommand)cmd; + DataTO srcData = cpyCommand.getSrcTO(); + DataTO destData = cpyCommand.getDestTO(); + + if (srcData.getObjectType() == DataObjectType.SNAPSHOT && destData.getObjectType() == DataObjectType.TEMPLATE) { + DataStoreTO srcStore = srcData.getDataStore(); + DataStoreTO destStore = destData.getDataStore(); + if (srcStore instanceof NfsTO && destStore instanceof NfsTO) { + HostVO host = hostDao.findById(hostId); + EndPoint ep = endPointSelector.selectHypervisorHost(new ZoneScope(host.getDataCenterId())); + host = hostDao.findById(ep.getId()); + hostDao.loadDetails(host); + boolean snapshotHotFix = Boolean.parseBoolean(host.getDetail(HostInfo.XS620_SNAPSHOT_HOTFIX)); + if (snapshotHotFix) { + return new Pair(Boolean.TRUE, new Long(ep.getId())); + } + } + } + } + return new Pair(Boolean.FALSE, new Long(hostId)); + } } diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java index ecdb82148ff..cd1b30b9f71 100755 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/discoverer/XcpServerDiscoverer.java @@ -34,13 +34,6 @@ import javax.persistence.EntityExistsException; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; -import com.xensource.xenapi.Connection; -import com.xensource.xenapi.Host; -import com.xensource.xenapi.Pool; -import com.xensource.xenapi.Session; -import com.xensource.xenapi.Types.SessionAuthenticationFailed; -import com.xensource.xenapi.Types.XenAPIException; - import com.cloud.agent.AgentManager; import com.cloud.agent.Listener; import com.cloud.agent.api.AgentControlAnswer; @@ -82,6 +75,7 @@ import com.cloud.hypervisor.xen.resource.XenServer602Resource; import com.cloud.hypervisor.xen.resource.XenServer610Resource; import com.cloud.hypervisor.xen.resource.XenServer620Resource; import com.cloud.hypervisor.xen.resource.XenServerConnectionPool; +import com.cloud.hypervisor.xen.resource.Xenserver625Resource; import com.cloud.resource.Discoverer; import com.cloud.resource.DiscovererBase; import com.cloud.resource.ResourceManager; @@ -98,6 +92,12 @@ import com.cloud.utils.db.QueryBuilder; import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.HypervisorVersionChangedException; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.Pool; +import com.xensource.xenapi.Session; +import com.xensource.xenapi.Types.SessionAuthenticationFailed; +import com.xensource.xenapi.Types.XenAPIException; @Local(value = Discoverer.class) public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, Listener, ResourceStateAdapter { @@ -112,6 +112,7 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L protected String _guestNic; protected boolean _setupMultipath; protected String _instance; + private String xs620snapshothotfix = "Xenserver-Vdi-Copy-HotFix"; @Inject protected AlertManager _alertMgr; @@ -149,6 +150,11 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L } } + protected boolean xenserverHotFixEnabled() { + //temporary fix, we should call xenserver api to get the hot fix is enabled or not. + return Boolean.parseBoolean(_configDao.getValue(Config.XenServerHotFix.name())); + } + @Override public Map> find(long dcId, Long podId, Long clusterId, URI url, String username, String password, List hostTags) throws DiscoveryException { @@ -274,6 +280,8 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L hostOS = record.softwareVersion.get("platform_name"); } + //Boolean xs620hotfix = record.tags.contains(xs620snapshothotfix); + Boolean xs620hotfix = xenserverHotFixEnabled(); String hostOSVer = prodVersion; String hostKernelVer = record.softwareVersion.get("linux"); @@ -303,6 +311,7 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L details.put(HostInfo.HOST_OS_VERSION, hostOSVer); details.put(HostInfo.HOST_OS_KERNEL_VERSION, hostKernelVer); details.put(HostInfo.HYPERVISOR_VERSION, xenVersion); + details.put(HostInfo.XS620_SNAPSHOT_HOTFIX, xs620hotfix.toString()); String privateNetworkLabel = _networkMgr.getDefaultManagementTrafficLabel(dcId, HypervisorType.XenServer); String storageNetworkLabel = _networkMgr.getDefaultStorageTrafficLabel(dcId, HypervisorType.XenServer); @@ -452,9 +461,21 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L return new XenServer602Resource(); else if (prodBrand.equals("XenServer") && prodVersion.equals("6.1.0")) return new XenServer610Resource(); - else if (prodBrand.equals("XenServer") && prodVersion.equals("6.2.0")) - return new XenServer620Resource(); - else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) { + else if (prodBrand.equals("XenServer") && prodVersion.equals("6.2.0")) { + /* + Set tags =record.tags; + if (tags.contains(xs620snapshothotfix)) { + return new Xenserver625Resource(); + } else { + return new XenServer620Resource(); + }*/ + boolean hotfix = xenserverHotFixEnabled(); + if (hotfix) { + return new Xenserver625Resource(); + } else { + return new XenServer620Resource(); + } + } else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) { String prodVersionTextShort = record.softwareVersion.get("product_version_text_short").trim(); if ("5.6 SP2".equals(prodVersionTextShort)) { return new XenServer56SP2Resource(); @@ -607,7 +628,12 @@ public class XcpServerDiscoverer extends DiscovererBase implements Discoverer, L } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.1.0")) { resource = XenServer610Resource.class.getName(); } else if (prodBrand.equals("XenServer") && prodVersion.equals("6.2.0")) { - resource = XenServer620Resource.class.getName(); + String hotfix = details.get("Xenserer620HotFix"); + if (hotfix != null && hotfix.equalsIgnoreCase("Xenserver-Vdi-Copy-HotFix")) { + resource = Xenserver625Resource.class.getName(); + } else { + resource = XenServer620Resource.class.getName(); + } } else if (prodBrand.equals("XenServer") && prodVersion.equals("5.6.100")) { String prodVersionTextShort = details.get("product_version_text_short").trim(); if ("5.6 SP2".equals(prodVersionTextShort)) { 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 f38bb3cd8c5..accaa85230f 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 @@ -16,9 +16,6 @@ // under the License. package com.cloud.hypervisor.xen.resource; -import static com.cloud.utils.ReflectUtil.flattenProperties; -import static com.google.common.collect.Lists.newArrayList; - import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -31,7 +28,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -49,6 +45,9 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import javax.xml.parsers.DocumentBuilderFactory; +import org.apache.cloudstack.storage.command.StorageSubSystemCommand; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; @@ -57,53 +56,11 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; -import com.google.gson.Gson; -import com.trilead.ssh2.SCPClient; -import com.xensource.xenapi.Bond; -import com.xensource.xenapi.Connection; -import com.xensource.xenapi.Console; -import com.xensource.xenapi.Host; -import com.xensource.xenapi.HostCpu; -import com.xensource.xenapi.HostMetrics; -import com.xensource.xenapi.Network; -import com.xensource.xenapi.PBD; -import com.xensource.xenapi.PIF; -import com.xensource.xenapi.PIF.Record; -import com.xensource.xenapi.Pool; -import com.xensource.xenapi.SR; -import com.xensource.xenapi.Session; -import com.xensource.xenapi.Task; -import com.xensource.xenapi.Types; -import com.xensource.xenapi.Types.BadAsyncResult; -import com.xensource.xenapi.Types.BadServerResponse; -import com.xensource.xenapi.Types.ConsoleProtocol; -import com.xensource.xenapi.Types.IpConfigurationMode; -import com.xensource.xenapi.Types.OperationNotAllowed; -import com.xensource.xenapi.Types.SrFull; -import com.xensource.xenapi.Types.VbdType; -import com.xensource.xenapi.Types.VmBadPowerState; -import com.xensource.xenapi.Types.VmPowerState; -import com.xensource.xenapi.Types.XenAPIException; -import com.xensource.xenapi.VBD; -import com.xensource.xenapi.VBDMetrics; -import com.xensource.xenapi.VDI; -import com.xensource.xenapi.VIF; -import com.xensource.xenapi.VLAN; -import com.xensource.xenapi.VM; -import com.xensource.xenapi.VMGuestMetrics; -import com.xensource.xenapi.XenAPIObject; - -import org.apache.cloudstack.storage.command.StorageSubSystemCommand; -import org.apache.cloudstack.storage.to.TemplateObjectTO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; - import com.cloud.agent.IAgentControl; import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachIsoCommand; import com.cloud.agent.api.AttachVolumeAnswer; import com.cloud.agent.api.AttachVolumeCommand; -import com.cloud.agent.api.BackupSnapshotAnswer; -import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.BumpUpPriorityCommand; import com.cloud.agent.api.CheckHealthAnswer; import com.cloud.agent.api.CheckHealthCommand; @@ -121,13 +78,9 @@ import com.cloud.agent.api.CleanupNetworkRulesCmd; import com.cloud.agent.api.ClusterSyncAnswer; import com.cloud.agent.api.ClusterSyncCommand; import com.cloud.agent.api.Command; -import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; -import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.CreateVMSnapshotAnswer; import com.cloud.agent.api.CreateVMSnapshotCommand; -import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; -import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.DeleteVMSnapshotAnswer; import com.cloud.agent.api.DeleteVMSnapshotCommand; @@ -147,8 +100,6 @@ import com.cloud.agent.api.HostStatsEntry; import com.cloud.agent.api.HostVmStateReportEntry; import com.cloud.agent.api.MaintainAnswer; import com.cloud.agent.api.MaintainCommand; -import com.cloud.agent.api.ManageSnapshotAnswer; -import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.MigrateAnswer; import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.ModifySshKeysCommand; @@ -243,11 +194,8 @@ import com.cloud.agent.api.routing.SetStaticRouteCommand; import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand; 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.CreateAnswer; import com.cloud.agent.api.storage.CreateCommand; -import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; @@ -262,10 +210,8 @@ import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; -import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.api.to.StorageFilerTO; -import com.cloud.agent.api.to.SwiftTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.exception.InternalErrorException; @@ -282,7 +228,6 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.resource.ServerResource; import com.cloud.resource.hypervisor.HypervisorResource; import com.cloud.storage.Storage; -import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; @@ -292,7 +237,6 @@ import com.cloud.storage.template.TemplateProp; import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; -import com.cloud.utils.S3Utils; import com.cloud.utils.StringUtils; import com.cloud.utils.Ternary; import com.cloud.utils.exception.CloudRuntimeException; @@ -303,6 +247,34 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.snapshot.VMSnapshot; +import com.google.gson.Gson; +import com.trilead.ssh2.SCPClient; +import com.xensource.xenapi.Bond; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Console; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.HostCpu; +import com.xensource.xenapi.HostMetrics; +import com.xensource.xenapi.Network; +import com.xensource.xenapi.PBD; +import com.xensource.xenapi.PIF; +import com.xensource.xenapi.Pool; +import com.xensource.xenapi.SR; +import com.xensource.xenapi.Session; +import com.xensource.xenapi.Task; +import com.xensource.xenapi.Types; +import com.xensource.xenapi.Types.BadServerResponse; +import com.xensource.xenapi.Types.VmPowerState; +import com.xensource.xenapi.Types.XenAPIException; +import com.xensource.xenapi.VBD; +import com.xensource.xenapi.VBDMetrics; +import com.xensource.xenapi.VDI; +import com.xensource.xenapi.VIF; +import com.xensource.xenapi.VLAN; +import com.xensource.xenapi.VM; +import com.xensource.xenapi.VMGuestMetrics; +import com.xensource.xenapi.XenAPIObject; + /** * CitrixResourceBase encapsulates the calls to the XenServer Xapi process @@ -537,25 +509,13 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } else if (clazz == ModifyStoragePoolCommand.class) { return execute((ModifyStoragePoolCommand)cmd); } else if (clazz == DeleteStoragePoolCommand.class) { - return execute((DeleteStoragePoolCommand)cmd); - } else if (clazz == CopyVolumeCommand.class) { - return execute((CopyVolumeCommand)cmd); - } else if (clazz == ResizeVolumeCommand.class) { - return execute((ResizeVolumeCommand)cmd); + return execute((DeleteStoragePoolCommand) cmd); + }else if (clazz == ResizeVolumeCommand.class) { + return execute((ResizeVolumeCommand) cmd); } else if (clazz == AttachVolumeCommand.class) { return execute((AttachVolumeCommand)cmd); } else if (clazz == AttachIsoCommand.class) { - return execute((AttachIsoCommand)cmd); - } else if (clazz == ManageSnapshotCommand.class) { - return execute((ManageSnapshotCommand)cmd); - } else if (clazz == BackupSnapshotCommand.class) { - return execute((BackupSnapshotCommand)cmd); - } else if (clazz == CreateVolumeFromSnapshotCommand.class) { - return execute((CreateVolumeFromSnapshotCommand)cmd); - } else if (clazz == CreatePrivateTemplateFromVolumeCommand.class) { - return execute((CreatePrivateTemplateFromVolumeCommand)cmd); - } else if (clazz == CreatePrivateTemplateFromSnapshotCommand.class) { - return execute((CreatePrivateTemplateFromSnapshotCommand)cmd); + return execute((AttachIsoCommand) cmd); } else if (clazz == UpgradeSnapshotCommand.class) { return execute((UpgradeSnapshotCommand)cmd); } else if (clazz == GetStorageStatsCommand.class) { @@ -1302,14 +1262,16 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - protected VBD createVbd(Connection conn, DiskTO volume, String vmName, VM vm, BootloaderType bootLoaderType, VDI vdi) throws XmlRpcException, XenAPIException { + protected VBD createVbd(Connection conn, DiskTO volume, String vmName, VM vm, BootloaderType bootLoaderType) throws XmlRpcException, XenAPIException { Volume.Type type = volume.getType(); - if (vdi == null) { - vdi = mount(conn, vmName, volume); - } + VDI vdi = mount(conn, vmName, volume); if (vdi != null) { + if ("detached".equals(vdi.getNameLabel(conn))) { + vdi.setNameLabel(conn, vmName + "-DATA"); + } + Map smConfig = vdi.getSmConfig(conn); for (String key : smConfig.keySet()) { if (key.startsWith("host_")) { @@ -1482,6 +1444,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return vm; } + protected void finalizeVmMetaData(VM vm, Connection conn, VirtualMachineTO vmSpec) throws Exception { Map details = vmSpec.getDetails(); @@ -1791,35 +1754,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vm = createVmFromTemplate(conn, vmSpec, host); for (DiskTO disk : vmSpec.getDisks()) { - VDI vdi = null; - - if (disk.getData() instanceof VolumeObjectTO) { - Map details = disk.getDetails(); - boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED)); - - if (isManaged) { - String iScsiName = details.get(DiskTO.IQN); - String storageHost = details.get(DiskTO.STORAGE_HOST); - String chapInitiatorUsername = disk.getDetails().get(DiskTO.CHAP_INITIATOR_USERNAME); - String chapInitiatorSecret = disk.getDetails().get(DiskTO.CHAP_INITIATOR_SECRET); - Long volumeSize = Long.parseLong(details.get(DiskTO.VOLUME_SIZE)); - String vdiNameLabel = vmName + "-DATA"; - - SR sr = getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true); - - vdi = getVDIbyUuid(conn, disk.getPath(), false); - - if (vdi == null) { - vdi = createVdi(sr, vdiNameLabel, volumeSize); - - iqnToPath.put(iScsiName, vdi.getUuid(conn)); - } else { - vdi.setNameLabel(conn, vdiNameLabel); - } - } - } - - createVbd(conn, disk, vmName, vm, vmSpec.getBootloader(), vdi); + createVbd(conn, disk, vmName, vm, vmSpec.getBootloader()); } if (vmSpec.getType() != VirtualMachine.Type.User) { @@ -1928,45 +1863,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe s_logger.debug("The VM is in stopped state, detected problem during startup : " + vmName); } } - - if (state != State.Running) { - disconnectManagedVolumes(conn, vm); - } - } - } - - private void disconnectManagedVolumes(Connection conn, VM vm) { - try { - Set vbds = vm.getVBDs(conn); - - for (VBD vbd : vbds) { - VDI vdi = vbd.getVDI(conn); - SR sr = null; - - try { - sr = vdi.getSR(conn); - } catch (Exception ex) { - continue; - } - - if (sr.getNameLabel(conn).startsWith("/iqn.")) { - VBD.Record vbdr = vbd.getRecord(conn); - - if (vbdr.currentlyAttached) { - vbd.unplug(conn); - } - - vbd.destroy(conn); - - vdi.setNameLabel(conn, "detached"); - - umount(conn, vdi); - - handleSrAndVdiDetach(sr.getNameLabel(conn)); - } - } - } catch (Exception ex) { - s_logger.debug(ex.getMessage()); } } @@ -3607,13 +3503,21 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe throw new CloudRuntimeException("Com'on no control domain? What the crap?!#@!##$@"); } + protected void umountSnapshotDir(Connection conn, Long dcId) { + try { + callHostPlugin(conn, "vmopsSnapshot", "unmountSnapshotsDir", "dcId", dcId.toString()); + } catch (Exception e) { + s_logger.debug("Failed to umount snapshot dir",e); + } + } + protected ReadyAnswer execute(ReadyCommand cmd) { Connection conn = getConnection(); Long dcId = cmd.getDataCenterId(); // Ignore the result of the callHostPlugin. Even if unmounting the // snapshots dir fails, let Ready command // succeed. - callHostPlugin(conn, "vmopsSnapshot", "unmountSnapshotsDir", "dcId", dcId.toString()); + umountSnapshotDir(conn, dcId); setupLinkLocalNetwork(conn); // try to destroy CD-ROM device for all system VMs on this host @@ -3655,7 +3559,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Iterator i = consoles.iterator(); while (i.hasNext()) { c = i.next(); - if (c.getProtocol(conn) == ConsoleProtocol.RFB) + if (c.getProtocol(conn) == Types.ConsoleProtocol.RFB) return c.getLocation(conn); } } catch (XenAPIException e) { @@ -4019,109 +3923,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - boolean swiftDownload(Connection conn, SwiftTO swift, String container, String rfilename, String dir, String lfilename, Boolean remote) { - String result = null; - try { - result = - callHostPluginAsync(conn, "swiftxen", "swift", 60 * 60, "op", "download", "url", swift.getUrl(), "account", swift.getAccount(), "username", - swift.getUserName(), "key", swift.getKey(), "rfilename", rfilename, "dir", dir, "lfilename", lfilename, "remote", remote.toString()); - if (result != null && result.equals("true")) { - return true; - } - } catch (Exception e) { - s_logger.warn("swift download failed due to ", e); - } - return false; - } - - boolean swiftUpload(Connection conn, SwiftTO swift, String container, String ldir, String lfilename, Boolean isISCSI, int wait) { - String result = null; - try { - result = - callHostPluginAsync(conn, "swiftxen", "swift", wait, "op", "upload", "url", swift.getUrl(), "account", swift.getAccount(), "username", - swift.getUserName(), "key", swift.getKey(), "container", container, "ldir", ldir, "lfilename", lfilename, "isISCSI", isISCSI.toString()); - if (result != null && result.equals("true")) { - return true; - } - } catch (Exception e) { - s_logger.warn("swift upload failed due to " + e.toString(), e); - } - return false; - } - - boolean swiftDelete(Connection conn, SwiftTO swift, String rfilename) { - String result = null; - try { - result = - callHostPlugin(conn, "swiftxen", "swift", "op", "delete", "url", swift.getUrl(), "account", swift.getAccount(), "username", swift.getUserName(), "key", - swift.getKey(), "rfilename", rfilename); - if (result != null && result.equals("true")) { - return true; - } - } catch (Exception e) { - s_logger.warn("swift download failed due to ", e); - } - return false; - } - - public String swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) { - String lfilename; - String ldir; - if (isISCSI) { - ldir = "/dev/VG_XenStorage-" + srUuid; - lfilename = "VHD-" + snapshotUuid; - } else { - ldir = "/var/run/sr-mount/" + srUuid; - lfilename = snapshotUuid + ".vhd"; - } - swiftUpload(conn, swift, container, ldir, lfilename, isISCSI, wait); - return lfilename; - } - - protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, - String snapshotUuid, String prevBackupUuid, Boolean isISCSI, int wait, Long secHostId) { - String backupSnapshotUuid = null; - - if (prevBackupUuid == null) { - prevBackupUuid = ""; - } - - // Each argument is put in a separate line for readability. - // Using more lines does not harm the environment. - String backupUuid = UUID.randomUUID().toString(); - String results = - callHostPluginAsync(conn, "vmopsSnapshot", "backupSnapshot", wait, "primaryStorageSRUuid", primaryStorageSRUuid, "dcId", dcId.toString(), "accountId", - accountId.toString(), "volumeId", volumeId.toString(), "secondaryStorageMountPath", secondaryStorageMountPath, "snapshotUuid", snapshotUuid, - "prevBackupUuid", prevBackupUuid, "backupUuid", backupUuid, "isISCSI", isISCSI.toString(), "secHostId", secHostId.toString()); - String errMsg = null; - if (results == null || results.isEmpty()) { - errMsg = - "Could not copy backupUuid: " + backupSnapshotUuid + " of volumeId: " + volumeId + " from primary storage " + primaryStorageSRUuid + - " to secondary storage " + secondaryStorageMountPath + " due to null"; - } else { - - String[] tmp = results.split("#"); - String status = tmp[0]; - backupSnapshotUuid = tmp[1]; - // status == "1" if and only if backupSnapshotUuid != null - // So we don't rely on status value but return backupSnapshotUuid as an - // indicator of success. - if (status != null && status.equalsIgnoreCase("1") && backupSnapshotUuid != null) { - s_logger.debug("Successfully copied backupUuid: " + backupSnapshotUuid + " of volumeId: " + volumeId + " to secondary storage"); - return backupSnapshotUuid; - } else { - errMsg = - "Could not copy backupUuid: " + backupSnapshotUuid + " of volumeId: " + volumeId + " from primary storage " + primaryStorageSRUuid + - " to secondary storage " + secondaryStorageMountPath + " due to " + tmp[1]; - } - } - String source = backupUuid + ".vhd"; - killCopyProcess(conn, source); - s_logger.warn(errMsg); - return null; - - } - protected String callHostPluginAsync(Connection conn, String plugin, String cmd, int wait, String... params) { int timeout = wait * 1000; Map args = new HashMap(); @@ -4238,8 +4039,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe try { if (vm.getPowerState(conn) == VmPowerState.HALTED) { - disconnectManagedVolumes(conn, vm); - Set vifs = vm.getVIFs(conn); List networks = new ArrayList(); for (VIF vif : vifs) { @@ -5135,7 +4934,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return false; } - src.reconfigureIp(conn, IpConfigurationMode.NONE, null, null, null, null); + src.reconfigureIp(conn, Types.IpConfigurationMode.NONE, null, null, null, null); return true; } @@ -5953,7 +5752,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe XsLocalNetwork nw = getNetworkByName(conn, label); s_logger.debug("Network object:" + nw.getNetwork().getUuid(conn)); PIF pif = nw.getPif(conn); - Record pifRec = pif.getRecord(conn); + PIF.Record pifRec = pif.getRecord(conn); s_logger.debug("PIF object:" + pifRec.uuid + "(" + pifRec.device + ")"); return new OvsFetchInterfaceAnswer(cmd, true, "Interface " + pifRec.device + " retrieved successfully", pifRec.IP, pifRec.netmask, pifRec.MAC); } catch (BadServerResponse e) { @@ -6273,14 +6072,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - void destroyVDI(Connection conn, VDI vdi) { - try { - vdi.destroy(conn); - } catch (Exception e) { - String msg = "destroy VDI failed due to " + e.toString(); - s_logger.warn(msg); - } - } public CreateAnswer execute(CreateCommand cmd) { Connection conn = getConnection(); @@ -6707,54 +6498,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new Answer(cmd, true, "Success"); } - public CopyVolumeAnswer execute(final CopyVolumeCommand cmd) { - Connection conn = getConnection(); - String volumeUUID = cmd.getVolumePath(); - StorageFilerTO poolTO = cmd.getPool(); - String secondaryStorageURL = cmd.getSecondaryStorageURL(); - boolean toSecondaryStorage = cmd.toSecondaryStorage(); - int wait = cmd.getWait(); - try { - URI uri = new URI(secondaryStorageURL); - String remoteVolumesMountPath = uri.getHost() + ":" + uri.getPath() + "/volumes/"; - String volumeFolder = String.valueOf(cmd.getVolumeId()) + "/"; - String mountpoint = remoteVolumesMountPath + volumeFolder; - SR primaryStoragePool = getStorageRepository(conn, poolTO.getUuid()); - String srUuid = primaryStoragePool.getUuid(conn); - if (toSecondaryStorage) { - // Create the volume folder - if (!createSecondaryStorageFolder(conn, remoteVolumesMountPath, volumeFolder)) { - throw new InternalErrorException("Failed to create the volume folder."); - } - SR secondaryStorage = null; - try { - // Create a SR for the volume UUID folder - secondaryStorage = createNfsSRbyURI(conn, new URI(secondaryStorageURL + "/volumes/" + volumeFolder), false); - // Look up the volume on the source primary storage pool - VDI srcVolume = getVDIbyUuid(conn, volumeUUID); - // Copy the volume to secondary storage - VDI destVolume = cloudVDIcopy(conn, srcVolume, secondaryStorage, wait); - String destVolumeUUID = destVolume.getUuid(conn); - return new CopyVolumeAnswer(cmd, true, null, null, destVolumeUUID); - } finally { - removeSR(conn, secondaryStorage); - } - } else { - try { - String volumePath = mountpoint + "/" + volumeUUID + ".vhd"; - String uuid = copy_vhd_from_secondarystorage(conn, volumePath, srUuid, wait); - return new CopyVolumeAnswer(cmd, true, null, srUuid, uuid); - } finally { - deleteSecondaryStorageFolder(conn, remoteVolumesMountPath, volumeFolder); - } - } - } catch (Exception e) { - String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString(); - s_logger.warn(msg, e); - return new CopyVolumeAnswer(cmd, false, msg, null, null); - } - } - protected VDI createVdi(SR sr, String vdiNameLabel, Long volumeSize) throws Types.XenAPIException, XmlRpcException { VDI vdi = null; @@ -6794,9 +6537,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return vdi; } - protected void handleSrAndVdiDetach(String iqn) throws Exception { - Connection conn = getConnection(); - + protected void handleSrAndVdiDetach(String iqn, Connection conn) throws Exception { SR sr = getStorageRepository(conn, iqn); removeSR(conn, sr); @@ -6904,7 +6645,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe vdi.setNameLabel(conn, "detached"); if (cmd.isManaged()) { - handleSrAndVdiDetach(cmd.get_iScsiName()); + handleSrAndVdiDetach(cmd.get_iScsiName(), conn); } return new AttachVolumeAnswer(cmd); @@ -7057,9 +6798,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs()); } catch (Exception e) { String msg = ""; - if (e instanceof BadAsyncResult) { + if (e instanceof Types.BadAsyncResult) { String licenseKeyWord = "LICENCE_RESTRICTION"; - BadAsyncResult errorResult = (BadAsyncResult)e; + Types.BadAsyncResult errorResult = (Types.BadAsyncResult)e; if (errorResult.shortDescription != null && errorResult.shortDescription.contains(licenseKeyWord)) { msg = licenseKeyWord; } @@ -7076,7 +6817,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe Set vbds = vmSnapshot.getVBDs(conn); for (VBD vbd : vbds) { VBD.Record vbdr = vbd.getRecord(conn); - if (vbdr.type == VbdType.DISK) { + if (vbdr.type == Types.VbdType.DISK) { VDI vdi = vbdr.VDI; vdi.destroy(conn); } @@ -7095,8 +6836,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } } - private VM createWorkingVM(Connection conn, String vmName, String guestOSType, List listVolumeTo) throws BadServerResponse, VmBadPowerState, SrFull, - OperationNotAllowed, XenAPIException, XmlRpcException { + private VM createWorkingVM(Connection conn, String vmName, String guestOSType, List listVolumeTo) throws BadServerResponse, Types.VmBadPowerState, Types.SrFull, + Types.OperationNotAllowed, XenAPIException, XmlRpcException { String guestOsTypeName = getGuestOsType(guestOSType, false); if (guestOsTypeName == null) { String msg = @@ -7151,7 +6892,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe VM snapshot = snapshots.iterator().next(); Set vbds = snapshot.getVBDs(conn); for (VBD vbd : vbds) { - if (vbd.getType(conn) == VbdType.DISK) { + if (vbd.getType(conn) == Types.VbdType.DISK) { VDI vdi = vbd.getVDI(conn); vdiList.add(vdi); } @@ -7276,140 +7017,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return SRType.LVMOHBA.equals(type) || SRType.LVMOISCSI.equals(type) || SRType.LVM.equals(type); } - protected ManageSnapshotAnswer execute(final ManageSnapshotCommand cmd) { - Connection conn = getConnection(); - long snapshotId = cmd.getSnapshotId(); - String snapshotName = cmd.getSnapshotName(); - - // By default assume failure - boolean success = false; - String cmdSwitch = cmd.getCommandSwitch(); - String snapshotOp = "Unsupported snapshot command." + cmdSwitch; - if (cmdSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) { - snapshotOp = "create"; - } else if (cmdSwitch.equals(ManageSnapshotCommand.DESTROY_SNAPSHOT)) { - snapshotOp = "destroy"; - } - String details = "ManageSnapshotCommand operation: " + snapshotOp + " Failed for snapshotId: " + snapshotId; - String snapshotUUID = null; - - try { - if (cmdSwitch.equals(ManageSnapshotCommand.CREATE_SNAPSHOT)) { - // Look up the volume - String volumeUUID = cmd.getVolumePath(); - VDI volume = VDI.getByUuid(conn, volumeUUID); - - // Create a snapshot - VDI snapshot = volume.snapshot(conn, new HashMap()); - - if (snapshotName != null) { - snapshot.setNameLabel(conn, snapshotName); - } - // Determine the UUID of the snapshot - - snapshotUUID = snapshot.getUuid(conn); - String preSnapshotUUID = cmd.getSnapshotPath(); - //check if it is a empty snapshot - if (preSnapshotUUID != null) { - SR sr = volume.getSR(conn); - String srUUID = sr.getUuid(conn); - String type = sr.getType(conn); - Boolean isISCSI = IsISCSI(type); - String snapshotParentUUID = getVhdParent(conn, srUUID, snapshotUUID, isISCSI); - - String preSnapshotParentUUID = getVhdParent(conn, srUUID, preSnapshotUUID, isISCSI); - if (snapshotParentUUID != null && snapshotParentUUID.equals(preSnapshotParentUUID)) { - // this is empty snapshot, remove it - snapshot.destroy(conn); - snapshotUUID = preSnapshotUUID; - } - - } - success = true; - details = null; - } else if (cmd.getCommandSwitch().equals(ManageSnapshotCommand.DESTROY_SNAPSHOT)) { - // Look up the snapshot - snapshotUUID = cmd.getSnapshotPath(); - VDI snapshot = getVDIbyUuid(conn, snapshotUUID); - - snapshot.destroy(conn); - snapshotUUID = null; - success = true; - details = null; - } - } catch (XenAPIException e) { - details += ", reason: " + e.toString(); - s_logger.warn(details, e); - } catch (Exception e) { - details += ", reason: " + e.toString(); - s_logger.warn(details, e); - } - return new ManageSnapshotAnswer(cmd, snapshotId, snapshotUUID, success, details); - } - - protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromVolumeCommand cmd) { - Connection conn = getConnection(); - String secondaryStoragePoolURL = cmd.getSecondaryStorageUrl(); - String volumeUUID = cmd.getVolumePath(); - Long accountId = cmd.getAccountId(); - String userSpecifiedName = cmd.getTemplateName(); - Long templateId = cmd.getTemplateId(); - int wait = cmd.getWait(); - String details = null; - SR tmpltSR = null; - boolean result = false; - String secondaryStorageMountPath = null; - String installPath = null; - try { - URI uri = new URI(secondaryStoragePoolURL); - secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); - installPath = "template/tmpl/" + accountId + "/" + templateId; - if (!createSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath)) { - details = " Filed to create folder " + installPath + " in secondary storage"; - s_logger.warn(details); - return new CreatePrivateTemplateAnswer(cmd, false, details); - } - - VDI volume = getVDIbyUuid(conn, volumeUUID); - // create template SR - URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath); - tmpltSR = createNfsSRbyURI(conn, tmpltURI, false); - - // copy volume to template SR - VDI tmpltVDI = cloudVDIcopy(conn, volume, tmpltSR, wait); - // scan makes XenServer pick up VDI physicalSize - tmpltSR.scan(conn); - if (userSpecifiedName != null) { - tmpltVDI.setNameLabel(conn, userSpecifiedName); - } - - String tmpltUUID = tmpltVDI.getUuid(conn); - String tmpltFilename = tmpltUUID + ".vhd"; - long virtualSize = tmpltVDI.getVirtualSize(conn); - long physicalSize = tmpltVDI.getPhysicalUtilisation(conn); - // create the template.properties file - String templatePath = secondaryStorageMountPath + "/" + installPath; - result = postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, templateId); - if (!result) { - throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI); - } - installPath = installPath + "/" + tmpltFilename; - removeSR(conn, tmpltSR); - tmpltSR = null; - return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, physicalSize, tmpltUUID, ImageFormat.VHD); - } catch (Exception e) { - if (tmpltSR != null) { - removeSR(conn, tmpltSR); - } - if (secondaryStorageMountPath != null) { - deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath); - } - details = "Creating template from volume " + volumeUUID + " failed due to " + e.toString(); - s_logger.error(details, e); - } - return new CreatePrivateTemplateAnswer(cmd, result, details); - } - protected Answer execute(final UpgradeSnapshotCommand cmd) { String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); @@ -7439,60 +7046,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new Answer(cmd, false, "failure"); } - protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromSnapshotCommand cmd) { - Connection conn = getConnection(); - Long accountId = cmd.getAccountId(); - Long volumeId = cmd.getVolumeId(); - String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); - String backedUpSnapshotUuid = cmd.getSnapshotUuid(); - Long newTemplateId = cmd.getNewTemplateId(); - String userSpecifiedName = cmd.getTemplateName(); - int wait = cmd.getWait(); - // By default, assume failure - String details = null; - boolean result = false; - String secondaryStorageMountPath = null; - String installPath = null; - try { - URI uri = new URI(secondaryStorageUrl); - secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); - installPath = "template/tmpl/" + accountId + "/" + newTemplateId; - if (!createSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath)) { - details = " Filed to create folder " + installPath + " in secondary storage"; - s_logger.warn(details); - return new CreatePrivateTemplateAnswer(cmd, false, details); - } - String templatePath = secondaryStorageMountPath + "/" + installPath; - // create snapshot SR - String filename = backedUpSnapshotUuid; - if (!filename.startsWith("VHD-") && !filename.endsWith(".vhd")) { - filename = backedUpSnapshotUuid + ".vhd"; - } - String snapshotPath = secondaryStorageMountPath + "/snapshots/" + accountId + "/" + volumeId + "/" + filename; - String results = createTemplateFromSnapshot(conn, templatePath, snapshotPath, wait); - String[] tmp = results.split("#"); - String tmpltUuid = tmp[1]; - long physicalSize = Long.parseLong(tmp[2]); - long virtualSize = Long.parseLong(tmp[3]) * 1024 * 1024; - String tmpltFilename = tmpltUuid + ".vhd"; - - // create the template.properties file - result = postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUuid, userSpecifiedName, null, physicalSize, virtualSize, newTemplateId); - if (!result) { - throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + templatePath); - } - installPath = installPath + "/" + tmpltFilename; - return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, physicalSize, tmpltUuid, ImageFormat.VHD); - } catch (Exception e) { - if (secondaryStorageMountPath != null) { - deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath); - } - details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.toString(); - s_logger.error(details, e); - } - return new CreatePrivateTemplateAnswer(cmd, result, details); - } - private boolean destroySnapshotOnPrimaryStorageExceptThis(Connection conn, String volumeUuid, String avoidSnapshotUuid) { try { VDI volume = getVDIbyUuid(conn, volumeUuid); @@ -7523,203 +7076,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return false; } - protected BackupSnapshotAnswer execute(final BackupSnapshotCommand cmd) { - Connection conn = getConnection(); - String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel(); - Long dcId = cmd.getDataCenterId(); - Long accountId = cmd.getAccountId(); - Long volumeId = cmd.getVolumeId(); - String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); - String snapshotUuid = cmd.getSnapshotUuid(); // not null: Precondition. - String prevBackupUuid = cmd.getPrevBackupUuid(); - String prevSnapshotUuid = cmd.getPrevSnapshotUuid(); - int wait = cmd.getWait(); - Long secHostId = cmd.getSecHostId(); - // By default assume failure - String details = null; - boolean success = false; - String snapshotBackupUuid = null; - boolean fullbackup = true; - try { - SR primaryStorageSR = getSRByNameLabelandHost(conn, primaryStorageNameLabel); - if (primaryStorageSR == null) { - throw new InternalErrorException("Could not backup snapshot because the primary Storage SR could not be created from the name label: " + - primaryStorageNameLabel); - } - String psUuid = primaryStorageSR.getUuid(conn); - Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn)); - URI uri = new URI(secondaryStorageUrl); - String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); - VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid); - String snapshotPaUuid = null; - if (prevBackupUuid != null) { - try { - snapshotPaUuid = getVhdParent(conn, psUuid, snapshotUuid, isISCSI); - if (snapshotPaUuid != null) { - String snashotPaPaPaUuid = getVhdParent(conn, psUuid, snapshotPaUuid, isISCSI); - String prevSnashotPaUuid = getVhdParent(conn, psUuid, prevSnapshotUuid, isISCSI); - if (snashotPaPaPaUuid != null && prevSnashotPaUuid != null && prevSnashotPaUuid.equals(snashotPaPaPaUuid)) { - fullbackup = false; - } - } - } catch (Exception e) { - } - } - - if (fullbackup) { - // the first snapshot is always a full snapshot - String folder = "snapshots/" + accountId + "/" + volumeId; - if (!createSecondaryStorageFolder(conn, secondaryStorageMountPath, folder)) { - details = " Filed to create folder " + folder + " in secondary storage"; - s_logger.warn(details); - return new BackupSnapshotAnswer(cmd, false, details, null, false); - } - String snapshotMountpoint = secondaryStorageUrl + "/" + folder; - SR snapshotSr = null; - try { - snapshotSr = createNfsSRbyURI(conn, new URI(snapshotMountpoint), false); - VDI backedVdi = cloudVDIcopy(conn, snapshotVdi, snapshotSr, wait); - snapshotBackupUuid = backedVdi.getUuid(conn); - if (cmd.getSwift() != null) { - try { - swiftBackupSnapshot(conn, cmd.getSwift(), snapshotSr.getUuid(conn), snapshotBackupUuid, "S-" + volumeId.toString(), false, wait); - snapshotBackupUuid = snapshotBackupUuid + ".vhd"; - } finally { - deleteSnapshotBackup(conn, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotBackupUuid); - } - } else if (cmd.getS3() != null) { - try { - backupSnapshotToS3(conn, cmd.getS3(), snapshotSr.getUuid(conn), snapshotBackupUuid, isISCSI, wait); - snapshotBackupUuid = snapshotBackupUuid + ".vhd"; - } finally { - deleteSnapshotBackup(conn, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotBackupUuid); - } - } - success = true; - } finally { - if (snapshotSr != null) { - removeSR(conn, snapshotSr); - } - } - } else { - String primaryStorageSRUuid = primaryStorageSR.getUuid(conn); - if (cmd.getSwift() != null) { - swiftBackupSnapshot(conn, cmd.getSwift(), primaryStorageSRUuid, snapshotPaUuid, "S-" + volumeId.toString(), isISCSI, wait); - if (isISCSI) { - snapshotBackupUuid = "VHD-" + snapshotPaUuid; - } else { - snapshotBackupUuid = snapshotPaUuid + ".vhd"; - } - success = true; - } else if (cmd.getS3() != null) { - backupSnapshotToS3(conn, cmd.getS3(), primaryStorageSRUuid, snapshotPaUuid, isISCSI, wait); - } else { - snapshotBackupUuid = - backupSnapshot(conn, primaryStorageSRUuid, dcId, accountId, volumeId, secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI, wait, - secHostId); - success = (snapshotBackupUuid != null); - } - } - String volumeUuid = cmd.getVolumePath(); - destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid); - if (success) { - details = "Successfully backedUp the snapshotUuid: " + snapshotUuid + " to secondary storage."; - - } - } catch (XenAPIException e) { - details = "BackupSnapshot Failed due to " + e.toString(); - s_logger.warn(details, e); - } catch (Exception e) { - details = "BackupSnapshot Failed due to " + e.getMessage(); - s_logger.warn(details, e); - } - - return new BackupSnapshotAnswer(cmd, success, details, snapshotBackupUuid, fullbackup); - } - - private boolean - backupSnapshotToS3(final Connection connection, final S3TO s3, final String srUuid, final String snapshotUuid, final Boolean iSCSIFlag, final int wait) { - - final String filename = iSCSIFlag ? "VHD-" + snapshotUuid : snapshotUuid + ".vhd"; - final String dir = (iSCSIFlag ? "/dev/VG_XenStorage-" : "/var/run/sr-mount/") + srUuid; - final String key = String.format("/snapshots/%1$s", snapshotUuid); - - try { - - final List parameters = newArrayList(flattenProperties(s3, S3Utils.ClientOptions.class)); - // https workaround for Introspector bug that does not - // recognize Boolean accessor methods ... - parameters.addAll(Arrays.asList("operation", "put", "filename", dir + "/" + filename, "iSCSIFlag", iSCSIFlag.toString(), "bucket", s3.getBucketName(), "key", - key, "https", s3.isHttps() != null ? s3.isHttps().toString() : "null")); - final String result = callHostPluginAsync(connection, "s3xen", "s3", wait, parameters.toArray(new String[parameters.size()])); - - if (result != null && result.equals("true")) { - return true; - } - - } catch (Exception e) { - s_logger.error(String.format("S3 upload failed of snapshot %1$s due to %2$s.", snapshotUuid, e.toString()), e); - } - - return false; - - } - - protected CreateVolumeFromSnapshotAnswer execute(final CreateVolumeFromSnapshotCommand cmd) { - Connection conn = getConnection(); - String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel(); - Long accountId = cmd.getAccountId(); - Long volumeId = cmd.getVolumeId(); - String secondaryStorageUrl = cmd.getSecondaryStorageUrl(); - String backedUpSnapshotUuid = cmd.getSnapshotUuid(); - int wait = cmd.getWait(); - boolean result = false; - // Generic error message. - String details = null; - String volumeUUID = null; - SR snapshotSR = null; - - if (secondaryStorageUrl == null) { - details += " because the URL passed: " + secondaryStorageUrl + " is invalid."; - return new CreateVolumeFromSnapshotAnswer(cmd, result, details, volumeUUID); - } - try { - SR primaryStorageSR = getSRByNameLabelandHost(conn, primaryStorageNameLabel); - if (primaryStorageSR == null) { - throw new InternalErrorException("Could not create volume from snapshot because the primary Storage SR could not be created from the name label: " + - primaryStorageNameLabel); - } - // Get the absolute path of the snapshot on the secondary storage. - URI snapshotURI = new URI(secondaryStorageUrl + "/snapshots/" + accountId + "/" + volumeId); - String filename = backedUpSnapshotUuid; - if (!filename.startsWith("VHD-") && !filename.endsWith(".vhd")) { - filename = backedUpSnapshotUuid + ".vhd"; - } - String snapshotPath = snapshotURI.getHost() + ":" + snapshotURI.getPath() + "/" + filename; - String srUuid = primaryStorageSR.getUuid(conn); - volumeUUID = copy_vhd_from_secondarystorage(conn, snapshotPath, srUuid, wait); - result = true; - } catch (XenAPIException e) { - details += " due to " + e.toString(); - s_logger.warn(details, e); - } catch (Exception e) { - details += " due to " + e.getMessage(); - s_logger.warn(details, e); - } finally { - // In all cases, if the temporary SR was created, forget it. - if (snapshotSR != null) { - removeSR(conn, snapshotSR); - } - } - if (!result) { - // Is this logged at a higher level? - s_logger.error(details); - } - - // In all cases return something. - return new CreateVolumeFromSnapshotAnswer(cmd, result, details, volumeUUID); - } - protected VM getVM(Connection conn, String vmName) { // Look up VMs with the specified name Set vms; @@ -7919,27 +7275,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return parentUuid; } - protected boolean destroySnapshotOnPrimaryStorage(Connection conn, String snapshotUuid) { - // Precondition snapshotUuid != null - try { - VDI snapshot = getVDIbyUuid(conn, snapshotUuid); - if (snapshot == null) { - throw new InternalErrorException("Could not destroy snapshot " + snapshotUuid + " because the snapshot VDI was null"); - } - snapshot.destroy(conn); - s_logger.debug("Successfully destroyed snapshotUuid: " + snapshotUuid + " on primary storage"); - return true; - } catch (XenAPIException e) { - String msg = "Destroy snapshotUuid: " + snapshotUuid + " on primary storage failed due to " + e.toString(); - s_logger.error(msg, e); - } catch (Exception e) { - String msg = "Destroy snapshotUuid: " + snapshotUuid + " on primary storage failed due to " + e.getMessage(); - s_logger.warn(msg, e); - } - - return false; - } - protected String deleteSnapshotBackup(Connection conn, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath, String backupUUID) { // If anybody modifies the formatting below again, I'll skin them @@ -7950,10 +7285,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return result; } - protected boolean deleteSnapshotsDir(Connection conn, Long dcId, Long accountId, Long volumeId, String secondaryStorageMountPath) { - return deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, "snapshots" + "/" + accountId.toString() + "/" + volumeId.toString()); - } - @Override public boolean start() { return true; diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java index d685c3f4321..767d77e1b42 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerPoolVms.java @@ -51,6 +51,11 @@ public class XenServerPoolVms { return pv == null ? State.Stopped : pv.second(); // if a VM is absent on the cluster, it is effectively in stopped state. } + public Ternary get(String clusterId, String name) { + HashMap> vms = getClusterVmState(clusterId); + return vms.get(name); + } + public void put(String clusterId, String hostUuid, String name, State state, String platform) { HashMap> vms = getClusterVmState(clusterId); vms.put(name, new Ternary(hostUuid, state, platform)); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java index 91711526c4d..5d2363f3b90 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageProcessor.java @@ -30,9 +30,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -import org.apache.log4j.Logger; -import org.apache.xmlrpc.XmlRpcException; - import com.xensource.xenapi.Connection; import com.xensource.xenapi.Host; import com.xensource.xenapi.PBD; @@ -40,6 +37,7 @@ import com.xensource.xenapi.Pool; import com.xensource.xenapi.SR; import com.xensource.xenapi.Types; import com.xensource.xenapi.Types.BadServerResponse; +import com.xensource.xenapi.Types.VmPowerState; import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VBD; import com.xensource.xenapi.VDI; @@ -64,6 +62,8 @@ import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CreateStoragePoolCommand; @@ -90,7 +90,7 @@ import com.cloud.utils.storage.encoding.Decoder; public class XenServerStorageProcessor implements StorageProcessor { private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class); protected CitrixResourceBase hypervisorResource; - private String BaseMountPointOnHost = "/var/run/cloud_mount"; + protected String BaseMountPointOnHost = "/var/run/cloud_mount"; public XenServerStorageProcessor(CitrixResourceBase resource) { hypervisorResource = resource; @@ -163,19 +163,39 @@ public class XenServerStorageProcessor implements StorageProcessor { @Override public AttachAnswer attachVolume(AttachCommand cmd) { - String vmName = cmd.getVmName(); - String vdiNameLabel = vmName + "-DATA"; DiskTO disk = cmd.getDisk(); DataTO data = disk.getData(); try { - Connection conn = hypervisorResource.getConnection(); + String vmName = cmd.getVmName(); + String vdiNameLabel = vmName + "-DATA"; - VDI vdi = null; + Connection conn = this.hypervisorResource.getConnection(); + VM vm = null; + + boolean vmNotRunning = true; + + try { + vm = this.hypervisorResource.getVM(conn, vmName); + + VM.Record vmr = vm.getRecord(conn); + + vmNotRunning = vmr.powerState != VmPowerState.RUNNING; + } + catch (CloudRuntimeException ex) { + } Map details = cmd.getDisk().getDetails(); boolean isManaged = Boolean.parseBoolean(details.get(DiskTO.MANAGED)); + // if the VM is not running and we're not dealing with managed storage, just return success (nothing to do here) + // this should probably never actually happen + if (vmNotRunning && !isManaged) { + return new AttachAnswer(disk); + } + + VDI vdi = null; + if (isManaged) { String iScsiName = details.get(DiskTO.IQN); String storageHost = details.get(DiskTO.STORAGE_HOST); @@ -190,47 +210,57 @@ public class XenServerStorageProcessor implements StorageProcessor { if (vdi == null) { vdi = hypervisorResource.createVdi(sr, vdiNameLabel, volumeSize); } - } else { + + if (vmNotRunning) { + DiskTO newDisk = new DiskTO(disk.getData(), disk.getDiskSeq(), vdi.getUuid(conn), disk.getType()); + + return new AttachAnswer(newDisk); + } + } + else { vdi = hypervisorResource.mount(conn, null, null, data.getPath()); } - // Look up the VM - VM vm = hypervisorResource.getVM(conn, vmName); /* For HVM guest, if no pv driver installed, no attach/detach */ - boolean isHVM; - if (vm.getPVBootloader(conn).equalsIgnoreCase("")) { - isHVM = true; - } else { - isHVM = false; - } + boolean isHVM = vm.getPVBootloader(conn).equalsIgnoreCase(""); + VMGuestMetrics vgm = vm.getGuestMetrics(conn); boolean pvDrvInstalled = false; - if (!hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { + + if (!this.hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { pvDrvInstalled = true; } + if (isHVM && !pvDrvInstalled) { s_logger.warn(": You attempted an operation on a VM which requires PV drivers to be installed but the drivers were not detected"); + return new AttachAnswer("You attempted an operation that requires PV drivers to be installed on the VM. Please install them by inserting xen-pv-drv.iso."); } // Figure out the disk number to attach the VM to String diskNumber = null; Long deviceId = disk.getDiskSeq(); + if (deviceId != null) { if (deviceId.longValue() == 3) { String msg = "Device 3 is reserved for CD-ROM, choose other device"; + return new AttachAnswer(msg); } + if (hypervisorResource.isDeviceUsed(conn, vm, deviceId)) { String msg = "Device " + deviceId + " is used in VM " + vmName; + return new AttachAnswer(msg); } + diskNumber = deviceId.toString(); } else { diskNumber = hypervisorResource.getUnusedDeviceNum(conn, vm); } - // Create a new VBD + VBD.Record vbdr = new VBD.Record(); + vbdr.VM = vm; vbdr.VDI = vdi; vbdr.bootable = false; @@ -238,6 +268,7 @@ public class XenServerStorageProcessor implements StorageProcessor { vbdr.mode = Types.VbdMode.RW; vbdr.type = Types.VbdType.DISK; vbdr.unpluggable = true; + VBD vbd = VBD.create(conn, vbdr); // Attach the VBD to the VM @@ -245,9 +276,10 @@ public class XenServerStorageProcessor implements StorageProcessor { // Update the VDI's label to include the VM name vdi.setNameLabel(conn, vdiNameLabel); - DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), vdi.getUuid(conn), disk.getType()); - return new AttachAnswer(newDisk); + DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), vdi.getUuid(conn), disk.getType()); + + return new AttachAnswer(newDisk); } catch (XenAPIException e) { String msg = "Failed to attach volume" + " for uuid: " + data.getPath() + " due to " + e.toString(); s_logger.warn(msg, e); @@ -325,54 +357,73 @@ public class XenServerStorageProcessor implements StorageProcessor { @Override public Answer dettachVolume(DettachCommand cmd) { - String vmName = cmd.getVmName(); DiskTO disk = cmd.getDisk(); DataTO data = disk.getData(); + try { - Connection conn = hypervisorResource.getConnection(); - // Look up the VDI - VDI vdi = hypervisorResource.mount(conn, null, null, data.getPath()); - // Look up the VM - VM vm = hypervisorResource.getVM(conn, vmName); - /* For HVM guest, if no pv driver installed, no attach/detach */ - boolean isHVM; - if (vm.getPVBootloader(conn).equalsIgnoreCase("")) { - isHVM = true; - } else { - isHVM = false; + Connection conn = this.hypervisorResource.getConnection(); + + String vmName = cmd.getVmName(); + VM vm = null; + + boolean vmNotRunning = true; + + try { + vm = this.hypervisorResource.getVM(conn, vmName); + + VM.Record vmr = vm.getRecord(conn); + + vmNotRunning = vmr.powerState != VmPowerState.RUNNING; } - VMGuestMetrics vgm = vm.getGuestMetrics(conn); - boolean pvDrvInstalled = false; - if (!hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { - pvDrvInstalled = true; - } - if (isHVM && !pvDrvInstalled) { - s_logger.warn(": You attempted an operation on a VM which requires PV drivers to be installed but the drivers were not detected"); - return new DettachAnswer( - "You attempted an operation that requires PV drivers to be installed on the VM. Please install them by inserting xen-pv-drv.iso."); + catch (CloudRuntimeException ex) { } - // Look up all VBDs for this VDI - Set vbds = vdi.getVBDs(conn); + // if the VM is not running and we're not dealing with managed storage, just return success (nothing to do here) + // this should probably never actually happen + if (vmNotRunning && !cmd.isManaged()) { + return new DettachAnswer(disk); + } - // Detach each VBD from its VM, and then destroy it - for (VBD vbd : vbds) { - VBD.Record vbdr = vbd.getRecord(conn); + if (!vmNotRunning) { + /* For HVM guest, if no pv driver installed, no attach/detach */ + boolean isHVM = vm.getPVBootloader(conn).equalsIgnoreCase(""); - if (vbdr.currentlyAttached) { - vbd.unplug(conn); + VMGuestMetrics vgm = vm.getGuestMetrics(conn); + boolean pvDrvInstalled = false; + + if (!this.hypervisorResource.isRefNull(vgm) && vgm.getPVDriversUpToDate(conn)) { + pvDrvInstalled = true; } - vbd.destroy(conn); + if (isHVM && !pvDrvInstalled) { + s_logger.warn(": You attempted an operation on a VM which requires PV drivers to be installed but the drivers were not detected"); + return new DettachAnswer("You attempted an operation that requires PV drivers to be installed on the VM. Please install them by inserting xen-pv-drv.iso."); + } + + VDI vdi = this.hypervisorResource.mount(conn, null, null, data.getPath()); + + // Look up all VBDs for this VDI + Set vbds = vdi.getVBDs(conn); + + // Detach each VBD from its VM, and then destroy it + for (VBD vbd : vbds) { + VBD.Record vbdr = vbd.getRecord(conn); + + if (vbdr.currentlyAttached) { + vbd.unplug(conn); + } + + vbd.destroy(conn); + } + + // Update the VDI's label to be "detached" + vdi.setNameLabel(conn, "detached"); + + this.hypervisorResource.umount(conn, vdi); } - // Update the VDI's label to be "detached" - vdi.setNameLabel(conn, "detached"); - - hypervisorResource.umount(conn, vdi); - if (cmd.isManaged()) { - hypervisorResource.handleSrAndVdiDetach(cmd.get_iScsiName()); + hypervisorResource.handleSrAndVdiDetach(cmd.get_iScsiName(), conn); } return new DettachAnswer(disk); @@ -777,7 +828,7 @@ public class XenServerStorageProcessor implements StorageProcessor { } } - private boolean IsISCSI(String type) { + protected boolean IsISCSI(String type) { return SRType.LVMOHBA.equals(type) || SRType.LVMOISCSI.equals(type) || SRType.LVM.equals(type); } @@ -1076,7 +1127,7 @@ public class XenServerStorageProcessor implements StorageProcessor { return lfilename; } - private String backupSnapshotToS3(final Connection connection, final S3TO s3, final String srUuid, final String folder, final String snapshotUuid, + protected String backupSnapshotToS3(final Connection connection, final S3TO s3, final String srUuid, final String folder, final String snapshotUuid, final Boolean iSCSIFlag, final int wait) { final String filename = iSCSIFlag ? "VHD-" + snapshotUuid : snapshotUuid + ".vhd"; @@ -1106,6 +1157,16 @@ public class XenServerStorageProcessor implements StorageProcessor { } + protected Long getSnapshotSize(Connection conn, String primaryStorageSRUuid, String snapshotUuid, Boolean isISCSI, int wait) { + String physicalSize = hypervisorResource.callHostPluginAsync(conn, "vmopsSnapshot", "getSnapshotSize", wait, + "primaryStorageSRUuid", primaryStorageSRUuid, "snapshotUuid", snapshotUuid, "isISCSI", isISCSI.toString()); + if (physicalSize == null || physicalSize.isEmpty()) { + return (long) 0; + } else { + return Long.parseLong(physicalSize); + } + } + protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, String localMountPoint, String path, String secondaryStorageMountPath, String snapshotUuid, String prevBackupUuid, Boolean isISCSI, int wait) { String backupSnapshotUuid = null; @@ -1136,7 +1197,7 @@ public class XenServerStorageProcessor implements StorageProcessor { // indicator of success. if (status != null && status.equalsIgnoreCase("1") && backupSnapshotUuid != null) { s_logger.debug("Successfully copied backupUuid: " + backupSnapshotUuid + " to secondary storage"); - return backupSnapshotUuid; + return results; } else { errMsg = "Could not copy backupUuid: " + backupSnapshotUuid + " from primary storage " + primaryStorageSRUuid + " to secondary storage " + @@ -1149,7 +1210,7 @@ public class XenServerStorageProcessor implements StorageProcessor { throw new CloudRuntimeException(errMsg); } - private boolean destroySnapshotOnPrimaryStorageExceptThis(Connection conn, String volumeUuid, String avoidSnapshotUuid) { + protected boolean destroySnapshotOnPrimaryStorageExceptThis(Connection conn, String volumeUuid, String avoidSnapshotUuid) { try { VDI volume = getVDIbyUuid(conn, volumeUuid); if (volume == null) { @@ -1232,6 +1293,7 @@ public class XenServerStorageProcessor implements StorageProcessor { String details = null; String snapshotBackupUuid = null; boolean fullbackup = true; + Long physicalSize = null; try { SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel); if (primaryStorageSR == null) { @@ -1278,6 +1340,8 @@ public class XenServerStorageProcessor implements StorageProcessor { snapshotSr = hypervisorResource.createNfsSRbyURI(conn, new URI(snapshotMountpoint), false); VDI backedVdi = hypervisorResource.cloudVDIcopy(conn, snapshotVdi, snapshotSr, wait); snapshotBackupUuid = backedVdi.getUuid(conn); + String primarySRuuid = snapshotSr.getUuid(conn); + physicalSize = getSnapshotSize(conn, primarySRuuid, snapshotBackupUuid, isISCSI, wait); if (destStore instanceof SwiftTO) { try { @@ -1330,9 +1394,12 @@ public class XenServerStorageProcessor implements StorageProcessor { throw new CloudRuntimeException("S3 upload of snapshots " + snapshotPaUuid + " failed"); } } else { - snapshotBackupUuid = + String results = backupSnapshot(conn, primaryStorageSRUuid, localMountPoint, folder, secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI, wait); + String[] tmp = results.split("#"); + snapshotBackupUuid = tmp[1]; + physicalSize = Long.parseLong(tmp[2]); finalPath = folder + File.separator + snapshotBackupUuid; } } @@ -1341,6 +1408,7 @@ public class XenServerStorageProcessor implements StorageProcessor { SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); newSnapshot.setPath(finalPath); + newSnapshot.setPhysicalSize(physicalSize); if (fullbackup) { newSnapshot.setParentSnapshotPath(null); } else { diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625Resource.java new file mode 100644 index 00000000000..7d2a2a13ecb --- /dev/null +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625Resource.java @@ -0,0 +1,112 @@ +/* + * 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.hypervisor.xen.resource; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; + +import org.apache.cloudstack.hypervisor.xenserver.XenServerResourceNewBase; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.StartupRoutingCommand; +import com.cloud.resource.ServerResource; +import com.cloud.storage.resource.StorageSubsystemCommandHandler; +import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; +import com.xensource.xenapi.Connection; + +@Local(value=ServerResource.class) +public class Xenserver625Resource extends XenServerResourceNewBase { + private static final Logger s_logger = Logger.getLogger(XenServer620Resource.class); + + public Xenserver625Resource() { + super(); + } + + @Override + protected void fillHostInfo(Connection conn, StartupRoutingCommand cmd) { + super.fillHostInfo(conn, cmd); + Map details = cmd.getHostDetails(); + details.put("Xenserer620HotFix", "Xenserver-Vdi-Copy-HotFix"); + } + + @Override + protected String getGuestOsType(String stdType, boolean bootFromCD) { + return CitrixHelper.getXenServer620GuestOsType(stdType, bootFromCD); + } + + @Override + protected List getPatchFiles() { + List files = new ArrayList(); + String patch = "scripts/vm/hypervisor/xenserver/xenserver62/patch"; + String patchfilePath = Script.findScript("", patch); + if (patchfilePath == null) { + throw new CloudRuntimeException("Unable to find patch file " + patch); + } + File file = new File(patchfilePath); + files.add(file); + return files; + } + + @Override + public long getStaticMax(String os, boolean b, long dynamicMinRam, long dynamicMaxRam){ + long recommendedValue = CitrixHelper.getXenServer620StaticMax(os, b); + if(recommendedValue == 0){ + s_logger.warn("No recommended value found for dynamic max, setting static max and dynamic max equal"); + return dynamicMaxRam; + } + long staticMax = Math.min(recommendedValue, 4l * dynamicMinRam); // XS constraint for stability + if (dynamicMaxRam > staticMax){ // XS contraint that dynamic max <= static max + s_logger.warn("dynamixMax " + dynamicMaxRam + " cant be greater than static max " + staticMax + ", can lead to stability issues. Setting static max as much as dynamic max "); + return dynamicMaxRam; + } + return staticMax; + } + + @Override + public long getStaticMin(String os, boolean b, long dynamicMinRam, long dynamicMaxRam){ + long recommendedValue = CitrixHelper.getXenServer620StaticMin(os, b); + if(recommendedValue == 0){ + s_logger.warn("No recommended value found for dynamic min"); + return dynamicMinRam; + } + + if(dynamicMinRam < recommendedValue){ // XS contraint that dynamic min > static min + s_logger.warn("Vm is set to dynamixMin " + dynamicMinRam + " less than the recommended static min " + recommendedValue + ", could lead to stability issues"); + } + return dynamicMinRam; + } + + @Override + protected StorageSubsystemCommandHandler getStorageHandler() { + XenServerStorageProcessor processor = new Xenserver625StorageProcessor(this); + return new StorageSubsystemCommandHandlerBase(processor); + } + + @Override + protected void umountSnapshotDir(Connection conn, Long dcId) { + + } + +} \ No newline at end of file diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java new file mode 100644 index 00000000000..dad3d145fae --- /dev/null +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/Xenserver625StorageProcessor.java @@ -0,0 +1,822 @@ +/* + * 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.hypervisor.xen.resource; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.command.CopyCommand; +import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import com.cloud.agent.api.to.NfsTO; +import com.cloud.agent.api.to.S3TO; +import com.cloud.agent.api.to.SwiftTO; +import com.cloud.exception.InternalErrorException; +import com.cloud.storage.Storage; +import com.cloud.utils.exception.CloudRuntimeException; +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.PBD; +import com.xensource.xenapi.SR; +import com.xensource.xenapi.Task; +import com.xensource.xenapi.Types; +import com.xensource.xenapi.VDI; + +public class Xenserver625StorageProcessor extends XenServerStorageProcessor { + private static final Logger s_logger = Logger.getLogger(XenServerStorageProcessor.class); + + public Xenserver625StorageProcessor(CitrixResourceBase resource) { + super(resource); + } + protected boolean mountNfs(Connection conn, String remoteDir, String localDir) { + if (localDir == null) { + localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(remoteDir.getBytes()); + } + String results = hypervisorResource.callHostPluginAsync(conn, "cloud-plugin-storage", "mountNfsSecondaryStorage", 100 * 1000, + "localDir", localDir, "remoteDir", remoteDir); + if (results == null || results.isEmpty()) { + String errMsg = "Could not mount secondary storage " + remoteDir + " on host "; + s_logger.warn(errMsg); + throw new CloudRuntimeException(errMsg); + } + return true; + } + + protected boolean makeDirectory(Connection conn, String path) { + String result = hypervisorResource.callHostPlugin(conn, "cloud-plugin-storage", "makeDirectory", "path", path); + if (result == null || result.isEmpty()) { + return false; + } + return true; + } + + protected SR createFileSR(Connection conn, String path) { + SR sr = null; + PBD pbd = null; + try { + Map smConfig = new HashMap(); + Host host = Host.getByUuid(conn, hypervisorResource.getHost().uuid); + String uuid = UUID.randomUUID().toString(); + + sr = SR.introduce(conn,uuid, uuid, uuid, "file", "file", false, smConfig); + PBD.Record record = new PBD.Record(); + record.host = host; + record.SR = sr; + smConfig.put("location", path); + record.deviceConfig = smConfig; + pbd = PBD.create(conn, record); + pbd.plug(conn); + sr.scan(conn); + return sr; + } catch (Exception e) { + try { + if (pbd != null) { + pbd.destroy(conn); + } + } catch (Exception e1) { + s_logger.debug("Failed to destroy pbd", e); + } + try { + if (sr != null) { + sr.forget(conn); + } + } catch (Exception e2) { + s_logger.error("Failed to forget sr", e); + } + String msg = "createFileSR failed! due to " + e.toString(); + s_logger.warn(msg, e); + throw new CloudRuntimeException(msg, e); + } + } + + protected SR createFileSr(Connection conn, String remotePath, String dir) { + String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(remotePath.getBytes()); + mountNfs(conn, remotePath, localDir); + SR sr = createFileSR(conn, localDir + "/" + dir); + return sr; + } + + @Override + public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) { + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + DataStoreTO srcStore = srcData.getDataStore(); + Connection conn = hypervisorResource.getConnection(); + SR srcSr = null; + try { + if ((srcStore instanceof NfsTO) && (srcData.getObjectType() == DataObjectType.TEMPLATE)) { + NfsTO srcImageStore = (NfsTO)srcStore; + TemplateObjectTO srcTemplate = (TemplateObjectTO)srcData; + String storeUrl = srcImageStore.getUrl(); + URI uri = new URI(storeUrl); + String volumePath = srcData.getPath(); + volumePath = StringUtils.stripEnd(volumePath, "/"); + String[] splits = volumePath.split("/"); + String volumeDirectory = volumePath; + if (splits.length > 4) { + //"template/tmpl/dcid/templateId/templatename" + int index = volumePath.lastIndexOf("/"); + volumeDirectory = volumePath.substring(0, index); + } + srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), volumeDirectory); + Set vdis = srcSr.getVDIs(conn); + if (vdis.size() != 1) { + return new CopyCmdAnswer("Can't find template VDI under: " + uri.getHost() + ":" + uri.getPath() + "/" + volumeDirectory); + } + + VDI srcVdi = vdis.iterator().next(); + + PrimaryDataStoreTO destStore = (PrimaryDataStoreTO)destData.getDataStore(); + String poolName = destStore.getUuid(); + + + SR poolsr = null; + Set srs = SR.getByNameLabel(conn, poolName); + if (srs.size() != 1) { + String msg = "There are " + srs.size() + " SRs with same name: " + poolName; + s_logger.warn(msg); + return new CopyCmdAnswer(msg); + } else { + poolsr = srs.iterator().next(); + } + String pUuid = poolsr.getUuid(conn); + boolean isISCSI = IsISCSI(poolsr.getType(conn)); + Task task = srcVdi.copyAsync2(conn, poolsr, null, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + VDI tmpl = Types.toVDI(task, conn); + VDI snapshotvdi = tmpl.snapshot(conn, new HashMap()); + snapshotvdi.setNameLabel(conn, "Template " + srcTemplate.getName()); + tmpl.destroy(conn); + poolsr.scan(conn); + try{ + Thread.sleep(5000); + } catch (Exception e) { + } + + TemplateObjectTO newVol = new TemplateObjectTO(); + newVol.setUuid(snapshotvdi.getUuid(conn)); + newVol.setPath(newVol.getUuid()); + newVol.setFormat(Storage.ImageFormat.VHD); + return new CopyCmdAnswer(newVol); + } + }catch (Exception e) { + String msg = "Catch Exception " + e.getClass().getName() + " for template + " + " due to " + e.toString(); + s_logger.warn(msg, e); + return new CopyCmdAnswer(msg); + } finally { + if (srcSr != null) { + hypervisorResource.removeSR(conn, srcSr); + } + } + return new CopyCmdAnswer("not implemented yet"); + } + + protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, String localMountPoint, String path, String secondaryStorageMountPath, String snapshotUuid, String prevBackupUuid, String prevSnapshotUuid, Boolean isISCSI, int wait) { + String errMsg = null; + boolean mounted = false; + boolean filesrcreated = false; + boolean copied = false; + if (prevBackupUuid == null) { + prevBackupUuid = ""; + } + SR ssSR = null; + + String remoteDir = secondaryStorageMountPath; + + try { + ssSR = createFileSr(conn, remoteDir, path); + filesrcreated = true; + + VDI snapshotvdi = VDI.getByUuid(conn, snapshotUuid); + Task task = null; + if (wait == 0) { + wait = 2 * 60 * 60; + } + VDI dvdi = null; + try { + VDI previousSnapshotVdi = null; + if (prevSnapshotUuid != null) { + previousSnapshotVdi = VDI.getByUuid(conn,prevSnapshotUuid); + } + task = snapshotvdi.copyAsync2(conn, ssSR, previousSnapshotVdi, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + dvdi = Types.toVDI(task, conn); + copied = true; + } finally { + if (task != null) { + try { + task.destroy(conn); + } catch (Exception e1) { + s_logger.warn("unable to destroy task(" + task.toString() + ") on host(" + + ") due to ", e1); + } + } + } + String backupUuid = dvdi.getUuid(conn); + return backupUuid; + } catch (Exception e) { + String msg = "Exception in backupsnapshot stage due to " + e.toString(); + s_logger.debug(msg); + throw new CloudRuntimeException(msg, e); + } finally { + try { + if (filesrcreated && ssSR != null) { + hypervisorResource.removeSR(conn, ssSR); + } + } catch (Exception e) { + s_logger.debug("Exception in backupsnapshot cleanup stage due to " + e.toString()); + } + } + } + + @Override + protected String getVhdParent(Connection conn, String primaryStorageSRUuid, String snapshotUuid, Boolean isISCSI) { + String parentUuid = hypervisorResource.callHostPlugin(conn, "cloud-plugin-storage", "getVhdParent", "primaryStorageSRUuid", primaryStorageSRUuid, + "snapshotUuid", snapshotUuid, "isISCSI", isISCSI.toString()); + + if (parentUuid == null || parentUuid.isEmpty() || parentUuid.equalsIgnoreCase("None")) { + s_logger.debug("Unable to get parent of VHD " + snapshotUuid + " in SR " + primaryStorageSRUuid); + // errString is already logged. + return null; + } + return parentUuid; + } + + @Override + public Answer backupSnapshot(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO cacheData = cmd.getCacheTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcData.getDataStore(); + String primaryStorageNameLabel = primaryStore.getUuid(); + String secondaryStorageUrl = null; + NfsTO cacheStore = null; + String destPath = null; + if (cacheData != null) { + cacheStore = (NfsTO)cacheData.getDataStore(); + secondaryStorageUrl = cacheStore.getUrl(); + destPath = cacheData.getPath(); + } else { + cacheStore = (NfsTO)destData.getDataStore(); + secondaryStorageUrl = cacheStore.getUrl(); + destPath = destData.getPath(); + } + + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)srcData; + SnapshotObjectTO snapshotOnImage = (SnapshotObjectTO)destData; + String snapshotUuid = snapshotTO.getPath(); + + String prevBackupUuid = snapshotOnImage.getParentSnapshotPath(); + String prevSnapshotUuid = snapshotTO.getParentSnapshotPath(); + Map options = cmd.getOptions(); + // By default assume failure + String details = null; + String snapshotBackupUuid = null; + boolean fullbackup = Boolean.parseBoolean(options.get("fullSnapshot")); + try { + SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel); + if (primaryStorageSR == null) { + throw new InternalErrorException("Could not backup snapshot because the primary Storage SR could not be created from the name label: " + primaryStorageNameLabel); + } + String psUuid = primaryStorageSR.getUuid(conn); + Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn)); + + VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid); + String snapshotPaUuid = null; + + URI uri = new URI(secondaryStorageUrl); + String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); + DataStoreTO destStore = destData.getDataStore(); + String folder = destPath; + String finalPath = null; + + String localMountPoint = BaseMountPointOnHost + File.separator + UUID.nameUUIDFromBytes(secondaryStorageUrl.getBytes()).toString(); + if (fullbackup) { + SR snapshotSr = null; + try { + String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(secondaryStorageMountPath.getBytes()); + mountNfs(conn, secondaryStorageMountPath, localDir); + boolean result = makeDirectory(conn, localDir + "/" + folder); + if (!result) { + details = " Filed to create folder " + folder + " in secondary storage"; + s_logger.warn(details); + return new CopyCmdAnswer(details); + } + + snapshotSr = createFileSr(conn, secondaryStorageMountPath, folder); + + Task task = snapshotVdi.copyAsync2(conn, snapshotSr, null, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + VDI backedVdi = Types.toVDI(task, conn); + snapshotBackupUuid = backedVdi.getUuid(conn); + + if( destStore instanceof SwiftTO) { + try { + String container = "S-" + snapshotTO.getVolume().getVolumeId().toString(); + String destSnapshotName = swiftBackupSnapshot(conn, (SwiftTO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, container, false, wait); + String swiftPath = container + File.separator + destSnapshotName; + finalPath = swiftPath; + } finally { + try { + deleteSnapshotBackup(conn, localMountPoint, folder, secondaryStorageMountPath, snapshotBackupUuid); + } catch (Exception e) { + s_logger.debug("Failed to delete snapshot on cache storages" ,e); + } + } + + } else if (destStore instanceof S3TO) { + try { + finalPath = backupSnapshotToS3(conn, (S3TO) destStore, snapshotSr.getUuid(conn), folder, snapshotBackupUuid, isISCSI, wait); + if (finalPath == null) { + throw new CloudRuntimeException("S3 upload of snapshots " + snapshotBackupUuid + " failed"); + } + } finally { + try { + deleteSnapshotBackup(conn, localMountPoint, folder, secondaryStorageMountPath, snapshotBackupUuid); + } catch (Exception e) { + s_logger.debug("Failed to delete snapshot on cache storages" ,e); + } + } + // finalPath = folder + File.separator + snapshotBackupUuid; + } else { + finalPath = folder + File.separator + snapshotBackupUuid; + } + + } finally { + if( snapshotSr != null) { + hypervisorResource.removeSR(conn, snapshotSr); + } + } + } else { + String primaryStorageSRUuid = primaryStorageSR.getUuid(conn); + if( destStore instanceof SwiftTO ) { + String container = "S-" + snapshotTO.getVolume().getVolumeId().toString(); + snapshotBackupUuid = swiftBackupSnapshot(conn, (SwiftTO)destStore, primaryStorageSRUuid, snapshotPaUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), isISCSI, wait); + finalPath = container + File.separator + snapshotBackupUuid; + } else if (destStore instanceof S3TO ) { + finalPath = backupSnapshotToS3(conn, (S3TO) destStore, primaryStorageSRUuid, folder, snapshotPaUuid, isISCSI, wait); + if (finalPath == null) { + throw new CloudRuntimeException("S3 upload of snapshots " + snapshotPaUuid + " failed"); + } + } else { + snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, localMountPoint, folder, + secondaryStorageMountPath, snapshotUuid, prevBackupUuid, prevSnapshotUuid, isISCSI, wait); + + finalPath = folder + File.separator + snapshotBackupUuid; + } + } + String volumeUuid = snapshotTO.getVolume().getPath(); + destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid); + + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(finalPath); + if (fullbackup) { + newSnapshot.setParentSnapshotPath(null); + } else { + newSnapshot.setParentSnapshotPath(prevBackupUuid); + } + return new CopyCmdAnswer(newSnapshot); + } catch (Types.XenAPIException e) { + details = "BackupSnapshot Failed due to " + e.toString(); + s_logger.warn(details, e); + } catch (Exception e) { + details = "BackupSnapshot Failed due to " + e.getMessage(); + s_logger.warn(details, e); + } + + return new CopyCmdAnswer(details); + } + + @Override + public Answer createTemplateFromVolume(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + VolumeObjectTO volume = (VolumeObjectTO)cmd.getSrcTO(); + TemplateObjectTO template = (TemplateObjectTO)cmd.getDestTO(); + NfsTO destStore = (NfsTO)cmd.getDestTO().getDataStore(); + int wait = cmd.getWait(); + + String secondaryStoragePoolURL = destStore.getUrl(); + String volumeUUID = volume.getPath(); + + String userSpecifiedName = template.getName(); + + + String details = null; + SR tmpltSR = null; + boolean result = false; + String secondaryStorageMountPath = null; + String installPath = null; + try { + URI uri = new URI(secondaryStoragePoolURL); + secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); + installPath = template.getPath(); + if( !hypervisorResource.createSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath)) { + details = " Filed to create folder " + installPath + " in secondary storage"; + s_logger.warn(details); + return new CopyCmdAnswer(details); + } + + VDI vol = getVDIbyUuid(conn, volumeUUID); + // create template SR + tmpltSR = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), installPath); + + // copy volume to template SR + Task task = vol.copyAsync2(conn, tmpltSR, null, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + VDI tmpltVDI = Types.toVDI(task, conn); + // scan makes XenServer pick up VDI physicalSize + tmpltSR.scan(conn); + if (userSpecifiedName != null) { + tmpltVDI.setNameLabel(conn, userSpecifiedName); + } + + String tmpltUUID = tmpltVDI.getUuid(conn); + String tmpltFilename = tmpltUUID + ".vhd"; + long virtualSize = tmpltVDI.getVirtualSize(conn); + long physicalSize = tmpltVDI.getPhysicalUtilisation(conn); + // create the template.properties file + String templatePath = secondaryStorageMountPath + "/" + installPath; + result = hypervisorResource.postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, template.getId()); + if (!result) { + throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir"); + } + installPath = installPath + "/" + tmpltFilename; + hypervisorResource.removeSR(conn, tmpltSR); + tmpltSR = null; + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(installPath); + newTemplate.setFormat(Storage.ImageFormat.VHD); + newTemplate.setSize(virtualSize); + newTemplate.setPhysicalSize(physicalSize); + newTemplate.setName(tmpltUUID); + CopyCmdAnswer answer = new CopyCmdAnswer(newTemplate); + return answer; + } catch (Exception e) { + if (tmpltSR != null) { + hypervisorResource.removeSR(conn, tmpltSR); + } + if ( secondaryStorageMountPath != null) { + hypervisorResource.deleteSecondaryStorageFolder(conn, secondaryStorageMountPath, installPath); + } + details = "Creating template from volume " + volumeUUID + " failed due to " + e.toString(); + s_logger.error(details, e); + } + return new CopyCmdAnswer(details); + } + + protected String getSnapshotUuid(String snapshotPath) { + int index = snapshotPath.lastIndexOf(File.separator); + String snapshotUuid = snapshotPath.substring(index + 1); + index = snapshotUuid.lastIndexOf("."); + if (index != -1) { + snapshotUuid = snapshotUuid.substring(0, index); + } + return snapshotUuid; + } + + @Override + public Answer createVolumeFromSnapshot(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + SnapshotObjectTO snapshot = (SnapshotObjectTO)srcData; + DataTO destData = cmd.getDestTO(); + PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); + VolumeObjectTO volume = (VolumeObjectTO)destData; + DataStoreTO imageStore = srcData.getDataStore(); + + if (!(imageStore instanceof NfsTO)) { + return new CopyCmdAnswer("unsupported protocol"); + } + + NfsTO nfsImageStore = (NfsTO)imageStore; + String primaryStorageNameLabel = pool.getUuid(); + String secondaryStorageUrl = nfsImageStore.getUrl(); + int wait = cmd.getWait(); + boolean result = false; + // Generic error message. + String details = null; + String volumeUUID = null; + + if (secondaryStorageUrl == null) { + details += " because the URL passed: " + secondaryStorageUrl + " is invalid."; + return new CopyCmdAnswer(details); + } + SR srcSr = null; + VDI destVdi = null; + try { + SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel); + if (primaryStorageSR == null) { + throw new InternalErrorException("Could not create volume from snapshot because the primary Storage SR could not be created from the name label: " + + primaryStorageNameLabel); + } + String nameLabel = "cloud-" + UUID.randomUUID().toString(); + destVdi = createVdi(conn, nameLabel, primaryStorageSR, volume.getSize()); + volumeUUID = destVdi.getUuid(conn); + String snapshotInstallPath = snapshot.getPath(); + int index = snapshotInstallPath.lastIndexOf(File.separator); + String snapshotDirectory = snapshotInstallPath.substring(0, index); + String snapshotUuid = getSnapshotUuid(snapshotInstallPath); + + URI uri = new URI(secondaryStorageUrl); + srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), snapshotDirectory); + + String[] parents = snapshot.getParents(); + List snapshotChains = new ArrayList(); + if (parents != null) { + for(int i = 0; i < parents.length; i++) { + String snChainPath = parents[i]; + String uuid = getSnapshotUuid(snChainPath); + VDI chain = VDI.getByUuid(conn, uuid); + snapshotChains.add(chain); + } + } + + VDI snapshotVdi = VDI.getByUuid(conn, snapshotUuid); + snapshotChains.add(snapshotVdi); + + for(VDI snapChain : snapshotChains) { + Task task = snapChain.copyAsync2(conn, null, null, destVdi); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + } + + result = true; + destVdi = VDI.getByUuid(conn, volumeUUID); + VDI.Record vdir = destVdi.getRecord(conn); + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(volumeUUID); + newVol.setSize(vdir.virtualSize); + return new CopyCmdAnswer(newVol); + } catch (Types.XenAPIException e) { + details += " due to " + e.toString(); + s_logger.warn(details, e); + } catch (Exception e) { + details += " due to " + e.getMessage(); + s_logger.warn(details, e); + } finally { + if (srcSr != null) { + hypervisorResource.removeSR(conn, srcSr); + } + if (!result && destVdi != null) { + try { + destVdi.destroy(conn); + } catch (Exception e) { + s_logger.debug("destroy dest vdi failed", e); + } + } + } + if (!result) { + // Is this logged at a higher level? + s_logger.error(details); + } + + // In all cases return something. + return new CopyCmdAnswer(details); + } + + @Override + public Answer copyVolumeFromPrimaryToSecondary(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + VolumeObjectTO srcVolume = (VolumeObjectTO)cmd.getSrcTO(); + VolumeObjectTO destVolume = (VolumeObjectTO)cmd.getDestTO(); + int wait = cmd.getWait(); + DataStoreTO destStore = destVolume.getDataStore(); + + if (destStore instanceof NfsTO) { + SR secondaryStorage = null; + try { + NfsTO nfsStore = (NfsTO)destStore; + URI uri = new URI(nfsStore.getUrl()); + // Create the volume folder + if (!hypervisorResource.createSecondaryStorageFolder(conn, uri.getHost() + ":" + uri.getPath(), destVolume.getPath())) { + throw new InternalErrorException("Failed to create the volume folder."); + } + + // Create a SR for the volume UUID folder + secondaryStorage = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), destVolume.getPath()); + // Look up the volume on the source primary storage pool + VDI srcVdi = getVDIbyUuid(conn, srcVolume.getPath()); + // Copy the volume to secondary storage + Task task = srcVdi.copyAsync2(conn, secondaryStorage, null, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + VDI destVdi = Types.toVDI(task, conn); + String destVolumeUUID = destVdi.getUuid(conn); + + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(destVolume.getPath() + File.separator + destVolumeUUID + ".vhd"); + newVol.setSize(srcVolume.getSize()); + return new CopyCmdAnswer(newVol); + } catch (Exception e) { + s_logger.debug("Failed to copy volume to secondary: " + e.toString()); + return new CopyCmdAnswer("Failed to copy volume to secondary: " + e.toString()); + } finally { + hypervisorResource.removeSR(conn, secondaryStorage); + } + } + return new CopyCmdAnswer("unsupported protocol"); + } + + @Override + public Answer copyVolumeFromImageCacheToPrimary(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + VolumeObjectTO srcVolume = (VolumeObjectTO)srcData; + VolumeObjectTO destVolume = (VolumeObjectTO)destData; + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)destVolume.getDataStore(); + DataStoreTO srcStore = srcVolume.getDataStore(); + + if (srcStore instanceof NfsTO) { + NfsTO nfsStore = (NfsTO)srcStore; + String volumePath = srcVolume.getPath(); + int index = volumePath.lastIndexOf("/"); + String volumeDirectory = volumePath.substring(0, index); + String volumeUuid = volumePath.substring(index + 1); + index = volumeUuid.indexOf("."); + if (index != -1) { + volumeUuid = volumeUuid.substring(0, index); + } + URI uri = null; + try { + uri = new URI(nfsStore.getUrl()); + } catch (Exception e) { + return new CopyCmdAnswer(e.toString()); + } + SR srcSr = createFileSr(conn, uri.getHost() + ":" + uri.getPath(), volumeDirectory); + try { + SR primaryStoragePool = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid()); + VDI srcVdi = VDI.getByUuid(conn, volumeUuid); + Task task = srcVdi.copyAsync2(conn, primaryStoragePool, null, null); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + VDI destVdi = Types.toVDI(task, conn); + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setPath(destVdi.getUuid(conn)); + newVol.setSize(srcVolume.getSize()); + + return new CopyCmdAnswer(newVol); + } catch (Exception e) { + String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString(); + s_logger.warn(msg, e); + return new CopyCmdAnswer(e.toString()); + } finally { + if (srcSr != null) { + hypervisorResource.removeSR(conn, srcSr); + } + } + } + + s_logger.debug("unsupported protocol"); + return new CopyCmdAnswer("unsupported protocol"); + } + + @Override + public Answer createTemplateFromSnapshot(CopyCommand cmd) { + Connection conn = hypervisorResource.getConnection(); + DataTO srcData = cmd.getSrcTO(); + DataTO destData = cmd.getDestTO(); + int wait = cmd.getWait(); + SnapshotObjectTO srcObj = (SnapshotObjectTO)srcData; + TemplateObjectTO destObj = (TemplateObjectTO)destData; + NfsTO srcStore = (NfsTO)srcObj.getDataStore(); + NfsTO destStore = (NfsTO)destObj.getDataStore(); + + URI srcUri = null; + URI destUri = null; + try { + srcUri = new URI(srcStore.getUrl()); + destUri = new URI(destStore.getUrl()); + } catch (Exception e) { + s_logger.debug("incorrect url", e); + return new CopyCmdAnswer("incorrect url" + e.toString()); + } + + String srcPath = srcObj.getPath(); + int index = srcPath.lastIndexOf("/"); + String srcDir = srcPath.substring(0, index); + String destDir = destObj.getPath(); + SR srcSr = null; + SR destSr = null; + VDI destVdi = null; + boolean result = false; + try { + srcSr = createFileSr(conn, srcUri.getHost() + ":" + srcUri.getPath(), srcDir); + + String destNfsPath = destUri.getHost() + ":" + destUri.getPath(); + String localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(destNfsPath.getBytes()); + mountNfs(conn, destUri.getHost() + ":" + destUri.getPath(), localDir); + makeDirectory(conn, localDir + "/" + destDir); + destSr = createFileSR(conn, localDir + "/" + destDir); + + String nameLabel = "cloud-" + UUID.randomUUID().toString(); + + String[] parents = srcObj.getParents(); + List snapshotChains = new ArrayList(); + if (parents != null) { + for(int i = 0; i < parents.length; i++) { + String snChainPath = parents[i]; + String uuid = getSnapshotUuid(snChainPath); + VDI chain = VDI.getByUuid(conn, uuid); + snapshotChains.add(chain); + } + } + String snapshotUuid = getSnapshotUuid(srcPath); + VDI snapshotVdi = VDI.getByUuid(conn, snapshotUuid); + snapshotChains.add(snapshotVdi); + + long templateVirtualSize = snapshotChains.get(0).getVirtualSize(conn); + destVdi = createVdi(conn, nameLabel, destSr, templateVirtualSize); + String destVdiUuid = destVdi.getUuid(conn); + + for(VDI snapChain : snapshotChains) { + Task task = snapChain.copyAsync2(conn, null, null, destVdi); + // poll every 1 seconds , + hypervisorResource.waitForTask(conn, task, 1000, wait * 1000); + hypervisorResource.checkForSuccess(conn, task); + } + + destVdi = VDI.getByUuid(conn, destVdiUuid); + String templatePath = destDir + "/" + destVdiUuid + ".vhd"; + templatePath = templatePath.replaceAll("//","/"); + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(templatePath); + newTemplate.setFormat(Storage.ImageFormat.VHD); + newTemplate.setSize(destVdi.getVirtualSize(conn)); + newTemplate.setPhysicalSize(destVdi.getPhysicalUtilisation(conn)); + newTemplate.setName(destVdiUuid); + + result = true; + return new CopyCmdAnswer(newTemplate); + } catch (Exception e) { + s_logger.error("Failed create template from snapshot", e); + return new CopyCmdAnswer("Failed create template from snapshot " + e.toString()); + } finally { + if (!result) { + if (destVdi != null) { + try { + destVdi.destroy(conn); + } catch (Exception e) { + s_logger.debug("Clean up left over on dest storage failed: ", e); + } + } + } + + if (destSr != null) { + hypervisorResource.removeSR(conn, destSr); + } + + if (srcSr != null) { + hypervisorResource.removeSR(conn, srcSr); + } + } + } + +} diff --git a/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java new file mode 100644 index 00000000000..1c9748679bd --- /dev/null +++ b/plugins/hypervisors/xen/src/org/apache/cloudstack/hypervisor/xenserver/XenServerResourceNewBase.java @@ -0,0 +1,320 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.hypervisor.xenserver; + + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeoutException; + +import org.apache.log4j.Logger; +import org.apache.xmlrpc.XmlRpcException; + +import com.xensource.xenapi.Connection; +import com.xensource.xenapi.Event; +import com.xensource.xenapi.Host; +import com.xensource.xenapi.Pool; +import com.xensource.xenapi.Task; +import com.xensource.xenapi.Types; +import com.xensource.xenapi.Types.XenAPIException; +import com.xensource.xenapi.VM; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ClusterSyncAnswer; +import com.cloud.agent.api.ClusterSyncCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.hypervisor.xen.resource.XenServer610Resource; +import com.cloud.utils.Ternary; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineName; + +public class XenServerResourceNewBase extends XenServer610Resource { + private static final Logger s_logger = Logger.getLogger(XenServerResourceNewBase.class); + protected VmEventListener _listener = null; + + @Override + public StartupCommand[] initialize() throws IllegalArgumentException { + StartupCommand[] cmds = super.initialize(); + + Connection conn = getConnection(); + Pool pool; + try { + pool = Pool.getByUuid(conn, _host.pool); + Pool.Record poolr = pool.getRecord(conn); + + Host.Record masterRecord = poolr.master.getRecord(conn); + if (_host.uuid.equals(masterRecord.uuid)) { + _listener = new VmEventListener(true); + _listener.start(); + } else { + _listener = new VmEventListener(false); + } + } catch (XenAPIException e) { + throw new CloudRuntimeException("Unable to determine who is the master", e); + } catch (XmlRpcException e) { + throw new CloudRuntimeException("Unable to determine who is the master", e); + } + return cmds; + } + + protected void waitForTask2(Connection c, Task task, long pollInterval, long timeout) throws XenAPIException, XmlRpcException, TimeoutException { + long beginTime = System.currentTimeMillis(); + if (s_logger.isTraceEnabled()) { + s_logger.trace("Task " + task.getNameLabel(c) + " (" + task.getType(c) + ") sent to " + c.getSessionReference() + " is pending completion with a " + timeout + + "ms timeout"); + } + Set classes = new HashSet(); + classes.add("Task/" + task.toString()); + String token = ""; + Double t = new Double(timeout / 1000); + while (true) { + Map map = Event.properFrom(c, classes, token, t); + token = (String)map.get("token"); + @SuppressWarnings("unchecked") + Set events = (Set)map.get("events"); + if (events.size() == 0) { + String msg = "Async " + timeout / 1000 + " seconds timeout for task " + task.toString(); + s_logger.warn(msg); + task.cancel(c); + throw new TimeoutException(msg); + } + for (Event.Record rec : events) { + if (!(rec.snapshot instanceof Task.Record)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Skipping over " + rec); + } + continue; + } + + Task.Record taskRecord = (Task.Record)rec.snapshot; + + if (taskRecord.status != Types.TaskStatusType.PENDING) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Task is done " + taskRecord.status); + } + return; + } else { + s_logger.debug("Task is not done " + taskRecord); + } + } + } + } + + @Override + protected Answer execute(final ClusterSyncCommand cmd) { + if (!_listener.isListening()) { + return new Answer(cmd); + } + + HashMap> newStates = _listener.getChanges(); + return new ClusterSyncAnswer(cmd.getClusterId(), newStates); + } + + protected class VmEventListener extends Thread { + boolean _stop = false; + HashMap> _changes = new HashMap>(); + boolean _isMaster; + Set _classes; + String _token = ""; + + public VmEventListener(boolean isMaster) { + _isMaster = isMaster; + _classes = new HashSet(); + _classes.add("VM"); + } + + @Override + public void run() { + setName("XS-Listener-" + _host.ip); + while (!_stop) { + try { + Connection conn = getConnection(); + Map results; + try { + results = Event.properFrom(conn, _classes, _token, new Double(30)); + } catch (Exception e) { + s_logger.error("Retrying the waiting on VM events due to: ", e); + continue; + } + + _token = (String)results.get("token"); + @SuppressWarnings("unchecked") + Set events = (Set)results.get("events"); + for (Event.Record event : events) { + try { + if (!(event.snapshot instanceof VM.Record)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("The snapshot is not a VM: " + event); + } + continue; + } + VM.Record vm = (VM.Record)event.snapshot; + + String hostUuid = null; + if (vm.residentOn != null && !vm.residentOn.toWireString().contains("OpaqueRef:NULL")) { + hostUuid = vm.residentOn.getUuid(conn); + } + recordChanges(conn, vm, hostUuid); + } catch (Exception e) { + s_logger.error("Skipping over " + event, e); + } + } + } catch (Throwable th) { + s_logger.error("Exception caught in eventlistener thread: ", th); + } + } + } + + protected void recordChanges(Connection conn, VM.Record rec, String hostUuid) { + String vm = rec.nameLabel; + if (!VirtualMachineName.isValidCloudStackVmName(vm, _instance)) { + s_logger.debug("Skipping over VMs that does not conform to CloudStack naming convention: " + vm); + return; + } + + VirtualMachine.State currentState = convertToState(rec.powerState); + if (vm.startsWith("migrating")) { + s_logger.warn("Skipping " + vm + " because it is migrating."); + return; + } + + if (currentState == VirtualMachine.State.Stopped) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Double check the power state to make sure we got the correct state for " + vm); + } + currentState = getRealPowerState(conn, vm); + } + + boolean updateMap = false; + boolean reportChange = false; + + // NOTE: For now we only record change when the VM is stopped. We don't find out any VMs starting for now. + synchronized (_cluster.intern()) { + Ternary oldState = s_vms.get(_cluster, vm); + if (oldState == null) { + if (s_logger.isTraceEnabled()) { + s_logger.trace("Unable to find " + vm + " from previous map. Assuming it was in Stopped state."); + } + oldState = new Ternary(null, VirtualMachine.State.Stopped, null); + } + + if (s_logger.isTraceEnabled()) { + s_logger.trace(vm + ": current state=" + currentState + ", previous state=" + oldState); + } + + if (oldState.second() == VirtualMachine.State.Starting) { + if (currentState == VirtualMachine.State.Running) { + updateMap = true; + reportChange = false; + } else if (currentState == VirtualMachine.State.Stopped) { + updateMap = false; + reportChange = false; + } + } else if (oldState.second() == VirtualMachine.State.Migrating) { + updateMap = true; + reportChange = false; + } else if (oldState.second() == VirtualMachine.State.Stopping) { + if (currentState == VirtualMachine.State.Stopped) { + updateMap = true; + reportChange = false; + } else if (currentState == VirtualMachine.State.Running) { + updateMap = false; + reportChange = false; + } + } else if (oldState.second() != currentState) { + updateMap = true; + reportChange = true; + } else if (hostUuid != null && !hostUuid.equals(oldState.first())) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Detecting " + vm + " moved from " + oldState.first() + " to " + hostUuid); + } + reportChange = true; + updateMap = true; + } + + if (updateMap) { + s_vms.put(_cluster, hostUuid, vm, currentState); + if (s_logger.isTraceEnabled()) { + s_logger.trace("Updated " + vm + " to [" + hostUuid + ", " + currentState); + } + } + if (reportChange) { + Ternary change = _changes.get(vm); + if (hostUuid == null) { + // This is really strange code. It looks like the sync + // code wants this to be set, which is extremely weird + // for VMs that are dead. Why would I want to set the + // hostUuid if the VM is stopped. + hostUuid = oldState.first(); + if (hostUuid == null) { + hostUuid = _host.uuid; + } + } + if (change == null) { + change = new Ternary(hostUuid, currentState, null); + } else { + change.first(hostUuid); + change.second(currentState); + } + _changes.put(vm, change); + } + } + } + + @Override + public void start() { + if (_isMaster) { + // Throw away the initial set of events because they're history + Connection conn = getConnection(); + Map results; + try { + results = Event.properFrom(conn, _classes, _token, new Double(30)); + } catch (Exception e) { + s_logger.error("Retrying the waiting on VM events due to: ", e); + throw new CloudRuntimeException("Unable to start a listener thread to listen to VM events", e); + } + _token = (String)results.get("token"); + s_logger.debug("Starting the event listener thread for " + _host.uuid); + super.start(); + } + } + + public boolean isListening() { + return _isMaster; + } + + public HashMap> getChanges() { + synchronized (_cluster.intern()) { + if (_changes.size() == 0) { + return null; + } + HashMap> diff = _changes; + _changes = new HashMap>(); + return diff; + } + } + + public void signalStop() { + _stop = true; + interrupt(); + } + } + +} diff --git a/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java b/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java index e8b6aa213fe..03fac266700 100644 --- a/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java +++ b/plugins/network-elements/cisco-vnmc/src/com/cloud/network/element/CiscoVnmcElement.java @@ -27,6 +27,7 @@ import java.util.UUID; import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import javax.persistence.EntityExistsException; import org.apache.log4j.Logger; @@ -847,8 +848,12 @@ public class CiscoVnmcElement extends AdapterBase implements SourceNatServicePro throw new InvalidParameterValueException("Could not find phyical network with ID: " + physicalNetworkId); } - ciscoAsa1000vResource = new CiscoAsa1000vDeviceVO(physicalNetworkId, cmd.getManagementIp(), cmd.getInPortProfile(), cmd.getClusterId()); - _ciscoAsa1000vDao.persist((CiscoAsa1000vDeviceVO)ciscoAsa1000vResource); + ciscoAsa1000vResource = new CiscoAsa1000vDeviceVO(physicalNetworkId, cmd.getManagementIp().trim(), cmd.getInPortProfile(), cmd.getClusterId()); + try { + _ciscoAsa1000vDao.persist((CiscoAsa1000vDeviceVO)ciscoAsa1000vResource); + } catch (EntityExistsException e) { + throw new InvalidParameterValueException("An ASA 1000v appliance already exists with same configuration"); + } return ciscoAsa1000vResource; } diff --git a/plugins/network-elements/f5/pom.xml b/plugins/network-elements/f5/pom.xml index d1a8cda2d17..a2c7b6b46d9 100644 --- a/plugins/network-elements/f5/pom.xml +++ b/plugins/network-elements/f5/pom.xml @@ -36,5 +36,10 @@ org.apache.axis axis + + commons-discovery + commons-discovery + 0.5 + diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ModelDatabase.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ModelDatabase.java index 7f66a3b8290..e2845b7644c 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ModelDatabase.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ModelDatabase.java @@ -31,7 +31,7 @@ public class ModelDatabase { TreeSet _vmTable; TreeSet _vnTable; - ModelDatabase() { + public ModelDatabase() { initDb(); } diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/VirtualMachineModel.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/VirtualMachineModel.java index f571f895a5f..df0af908320 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/VirtualMachineModel.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/VirtualMachineModel.java @@ -337,8 +337,27 @@ public class VirtualMachineModel extends ModelObjectBase { @Override public boolean verify(ModelController controller) { - // TODO Auto-generated method stub - return false; + assert _initialized : "initialized is false"; + assert _uuid != null : "uuid is not set"; + + ApiConnector api = controller.getApiAccessor(); + + try { + _vm = (VirtualMachine) api.findById(VirtualMachine.class, _uuid); + } catch (IOException e) { + s_logger.error("virtual-machine verify", e); + } + + if (_vm == null) { + return false; + } + + for (ModelObject successor: successors()) { + if (!successor.verify(controller)) { + return false; + } + } + return true; } @Override diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualMachineModelTest.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualMachineModelTest.java new file mode 100644 index 00000000000..f85beb62fe6 --- /dev/null +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualMachineModelTest.java @@ -0,0 +1,124 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.cloudstack.network.contrail.model; + +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.util.UUID; + +import junit.framework.TestCase; +import net.juniper.contrail.api.ApiConnector; +import net.juniper.contrail.api.ApiConnectorMock; + +import org.apache.log4j.Logger; +import org.junit.Test; + +import org.apache.cloudstack.network.contrail.management.ContrailManagerImpl; +import org.apache.cloudstack.network.contrail.management.ModelDatabase; + +import com.cloud.network.Network; +import com.cloud.network.dao.NetworkVO; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.UserVmDao; + +public class VirtualMachineModelTest extends TestCase { + private static final Logger s_logger = + Logger.getLogger(VirtualMachineModelTest.class); + + @Test + public void testVirtualMachineDBLookup() { + ModelDatabase db = new ModelDatabase(); + VMInstanceVO vm = mock(VMInstanceVO.class); + + // Create 3 dummy Virtual Machine model objects + // Add these models to database. + // Each VM is identified by unique UUId. + VirtualMachineModel vm0 = new VirtualMachineModel(vm, "fbc1f8fa-4b78-45ee-bba0-b551dbf72353"); + db.getVirtualMachines().add(vm0); + + VirtualMachineModel vm1 = new VirtualMachineModel(vm, "fbc1f8fa-4b78-45ee-bba0-b551dbf83464"); + db.getVirtualMachines().add(vm1); + + VirtualMachineModel vm2 = new VirtualMachineModel(vm, "fbc1f8fa-4b78-45ee-bba0-b551dbf94575"); + db.getVirtualMachines().add(vm2); + + s_logger.debug("No of Vitual Machines added to database : " + db.getVirtualMachines().size()); + + assertEquals(3, db.getVirtualMachines().size()); + + assertSame(vm0, db.lookupVirtualMachine("fbc1f8fa-4b78-45ee-bba0-b551dbf72353")); + assertSame(vm1, db.lookupVirtualMachine("fbc1f8fa-4b78-45ee-bba0-b551dbf83464")); + assertSame(vm2, db.lookupVirtualMachine("fbc1f8fa-4b78-45ee-bba0-b551dbf94575")); + } + + @Test + public void testCreateVirtualMachine() throws IOException { + + String uuid = UUID.randomUUID().toString(); + ContrailManagerImpl contrailMgr = mock(ContrailManagerImpl.class); + ModelController controller = mock(ModelController.class); + ApiConnector api = new ApiConnectorMock(null, 0); + when(controller.getManager()).thenReturn(contrailMgr); + when(controller.getApiAccessor()).thenReturn(api); + + // Create Virtual-Network (VN) + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(Network.State.Allocated); + when(network.getGateway()).thenReturn("10.1.1.1"); + when(network.getCidr()).thenReturn("10.1.1.0/24"); + when(network.getPhysicalNetworkId()).thenReturn(42L); + when(network.getDomainId()).thenReturn(10L); + when(network.getAccountId()).thenReturn(42L); + + when(contrailMgr.getCanonicalName(network)).thenReturn("testnetwork"); + when(contrailMgr.getProjectId(network.getDomainId(), network.getAccountId())).thenReturn("testProjectId"); + + // Create Virtual-Machine (VM) + VMInstanceVO vm = mock(VMInstanceVO.class); + when(vm.getInstanceName()).thenReturn("testVM1"); + when(vm.getState()).thenReturn(VirtualMachine.State.Starting); + when(vm.getDomainId()).thenReturn(10L); + when(vm.getAccountId()).thenReturn(42L); + + UserVmDao VmDao = mock(UserVmDao.class); + when(VmDao.findById(anyLong())).thenReturn(null); + when(controller.getVmDao()).thenReturn(VmDao); + + VirtualMachineModel vmModel = new VirtualMachineModel(vm, uuid); + + assertEquals(vmModel.getInstanceName(), "testVM1"); + assertEquals(vmModel.getUuid(), uuid); + + vmModel.build(controller, vm); + try { + vmModel.update(controller); + } catch (Exception ex) { + fail("virtual-network update failed "); + } + + //verify + assertTrue(vmModel.verify(controller)); + + } + +} diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/VirtualNetworkModelTest.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualNetworkModelTest.java similarity index 52% rename from plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/VirtualNetworkModelTest.java rename to plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualNetworkModelTest.java index 09385417440..b1b5ae1ebff 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/VirtualNetworkModelTest.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/model/VirtualNetworkModelTest.java @@ -15,18 +15,26 @@ // specific language governing permissions and limitations // under the License. -package org.apache.cloudstack.network.contrail.management; +package org.apache.cloudstack.network.contrail.model; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; import java.util.UUID; import junit.framework.TestCase; +import net.juniper.contrail.api.ApiConnector; +import net.juniper.contrail.api.ApiConnectorMock; import org.apache.log4j.Logger; import org.junit.Test; -import org.mockito.Mockito; -import org.apache.cloudstack.network.contrail.model.VirtualNetworkModel; +import org.apache.cloudstack.network.contrail.management.ContrailManager; +import org.apache.cloudstack.network.contrail.management.ContrailManagerImpl; +import org.apache.cloudstack.network.contrail.management.ModelDatabase; +import com.cloud.network.Network.State; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.NetworkVO; @@ -36,7 +44,7 @@ public class VirtualNetworkModelTest extends TestCase { @Test public void testDBLookup() { ModelDatabase db = new ModelDatabase(); - NetworkVO network = Mockito.mock(NetworkVO.class); + NetworkVO network = mock(NetworkVO.class); VirtualNetworkModel storageModel = new VirtualNetworkModel(network, null, ContrailManager.managementNetworkName, TrafficType.Storage); db.getVirtualNetworks().add(storageModel); VirtualNetworkModel mgmtModel = new VirtualNetworkModel(network, null, ContrailManager.managementNetworkName, TrafficType.Management); @@ -46,6 +54,7 @@ public class VirtualNetworkModelTest extends TestCase { VirtualNetworkModel guestModel2 = new VirtualNetworkModel(network, UUID.randomUUID().toString(), "test", TrafficType.Guest); db.getVirtualNetworks().add(guestModel2); s_logger.debug("networks: " + db.getVirtualNetworks().size()); + s_logger.debug("No of Vitual Networks added to database : " + db.getVirtualNetworks().size()); assertEquals(4, db.getVirtualNetworks().size()); assertSame(storageModel, db.lookupVirtualNetwork(null, storageModel.getName(), TrafficType.Storage)); assertSame(mgmtModel, db.lookupVirtualNetwork(null, mgmtModel.getName(), TrafficType.Management)); @@ -53,4 +62,41 @@ public class VirtualNetworkModelTest extends TestCase { assertSame(guestModel2, db.lookupVirtualNetwork(guestModel2.getUuid(), null, TrafficType.Guest)); } + @Test + public void testCreateVirtualNetwork() throws IOException { + + String uuid = UUID.randomUUID().toString(); + ContrailManagerImpl contrailMgr = mock(ContrailManagerImpl.class); + ModelController controller = mock(ModelController.class); + ApiConnector api = new ApiConnectorMock(null, 0); + when(controller.getManager()).thenReturn(contrailMgr); + when(controller.getApiAccessor()).thenReturn(api); + + // Create Virtual-Network (VN) + NetworkVO network = mock(NetworkVO.class); + when(network.getName()).thenReturn("testnetwork"); + when(network.getState()).thenReturn(State.Allocated); + when(network.getGateway()).thenReturn("10.1.1.1"); + when(network.getCidr()).thenReturn("10.1.1.0/24"); + when(network.getPhysicalNetworkId()).thenReturn(42L); + when(network.getDomainId()).thenReturn(10L); + when(network.getAccountId()).thenReturn(42L); + + when(contrailMgr.getCanonicalName(network)).thenReturn("testnetwork"); + when(contrailMgr.getProjectId(network.getDomainId(), network.getAccountId())).thenReturn("testProjectId"); + + VirtualNetworkModel vnModel = new VirtualNetworkModel(network, uuid, "testnetwork", TrafficType.Guest); + + assertEquals(vnModel.getName(), "testnetwork"); + assertEquals(vnModel.getUuid(), uuid); + + vnModel.build(controller, network); + try { + vnModel.update(controller); + } catch (Exception ex) { + fail("virtual-network update failed "); + } + assertTrue(vnModel.verify(controller)); + } + } diff --git a/plugins/network-elements/nicira-nvp/pom.xml b/plugins/network-elements/nicira-nvp/pom.xml index 3bc8d141093..a4b4c494da9 100644 --- a/plugins/network-elements/nicira-nvp/pom.xml +++ b/plugins/network-elements/nicira-nvp/pom.xml @@ -26,39 +26,36 @@ 4.4.0-SNAPSHOT ../../pom.xml - + + + + integration + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java index 96712906047..2dbf828d666 100644 --- a/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/element/NiciraNvpElement.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.network.element; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -126,9 +127,9 @@ import com.cloud.vm.dao.NicDao; @Component @Local(value = {NetworkElement.class, ConnectivityProvider.class, SourceNatServiceProvider.class, StaticNatServiceProvider.class, PortForwardingServiceProvider.class, - IpDeployer.class}) + IpDeployer.class}) public class NiciraNvpElement extends AdapterBase implements ConnectivityProvider, SourceNatServiceProvider, PortForwardingServiceProvider, StaticNatServiceProvider, - NiciraNvpElementService, ResourceStateAdapter, IpDeployer { +NiciraNvpElementService, ResourceStateAdapter, IpDeployer { private static final int MAX_PORT = 65535; private static final int MIN_PORT = 0; @@ -210,7 +211,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide @Override public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { + ResourceUnavailableException, InsufficientCapacityException { s_logger.debug("entering NiciraNvpElement implement function for network " + network.getDisplayText() + " (state " + network.getState() + ")"); if (!canHandle(network, Service.Connectivity)) { @@ -245,12 +246,25 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide PublicIp sourceNatIp = ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network); String publicCidr = sourceNatIp.getAddress().addr() + "/" + NetUtils.getCidrSize(sourceNatIp.getVlanNetmask()); String internalCidr = network.getGateway() + "/" + network.getCidr().split("/")[1]; - long vlanid = (Vlan.UNTAGGED.equals(sourceNatIp.getVlanTag())) ? 0 : Long.parseLong(sourceNatIp.getVlanTag()); + // assuming a vlan: + String vtag = sourceNatIp.getVlanTag(); + BroadcastDomainType tiep = null; + try { + tiep = BroadcastDomainType.getTypeOf(vtag); + } catch (URISyntaxException use) { + throw new CloudRuntimeException("vlantag for sourceNatIp is not valid: " + vtag, use); + } + if (tiep == BroadcastDomainType.Vlan) { + vtag = BroadcastDomainType.Vlan.getValueFrom(BroadcastDomainType.fromString(vtag)); + } else if (!(tiep == BroadcastDomainType.UnDecided || tiep == BroadcastDomainType.Native)) { + throw new CloudRuntimeException("only vlans are supported for sourceNatIp, at this moment: " + vtag); + } + long vlanid = (Vlan.UNTAGGED.equals(vtag)) ? 0 : Long.parseLong(vtag); CreateLogicalRouterCommand cmd = - new CreateLogicalRouterCommand(niciraNvpHost.getDetail("l3gatewayserviceuuid"), vlanid, BroadcastDomainType.getValue(network.getBroadcastUri()), - "router-" + network.getDisplayText(), publicCidr, sourceNatIp.getGateway(), internalCidr, context.getDomain().getName() + "-" + - context.getAccount().getAccountName()); + new CreateLogicalRouterCommand(niciraNvpHost.getDetail("l3gatewayserviceuuid"), vlanid, BroadcastDomainType.getValue(network.getBroadcastUri()), + "router-" + network.getDisplayText(), publicCidr, sourceNatIp.getGateway(), internalCidr, context.getDomain().getName() + "-" + + context.getAccount().getAccountName()); CreateLogicalRouterAnswer answer = (CreateLogicalRouterAnswer)agentMgr.easySend(niciraNvpHost.getId(), cmd); if (answer.getResult() == false) { s_logger.error("Failed to create Logical Router for network " + network.getDisplayText()); @@ -267,7 +281,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide @Override public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) - throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { if (!canHandle(network, Service.Connectivity)) { return false; @@ -296,8 +310,8 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide if (answer.getResult()) { s_logger.warn("Existing Logical Switchport found for nic " + nic.getName() + " with uuid " + existingNicMap.getLogicalSwitchPortUuid()); UpdateLogicalSwitchPortCommand cmd = - new UpdateLogicalSwitchPortCommand(existingNicMap.getLogicalSwitchPortUuid(), BroadcastDomainType.getValue(network.getBroadcastUri()), - nicVO.getUuid(), context.getDomain().getName() + "-" + context.getAccount().getAccountName(), nic.getName()); + new UpdateLogicalSwitchPortCommand(existingNicMap.getLogicalSwitchPortUuid(), BroadcastDomainType.getValue(network.getBroadcastUri()), + nicVO.getUuid(), context.getDomain().getName() + "-" + context.getAccount().getAccountName(), nic.getName()); agentMgr.easySend(niciraNvpHost.getId(), cmd); return true; } else { @@ -307,8 +321,8 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide } CreateLogicalSwitchPortCommand cmd = - new CreateLogicalSwitchPortCommand(BroadcastDomainType.getValue(network.getBroadcastUri()), nicVO.getUuid(), context.getDomain().getName() + "-" + - context.getAccount().getAccountName(), nic.getName()); + new CreateLogicalSwitchPortCommand(BroadcastDomainType.getValue(network.getBroadcastUri()), nicVO.getUuid(), context.getDomain().getName() + "-" + + context.getAccount().getAccountName(), nic.getName()); CreateLogicalSwitchPortAnswer answer = (CreateLogicalSwitchPortAnswer)agentMgr.easySend(niciraNvpHost.getId(), cmd); if (answer == null || !answer.getResult()) { @@ -317,7 +331,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide } NiciraNvpNicMappingVO nicMap = - new NiciraNvpNicMappingVO(BroadcastDomainType.getValue(network.getBroadcastUri()), answer.getLogicalSwitchPortUuid(), nicVO.getUuid()); + new NiciraNvpNicMappingVO(BroadcastDomainType.getValue(network.getBroadcastUri()), answer.getLogicalSwitchPortUuid(), nicVO.getUuid()); niciraNvpNicMappingDao.persist(nicMap); return true; @@ -325,7 +339,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide @Override public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, - ResourceUnavailableException { + ResourceUnavailableException { if (!canHandle(network, Service.Connectivity)) { return false; @@ -420,7 +434,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide @Override public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, - ResourceUnavailableException { + ResourceUnavailableException { // Nothing to do here. return true; } @@ -485,6 +499,9 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide ServerResource resource = new NiciraNvpResource(); final String deviceName = Network.Provider.NiciraNvp.getName(); NetworkDevice networkDevice = NetworkDevice.getNetworkDevice(deviceName); + if (networkDevice == null) { + throw new CloudRuntimeException("No network device found for " + deviceName); + } final Long physicalNetworkId = cmd.getPhysicalNetworkId(); PhysicalNetworkVO physicalNetwork = physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { @@ -493,13 +510,13 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide long zoneId = physicalNetwork.getDataCenterId(); final PhysicalNetworkServiceProviderVO ntwkSvcProvider = - physicalNetworkServiceProviderDao.findByServiceProvider(physicalNetwork.getId(), networkDevice.getNetworkServiceProvder()); + physicalNetworkServiceProviderDao.findByServiceProvider(physicalNetwork.getId(), networkDevice.getNetworkServiceProvder()); if (ntwkSvcProvider == null) { throw new CloudRuntimeException("Network Service Provider: " + networkDevice.getNetworkServiceProvder() + " is not enabled in the physical network: " + - physicalNetworkId + "to add this device"); + physicalNetworkId + "to add this device"); } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + " is in shutdown state in the physical network: " + - physicalNetworkId + "to add this device"); + physicalNetworkId + "to add this device"); } if (niciraNvpDao.listByPhysicalNetwork(physicalNetworkId).size() != 0) { @@ -584,12 +601,13 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide // Lets see if there are networks that use us // Find the nicira networks on this physical network List networkList = networkDao.listByPhysicalNetwork(physicalNetworkId); - - // Networks with broadcast type lswitch are ours - for (NetworkVO network : networkList) { - if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.Lswitch) { - if ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy)) { - throw new CloudRuntimeException("This Nicira Nvp device can not be deleted as there are one or more logical networks provisioned by cloudstack."); + if (networkList != null) { + // Networks with broadcast type lswitch are ours + for (NetworkVO network : networkList) { + if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.Lswitch) { + if ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy)) { + throw new CloudRuntimeException("This Nicira Nvp device can not be deleted as there are one or more logical networks provisioned by cloudstack."); + } } } } @@ -651,6 +669,9 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide // Find the nicira networks on this physical network List networkList = networkDao.listByPhysicalNetwork(physicalNetworkId); + if (networkList == null) { + return Collections.emptyList(); + } // Networks with broadcast type lswitch are ours List responseList = new ArrayList(); @@ -733,7 +754,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide cidrs.add(ip.getAddress().addr() + "/" + NetUtils.getCidrSize(ip.getNetmask())); } ConfigurePublicIpsOnLogicalRouterCommand cmd = - new ConfigurePublicIpsOnLogicalRouterCommand(routermapping.getLogicalRouterUuid(), niciraNvpHost.getDetail("l3gatewayserviceuuid"), cidrs); + new ConfigurePublicIpsOnLogicalRouterCommand(routermapping.getLogicalRouterUuid(), niciraNvpHost.getDetail("l3gatewayserviceuuid"), cidrs); ConfigurePublicIpsOnLogicalRouterAnswer answer = (ConfigurePublicIpsOnLogicalRouterAnswer)agentMgr.easySend(niciraNvpHost.getId(), cmd); //FIXME answer can be null if the host is down return answer.getResult(); @@ -774,7 +795,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide // we only need the source and destination ip. Unfortunately no mention if a rule // is new. StaticNatRuleTO ruleTO = - new StaticNatRuleTO(1, sourceIp.getAddress().addr(), MIN_PORT, MAX_PORT, rule.getDestIpAddress(), MIN_PORT, MAX_PORT, "any", rule.isForRevoke(), false); + new StaticNatRuleTO(1, sourceIp.getAddress().addr(), MIN_PORT, MAX_PORT, rule.getDestIpAddress(), MIN_PORT, MAX_PORT, "any", rule.isForRevoke(), false); staticNatRules.add(ruleTO); } @@ -816,7 +837,7 @@ public class NiciraNvpElement extends AdapterBase implements ConnectivityProvide } ConfigurePortForwardingRulesOnLogicalRouterCommand cmd = - new ConfigurePortForwardingRulesOnLogicalRouterCommand(routermapping.getLogicalRouterUuid(), portForwardingRules); + new ConfigurePortForwardingRulesOnLogicalRouterCommand(routermapping.getLogicalRouterUuid(), portForwardingRules); ConfigurePortForwardingRulesOnLogicalRouterAnswer answer = (ConfigurePortForwardingRulesOnLogicalRouterAnswer)agentMgr.easySend(niciraNvpHost.getId(), cmd); return answer.getResult(); diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessConfiguration.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessConfiguration.java new file mode 100644 index 00000000000..74fe19d644e --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessConfiguration.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +import java.io.Serializable; +import java.util.List; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +@SuppressWarnings("serial") +public abstract class AccessConfiguration implements Serializable { + + protected String displayName; + protected List logicalPortEgressRules; + protected List logicalPortIngressRules; + protected List tags; + protected String uuid; + protected String href; + protected String schema; + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(final String displayName) { + this.displayName = displayName; + } + + public List getLogicalPortEgressRules() { + return logicalPortEgressRules; + } + + public void setLogicalPortEgressRules(final List logicalPortEgressRules) { + this.logicalPortEgressRules = logicalPortEgressRules; + } + + public List getLogicalPortIngressRules() { + return logicalPortIngressRules; + } + + public void setLogicalPortIngressRules(final List logicalPortIngressRules) { + this.logicalPortIngressRules = logicalPortIngressRules; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(final String uuid) { + this.uuid = uuid; + } + + public String getHref() { + return href; + } + + public void setHref(final String href) { + this.href = href; + } + + public String getSchema() { + return schema; + } + + public void setSchema(final String schema) { + this.schema = schema; + } + + public List getTags() { + return tags; + } + + public void setTags(final List tags) { + this.tags = tags; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE, false); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 31) + .append(displayName).append(logicalPortEgressRules) + .append(logicalPortIngressRules).append(tags) + .append(uuid).append(href).append(schema) + .toHashCode(); + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(this.getClass().isInstance(obj))) { + return false; + } + final AccessConfiguration another = + (AccessConfiguration) obj; + return new EqualsBuilder() + .append(displayName, another.displayName) + .append(uuid, another.uuid) + .append(href, another.href) + .append(schema, another.schema) + .isEquals(); + } +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessRule.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessRule.java new file mode 100644 index 00000000000..cf02eb16a42 --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AccessRule.java @@ -0,0 +1,55 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +import java.io.Serializable; + +import org.apache.commons.lang.builder.ReflectionToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; + +@SuppressWarnings("serial") +public abstract class AccessRule implements Serializable { + + public static final String ETHERTYPE_IPV4 = "IPv4"; + public static final String ETHERTYPE_IPV6 = "IPv6"; + + protected String ethertype = ETHERTYPE_IPV4; + + protected int protocol; + + + public String getEthertype() { + return ethertype; + } + + public void setEthertype(String ethertype) { + this.ethertype = ethertype; + } + + public int getProtocol() { + return protocol; + } + + public void setProtocol(int protocol) { + this.protocol = protocol; + } + + @Override + public String toString() { + return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE, false); + } +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/Acl.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/Acl.java new file mode 100644 index 00000000000..3a9b387cd2c --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/Acl.java @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +@SuppressWarnings("serial") +public class Acl extends AccessConfiguration { +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AclRule.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AclRule.java new file mode 100644 index 00000000000..aa4f637886b --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/AclRule.java @@ -0,0 +1,206 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +@SuppressWarnings("serial") +public class AclRule extends AccessRule { + + public static final String ETHERTYPE_ARP = "ARP"; + + /** + * @TODO Convert this String into Enum and check the JSON communication still works + */ + protected String action; + + protected String sourceIpPrefix; + + protected String destinationIpPrefix; + + protected String sourceMacAddress; + + protected String destinationMacAddress; + + protected Integer sourcePortRangeMin; + + protected Integer destinationPortRangeMin; + + protected Integer sourcePortRangeMax; + + protected Integer destinationPortRangeMax; + + protected Integer icmpProtocolCode; + + protected Integer icmpProtocolType; + + protected int order; + + + /** + * Default constructor + */ + public AclRule() { + } + + /** + * Fully parameterized constructor + */ + public AclRule(String ethertype, int protocol, String action, String sourceMacAddress, + String destinationMacAddress, String sourceIpPrefix, String destinationIpPrefix, + Integer sourcePortRangeMin, Integer sourcePortRangeMax, + Integer destinationPortRangeMin, Integer destinationPortRangeMax, + int order, Integer icmpProtocolCode, Integer icmpProtocolType) { + this.ethertype = ethertype; + this.protocol = protocol; + this.action = action; + this.sourceMacAddress = sourceMacAddress; + this.destinationMacAddress = destinationMacAddress; + this.sourceIpPrefix = sourceIpPrefix; + this.destinationIpPrefix = destinationIpPrefix; + this.sourcePortRangeMin = sourcePortRangeMin; + this.sourcePortRangeMax = sourcePortRangeMax; + this.destinationPortRangeMin = destinationPortRangeMin; + this.destinationPortRangeMax = destinationPortRangeMax; + this.order = order; + this.icmpProtocolCode = icmpProtocolCode; + this.icmpProtocolType = icmpProtocolType; + } + + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public String getSourceIpPrefix() { + return sourceIpPrefix; + } + + public void setSourceIpPrefix(String sourceIpPrefix) { + this.sourceIpPrefix = sourceIpPrefix; + } + + public String getDestinationIpPrefix() { + return destinationIpPrefix; + } + + public void setDestinationIpPrefix(String destinationIpPrefix) { + this.destinationIpPrefix = destinationIpPrefix; + } + + public String getSourceMacAddress() { + return sourceMacAddress; + } + + public void setSourceMacAddress(String sourceMacAddress) { + this.sourceMacAddress = sourceMacAddress; + } + + public String getDestinationMacAddress() { + return destinationMacAddress; + } + + public void setDestinationMacAddress(String destinationMacAddress) { + this.destinationMacAddress = destinationMacAddress; + } + + public Integer getSourcePortRangeMin() { + return sourcePortRangeMin; + } + + public void setSourcePortRangeMin(Integer sourcePortRangeMin) { + this.sourcePortRangeMin = sourcePortRangeMin; + } + + public Integer getDestinationPortRangeMin() { + return destinationPortRangeMin; + } + + public void setDestinationPortRangeMin(Integer destinationPortRangeMin) { + this.destinationPortRangeMin = destinationPortRangeMin; + } + + public Integer getSourcePortRangeMax() { + return sourcePortRangeMax; + } + + public void setSourcePortRangeMax(Integer sourcePortRangeMax) { + this.sourcePortRangeMax = sourcePortRangeMax; + } + + public Integer getDestinationPortRangeMax() { + return destinationPortRangeMax; + } + + public void setDestinationPortRangeMax(Integer destinationPortRangeMax) { + this.destinationPortRangeMax = destinationPortRangeMax; + } + + public Integer getIcmpProtocolCode() { + return icmpProtocolCode; + } + + public void setIcmpProtocolCode(Integer icmpProtocolCode) { + this.icmpProtocolCode = icmpProtocolCode; + } + + public Integer getIcmpProtocolType() { + return icmpProtocolType; + } + + public void setIcmpProtocolType(Integer icmpProtocolType) { + this.icmpProtocolType = icmpProtocolType; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 31) + .append(ethertype).append(protocol) + .toHashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(obj instanceof AclRule)) { + return false; + } + AclRule another = (AclRule) obj; + return new EqualsBuilder() + .append(ethertype, another.ethertype) + .append(protocol, another.protocol) + .isEquals(); + } +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/NiciraNvpApi.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/NiciraNvpApi.java index 6f695ade9bd..92c23ebf5b9 100644 --- a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/NiciraNvpApi.java +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/NiciraNvpApi.java @@ -71,11 +71,18 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; +@SuppressWarnings("rawtypes") public class NiciraNvpApi { + protected static final String GET_METHOD_TYPE = "get"; + protected static final String DELETE_METHOD_TYPE = "delete"; + protected static final String PUT_METHOD_TYPE = "put"; + protected static final String POST_METHOD_TYPE = "post"; private static final String TEXT_HTML_CONTENT_TYPE = "text/html"; private static final String JSON_CONTENT_TYPE = "application/json"; private static final String CONTENT_TYPE = "Content-Type"; private static final int BODY_RESP_MAX_LEN = 1024; + protected static final String SEC_PROFILE_URI_PREFIX = "/ws.v1/security-profile"; + protected static final String ACL_URI_PREFIX = "/ws.v1/acl"; private static final String SWITCH_URI_PREFIX = "/ws.v1/lswitch"; private static final String ROUTER_URI_PREFIX = "/ws.v1/lrouter"; private static final int HTTPS_PORT = 443; @@ -83,7 +90,6 @@ public class NiciraNvpApi { private final static String protocol = "https"; private final static MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager(); - private String name; private String host; private String adminuser; private String adminpass; @@ -93,6 +99,27 @@ public class NiciraNvpApi { private final Gson gson; + protected static Map prefixMap; + + protected static Map listTypeMap; + + protected static Map defaultListParams; + + static { + prefixMap = new HashMap(); + prefixMap.put(SecurityProfile.class, SEC_PROFILE_URI_PREFIX); + prefixMap.put(Acl.class, ACL_URI_PREFIX); + + listTypeMap = new HashMap(); + listTypeMap.put(SecurityProfile.class, new TypeToken>() { + }.getType()); + listTypeMap.put(Acl.class, new TypeToken>() { + }.getType()); + + defaultListParams = new HashMap(); + defaultListParams.put("fields", "*"); + } + /* This factory method is protected so we can extend this * in the unittests. */ @@ -104,18 +131,18 @@ public class NiciraNvpApi { String url; try { url = new URL(protocol, host, uri).toString(); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { s_logger.error("Unable to build Nicira API URL", e); throw new NiciraNvpApiException("Unable to build Nicira API URL", e); } - if ("post".equalsIgnoreCase(type)) { + if (POST_METHOD_TYPE.equalsIgnoreCase(type)) { return new PostMethod(url); - } else if ("get".equalsIgnoreCase(type)) { + } else if (GET_METHOD_TYPE.equalsIgnoreCase(type)) { return new GetMethod(url); - } else if ("delete".equalsIgnoreCase(type)) { + } else if (DELETE_METHOD_TYPE.equalsIgnoreCase(type)) { return new DeleteMethod(url); - } else if ("put".equalsIgnoreCase(type)) { + } else if (PUT_METHOD_TYPE.equalsIgnoreCase(type)) { return new PutMethod(url); } else { throw new NiciraNvpApiException("Requesting unknown method type"); @@ -129,7 +156,7 @@ public class NiciraNvpApi { try { // Cast to ProtocolSocketFactory to avoid the deprecated constructor with the SecureProtocolSocketFactory parameter Protocol.registerProtocol("https", new Protocol("https", (ProtocolSocketFactory)new TrustingProtocolSocketFactory(), HTTPS_PORT)); - } catch (IOException e) { + } catch (final IOException e) { s_logger.warn("Failed to register the TrustingProtocolSocketFactory, falling back to default SSLSocketFactory", e); } @@ -160,20 +187,20 @@ public class NiciraNvpApi { try { url = new URL(protocol, host, "/ws.v1/login").toString(); - } catch (MalformedURLException e) { + } catch (final MalformedURLException e) { s_logger.error("Unable to build Nicira API URL", e); throw new NiciraNvpApiException("Unable to build Nicira API URL", e); } - PostMethod pm = new PostMethod(url); + final PostMethod pm = new PostMethod(url); pm.addParameter("username", adminuser); pm.addParameter("password", adminpass); try { client.executeMethod(pm); - } catch (HttpException e) { + } catch (final HttpException e) { throw new NiciraNvpApiException("Nicira NVP API login failed ", e); - } catch (IOException e) { + } catch (final IOException e) { throw new NiciraNvpApiException("Nicira NVP API login failed ", e); } finally { pm.releaseConnection(); @@ -193,69 +220,263 @@ public class NiciraNvpApi { // Success; the cookie required for login is kept in _client } + /** + * POST {@link SecurityProfile} + * + * @param securityProfile + * @return + * @throws NiciraNvpApiException + */ + public SecurityProfile createSecurityProfile(final SecurityProfile securityProfile) throws NiciraNvpApiException { + return create(securityProfile); + } + + /** + * GET list of {@link SecurityProfile} + * + * @return + * @throws NiciraNvpApiException + */ + public NiciraNvpList findSecurityProfile() throws NiciraNvpApiException { + return findSecurityProfile(null); + } + + /** + * GET list of {@link SecurityProfile} filtered by UUID + * + * We could have invoked the service: + * SEC_PROFILE_URI_PREFIX + "/" + securityProfileUuid + * but it is not working currently + * + * @param uuid + * @return + * @throws NiciraNvpApiException + */ + public NiciraNvpList findSecurityProfile(final String uuid) throws NiciraNvpApiException { + return find(uuid, SecurityProfile.class); + } + + /** + * PUT {@link SecurityProfile} given a UUID as key and a {@link SecurityProfile} + * with the new data + * + * @param securityProfile + * @param securityProfileUuid + * @throws NiciraNvpApiException + */ + public void updateSecurityProfile(final SecurityProfile securityProfile, + final String securityProfileUuid) + throws NiciraNvpApiException { + update(securityProfile, securityProfileUuid); + } + + /** + * DELETE Security Profile given a UUID as key + * + * @param securityProfileUuid + * @throws NiciraNvpApiException + */ + public void deleteSecurityProfile(final String securityProfileUuid) + throws NiciraNvpApiException { + delete(securityProfileUuid, SecurityProfile.class); + } + + + /** + * POST {@link Acl} + * + * @param acl + * @return + * @throws NiciraNvpApiException + */ + public Acl createAcl(final Acl acl) throws NiciraNvpApiException { + return create(acl); + } + + /** + * GET list of {@link Acl} + * + * @return + * @throws NiciraNvpApiException + */ + public NiciraNvpList findAcl() throws NiciraNvpApiException { + return findAcl(null); + } + + /** + * GET list of {@link Acl} filtered by UUID + * + * @param uuid + * @return + * @throws NiciraNvpApiException + */ + public NiciraNvpList findAcl(final String uuid) throws NiciraNvpApiException { + return find(uuid, Acl.class); + } + + /** + * PUT {@link Acl} given a UUID as key and a {@link Acl} + * with the new data + * + * @param acl + * @param aclUuid + * @throws NiciraNvpApiException + */ + public void updateAcl(final Acl acl, + final String aclUuid) + throws NiciraNvpApiException { + update(acl, aclUuid); + } + + /** + * DELETE Acl given a UUID as key + * + * @param acl + * @throws NiciraNvpApiException + */ + public void deleteAcl(final String aclUuid) throws NiciraNvpApiException { + delete(aclUuid, Acl.class); + } + + /** + * POST + * + * @param entity + * @return + * @throws NiciraNvpApiException + */ + protected T create(final T entity) throws NiciraNvpApiException { + final String uri = prefixMap.get(entity.getClass()); + final T createdEntity = executeCreateObject(entity, new TypeToken() { + }.getType(), uri, Collections. emptyMap()); + + return createdEntity; + } + + /** + * GET list of items + * + * @return + * @throws NiciraNvpApiException + */ + protected NiciraNvpList find(final Class clazz) throws NiciraNvpApiException { + return find(null, clazz); + } + + /** + * GET list of items + * + * @param uuid + * @return + * @throws NiciraNvpApiException + */ + public NiciraNvpList find(final String uuid, final Class clazz) throws NiciraNvpApiException { + final String uri = prefixMap.get(clazz); + Map params = defaultListParams; + if (uuid != null) { + params = new HashMap(defaultListParams); + params.put("uuid", uuid); + } + + final NiciraNvpList entities = executeRetrieveObject(listTypeMap.get(clazz), uri, params); + + if (entities == null) { + throw new NiciraNvpApiException("Unexpected response from API"); + } + + return entities; + } + + /** + * PUT item given a UUID as key and an item object + * with the new data + * + * @param item + * @param uuid + * @throws NiciraNvpApiException + */ + public void update(final T item, + final String uuid) + throws NiciraNvpApiException { + final String uri = prefixMap.get(item.getClass()) + "/" + uuid; + executeUpdateObject(item, uri, Collections. emptyMap()); + } + + /** + * DELETE Security Profile given a UUID as key + * + * @param securityProfileUuid + * @throws NiciraNvpApiException + */ + public void delete(final String uuid, final Class clazz) + throws NiciraNvpApiException { + final String uri = prefixMap.get(clazz) + "/" + uuid; + executeDeleteObject(uri); + } + public LogicalSwitch createLogicalSwitch(final LogicalSwitch logicalSwitch) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX; - LogicalSwitch createdLogicalSwitch = executeCreateObject(logicalSwitch, new TypeToken() { + final String uri = SWITCH_URI_PREFIX; + final LogicalSwitch createdLogicalSwitch = executeCreateObject(logicalSwitch, new TypeToken() { }.getType(), uri, Collections. emptyMap()); return createdLogicalSwitch; } public void deleteLogicalSwitch(final String uuid) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + uuid; + final String uri = SWITCH_URI_PREFIX + "/" + uuid; executeDeleteObject(uri); } public LogicalSwitchPort createLogicalSwitchPort(final String logicalSwitchUuid, final LogicalSwitchPort logicalSwitchPort) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + logicalSwitchUuid + "/lport"; - LogicalSwitchPort createdLogicalSwitchPort = executeCreateObject(logicalSwitchPort, new TypeToken() { + final String uri = SWITCH_URI_PREFIX + "/" + logicalSwitchUuid + "/lport"; + final LogicalSwitchPort createdLogicalSwitchPort = executeCreateObject(logicalSwitchPort, new TypeToken() { }.getType(), uri, Collections. emptyMap()); return createdLogicalSwitchPort; } - public void modifyLogicalSwitchPortAttachment(final String logicalSwitchUuid, final String logicalSwitchPortUuid, Attachment attachment) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + logicalSwitchUuid + "/lport/" + logicalSwitchPortUuid + "/attachment"; + public void modifyLogicalSwitchPortAttachment(final String logicalSwitchUuid, final String logicalSwitchPortUuid, final Attachment attachment) throws NiciraNvpApiException { + final String uri = SWITCH_URI_PREFIX + "/" + logicalSwitchUuid + "/lport/" + logicalSwitchPortUuid + "/attachment"; executeUpdateObject(attachment, uri, Collections. emptyMap()); } public void deleteLogicalSwitchPort(final String logicalSwitchUuid, final String logicalSwitchPortUuid) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + logicalSwitchUuid + "/lport/" + logicalSwitchPortUuid; + final String uri = SWITCH_URI_PREFIX + "/" + logicalSwitchUuid + "/lport/" + logicalSwitchPortUuid; executeDeleteObject(uri); } public String findLogicalSwitchPortUuidByVifAttachmentUuid(final String logicalSwitchUuid, final String vifAttachmentUuid) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + logicalSwitchUuid + "/lport"; - Map params = new HashMap(); + final String uri = SWITCH_URI_PREFIX + "/" + logicalSwitchUuid + "/lport"; + final Map params = new HashMap(); params.put("attachment_vif_uuid", vifAttachmentUuid); params.put("fields", "uuid"); - NiciraNvpList lspl = executeRetrieveObject(new TypeToken>() { + final NiciraNvpList lspl = executeRetrieveObject(new TypeToken>() { }.getType(), uri, params); if (lspl == null || lspl.getResultCount() != 1) { throw new NiciraNvpApiException("Unexpected response from API"); } - LogicalSwitchPort lsp = lspl.getResults().get(0); + final LogicalSwitchPort lsp = lspl.getResults().get(0); return lsp.getUuid(); } public ControlClusterStatus getControlClusterStatus() throws NiciraNvpApiException { - String uri = "/ws.v1/control-cluster/status"; - ControlClusterStatus ccs = executeRetrieveObject(new TypeToken() { + final String uri = "/ws.v1/control-cluster/status"; + final ControlClusterStatus ccs = executeRetrieveObject(new TypeToken() { }.getType(), uri, null); return ccs; } public NiciraNvpList findLogicalSwitchPortsByUuid(final String logicalSwitchUuid, final String logicalSwitchPortUuid) throws NiciraNvpApiException { - String uri = SWITCH_URI_PREFIX + logicalSwitchUuid + "/lport"; - Map params = new HashMap(); + final String uri = SWITCH_URI_PREFIX + "/" + logicalSwitchUuid + "/lport"; + final Map params = new HashMap(); params.put("uuid", logicalSwitchPortUuid); params.put("fields", "uuid"); - NiciraNvpList lspl = executeRetrieveObject(new TypeToken>() { + final NiciraNvpList lspl = executeRetrieveObject(new TypeToken>() { }.getType(), uri, params); if (lspl == null) { @@ -266,69 +487,69 @@ public class NiciraNvpApi { } public LogicalRouterConfig createLogicalRouter(final LogicalRouterConfig logicalRouterConfig) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX; + final String uri = ROUTER_URI_PREFIX; - LogicalRouterConfig lrc = executeCreateObject(logicalRouterConfig, new TypeToken() { + final LogicalRouterConfig lrc = executeCreateObject(logicalRouterConfig, new TypeToken() { }.getType(), uri, Collections. emptyMap()); return lrc; } public void deleteLogicalRouter(final String logicalRouterUuid) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid; + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid; executeDeleteObject(uri); } public LogicalRouterPort createLogicalRouterPort(final String logicalRouterUuid, final LogicalRouterPort logicalRouterPort) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport"; + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport"; - LogicalRouterPort lrp = executeCreateObject(logicalRouterPort, new TypeToken() { + final LogicalRouterPort lrp = executeCreateObject(logicalRouterPort, new TypeToken() { }.getType(), uri, Collections. emptyMap()); return lrp; } public void deleteLogicalRouterPort(final String logicalRouterUuid, final String logicalRouterPortUuid) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport/" + logicalRouterPortUuid; + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport/" + logicalRouterPortUuid; executeDeleteObject(uri); } public void modifyLogicalRouterPort(final String logicalRouterUuid, final LogicalRouterPort logicalRouterPort) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport/" + logicalRouterPort.getUuid(); + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport/" + logicalRouterPort.getUuid(); executeUpdateObject(logicalRouterPort, uri, Collections. emptyMap()); } public void modifyLogicalRouterPortAttachment(final String logicalRouterUuid, final String logicalRouterPortUuid, final Attachment attachment) - throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport/" + logicalRouterPortUuid + "/attachment"; + throws NiciraNvpApiException { + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport/" + logicalRouterPortUuid + "/attachment"; executeUpdateObject(attachment, uri, Collections. emptyMap()); } public NatRule createLogicalRouterNatRule(final String logicalRouterUuid, final NatRule natRule) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/nat"; + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/nat"; return executeCreateObject(natRule, new TypeToken() { }.getType(), uri, Collections. emptyMap()); } public void modifyLogicalRouterNatRule(final String logicalRouterUuid, final NatRule natRule) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/nat/" + natRule.getUuid(); + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/nat/" + natRule.getUuid(); executeUpdateObject(natRule, uri, Collections. emptyMap()); } public void deleteLogicalRouterNatRule(final String logicalRouterUuid, final UUID natRuleUuid) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/nat/" + natRuleUuid.toString(); + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/nat/" + natRuleUuid.toString(); executeDeleteObject(uri); } public NiciraNvpList findLogicalRouterPortByGatewayServiceAndVlanId(final String logicalRouterUuid, final String gatewayServiceUuid, - final long vlanId) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport"; - Map params = new HashMap(); + final long vlanId) throws NiciraNvpApiException { + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport"; + final Map params = new HashMap(); params.put("attachment_gwsvc_uuid", gatewayServiceUuid); params.put("attachment_vlan", "0"); params.put("fields", "*"); @@ -338,21 +559,21 @@ public class NiciraNvpApi { } public LogicalRouterConfig findOneLogicalRouterByUuid(final String logicalRouterUuid) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid; + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid; return executeRetrieveObject(new TypeToken() { }.getType(), uri, Collections. emptyMap()); } public void updateLogicalRouterPortConfig(final String logicalRouterUuid, final LogicalRouterPort logicalRouterPort) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport" + logicalRouterPort.getUuid(); + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport" + logicalRouterPort.getUuid(); executeUpdateObject(logicalRouterPort, uri, Collections. emptyMap()); } public NiciraNvpList findNatRulesByLogicalRouterUuid(final String logicalRouterUuid) throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/nat"; - Map params = new HashMap(); + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/nat"; + final Map params = new HashMap(); params.put("fields", "*"); return executeRetrieveObject(new TypeToken>() { @@ -360,9 +581,9 @@ public class NiciraNvpApi { } public NiciraNvpList findLogicalRouterPortByGatewayServiceUuid(final String logicalRouterUuid, final String l3GatewayServiceUuid) - throws NiciraNvpApiException { - String uri = ROUTER_URI_PREFIX + logicalRouterUuid + "/lport"; - Map params = new HashMap(); + throws NiciraNvpApiException { + final String uri = ROUTER_URI_PREFIX + "/" + logicalRouterUuid + "/lport"; + final Map params = new HashMap(); params.put("fields", "*"); params.put("attachment_gwsvc_uuid", l3GatewayServiceUuid); @@ -375,18 +596,18 @@ public class NiciraNvpApi { throw new NiciraNvpApiException("Hostname/credentials are null or empty"); } - PutMethod pm = (PutMethod)createMethod("put", uri); + final PutMethod pm = (PutMethod)createMethod(PUT_METHOD_TYPE, uri); pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); try { pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null)); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { throw new NiciraNvpApiException("Failed to encode json request body", e); } executeMethod(pm); if (pm.getStatusCode() != HttpStatus.SC_OK) { - String errorMessage = responseToErrorMessage(pm); + final String errorMessage = responseToErrorMessage(pm); pm.releaseConnection(); s_logger.error("Failed to update object : " + errorMessage); throw new NiciraNvpApiException("Failed to update object : " + errorMessage); @@ -394,24 +615,25 @@ public class NiciraNvpApi { pm.releaseConnection(); } + @SuppressWarnings("unchecked") protected T executeCreateObject(final T newObject, final Type returnObjectType, final String uri, final Map parameters) - throws NiciraNvpApiException { + throws NiciraNvpApiException { if (host == null || host.isEmpty() || adminuser == null || adminuser.isEmpty() || adminpass == null || adminpass.isEmpty()) { throw new NiciraNvpApiException("Hostname/credentials are null or empty"); } - PostMethod pm = (PostMethod)createMethod("post", uri); + final PostMethod pm = (PostMethod)createMethod(POST_METHOD_TYPE, uri); pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); try { pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null)); - } catch (UnsupportedEncodingException e) { + } catch (final UnsupportedEncodingException e) { throw new NiciraNvpApiException("Failed to encode json request body", e); } executeMethod(pm); if (pm.getStatusCode() != HttpStatus.SC_CREATED) { - String errorMessage = responseToErrorMessage(pm); + final String errorMessage = responseToErrorMessage(pm); pm.releaseConnection(); s_logger.error("Failed to create object : " + errorMessage); throw new NiciraNvpApiException("Failed to create object : " + errorMessage); @@ -420,7 +642,7 @@ public class NiciraNvpApi { T result; try { result = (T)gson.fromJson(pm.getResponseBodyAsString(), TypeToken.get(newObject.getClass()).getType()); - } catch (IOException e) { + } catch (final IOException e) { throw new NiciraNvpApiException("Failed to decode json response body", e); } finally { pm.releaseConnection(); @@ -434,13 +656,13 @@ public class NiciraNvpApi { throw new NiciraNvpApiException("Hostname/credentials are null or empty"); } - DeleteMethod dm = (DeleteMethod)createMethod("delete", uri); + final DeleteMethod dm = (DeleteMethod)createMethod(DELETE_METHOD_TYPE, uri); dm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); executeMethod(dm); if (dm.getStatusCode() != HttpStatus.SC_NO_CONTENT) { - String errorMessage = responseToErrorMessage(dm); + final String errorMessage = responseToErrorMessage(dm); dm.releaseConnection(); s_logger.error("Failed to delete object : " + errorMessage); throw new NiciraNvpApiException("Failed to delete object : " + errorMessage); @@ -448,16 +670,17 @@ public class NiciraNvpApi { dm.releaseConnection(); } + @SuppressWarnings("unchecked") protected T executeRetrieveObject(final Type returnObjectType, final String uri, final Map parameters) throws NiciraNvpApiException { if (host == null || host.isEmpty() || adminuser == null || adminuser.isEmpty() || adminpass == null || adminpass.isEmpty()) { throw new NiciraNvpApiException("Hostname/credentials are null or empty"); } - GetMethod gm = (GetMethod)createMethod("get", uri); + final GetMethod gm = (GetMethod)createMethod(GET_METHOD_TYPE, uri); gm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); if (parameters != null && !parameters.isEmpty()) { - List nameValuePairs = new ArrayList(parameters.size()); - for (Entry e : parameters.entrySet()) { + final List nameValuePairs = new ArrayList(parameters.size()); + for (final Entry e : parameters.entrySet()) { nameValuePairs.add(new NameValuePair(e.getKey(), e.getValue())); } gm.setQueryString(nameValuePairs.toArray(new NameValuePair[0])); @@ -466,7 +689,7 @@ public class NiciraNvpApi { executeMethod(gm); if (gm.getStatusCode() != HttpStatus.SC_OK) { - String errorMessage = responseToErrorMessage(gm); + final String errorMessage = responseToErrorMessage(gm); gm.releaseConnection(); s_logger.error("Failed to retrieve object : " + errorMessage); throw new NiciraNvpApiException("Failed to retrieve object : " + errorMessage); @@ -475,7 +698,7 @@ public class NiciraNvpApi { T returnValue; try { returnValue = (T)gson.fromJson(gm.getResponseBodyAsString(), returnObjectType); - } catch (IOException e) { + } catch (final IOException e) { s_logger.error("IOException while retrieving response body", e); throw new NiciraNvpApiException(e); } finally { @@ -493,11 +716,11 @@ public class NiciraNvpApi { login(); client.executeMethod(method); } - } catch (HttpException e) { + } catch (final HttpException e) { s_logger.error("HttpException caught while trying to connect to the Nicira NVP Controller", e); method.releaseConnection(); throw new NiciraNvpApiException("API call to Nicira NVP Controller Failed", e); - } catch (IOException e) { + } catch (final IOException e) { s_logger.error("IOException caught while trying to connect to the Nicira NVP Controller", e); method.releaseConnection(); throw new NiciraNvpApiException("API call to Nicira NVP Controller Failed", e); @@ -513,7 +736,7 @@ public class NiciraNvpApi { // and will clutter the logs try { return method.getResponseBodyAsString(BODY_RESP_MAX_LEN); - } catch (IOException e) { + } catch (final IOException e) { s_logger.debug("Error while loading response body", e); } } @@ -533,7 +756,7 @@ public class NiciraNvpApi { public TrustingProtocolSocketFactory() throws IOException { // Create a trust manager that does not validate certificate chains - TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { + final TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; @@ -552,12 +775,12 @@ public class NiciraNvpApi { try { // Install the all-trusting trust manager - SSLContext sc = SSLContext.getInstance("SSL"); + final SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); ssf = sc.getSocketFactory(); - } catch (KeyManagementException e) { + } catch (final KeyManagementException e) { throw new IOException(e); - } catch (NoSuchAlgorithmException e) { + } catch (final NoSuchAlgorithmException e) { throw new IOException(e); } } @@ -579,12 +802,12 @@ public class NiciraNvpApi { @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) - throws IOException, UnknownHostException, ConnectTimeoutException { - int timeout = params.getConnectionTimeout(); + throws IOException, UnknownHostException, ConnectTimeoutException { + final int timeout = params.getConnectionTimeout(); if (timeout == 0) { return createSocket(host, port, localAddress, localPort); } else { - Socket s = ssf.createSocket(); + final Socket s = ssf.createSocket(); s.bind(new InetSocketAddress(localAddress, localPort)); s.connect(new InetSocketAddress(host, port), timeout); return s; @@ -596,13 +819,13 @@ public class NiciraNvpApi { @Override public NatRule deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context) throws JsonParseException { - JsonObject jsonObject = jsonElement.getAsJsonObject(); + final JsonObject jsonObject = jsonElement.getAsJsonObject(); if (!jsonObject.has("type")) { throw new JsonParseException("Deserializing as a NatRule, but no type present in the json object"); } - String natRuleType = jsonObject.get("type").getAsString(); + final String natRuleType = jsonObject.get("type").getAsString(); if ("SourceNatRule".equals(natRuleType)) { return context.deserialize(jsonElement, SourceNatRule.class); } else if ("DestinationNatRule".equals(natRuleType)) { diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityProfile.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityProfile.java new file mode 100644 index 00000000000..fb3f0743cd0 --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityProfile.java @@ -0,0 +1,21 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +@SuppressWarnings("serial") +public class SecurityProfile extends AccessConfiguration { +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityRule.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityRule.java new file mode 100644 index 00000000000..06181f0725d --- /dev/null +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SecurityRule.java @@ -0,0 +1,135 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + +@SuppressWarnings("serial") +public class SecurityRule extends AccessRule { + + protected String ipPrefix; + + protected int portRangeMin; + + protected int portRangeMax; + + protected String profileUuid; + + + /** + * Default constructor + */ + public SecurityRule() { + } + + /** + * Fully parameterized constructor + */ + public SecurityRule(final String ethertype, final String ipPrefix, final String profileUuid, + final int portRangeMin, final int portRangeMax, final int protocol) { + this.ethertype = ethertype; + this.ipPrefix = ipPrefix; + this.portRangeMin = portRangeMin; + this.portRangeMax = portRangeMax; + this.profileUuid = profileUuid; + this.protocol = protocol; + } + + @Override + public String getEthertype() { + return ethertype; + } + + @Override + public void setEthertype(final String ethertype) { + this.ethertype = ethertype; + } + + public String getIpPrefix() { + return ipPrefix; + } + + public void setIpPrefix(final String ipPrefix) { + this.ipPrefix = ipPrefix; + } + + public int getPortRangeMin() { + return portRangeMin; + } + + public void setPortRangeMin(final int portRangeMin) { + this.portRangeMin = portRangeMin; + } + + public int getPortRangeMax() { + return portRangeMax; + } + + public void setPortRangeMax(final int portRangeMax) { + this.portRangeMax = portRangeMax; + } + + public String getProfileUuid() { + return profileUuid; + } + + public void setProfileUuid(final String profileUuid) { + this.profileUuid = profileUuid; + } + + @Override + public int getProtocol() { + return protocol; + } + + @Override + public void setProtocol(final int protocol) { + this.protocol = protocol; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 31) + .append(ethertype).append(ipPrefix) + .append(portRangeMin).append(portRangeMax) + .append(profileUuid).append(protocol) + .toHashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(obj instanceof SecurityRule)) { + return false; + } + final SecurityRule another = (SecurityRule) obj; + return new EqualsBuilder() + .append(ethertype, another.ethertype) + .append(ipPrefix, another.ipPrefix) + .append(portRangeMin, another.portRangeMin) + .append(portRangeMax, another.portRangeMax) + .append(profileUuid, another.profileUuid) + .append(protocol, another.protocol) + .isEquals(); + } +} diff --git a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SingleDefaultRouteImplictRoutingConfig.java b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SingleDefaultRouteImplictRoutingConfig.java index bbe1cb56ea2..d386e7020b5 100644 --- a/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SingleDefaultRouteImplictRoutingConfig.java +++ b/plugins/network-elements/nicira-nvp/src/com/cloud/network/nicira/SingleDefaultRouteImplictRoutingConfig.java @@ -21,7 +21,7 @@ package com.cloud.network.nicira; */ public class SingleDefaultRouteImplictRoutingConfig extends RoutingConfig { public RouterNextHop defaultRouteNextHop; - public String type = "SingleDefaultRouteImplicitRoutingConfig"; + public final String type = "SingleDefaultRouteImplicitRoutingConfig"; public SingleDefaultRouteImplictRoutingConfig(RouterNextHop routerNextHop) { defaultRouteNextHop = routerNextHop; diff --git a/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiIT.java b/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiIT.java new file mode 100644 index 00000000000..c541d87cae3 --- /dev/null +++ b/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiIT.java @@ -0,0 +1,163 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.nicira; + +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.cloud.utils.PropertiesUtil; + +public class NiciraNvpApiIT { + + protected NiciraNvpApi api; + + protected long timestamp = System.currentTimeMillis(); + + @Before + public void setup() throws IOException { + final Properties properties = new Properties(); + PropertiesUtil.loadFromFile(properties, PropertiesUtil.findConfigFile("config.properties")); + api = new NiciraNvpApi(); + api.setControllerAddress(properties.getProperty("nvp.host")); + api.setAdminCredentials(properties.getProperty("nvp.admin.user"), + properties.getProperty("nvp.admin.pwd")); + } + + @Test + public void testCRUDSecurityProfile() throws NiciraNvpApiException { + SecurityProfile sProfile = new SecurityProfile(); + sProfile.setDisplayName("SecProfile"+timestamp); + + final List egressRules = new ArrayList(); + sProfile.setLogicalPortEgressRules(egressRules); + egressRules.add(new SecurityRule(SecurityRule.ETHERTYPE_IPV4, "1.10.10.0", null, 80, 88, 6)); + egressRules.add(new SecurityRule(SecurityRule.ETHERTYPE_IPV6, "2a80:34ac::1", null, 90, 98, 6)); + + final List ingressRules = new ArrayList(); + sProfile.setLogicalPortIngressRules(ingressRules); + ingressRules.add(new SecurityRule(SecurityRule.ETHERTYPE_IPV4, "1.10.10.0", null, 50, 58, 6)); + ingressRules.add(new SecurityRule(SecurityRule.ETHERTYPE_IPV6, "280a:3ac4::1", null, 60, 68, 6)); + + final List tags = new ArrayList(); + sProfile.setTags(tags); + tags.add(new NiciraNvpTag("nvp", "MyTag1")); + tags.add(new NiciraNvpTag("nicira", "MyTag2")); + // In the creation we don't get to specify UUID, href or schema: they don't exist yet + + try { + sProfile = api.createSecurityProfile(sProfile); + + // We can now update the new entity + sProfile.setDisplayName("UpdatedSecProfile"+timestamp); + api.updateSecurityProfile(sProfile, sProfile.getUuid()); + + // Read them all + NiciraNvpList profiles = api.findSecurityProfile(); + SecurityProfile scInList = null; + for(final SecurityProfile iProfile : profiles.getResults()) { + if (iProfile.getUuid().equalsIgnoreCase(sProfile.getUuid())) { + scInList = iProfile; + } + } + Assert.assertEquals("Read a Security Profile different from the one just created and updated", + sProfile, scInList); + + // Read them filtered by uuid (get one) + profiles = api.findSecurityProfile(sProfile.getUuid()); + Assert.assertEquals("Read a Security Profile different from the one just created and updated", + sProfile, + profiles.getResults().get(0)); + Assert.assertEquals("Read a Security Profile filtered by unique id (UUID) with more than one item", + 1, profiles.getResults().size()); + + // We can now delete the new entity + api.deleteSecurityProfile(sProfile.getUuid()); + } catch (final NiciraNvpApiException e) { + e.printStackTrace(); + assertTrue("Errors creating Security Profile", false); + } + } + + @Test + public void testCRUDAcl() throws NiciraNvpApiException { + Acl acl = new Acl(); + acl.setDisplayName("Acl"+timestamp); + + // Note that if the protocol is 6 (TCP) then you cannot put ICMP code and type + // Note that if the protocol is 1 (ICMP) then you cannot put ports + final List egressRules = new ArrayList(); + acl.setLogicalPortEgressRules(egressRules); + egressRules.add(new AclRule(AclRule.ETHERTYPE_IPV4, 1, "allow", null, null, + "1.10.10.0", "1.10.10.1", null, null, null, null, 0, 0, 5)); + egressRules.add(new AclRule(AclRule.ETHERTYPE_IPV4, 6, "allow", null, null, + "1.10.10.6", "1.10.10.7", 80, 80, 80, 80, 1, null, null)); + + final List ingressRules = new ArrayList(); + acl.setLogicalPortIngressRules(ingressRules); + ingressRules.add(new AclRule(AclRule.ETHERTYPE_IPV4, 1, "allow", null, null, + "1.10.10.0", "1.10.10.1", null, null, null, null, 0, 0, 5)); + ingressRules.add(new AclRule(AclRule.ETHERTYPE_IPV4, 6, "allow", null, null, + "1.10.10.6", "1.10.10.7", 80, 80, 80, 80, 1, null, null)); + + final List tags = new ArrayList(); + acl.setTags(tags); + tags.add(new NiciraNvpTag("nvp", "MyTag1")); + tags.add(new NiciraNvpTag("nicira", "MyTag2")); + // In the creation we don't get to specify UUID, href or schema: they don't exist yet + + try { + acl = api.createAcl(acl); + + // We can now update the new entity + acl.setDisplayName("UpdatedAcl"+timestamp); + api.updateAcl(acl, acl.getUuid()); + + // Read them all + NiciraNvpList acls = api.findAcl(); + Acl scInList = null; + for(final Acl iAcl : acls.getResults()) { + if (iAcl.getUuid().equalsIgnoreCase(acl.getUuid())) { + scInList = iAcl; + } + } + Assert.assertEquals("Read a ACL different from the one just created and updated", + acl, scInList); + + // Read them filtered by uuid (get one) + acls = api.findAcl(acl.getUuid()); + Assert.assertEquals("Read a ACL different from the one just created and updated", + acl, + acls.getResults().get(0)); + Assert.assertEquals("Read a ACL filtered by unique id (UUID) with more than one item", + 1, acls.getResults().size()); + + // We can now delete the new entity + api.deleteAcl(acl.getUuid()); + } catch (final NiciraNvpApiException e) { + e.printStackTrace(); + assertTrue("Errors creating ACL", false); + } + } +} diff --git a/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiTest.java b/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiTest.java index 26517b5fb88..2e4cfaf1a05 100644 --- a/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiTest.java +++ b/plugins/network-elements/nicira-nvp/test/com/cloud/network/nicira/NiciraNvpApiTest.java @@ -17,6 +17,7 @@ package com.cloud.network.nicira; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -35,18 +36,46 @@ import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.httpclient.NameValuePair; import org.apache.http.HttpStatus; import org.junit.Before; import org.junit.Test; public class NiciraNvpApiTest { + protected static final String UUID = "aaaa"; + protected static final String UUID2 = "bbbb"; + protected static final String UUID_SEC_PROFILE_URI = NiciraNvpApi.SEC_PROFILE_URI_PREFIX + "/aaaa"; + protected static final String SCHEMA = "myTestSchema"; + protected static final String SCHEMA2 = "myTestSchema2"; + protected static final String HREF = "myTestHref"; + protected static final String HREF2 = "myTestHref2"; + protected static final String DISPLAY_NAME = "myTestName"; + protected static final String UUID_JSON_RESPONSE = "{\"uuid\" : \"aaaa\"}"; + protected static final String SEC_PROFILE_JSON_RESPONSE = + "{\"uuid\" : \"aaaa\"," + + "\"display_name\" : \"myTestName\"," + + "\"href\" : \"myTestHref\"," + + "\"schema\" : \"myTestSchema\"}"; + + protected static final String SEC_PROFILE_LIST_JSON_RESPONSE = "{\"results\" : [{\"uuid\" : \"aaaa\"," + + "\"display_name\" : \"myTestName\"," + + "\"href\" : \"myTestHref\"," + + "\"schema\" : \"myTestSchema\"}," + + "{ \"uuid\" : \"bbbb\"," + + "\"display_name\" : \"myTestName2\"," + + "\"href\" : \"myTestHref2\"," + + "\"schema\" : \"myTestSchema2\"}]," + + "\"result_count\": 2}"; + NiciraNvpApi api; HttpClient client = mock(HttpClient.class); HttpMethod method; + String type; + String uri; @Before public void setUp() { - HttpClientParams hmp = mock(HttpClientParams.class); + final HttpClientParams hmp = mock(HttpClientParams.class); when(client.getParams()).thenReturn(hmp); api = new NiciraNvpApi() { @Override @@ -55,7 +84,9 @@ public class NiciraNvpApiTest { } @Override - protected HttpMethod createMethod(String type, String uri) { + protected HttpMethod createMethod(final String newType, final String newUri) { + type = newType; + uri = newUri; return method; } }; @@ -125,7 +156,7 @@ public class NiciraNvpApiTest { @Test public void executeMethodTest() throws NiciraNvpApiException { - GetMethod gm = mock(GetMethod.class); + final GetMethod gm = mock(GetMethod.class); when(gm.getStatusCode()).thenReturn(HttpStatus.SC_OK); api.executeMethod(gm); @@ -137,7 +168,7 @@ public class NiciraNvpApiTest { */ @Test(expected = NiciraNvpApiException.class) public void executeMethodTestWithLogin() throws NiciraNvpApiException, HttpException, IOException { - GetMethod gm = mock(GetMethod.class); + final GetMethod gm = mock(GetMethod.class); when(client.executeMethod((HttpMethod)any())).thenThrow(new HttpException()); when(gm.getStatusCode()).thenReturn(HttpStatus.SC_UNAUTHORIZED).thenReturn(HttpStatus.SC_UNAUTHORIZED); api.executeMethod(gm); @@ -149,9 +180,9 @@ public class NiciraNvpApiTest { LogicalSwitch ls = new LogicalSwitch(); method = mock(PostMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_CREATED); - when(method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }"); + when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE); ls = api.executeCreateObject(ls, LogicalSwitch.class, "/", Collections. emptyMap()); - assertTrue("aaaa".equals(ls.getUuid())); + assertTrue(UUID.equals(ls.getUuid())); verify(method, times(1)).releaseConnection(); } @@ -161,7 +192,7 @@ public class NiciraNvpApiTest { LogicalSwitch ls = new LogicalSwitch(); method = mock(PostMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR); - Header header = mock(Header.class); + final Header header = mock(Header.class); when(header.getValue()).thenReturn("text/html"); when(method.getResponseHeader("Content-Type")).thenReturn(header); when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later."); @@ -179,7 +210,7 @@ public class NiciraNvpApiTest { when(client.executeMethod((HttpMethod)any())).thenThrow(new HttpException()); method = mock(PostMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR); - Header header = mock(Header.class); + final Header header = mock(Header.class); when(header.getValue()).thenReturn("text/html"); when(method.getResponseHeader("Content-Type")).thenReturn(header); when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later."); @@ -192,7 +223,7 @@ public class NiciraNvpApiTest { @Test public void testExecuteUpdateObject() throws NiciraNvpApiException, IOException { - LogicalSwitch ls = new LogicalSwitch(); + final LogicalSwitch ls = new LogicalSwitch(); method = mock(PutMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); api.executeUpdateObject(ls, "/", Collections. emptyMap()); @@ -202,10 +233,10 @@ public class NiciraNvpApiTest { @Test(expected = NiciraNvpApiException.class) public void testExecuteUpdateObjectFailure() throws NiciraNvpApiException, IOException { - LogicalSwitch ls = new LogicalSwitch(); + final LogicalSwitch ls = new LogicalSwitch(); method = mock(PutMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR); - Header header = mock(Header.class); + final Header header = mock(Header.class); when(header.getValue()).thenReturn("text/html"); when(method.getResponseHeader("Content-Type")).thenReturn(header); when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later."); @@ -219,7 +250,7 @@ public class NiciraNvpApiTest { @Test(expected = NiciraNvpApiException.class) public void testExecuteUpdateObjectException() throws NiciraNvpApiException, IOException { - LogicalSwitch ls = new LogicalSwitch(); + final LogicalSwitch ls = new LogicalSwitch(); method = mock(PutMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); when(client.executeMethod((HttpMethod)any())).thenThrow(new IOException()); @@ -243,7 +274,7 @@ public class NiciraNvpApiTest { public void testExecuteDeleteObjectFailure() throws NiciraNvpApiException, IOException { method = mock(DeleteMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR); - Header header = mock(Header.class); + final Header header = mock(Header.class); when(header.getValue()).thenReturn("text/html"); when(method.getResponseHeader("Content-Type")).thenReturn(header); when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later."); @@ -271,7 +302,7 @@ public class NiciraNvpApiTest { public void testExecuteRetrieveObject() throws NiciraNvpApiException, IOException { method = mock(GetMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); - when(method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }"); + when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE); api.executeRetrieveObject(LogicalSwitch.class, "/", Collections. emptyMap()); verify(method, times(1)).releaseConnection(); verify(client, times(1)).executeMethod(method); @@ -281,8 +312,8 @@ public class NiciraNvpApiTest { public void testExecuteRetrieveObjectFailure() throws NiciraNvpApiException, IOException { method = mock(GetMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_INTERNAL_SERVER_ERROR); - when(method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }"); - Header header = mock(Header.class); + when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE); + final Header header = mock(Header.class); when(header.getValue()).thenReturn("text/html"); when(method.getResponseHeader("Content-Type")).thenReturn(header); when(method.getResponseBodyAsString()).thenReturn("Off to timbuktu, won't be back later."); @@ -298,7 +329,7 @@ public class NiciraNvpApiTest { public void testExecuteRetrieveObjectException() throws NiciraNvpApiException, IOException { method = mock(GetMethod.class); when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); - when(method.getResponseBodyAsString()).thenReturn("{ \"uuid\" : \"aaaa\" }"); + when(method.getResponseBodyAsString()).thenReturn(UUID_JSON_RESPONSE); when(client.executeMethod((HttpMethod)any())).thenThrow(new HttpException()); try { api.executeRetrieveObject(LogicalSwitch.class, "/", Collections. emptyMap()); @@ -307,4 +338,135 @@ public class NiciraNvpApiTest { } } + @Test + public void testFindSecurityProfile() throws NiciraNvpApiException, IOException { + // Prepare + method = mock(GetMethod.class); + when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); + when(method.getResponseBodyAsString()).thenReturn(SEC_PROFILE_LIST_JSON_RESPONSE); + final NameValuePair[] queryString = new NameValuePair[]{ + new NameValuePair("fields","*")}; + + // Execute + final NiciraNvpList actualProfiles = api.findSecurityProfile(); + + // Assert + verify(method, times(1)).releaseConnection(); + verify(method, times(1)).setQueryString(queryString); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + UUID, actualProfiles.getResults().get(0).getUuid()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + HREF, actualProfiles.getResults().get(0).getHref()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + SCHEMA, actualProfiles.getResults().get(0).getSchema()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + UUID2, actualProfiles.getResults().get(1).getUuid()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + HREF2, actualProfiles.getResults().get(1).getHref()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + SCHEMA2, actualProfiles.getResults().get(1).getSchema()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + 2, actualProfiles.getResultCount()); + assertEquals("Wrong URI for SecurityProfile creation REST service", + NiciraNvpApi.SEC_PROFILE_URI_PREFIX, uri); + assertEquals("Wrong URI for SecurityProfile creation REST service", + NiciraNvpApi.GET_METHOD_TYPE, type); + } + + @Test + public void testFindSecurityProfileByUuid() throws NiciraNvpApiException, IOException { + // Prepare + method = mock(GetMethod.class); + when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); + when(method.getResponseBodyAsString()).thenReturn(SEC_PROFILE_LIST_JSON_RESPONSE); + final NameValuePair[] queryString = new NameValuePair[]{ + new NameValuePair("uuid", UUID), + new NameValuePair("fields","*") + }; + + // Execute + final NiciraNvpList actualProfiles = api.findSecurityProfile(UUID); + + // Assert + verify(method, times(1)).releaseConnection(); + verify(method, times(1)).setQueryString(queryString); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + UUID, actualProfiles.getResults().get(0).getUuid()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + HREF, actualProfiles.getResults().get(0).getHref()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + SCHEMA, actualProfiles.getResults().get(0).getSchema()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + UUID2, actualProfiles.getResults().get(1).getUuid()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + HREF2, actualProfiles.getResults().get(1).getHref()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + SCHEMA2, actualProfiles.getResults().get(1).getSchema()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + 2, actualProfiles.getResultCount()); + assertEquals("Wrong URI for SecurityProfile creation REST service", + NiciraNvpApi.SEC_PROFILE_URI_PREFIX, uri); + assertEquals("Wrong HTTP method for SecurityProfile creation REST service", + NiciraNvpApi.GET_METHOD_TYPE, type); + } + + @Test + public void testCreateSecurityProfile() throws NiciraNvpApiException, IOException { + // Prepare + final SecurityProfile inputSecProfile = new SecurityProfile(); + method = mock(PostMethod.class); + when(method.getStatusCode()).thenReturn(HttpStatus.SC_CREATED); + when(method.getResponseBodyAsString()).thenReturn(SEC_PROFILE_JSON_RESPONSE); + + // Execute + final SecurityProfile actualSecProfile = api.createSecurityProfile(inputSecProfile); + + // Assert + verify(method, times(1)).releaseConnection(); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + UUID, actualSecProfile.getUuid()); + assertEquals("Wrong Uuid in the newly created SecurityProfile", + HREF, actualSecProfile.getHref()); + assertEquals("Wrong Schema in the newly created SecurityProfile", + SCHEMA, actualSecProfile.getSchema()); + assertEquals("Wrong URI for SecurityProfile creation REST service", + NiciraNvpApi.SEC_PROFILE_URI_PREFIX, uri); + assertEquals("Wrong HTTP method for SecurityProfile creation REST service", + NiciraNvpApi.POST_METHOD_TYPE, type); + } + + @Test + public void testUpdateSecurityProfile() throws NiciraNvpApiException, IOException { + // Prepare + final SecurityProfile inputSecProfile = new SecurityProfile(); + method = mock(PutMethod.class); + when(method.getStatusCode()).thenReturn(HttpStatus.SC_OK); + + // Execute + api.updateSecurityProfile(inputSecProfile, UUID); + + // Assert + verify(method, times(1)).releaseConnection(); + assertEquals("Wrong URI for SecurityProfile creation REST service", + UUID_SEC_PROFILE_URI, uri); + assertEquals("Wrong HTTP method for SecurityProfile creation REST service", + NiciraNvpApi.PUT_METHOD_TYPE, type); + } + + @Test + public void testDeleteSecurityProfile() throws NiciraNvpApiException, IOException { + // Prepare + method = mock(DeleteMethod.class); + when(method.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT); + + // Execute + api.deleteSecurityProfile(UUID); + + // Assert + verify(method, times(1)).releaseConnection(); + assertEquals("Wrong URI for SecurityProfile deletion REST service", + UUID_SEC_PROFILE_URI, uri); + assertEquals("Wrong HTTP method for SecurityProfile deletion REST service", + NiciraNvpApi.DELETE_METHOD_TYPE, type); + } } diff --git a/plugins/network-elements/nicira-nvp/test/resources/config.properties b/plugins/network-elements/nicira-nvp/test/resources/config.properties new file mode 100644 index 00000000000..67576923823 --- /dev/null +++ b/plugins/network-elements/nicira-nvp/test/resources/config.properties @@ -0,0 +1,21 @@ +# 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. + + +nvp.host=${nvp-host} +nvp.admin.user=${nvp-admin-user} +nvp.admin.pwd=${nvp-admin-pwd} diff --git a/plugins/network-elements/opendaylight/pom.xml b/plugins/network-elements/opendaylight/pom.xml new file mode 100644 index 00000000000..47d66f60b13 --- /dev/null +++ b/plugins/network-elements/opendaylight/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + cloud-plugin-network-opendaylight + Apache CloudStack Plugin - Network Opendaylight + + org.apache.cloudstack + cloudstack-plugins + 4.4.0-SNAPSHOT + ../../pom.xml + + + + + ${basedir}/src/main/java + ${basedir}/src/main/scripts + ${basedir}/src/test/java + ${basedir}/target/classes + ${basedir}/target/test-classes + + + ${basedir}/src/main/resources + + + + + ${basedir}/src/test/resources + + + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.9.0 + + + process-classes + + format + + + + + true + true +
../../../LICENSE.header
+ + XML_STYLE +     DOUBLESLASH_STYLE +     SEMICOLON_STYLE + + false + + target/** + .settings/** + .checkstyle + .project + .classpath + +
+
+
+
+ + + integration + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + +
diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java new file mode 100644 index 00000000000..9761e744f6b --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightElement.java @@ -0,0 +1,174 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import org.apache.cloudstack.network.opendaylight.agent.commands.StartupOpenDaylightControllerCommand; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.element.ConnectivityProvider; +import com.cloud.network.element.NetworkElement; +import com.cloud.offering.NetworkOffering; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachineProfile; + +@Component +@Local(value = {NetworkElement.class, ConnectivityProvider.class}) +public class OpendaylightElement extends AdapterBase implements ConnectivityProvider, ResourceStateAdapter { + + private static final Logger s_logger = Logger.getLogger(OpendaylightElement.class); + private static final Map> s_capabilities = setCapabilities(); + + @Inject + ResourceManager resourceManager; + + @Override + public Map> getCapabilities() { + return s_capabilities; + } + + @Override + public Provider getProvider() { + return Provider.Opendaylight; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + boolean configured = super.configure(name, params); + if (configured) + resourceManager.registerResourceStateAdapter(name, this); + return configured; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException, InsufficientCapacityException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return true; + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return false; + } + + @Override + public boolean verifyServicesCombination(Set services) { + if (services.contains(Service.Connectivity) && services.size() == 1) + return true; + return false; + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] startup) { + if (!(startup[0] instanceof StartupOpenDaylightControllerCommand)) { + return null; + } + throw new CloudRuntimeException("createHostVOForConnectedAgent is not implemented for OpendaylightElement"); + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, List hostTags) { + if (!(startup[0] instanceof StartupOpenDaylightControllerCommand)) { + return null; + } + host.setType(Host.Type.L2Networking); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + return new DeleteHostAnswer(true); + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + // L2 Support : SDN provisioning + capabilities.put(Service.Connectivity, null); + + return capabilities; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java new file mode 100644 index 00000000000..0b547e76f18 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java @@ -0,0 +1,276 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight; + +import java.util.List; +import java.util.UUID; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.network.opendaylight.agent.commands.AddHypervisorCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.ConfigureNetworkCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.ConfigurePortCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.DestroyNetworkCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.DestroyPortCommand; +import org.apache.cloudstack.network.opendaylight.agent.responses.AddHypervisorAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.ConfigureNetworkAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.ConfigurePortAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.DestroyNetworkAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.DestroyPortAnswer; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerMappingDao; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerVO; + +import com.cloud.agent.AgentManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapcityException; +import com.cloud.network.Network; +import com.cloud.network.Network.GuestType; +import com.cloud.network.Network.Provider; +import com.cloud.network.Network.Service; +import com.cloud.network.Network.State; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Networks.BroadcastDomainType; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetwork.IsolationMethod; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.guru.GuestNetworkGuru; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachineProfile; + +public class OpendaylightGuestNetworkGuru extends GuestNetworkGuru { + private static final Logger s_logger = Logger.getLogger(OpendaylightGuestNetworkGuru.class); + + @Inject + protected NetworkOfferingServiceMapDao ntwkOfferingSrvcDao; + @Inject + PhysicalNetworkDao physicalNetworkDao; + @Inject + OpenDaylightControllerMappingDao openDaylightControllerMappingDao; + @Inject + NetworkModel networkModel; + @Inject + AgentManager agentManager; + @Inject + NetworkDao networkDao; + + public OpendaylightGuestNetworkGuru() { + _isolationMethods = new IsolationMethod[] {IsolationMethod.ODL}; + } + + @Override + protected boolean canHandle(NetworkOffering offering, NetworkType networkType, PhysicalNetwork physicalNetwork) { + if (networkType == NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) && offering.getGuestType() == Network.GuestType.Isolated && + isMyIsolationMethod(physicalNetwork) && ntwkOfferingSrvcDao.areServicesSupportedByNetworkOffering(offering.getId(), Service.Connectivity) + && ntwkOfferingSrvcDao.isProviderForNetworkOffering(offering.getId(), Provider.Opendaylight)) { + return true; + } else { + s_logger.trace("We only take care of Guest networks of type " + GuestType.Isolated + " in zone of type " + NetworkType.Advanced); + return false; + } + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + PhysicalNetworkVO physnet = physicalNetworkDao.findById(plan.getPhysicalNetworkId()); + DataCenter dc = _dcDao.findById(plan.getDataCenterId()); + if (!canHandle(offering, dc.getNetworkType(), physnet)) { + s_logger.debug("Refusing to design this network"); + return null; + } + + List devices = openDaylightControllerMappingDao.listByPhysicalNetwork(physnet.getId()); + if (devices.isEmpty()) { + s_logger.error("No Controller on physical network " + physnet.getName()); + return null; + } + s_logger.debug("Controller " + devices.get(0).getUuid() + " found on physical network " + physnet.getId()); + s_logger.debug("Physical isolation type is ODL, asking GuestNetworkGuru to design this network"); + + NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, owner); + if (networkObject == null) { + return null; + } + // Override the broadcast domain type + networkObject.setBroadcastDomainType(BroadcastDomainType.OpenDaylight); + + return networkObject; + } + + @Override + public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapcityException { + assert (network.getState() == State.Implementing) : "Why are we implementing " + network; + + long dcId = dest.getDataCenter().getId(); + + //get physical network id + Long physicalNetworkId = network.getPhysicalNetworkId(); + + // physical network id can be null in Guest Network in Basic zone, so locate the physical network + if (physicalNetworkId == null) { + physicalNetworkId = networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType()); + } + + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), network.getBroadcastDomainType(), network.getNetworkOfferingId(), State.Allocated, + network.getDataCenterId(), physicalNetworkId); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + // Name is either the given name or the uuid + String name = network.getName(); + if (name == null || name.isEmpty()) { + name = ((NetworkVO)network).getUuid(); + } + + List devices = openDaylightControllerMappingDao.listByPhysicalNetwork(physicalNetworkId); + if (devices.isEmpty()) { + s_logger.error("No Controller on physical network " + physicalNetworkId); + return null; + } + OpenDaylightControllerVO controller = devices.get(0); + + ConfigureNetworkCommand cmd = new ConfigureNetworkCommand(name, context.getAccount().getAccountName()); + ConfigureNetworkAnswer answer = (ConfigureNetworkAnswer)agentManager.easySend(controller.getHostId(), cmd); + + if (answer == null || !answer.getResult()) { + s_logger.error("ConfigureNetworkCommand failed"); + return null; + } + + implemented.setBroadcastUri(BroadcastDomainType.OpenDaylight.toUri(answer.getNetworkUuid())); + implemented.setBroadcastDomainType(BroadcastDomainType.OpenDaylight); + s_logger.info("Implemented OK, network linked to = " + implemented.getBroadcastUri().toString()); + + return implemented; + } + + @Override + public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) + throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { + super.reserve(nic, network, vm, dest, context); + + //get physical network id + Long physicalNetworkId = network.getPhysicalNetworkId(); + + List devices = openDaylightControllerMappingDao.listByPhysicalNetwork(physicalNetworkId); + if (devices.isEmpty()) { + s_logger.error("No Controller on physical network " + physicalNetworkId); + throw new InsufficientVirtualNetworkCapcityException("No OpenDaylight Controller configured for this network", dest.getPod().getId()); + } + OpenDaylightControllerVO controller = devices.get(0); + + AddHypervisorCommand addCmd = new AddHypervisorCommand(dest.getHost().getUuid(), dest.getHost().getPrivateIpAddress()); + AddHypervisorAnswer addAnswer = (AddHypervisorAnswer)agentManager.easySend(controller.getHostId(), addCmd); + if (addAnswer == null || !addAnswer.getResult()) { + s_logger.error("Failed to add " + dest.getHost().getName() + " as a node to the controller"); + throw new InsufficientVirtualNetworkCapcityException("Failed to add destination hypervisor to the OpenDaylight Controller", dest.getPod().getId()); + } + + ConfigurePortCommand cmd = new ConfigurePortCommand(UUID.fromString(nic.getUuid()), UUID.fromString(BroadcastDomainType.getValue(network.getBroadcastUri())), context + .getAccount().getAccountName(), nic.getMacAddress()); + ConfigurePortAnswer answer = (ConfigurePortAnswer)agentManager.easySend(controller.getHostId(), cmd); + + if (answer == null || !answer.getResult()) { + s_logger.error("ConfigureNetworkCommand failed"); + throw new InsufficientVirtualNetworkCapcityException("Failed to configure the port on the OpenDaylight Controller", dest.getPod().getId()); + } + + } + + @Override + public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { + boolean success = super.release(nic, vm, reservationId); + + if (success) { + //get physical network id + NetworkVO network = _networkDao.findById(nic.getNetworkId()); + Long physicalNetworkId = network.getPhysicalNetworkId(); + + List devices = openDaylightControllerMappingDao.listByPhysicalNetwork(physicalNetworkId); + if (devices.isEmpty()) { + s_logger.error("No Controller on physical network " + physicalNetworkId); + throw new CloudRuntimeException("No OpenDaylight controller on this physical network"); + } + OpenDaylightControllerVO controller = devices.get(0); + + DestroyPortCommand cmd = new DestroyPortCommand(UUID.fromString(nic.getUuid())); + DestroyPortAnswer answer = (DestroyPortAnswer)agentManager.easySend(controller.getHostId(), cmd); + + if (answer == null || !answer.getResult()) { + s_logger.error("DestroyPortCommand failed"); + success = false; + } + } + + return success; + } + + @Override + public void shutdown(NetworkProfile profile, NetworkOffering offering) { + NetworkVO networkObject = networkDao.findById(profile.getId()); + if (networkObject.getBroadcastDomainType() != BroadcastDomainType.OpenDaylight || networkObject.getBroadcastUri() == null) { + s_logger.warn("BroadcastUri is empty or incorrect for guestnetwork " + networkObject.getDisplayText()); + return; + } + + List devices = openDaylightControllerMappingDao.listByPhysicalNetwork(networkObject.getPhysicalNetworkId()); + if (devices.isEmpty()) { + s_logger.error("No Controller on physical network " + networkObject.getPhysicalNetworkId()); + return; + } + OpenDaylightControllerVO controller = devices.get(0); + + DestroyNetworkCommand cmd = new DestroyNetworkCommand(BroadcastDomainType.getValue(networkObject.getBroadcastUri())); + DestroyNetworkAnswer answer = (DestroyNetworkAnswer)agentManager.easySend(controller.getHostId(), cmd); + + if (answer == null || !answer.getResult()) { + s_logger.error("DestroyNetworkCommand failed"); + } + + super.shutdown(profile, offering); + } + + @Override + public boolean trash(Network network, NetworkOffering offering) { + // TODO Auto-generated method stub + return super.trash(network, offering); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResource.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResource.java new file mode 100644 index 00000000000..c2b7a67244e --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResource.java @@ -0,0 +1,344 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent; + +import java.net.MalformedURLException; +import java.net.URL; +import java.security.InvalidParameterException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.UUID; + +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.network.opendaylight.agent.commands.AddHypervisorCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.ConfigureNetworkCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.ConfigurePortCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.DestroyNetworkCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.DestroyPortCommand; +import org.apache.cloudstack.network.opendaylight.agent.commands.StartupOpenDaylightControllerCommand; +import org.apache.cloudstack.network.opendaylight.agent.responses.AddHypervisorAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.ConfigureNetworkAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.ConfigurePortAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.DestroyNetworkAnswer; +import org.apache.cloudstack.network.opendaylight.agent.responses.DestroyPortAnswer; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetwork; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworkWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworksList; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNode; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodeWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodesList; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPort; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortWrapper; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNetworksNorthboundAction; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNodesNorthboundAction; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronPortsNorthboundAction; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.MaintainAnswer; +import com.cloud.agent.api.MaintainCommand; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; +import com.cloud.host.Host.Type; +import com.cloud.resource.ServerResource; + +public class OpenDaylightControllerResource implements ServerResource { + private static final Logger s_logger = Logger.getLogger(OpenDaylightControllerResource.class); + private Map configuration = new HashMap(); + + private URL controllerUrl; + private String controllerUsername; + private String controllerPassword; + + private int runLevel; + + @Override + public String getName() { + if (configuration.containsKey("name")) + return (String)configuration.get("name"); + else + return null; + } + + @Override + public void setName(String name) { + configuration.put("name", name); + } + + @Override + public void setConfigParams(Map params) { + for (Entry entry : params.entrySet()) { + configuration.put(entry.getKey(), entry.getValue()); + } + updateConfiguration(); + } + + @Override + public Map getConfigParams() { + return Collections.unmodifiableMap(configuration); + } + + @Override + public int getRunLevel() { + return runLevel; + } + + @Override + public void setRunLevel(int level) { + runLevel = level; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + for (Entry entry : params.entrySet()) { + configuration.put(entry.getKey(), entry.getValue()); + } + updateConfiguration(); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public Type getType() { + return Type.L2Networking; + } + + @Override + public StartupCommand[] initialize() { + StartupOpenDaylightControllerCommand sc = new StartupOpenDaylightControllerCommand(); + sc.setGuid((String)configuration.get("guid")); + sc.setName(getName()); + sc.setDataCenter((String)configuration.get("zoneId")); + sc.setPod(""); + sc.setPrivateIpAddress(""); + sc.setStorageIpAddress(""); + sc.setVersion(OpenDaylightControllerResource.class.getPackage().getImplementationVersion()); + return new StartupCommand[] {sc}; + + } + + @Override + public PingCommand getCurrentStatus(long id) { + return new PingCommand(Host.Type.L2Networking, id); + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ConfigureNetworkCommand) { + return executeRequest((ConfigureNetworkCommand)cmd); + } else if (cmd instanceof DestroyNetworkCommand) { + return executeRequest((DestroyNetworkCommand)cmd); + } else if (cmd instanceof ConfigurePortCommand) { + return executeRequest((ConfigurePortCommand)cmd); + } else if (cmd instanceof DestroyPortCommand) { + return executeRequest((DestroyPortCommand)cmd); + } else if (cmd instanceof AddHypervisorCommand) { + return executeRequest((AddHypervisorCommand)cmd); + } else if (cmd instanceof ReadyCommand) { + return executeRequest((ReadyCommand)cmd); + } else if (cmd instanceof MaintainCommand) { + return executeRequest((MaintainCommand)cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + @Override + public void disconnected() { + s_logger.warn("OpenDaylightControllerResource is disconnected from the controller at " + controllerUrl); + + } + + @Override + public IAgentControl getAgentControl() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + // TODO Auto-generated method stub + + } + + private Answer executeRequest(final ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer executeRequest(final MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + private Answer executeRequest(ConfigureNetworkCommand cmd) { + NeutronNetworksNorthboundAction configureNetwork = new NeutronNetworksNorthboundAction(controllerUrl, controllerUsername, controllerPassword); + + // Find free gre key + int gre_key = -1; + Random keyGenerator = new Random(System.currentTimeMillis()); + try { + NeutronNetworksList networks = configureNetwork.listAllNetworks(); + while (true) { + int i = keyGenerator.nextInt(); + for (NeutronNetwork network : networks.getNetworks()) { + if (network.getSegmentationId() == i) { + continue; + } + } + gre_key = i; + break; + } + } catch (NeutronRestApiException e) { + s_logger.error("Failed to list existing networks on the ODL Controller", e); + return new ConfigureNetworkAnswer(cmd, e); + } + + NeutronNetwork newNetwork = new NeutronNetwork(); + + // Configuration from the command + newNetwork.setName(cmd.getName()); + newNetwork.setTenantId(cmd.getTenantId()); + + // Static configuation + newNetwork.setNetworkType("gre"); + newNetwork.setShared(false); + newNetwork.setSegmentationId(gre_key); + newNetwork.setId(UUID.randomUUID()); + + NeutronNetworkWrapper wrapper = new NeutronNetworkWrapper(); + wrapper.setNetwork(newNetwork); + try { + wrapper = configureNetwork.createNeutronNetwork(wrapper); + } catch (NeutronRestApiException e) { + s_logger.error("createNeutronNetwork failed", e); + return new ConfigureNetworkAnswer(cmd, e); + } + + return new ConfigureNetworkAnswer(cmd, true, null, wrapper.getNetwork().getId().toString()); + } + + private Answer executeRequest(DestroyNetworkCommand cmd) { + NeutronNetworksNorthboundAction configureNetwork = new NeutronNetworksNorthboundAction(controllerUrl, controllerUsername, controllerPassword); + try { + configureNetwork.deleteNeutronNetwork(cmd.getNetworkUuid()); + } catch (NeutronRestApiException e) { + s_logger.error("deleteNeutronNetwork failed", e); + return new DestroyNetworkAnswer(cmd, e); + } + + return new DestroyNetworkAnswer(cmd, true, "Network " + cmd.getNetworkUuid() + " deleted"); + } + + private Answer executeRequest(ConfigurePortCommand cmd) { + NeutronPortsNorthboundAction configurePort = new NeutronPortsNorthboundAction(controllerUrl, controllerUsername, controllerPassword); + NeutronPort newPort = new NeutronPort(); + + // Configuration from the command + newPort.setId(cmd.getPortId()); + newPort.setTenantId(cmd.getTennantId()); + newPort.setAdminStateUp(true); + newPort.setName(cmd.getPortId().toString()); + newPort.setNetworkId(cmd.getNetworkId()); + newPort.setMacAddress(cmd.getMacAddress()); + newPort.setDeviceId(UUID.randomUUID()); + + // Static valus + newPort.setStatus("ACTIVE"); + newPort.setFixedIps(Collections. emptyList()); + + NeutronPortWrapper portWrapper = new NeutronPortWrapper(); + portWrapper.setPort(newPort); + try { + portWrapper = configurePort.createNeutronPort(portWrapper); + } catch (NeutronRestApiException e) { + s_logger.error("createPortCommand failed", e); + return new ConfigurePortAnswer(cmd, e); + } + + return new ConfigurePortAnswer(cmd, true, "Port " + portWrapper.getPort().getId().toString() + " created"); + + } + + private Answer executeRequest(DestroyPortCommand cmd) { + NeutronPortsNorthboundAction configurePort = new NeutronPortsNorthboundAction(controllerUrl, controllerUsername, controllerPassword); + try { + configurePort.deleteNeutronPort(cmd.getPortId().toString()); + } catch (NeutronRestApiException e) { + s_logger.error("deleteNeutronPort failed", e); + return new DestroyPortAnswer(cmd, e); + } + + return new DestroyPortAnswer(cmd, true, "Port " + cmd.getPortId().toString() + " deleted"); + } + + private Answer executeRequest(AddHypervisorCommand cmd) { + NeutronNodesNorthboundAction nodeActions = new NeutronNodesNorthboundAction(controllerUrl, controllerUsername, controllerPassword); + try { + NeutronNodesList nodes = nodeActions.listAllNodes(); + if (nodes.getNodes() != null) { + for (NeutronNodeWrapper nodeWrapper : nodes.getNodes()) { + NeutronNode node = nodeWrapper.getNode(); + if (node.getId().equals(cmd.getHostId())) { + return new AddHypervisorAnswer(cmd, true, "Hypervisor already connected"); + } + } + } + + // Not found in the existing node list, add it + nodeActions.updateNeutronNodeV2("OVS", cmd.getHostId(), cmd.getIpAddress(), 6640); + } catch (NeutronRestApiException e) { + s_logger.error("Call to OpenDaylight failed", e); + return new AddHypervisorAnswer(cmd, e); + } + return new AddHypervisorAnswer(cmd, true, "Hypervisor " + cmd.getHostId() + " added"); + } + + private void updateConfiguration() { + if (!configuration.containsKey("url") || !configuration.containsKey("username") || !configuration.containsKey("password")) + throw new InvalidParameterException("OpenDaylightControllerResource needs a url, username and password."); + try { + controllerUrl = new URL((String)configuration.get("url")); + } catch (MalformedURLException e) { + throw new InvalidParameterException("OpenDaylightControllerResource found an invalid controller url"); + } + controllerUsername = (String)configuration.get("username"); + controllerPassword = (String)configuration.get("password"); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManager.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManager.java new file mode 100644 index 00000000000..5f6161a481c --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManager.java @@ -0,0 +1,42 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent; + +import java.util.List; + +import org.apache.cloudstack.network.opendaylight.api.commands.AddOpenDaylightControllerCmd; +import org.apache.cloudstack.network.opendaylight.api.commands.DeleteOpenDaylightControllerCmd; +import org.apache.cloudstack.network.opendaylight.api.commands.ListOpenDaylightControllersCmd; +import org.apache.cloudstack.network.opendaylight.api.responses.OpenDaylightControllerResponse; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerVO; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.component.PluggableService; + +public interface OpenDaylightControllerResourceManager extends PluggableService { + + public OpenDaylightControllerVO addController(AddOpenDaylightControllerCmd cmd); + + public void deleteController(DeleteOpenDaylightControllerCmd cmd) throws InvalidParameterValueException; + + public List listControllers(ListOpenDaylightControllersCmd cmd); + + public OpenDaylightControllerResponse createResponseFromVO(OpenDaylightControllerVO controller); +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManagerImpl.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManagerImpl.java new file mode 100644 index 00000000000..abc48bb478f --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/OpenDaylightControllerResourceManagerImpl.java @@ -0,0 +1,219 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice; +import org.apache.cloudstack.network.opendaylight.api.commands.AddOpenDaylightControllerCmd; +import org.apache.cloudstack.network.opendaylight.api.commands.DeleteOpenDaylightControllerCmd; +import org.apache.cloudstack.network.opendaylight.api.commands.ListOpenDaylightControllersCmd; +import org.apache.cloudstack.network.opendaylight.api.responses.OpenDaylightControllerResponse; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerMappingDao; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerVO; + +import com.cloud.api.ApiDBUtils; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceState; +import com.cloud.resource.ServerResource; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; + +public class OpenDaylightControllerResourceManagerImpl implements OpenDaylightControllerResourceManager { + private final static Logger s_logger = Logger.getLogger(OpenDaylightControllerResourceManagerImpl.class); + + @Inject + HostDao hostDao; + @Inject + ResourceManager resourceManager; + @Inject + PhysicalNetworkDao physicalNetworkDao; + @Inject + PhysicalNetworkServiceProviderDao physicalNetworkServiceProviderDao; + @Inject + OpenDaylightControllerMappingDao openDaylightControllerMappingDao; + @Inject + NetworkDao networkDao; + + @Override + public List> getCommands() { + List> commands = new ArrayList>(); + commands.add(AddOpenDaylightControllerCmd.class); + commands.add(DeleteOpenDaylightControllerCmd.class); + commands.add(ListOpenDaylightControllersCmd.class); + return commands; + } + + @Override + public OpenDaylightControllerVO addController(AddOpenDaylightControllerCmd cmd) { + ServerResource odlController = new OpenDaylightControllerResource(); + + final String deviceName = NetworkDevice.OpenDaylightController.getName(); + NetworkDevice networkDevice = NetworkDevice.getNetworkDevice(deviceName); + if (networkDevice == null) { + throw new CloudRuntimeException("No network device found for name " + deviceName); + } + final Long physicalNetworkId = cmd.getPhysicalNetworkId(); + PhysicalNetworkVO physicalNetwork = physicalNetworkDao.findById(physicalNetworkId); + if (physicalNetwork == null) { + throw new InvalidParameterValueException("Could not find phyical network with ID: " + physicalNetworkId); + } + long zoneId = physicalNetwork.getDataCenterId(); + + final PhysicalNetworkServiceProviderVO ntwkSvcProvider = physicalNetworkServiceProviderDao.findByServiceProvider(physicalNetwork.getId(), + networkDevice.getNetworkServiceProvder()); + if (ntwkSvcProvider == null) { + throw new CloudRuntimeException("Network Service Provider: " + networkDevice.getNetworkServiceProvder() + " is not enabled in the physical network: " + + physicalNetworkId + "to add this device"); + } else if (ntwkSvcProvider.getState() == PhysicalNetworkServiceProvider.State.Shutdown) { + throw new CloudRuntimeException("Network Service Provider: " + ntwkSvcProvider.getProviderName() + " is in shutdown state in the physical network: " + + physicalNetworkId + "to add this device"); + } + + final Map hostParams = new HashMap(); + hostParams.put("guid", UUID.randomUUID().toString()); + hostParams.put("zoneId", String.valueOf(physicalNetwork.getDataCenterId())); + hostParams.put("physicalNetworkId", String.valueOf(physicalNetwork.getId())); + hostParams.put("name", "ODL Controller - " + hostParams.get("guid")); + hostParams.put("url", cmd.getUrl()); + hostParams.put("username", cmd.getUsername()); + hostParams.put("password", cmd.getPassword()); + + Map hostdetails = new HashMap(); + hostdetails.putAll(hostParams); + + try { + odlController.configure(hostParams.get("name"), hostdetails); + final Host host = resourceManager.addHost(zoneId, odlController, Host.Type.L2Networking, hostParams); + if (host != null) { + return Transaction.execute(new TransactionCallback() { + @Override + public OpenDaylightControllerVO doInTransaction(TransactionStatus status) { + OpenDaylightControllerVO controller = new OpenDaylightControllerVO(host.getId(), physicalNetworkId, ntwkSvcProvider.getProviderName(), hostParams + .get("name")); + openDaylightControllerMappingDao.persist(controller); + return controller; + } + }); + } else { + throw new CloudRuntimeException("Failed to create host object for ODL Controller"); + } + } catch (ConfigurationException e) { + throw new CloudRuntimeException("Failed to add ODL Controller as a resource", e); + } + } + + @Override + public void deleteController(DeleteOpenDaylightControllerCmd cmd) throws InvalidParameterValueException { + OpenDaylightControllerVO controller = openDaylightControllerMappingDao.findById(cmd.getId()); + if (controller == null) { + throw new InvalidParameterValueException("No ODL Controller with id " + cmd.getId()); + } + + // Find the physical network we work for + Long physicalNetworkId = controller.getPhysicalNetworkId(); + PhysicalNetworkVO physicalNetwork = physicalNetworkDao.findById(physicalNetworkId); + if (physicalNetwork != null) { + // Lets see if there are networks that use us + List networkList = networkDao.listByPhysicalNetwork(physicalNetworkId); + + if (networkList != null) { + // Networks with broadcast type lswitch are ours + for (NetworkVO network : networkList) { + if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.OpenDaylight) { + if ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy)) { + throw new CloudRuntimeException("This Controller can not be deleted as there are one or more logical networks provisioned by cloudstack."); + } + } + } + } + } + + HostVO host = hostDao.findById(controller.getHostId()); + Long hostId = host.getId(); + + host.setResourceState(ResourceState.Maintenance); + hostDao.update(hostId, host); + resourceManager.deleteHost(hostId, false, false); + + openDaylightControllerMappingDao.remove(cmd.getId()); + } + + @Override + public List listControllers(ListOpenDaylightControllersCmd cmd) { + if (cmd.getId() != null) { + List foundControllers = new ArrayList(); + OpenDaylightControllerVO controller = openDaylightControllerMappingDao.findById(cmd.getId()); + if (controller != null) { + foundControllers.add(controller); + } + return foundControllers; + } else if (cmd.getPhysicalNetworkId() != null) { + return openDaylightControllerMappingDao.listByPhysicalNetwork(cmd.getPhysicalNetworkId()); + } + return openDaylightControllerMappingDao.listAll(); + } + + @Override + public OpenDaylightControllerResponse createResponseFromVO(OpenDaylightControllerVO controller) { + OpenDaylightControllerResponse response = new OpenDaylightControllerResponse(); + HostVO controllerHost = hostDao.findById(controller.getHostId()); + hostDao.loadDetails(controllerHost); + + PhysicalNetwork pnw = ApiDBUtils.findPhysicalNetworkById(controller.getPhysicalNetworkId()); + if (pnw != null) { + response.setPhysicalNetworkId(pnw.getUuid()); + } + + response.setObjectName("opendaylightcontroller"); + response.setId(controller.getUuid()); + response.setUrl(controllerHost.getDetail("url")); + response.setName(controllerHost.getDetail("name")); + response.setUsername(controllerHost.getDetail("username")); + + return response; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/AddHypervisorCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/AddHypervisorCommand.java new file mode 100644 index 00000000000..06e94b35390 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/AddHypervisorCommand.java @@ -0,0 +1,58 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import com.cloud.agent.api.Command; + +public class AddHypervisorCommand extends Command { + private String hostId; + private String ipAddress; + + public AddHypervisorCommand() { + } + + public AddHypervisorCommand(String hostId, String ipAddress) { + this.hostId = hostId; + this.ipAddress = ipAddress; + } + + public String getHostId() { + return hostId; + } + + public void setHostId(String hostId) { + this.hostId = hostId; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigureNetworkCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigureNetworkCommand.java new file mode 100644 index 00000000000..bfbcde4ca43 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigureNetworkCommand.java @@ -0,0 +1,54 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import com.cloud.agent.api.Command; + +public class ConfigureNetworkCommand extends Command { + private String name; + private String tenantId; + + public ConfigureNetworkCommand(String name, String tenantId) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(String tennantId) { + tenantId = tennantId; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigurePortCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigurePortCommand.java new file mode 100644 index 00000000000..3d4d2a2a69a --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/ConfigurePortCommand.java @@ -0,0 +1,80 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import java.util.UUID; + +import com.cloud.agent.api.Command; + +public class ConfigurePortCommand extends Command { + private UUID networkId; + private String tennantId; + private String macAddress; + private UUID portId; + + public ConfigurePortCommand() { + } + + public ConfigurePortCommand(UUID portId, UUID networkId, String tennantId, String macAddress) { + this.portId = portId; + this.networkId = networkId; + this.tennantId = tennantId; + this.macAddress = macAddress; + } + + public UUID getNetworkId() { + return networkId; + } + + public void setNetworkId(UUID networkId) { + this.networkId = networkId; + } + + public String getTennantId() { + return tennantId; + } + + public void setTennantId(String tennantId) { + this.tennantId = tennantId; + } + + public String getMacAddress() { + return macAddress; + } + + public void setMacAddress(String macAddress) { + this.macAddress = macAddress; + } + + public UUID getPortId() { + return portId; + } + + public void setPortId(UUID portId) { + this.portId = portId; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyNetworkCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyNetworkCommand.java new file mode 100644 index 00000000000..6d1bc044315 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyNetworkCommand.java @@ -0,0 +1,45 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import com.cloud.agent.api.Command; + +public class DestroyNetworkCommand extends Command { + private String networkUuid; + + public DestroyNetworkCommand(String networkUuid) { + this.networkUuid = networkUuid; + } + + public String getNetworkUuid() { + return networkUuid; + } + + public void setNetworkUuid(String networkUuid) { + this.networkUuid = networkUuid; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyPortCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyPortCommand.java new file mode 100644 index 00000000000..c0a89371c1a --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/DestroyPortCommand.java @@ -0,0 +1,50 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import java.util.UUID; + +import com.cloud.agent.api.Command; + +public class DestroyPortCommand extends Command { + private UUID portId; + + public DestroyPortCommand() { + } + + public DestroyPortCommand(UUID portId) { + this.portId = portId; + } + + public UUID getPortId() { + return portId; + } + + public void setPortId(UUID portId) { + this.portId = portId; + } + + @Override + public boolean executeInSequence() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/StartupOpenDaylightControllerCommand.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/StartupOpenDaylightControllerCommand.java new file mode 100644 index 00000000000..de3a7a3b54b --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/commands/StartupOpenDaylightControllerCommand.java @@ -0,0 +1,29 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.commands; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; + +public class StartupOpenDaylightControllerCommand extends StartupCommand { + public StartupOpenDaylightControllerCommand() { + super(Host.Type.L2Networking); + } +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/AddHypervisorAnswer.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/AddHypervisorAnswer.java new file mode 100644 index 00000000000..79867051e13 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/AddHypervisorAnswer.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.responses; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class AddHypervisorAnswer extends Answer { + + public AddHypervisorAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public AddHypervisorAnswer(Command command, Exception e) { + super(command, e); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigureNetworkAnswer.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigureNetworkAnswer.java new file mode 100644 index 00000000000..81c4a6fd45d --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigureNetworkAnswer.java @@ -0,0 +1,43 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.responses; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class ConfigureNetworkAnswer extends Answer { + private String networkUuid; + + public ConfigureNetworkAnswer(Command command, boolean success, String details, String networkUuid) { + this.networkUuid = networkUuid; + } + + public ConfigureNetworkAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public ConfigureNetworkAnswer(Command command, Exception e) { + super(command, e); + } + + public String getNetworkUuid() { + return networkUuid; + } +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigurePortAnswer.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigurePortAnswer.java new file mode 100644 index 00000000000..cf9acc1d535 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/ConfigurePortAnswer.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.responses; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class ConfigurePortAnswer extends Answer { + + public ConfigurePortAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public ConfigurePortAnswer(Command command, Exception e) { + super(command, e); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyNetworkAnswer.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyNetworkAnswer.java new file mode 100644 index 00000000000..10284b4918b --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyNetworkAnswer.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.responses; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class DestroyNetworkAnswer extends Answer { + + public DestroyNetworkAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public DestroyNetworkAnswer(Command command, Exception e) { + super(command, e); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyPortAnswer.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyPortAnswer.java new file mode 100644 index 00000000000..07bb0dbfff3 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/agent/responses/DestroyPortAnswer.java @@ -0,0 +1,35 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.agent.responses; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class DestroyPortAnswer extends Answer { + + public DestroyPortAnswer(Command command, boolean success, String details) { + super(command, success, details); + } + + public DestroyPortAnswer(Command command, Exception e) { + super(command, e); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronInvalidCredentialsException.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronInvalidCredentialsException.java new file mode 100644 index 00000000000..f46e0b686d9 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronInvalidCredentialsException.java @@ -0,0 +1,38 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api; + +public class NeutronInvalidCredentialsException extends Exception { + + public NeutronInvalidCredentialsException() { + } + + public NeutronInvalidCredentialsException(final String message) { + super(message); + } + + public NeutronInvalidCredentialsException(final Throwable cause) { + super(cause); + } + + public NeutronInvalidCredentialsException(final String message, final Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApi.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApi.java new file mode 100644 index 00000000000..2d0c5324341 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApi.java @@ -0,0 +1,217 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URL; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.cookie.CookiePolicy; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; +import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; +import org.apache.log4j.Logger; + +public class NeutronRestApi { + + private static final Logger s_logger = Logger.getLogger(NeutronRestApi.class); + private static final MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager(); + + private static final String PROTOCOL = "https"; + private static final int HTTPS_PORT = 443; + + private final HttpClient client; + + private Class httpClazz; + + protected NeutronRestApi(final Class httpClazz) { + this(httpClazz, PROTOCOL, HTTPS_PORT); + } + + protected NeutronRestApi(final Class httpClazz, final String protocol, final int port) { + client = createHttpClient(); + client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); + this.httpClazz = httpClazz; + + try { + // Cast to ProtocolSocketFactory to avoid the deprecated constructor + // with the SecureProtocolSocketFactory parameter + Protocol.registerProtocol(protocol, new Protocol(protocol, (ProtocolSocketFactory) new TrustingProtocolSocketFactory(), HTTPS_PORT)); + } catch (IOException e) { + s_logger.warn("Failed to register the TrustingProtocolSocketFactory, falling back to default SSLSocketFactory", e); + } + } + + public Class getHttpClazz() { + return httpClazz; + } + + public HttpMethodBase createMethod(final URL neutronUrl, final String uri) throws NeutronRestApiException { + String url; + try { + String formattedUrl = neutronUrl.toString() + uri; + url = new URL(formattedUrl).toString(); + + Constructor httpMethodConstructor = httpClazz.getConstructor(String.class); + HttpMethodBase httpMethod = httpMethodConstructor.newInstance(url); + + return httpMethod; + } catch (MalformedURLException e) { + String error = "Unable to build Neutron API URL"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (NoSuchMethodException e) { + String error = "Unable to build Neutron API URL due to reflection error"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (SecurityException e) { + String error = "Unable to build Neutron API URL due to security violation"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (InstantiationException e) { + String error = "Unable to build Neutron API due to instantiation error"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (IllegalAccessException e) { + String error = "Unable to build Neutron API URL due to absence of access modifier"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (IllegalArgumentException e) { + String error = "Unable to build Neutron API URL due to wrong argument in constructor"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } catch (InvocationTargetException e) { + String error = "Unable to build Neutron API URL due to target error"; + s_logger.error(error, e); + throw new NeutronRestApiException(error, e); + } + } + + public void executeMethod(final HttpMethodBase method) throws NeutronRestApiException { + try { + client.executeMethod(method); + } catch (HttpException e) { + s_logger.error("HttpException caught while trying to connect to the Neutron Controller", e); + method.releaseConnection(); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } catch (IOException e) { + s_logger.error("IOException caught while trying to connect to the Neutron Controller", e); + method.releaseConnection(); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } + } + + /* + * This factory method is protected so we can extend this in the unit tests. + */ + protected HttpClient createHttpClient() { + return new HttpClient(s_httpClientManager); + } + + /* + * It uses a self-signed certificate. The TrustingProtocolSocketFactory will + * accept any provided certificate when making an SSL connection to the SDN + * Manager + */ + private class TrustingProtocolSocketFactory implements SecureProtocolSocketFactory { + + private SSLSocketFactory ssf; + + public TrustingProtocolSocketFactory() throws IOException { + // Create a trust manager that does not validate certificate chains + TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkClientTrusted(final X509Certificate[] certs, final String authType) { + // Trust always + } + + @Override + public void checkServerTrusted(final X509Certificate[] certs, final String authType) { + // Trust always + } + } }; + + try { + // Install the all-trusting trust manager + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + ssf = sc.getSocketFactory(); + } catch (KeyManagementException e) { + throw new IOException(e); + } catch (NoSuchAlgorithmException e) { + throw new IOException(e); + } + } + + @Override + public Socket createSocket(final String host, final int port) throws IOException { + return ssf.createSocket(host, port); + } + + @Override + public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort) throws IOException, UnknownHostException { + return ssf.createSocket(address, port, localAddress, localPort); + } + + @Override + public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException, UnknownHostException { + return ssf.createSocket(socket, host, port, autoClose); + } + + @Override + public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException, + UnknownHostException, ConnectTimeoutException { + int timeout = params.getConnectionTimeout(); + if (timeout == 0) { + return createSocket(host, port, localAddress, localPort); + } else { + Socket s = ssf.createSocket(); + s.bind(new InetSocketAddress(localAddress, localPort)); + s.connect(new InetSocketAddress(host, port), timeout); + return s; + } + } + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApiException.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApiException.java new file mode 100644 index 00000000000..58bcd0d97b6 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestApiException.java @@ -0,0 +1,38 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api; + +public class NeutronRestApiException extends Exception { + + public NeutronRestApiException() { + } + + public NeutronRestApiException(final String message) { + super(message); + } + + public NeutronRestApiException(final Throwable cause) { + super(cause); + } + + public NeutronRestApiException(final String message, final Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestFactory.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestFactory.java new file mode 100644 index 00000000000..10ac5e0603b --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/NeutronRestFactory.java @@ -0,0 +1,55 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api; + +import java.util.Hashtable; +import java.util.Map; + +import org.apache.commons.httpclient.HttpMethodBase; + +public class NeutronRestFactory { + + private Map flyweight = new Hashtable(); + + private static NeutronRestFactory instance; + + static { + instance = new NeutronRestFactory(); + } + + private NeutronRestFactory() { + } + + public static NeutronRestFactory getInstance() { + return instance; + } + + public NeutronRestApi getNeutronApi(final Class clazz) { + if (!flyweight.containsKey(clazz.getName())) { + NeutronRestApi api = new NeutronRestApi(clazz); + addNeutronApi(api); + } + return flyweight.get(clazz.getName()); + } + + public void addNeutronApi(final NeutronRestApi api) { + flyweight.put(api.getHttpClazz().getName(), api); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/AddOpenDaylightControllerCmd.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/AddOpenDaylightControllerCmd.java new file mode 100644 index 00000000000..dca80070286 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/AddOpenDaylightControllerCmd.java @@ -0,0 +1,114 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.network.opendaylight.agent.OpenDaylightControllerResourceManager; +import org.apache.cloudstack.network.opendaylight.api.responses.OpenDaylightControllerResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +@APICommand(name = "addOpenDaylightController", responseObject = OpenDaylightControllerResponse.class, description = "Adds an OpenDyalight controler") +public class AddOpenDaylightControllerCmd extends BaseAsyncCmd { + + @Inject + private OpenDaylightControllerResourceManager resourceManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = true, + description = "the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.URL, type = CommandType.STRING, required = true, description = "Api URL of the OpenDaylight Controller.") + private String url; + + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username to access the OpenDaylight API") + private String username; + + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Credential to access the OpenDaylight API") + private String password; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return "addOpenDaylightController"; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_OPENDAYLIGHT_ADD_CONTROLLER; + } + + @Override + public String getEventDescription() { + return "Adding OpenDaylight controller"; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + resourceManager.addController(this); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/DeleteOpenDaylightControllerCmd.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/DeleteOpenDaylightControllerCmd.java new file mode 100644 index 00000000000..7359ce9d015 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/DeleteOpenDaylightControllerCmd.java @@ -0,0 +1,99 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.network.opendaylight.agent.OpenDaylightControllerResourceManager; +import org.apache.cloudstack.network.opendaylight.api.responses.OpenDaylightControllerResponse; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +@APICommand(name = "deleteOpenDaylightController", responseObject = OpenDaylightControllerResponse.class, description = "Removes an OpenDyalight controler") +public class DeleteOpenDaylightControllerCmd extends BaseAsyncCmd { + @Inject + private OpenDaylightControllerResourceManager resourceManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OpenDaylightControllerResponse.class, required = true, description = "OpenDaylight Controller ID") + private Long id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_OPENDAYLIGHT_DELETE_CONTROLLER; + } + + @Override + public String getEventDescription() { + return "Deleted OpenDaylight Controller"; + } + + @Override + public String getCommandName() { + return "deleteOpenDaylightController"; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + try { + resourceManager.deleteController(this); + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + setResponseObject(response); //FIXME + } catch (InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete OpenDaylight controller."); + } + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/ListOpenDaylightControllersCmd.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/ListOpenDaylightControllersCmd.java new file mode 100644 index 00000000000..11f196bc3f0 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/commands/ListOpenDaylightControllersCmd.java @@ -0,0 +1,104 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.network.opendaylight.agent.OpenDaylightControllerResourceManager; +import org.apache.cloudstack.network.opendaylight.api.responses.OpenDaylightControllerResponse; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerVO; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; + +@APICommand(name = "listOpenDaylightControllers", responseObject = OpenDaylightControllerResponse.class, description = "Lists OpenDyalight controllers") +public class ListOpenDaylightControllersCmd extends BaseCmd { + @Inject + private OpenDaylightControllerResourceManager resourceManager; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PHYSICAL_NETWORK_ID, type = CommandType.UUID, entityType = PhysicalNetworkResponse.class, required = false, + description = "the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = OpenDaylightControllerResponse.class, required = false, + description = "the ID of a OpenDaylight Controller") + private Long Id; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return "listOpenDaylightControllers"; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public Long getId() { + return Id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, + NetworkRuleConflictException { + List controllers = resourceManager.listControllers(this); + + List controllerList = new ArrayList(); + for (OpenDaylightControllerVO controller: controllers) { + OpenDaylightControllerResponse responseObject = resourceManager.createResponseFromVO(controller); + controllerList.add(responseObject); + } + ListResponse responseList = new ListResponse(); + responseList.setResponseName(getCommandName()); + responseList.setResponses(controllerList); + setResponseObject(responseList); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/enums/NeutronNorthboundEnum.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/enums/NeutronNorthboundEnum.java new file mode 100644 index 00000000000..8411e231f7f --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/enums/NeutronNorthboundEnum.java @@ -0,0 +1,44 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.enums; + +public enum NeutronNorthboundEnum { + + NETWORKS_URI("/controller/nb/v2/neutron/networks"), + NETWORK_PARAM_URI("/controller/nb/v2/neutron/networks/{0}"), + + PORTS_URI("/controller/nb/v2/neutron/ports"), + PORTS_PARAM_URI("/controller/nb/v2/neutron/ports/{0}"), + + NODES_URI("/controller/nb/v2/connectionmanager/nodes"), + NODE_PARAM_URI("/controller/nb/v2/connectionmanager/node/{0}/{1}"), + NODE_PORT_PER_NODE_URI("/controller/nb/v2/connectionmanager/node/{0}/address/{1}/port/{2}"), + NODE_PORT_PER_TYPE_URI("/controller/nb/v2/connectionmanager/node/{0}/{1}/address/{2}/port/{3}"); + + private String uri; + + private NeutronNorthboundEnum(String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetwork.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetwork.java new file mode 100644 index 00000000000..7d1ebf0b9cc --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetwork.java @@ -0,0 +1,213 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +import java.util.UUID; + +import com.google.gson.annotations.SerializedName; + +public class NeutronNetwork { + + private UUID id; + private String name; + private boolean shared; + private String tenantId; + @SerializedName("provider:network_type") + private String networkType; + @SerializedName("provider:segmentation_id") + private Integer segmentationId; + + public NeutronNetwork() { + } + + public NeutronNetwork(final UUID id, final String name, final boolean shared, final String tenantId, final String networkType, final Integer segmentationId) { + this.id = id; + this.name = name; + this.shared = shared; + this.tenantId = tenantId; + this.networkType = networkType; + this.segmentationId = segmentationId; + } + + public UUID getId() { + return id; + } + + public void setId(final UUID uuid) { + id = uuid; + } + + public String getNetworkType() { + return networkType; + } + + public void setNetworkType(final String networkType) { + this.networkType = networkType; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public boolean isShared() { + return shared; + } + + public void setShared(final boolean shared) { + this.shared = shared; + } + + public Integer getSegmentationId() { + return segmentationId; + } + + public void setSegmentationId(final Integer segmentationId) { + this.segmentationId = segmentationId; + } + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(final String tenantId) { + this.tenantId = tenantId; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + (networkType == null ? 0 : networkType.hashCode()); + result = prime * result + (segmentationId == null ? 0 : segmentationId.hashCode()); + result = prime * result + (shared ? 1231 : 1237); + result = prime * result + (tenantId == null ? 0 : tenantId.hashCode()); + result = prime * result + (id == null ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronNetwork other = (NeutronNetwork) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (networkType == null) { + if (other.networkType != null) { + return false; + } + } else if (!networkType.equals(other.networkType)) { + return false; + } + if (segmentationId == null) { + if (other.segmentationId != null) { + return false; + } + } else if (!segmentationId.equals(other.segmentationId)) { + return false; + } + if (shared != other.shared) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + + public boolean equalsIgnoreUuid(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronNetwork other = (NeutronNetwork) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (networkType == null) { + if (other.networkType != null) { + return false; + } + } else if (!networkType.equals(other.networkType)) { + return false; + } + if (segmentationId == null) { + if (other.segmentationId != null) { + return false; + } + } else if (!segmentationId.equals(other.segmentationId)) { + return false; + } + if (shared != other.shared) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "NeutronNetwork [uuid=" + id + ", networkType=" + networkType + ", name=" + name + ", shared=" + shared + ", segmentationId=" + segmentationId + ", tenantId=" + + tenantId + "]"; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworkWrapper.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworkWrapper.java new file mode 100644 index 00000000000..be1d85e5cb7 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworkWrapper.java @@ -0,0 +1,70 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +public class NeutronNetworkWrapper { + + private NeutronNetwork network; + + public NeutronNetworkWrapper() { + } + + public NeutronNetworkWrapper(final NeutronNetwork network) { + this.network = network; + } + + public NeutronNetwork getNetwork() { + return network; + } + + public void setNetwork(final NeutronNetwork network) { + this.network = network; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (network == null ? 0 : network.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronNetworkWrapper other = (NeutronNetworkWrapper) obj; + if (network == null) { + if (other.network != null) { + return false; + } + } else if (!network.equals(other.network)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworksList.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworksList.java new file mode 100644 index 00000000000..1f9ab59ab22 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNetworksList.java @@ -0,0 +1,42 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +import java.util.List; + +public class NeutronNetworksList { + + private List networks; + + public NeutronNetworksList() { + } + + public NeutronNetworksList(final List networks) { + this.networks = networks; + } + + public List getNetworks() { + return networks; + } + + public void setNetworks(final List networks) { + this.networks = networks; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNode.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNode.java new file mode 100644 index 00000000000..d5b14370a2e --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNode.java @@ -0,0 +1,98 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + + +public class NeutronNode { + + private String id; + private String type; + + public NeutronNode() { + } + + public NeutronNode(final String id, final String type) { + this.id = id; + this.type = type; + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (id == null ? 0 : id.hashCode()); + result = prime * result + (type == null ? 0 : type.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NeutronNode other = (NeutronNode) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } + + public boolean equalsIgnoreUuid(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NeutronNode other = (NeutronNode) obj; + if (type == null) { + if (other.type != null) + return false; + } else if (!type.equals(other.type)) + return false; + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodeWrapper.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodeWrapper.java new file mode 100644 index 00000000000..4dcb4c9b19d --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodeWrapper.java @@ -0,0 +1,65 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +public class NeutronNodeWrapper { + + private NeutronNode node; + + public NeutronNodeWrapper() { + } + + public NeutronNodeWrapper(final NeutronNode node) { + this.node = node; + } + + public NeutronNode getNode() { + return node; + } + + public void setNode(final NeutronNode node) { + this.node = node; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (node == null ? 0 : node.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + NeutronNodeWrapper other = (NeutronNodeWrapper) obj; + if (node == null) { + if (other.node != null) + return false; + } else if (!node.equals(other.node)) + return false; + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodesList.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodesList.java new file mode 100644 index 00000000000..a935bef9432 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronNodesList.java @@ -0,0 +1,42 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +import java.util.List; + +public class NeutronNodesList { + + private List nodes; + + public NeutronNodesList() { + } + + public NeutronNodesList(final List nodes) { + this.nodes = nodes; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(final List nodes) { + this.nodes = nodes; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPort.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPort.java new file mode 100644 index 00000000000..9824feeadc0 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPort.java @@ -0,0 +1,265 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +import java.util.List; +import java.util.UUID; + +public class NeutronPort { + + private UUID id; + private String name; + private String tenantId; + private UUID networkId; + private String macAddress; + private UUID deviceId; + private boolean adminStateUp; + private String status; + private List fixedIps; + + public NeutronPort() { + } + + public NeutronPort(final UUID id, final String name, final String tenantId, final UUID networkId, final String macAddress, final UUID deviceId, final boolean adminStateUp, + final String status, final List fixedIps) { + this.id = id; + this.name = name; + this.tenantId = tenantId; + this.networkId = networkId; + this.macAddress = macAddress; + this.deviceId = deviceId; + this.adminStateUp = adminStateUp; + this.status = status; + this.fixedIps = fixedIps; + } + + public UUID getId() { + return id; + } + + public void setId(final UUID uuid) { + id = uuid; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(final String tenantId) { + this.tenantId = tenantId; + } + + public UUID getNetworkId() { + return networkId; + } + + public void setNetworkId(final UUID networkId) { + this.networkId = networkId; + } + + public String getMacAddress() { + return macAddress; + } + + public void setMacAddress(final String macAddress) { + this.macAddress = macAddress; + } + + public UUID getDeviceId() { + return deviceId; + } + + public void setDeviceId(final UUID deviceId) { + this.deviceId = deviceId; + } + + public boolean isAdminStateUp() { + return adminStateUp; + } + + public void setAdminStateUp(final boolean adminStateUp) { + this.adminStateUp = adminStateUp; + } + + public String getStatus() { + return status; + } + + public void setStatus(final String status) { + this.status = status; + } + + public List getFixedIps() { + return fixedIps; + } + + public void setFixedIps(final List fixedIps) { + this.fixedIps = fixedIps; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (adminStateUp ? 1231 : 1237); + result = prime * result + (deviceId == null ? 0 : deviceId.hashCode()); + result = prime * result + (macAddress == null ? 0 : macAddress.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + (networkId == null ? 0 : networkId.hashCode()); + result = prime * result + (status == null ? 0 : status.hashCode()); + result = prime * result + (tenantId == null ? 0 : tenantId.hashCode()); + result = prime * result + (id == null ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronPort other = (NeutronPort) obj; + if (adminStateUp != other.adminStateUp) { + return false; + } + if (deviceId == null) { + if (other.deviceId != null) { + return false; + } + } else if (!deviceId.equals(other.deviceId)) { + return false; + } + if (macAddress == null) { + if (other.macAddress != null) { + return false; + } + } else if (!macAddress.equals(other.macAddress)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (networkId == null) { + if (other.networkId != null) { + return false; + } + } else if (!networkId.equals(other.networkId)) { + return false; + } + if (status == null) { + if (other.status != null) { + return false; + } + } else if (!status.equals(other.status)) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + return true; + } + + public boolean equalsIgnoreUuid(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronPort other = (NeutronPort) obj; + if (adminStateUp != other.adminStateUp) { + return false; + } + if (deviceId == null) { + if (other.deviceId != null) { + return false; + } + } else if (!deviceId.equals(other.deviceId)) { + return false; + } + if (macAddress == null) { + if (other.macAddress != null) { + return false; + } + } else if (!macAddress.equals(other.macAddress)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (networkId == null) { + if (other.networkId != null) { + return false; + } + } else if (!networkId.equals(other.networkId)) { + return false; + } + if (status == null) { + if (other.status != null) { + return false; + } + } else if (!status.equals(other.status)) { + return false; + } + if (tenantId == null) { + if (other.tenantId != null) { + return false; + } + } else if (!tenantId.equals(other.tenantId)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortWrapper.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortWrapper.java new file mode 100644 index 00000000000..be7817f6064 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortWrapper.java @@ -0,0 +1,70 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +public class NeutronPortWrapper { + + private NeutronPort port; + + public NeutronPortWrapper() { + } + + public NeutronPortWrapper(final NeutronPort port) { + this.port = port; + } + + public NeutronPort getPort() { + return port; + } + + public void setPort(final NeutronPort port) { + this.port = port; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (port == null ? 0 : port.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + NeutronPortWrapper other = (NeutronPortWrapper) obj; + if (port == null) { + if (other.port != null) { + return false; + } + } else if (!port.equals(other.port)) { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortsList.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortsList.java new file mode 100644 index 00000000000..1e6518c91e7 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/model/NeutronPortsList.java @@ -0,0 +1,42 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.model; + +import java.util.List; + +public class NeutronPortsList { + + private List ports; + + public NeutronPortsList() { + } + + public NeutronPortsList(final List ports) { + this.ports = ports; + } + + public List getPorts() { + return ports; + } + + public void setPorts(final List ports) { + this.ports = ports; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/Action.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/Action.java new file mode 100644 index 00000000000..fb764bad4a8 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/Action.java @@ -0,0 +1,287 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.resources; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.cloudstack.network.opendaylight.api.NeutronInvalidCredentialsException; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApi; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestFactory; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.log4j.Logger; + +public abstract class Action { + + private static final Logger s_logger = Logger.getLogger(Action.class); + private static final int BODY_RESP_MAX_LEN = 1024; + + // private static final String DEFAULT + + protected static final String TEXT_HTML_CONTENT_TYPE = "text/html"; + protected static final String JSON_CONTENT_TYPE = "application/json"; + protected static final String CONTENT_TYPE = "Content-Type"; + + private final URL url; + private final String username; + private final String password; + + public Action(final URL url, final String username, final String password) { + + this.url = url; + this.username = username; + this.password = password; + } + + public String executeGet(final String uri, final Map parameters) throws NeutronRestApiException { + try { + validateCredentials(); + } catch (NeutronInvalidCredentialsException e) { + throw new NeutronRestApiException("Invalid credentials!", e); + } + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi neutronRestApi = factory.getNeutronApi(GetMethod.class); + GetMethod getMethod = (GetMethod) neutronRestApi.createMethod(url, uri); + + try { + getMethod.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); + + String encodedCredentials = encodeCredentials(); + getMethod.setRequestHeader("Authorization", "Basic " + encodedCredentials); + + if (parameters != null && !parameters.isEmpty()) { + List nameValuePairs = new ArrayList(parameters.size()); + for (Entry e : parameters.entrySet()) { + nameValuePairs.add(new NameValuePair(e.getKey(), e.getValue())); + } + getMethod.setQueryString(nameValuePairs.toArray(new NameValuePair[0])); + } + + neutronRestApi.executeMethod(getMethod); + + if (getMethod.getStatusCode() != HttpStatus.SC_OK) { + String errorMessage = responseToErrorMessage(getMethod); + getMethod.releaseConnection(); + s_logger.error("Failed to retrieve object : " + errorMessage); + throw new NeutronRestApiException("Failed to retrieve object : " + errorMessage); + } + + return getMethod.getResponseBodyAsString(); + + } catch (NeutronRestApiException e) { + s_logger.error("NeutronRestApiException caught while trying to execute HTTP Method on the Neutron Controller", e); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } catch (IOException e) { + throw new NeutronRestApiException(e); + } finally { + getMethod.releaseConnection(); + } + } + + protected String executePost(final String uri, final StringRequestEntity entity) throws NeutronRestApiException { + try { + validateCredentials(); + } catch (NeutronInvalidCredentialsException e) { + throw new NeutronRestApiException("Invalid credentials!", e); + } + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi neutronRestApi = factory.getNeutronApi(PostMethod.class); + PostMethod postMethod = (PostMethod) neutronRestApi.createMethod(url, uri); + + try { + postMethod.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); + postMethod.setRequestEntity(entity); + + String encodedCredentials = encodeCredentials(); + postMethod.setRequestHeader("Authorization", "Basic " + encodedCredentials); + + neutronRestApi.executeMethod(postMethod); + + if (postMethod.getStatusCode() != HttpStatus.SC_CREATED) { + String errorMessage = responseToErrorMessage(postMethod); + postMethod.releaseConnection(); + s_logger.error("Failed to create object : " + errorMessage); + throw new NeutronRestApiException("Failed to create object : " + errorMessage); + } + + return postMethod.getResponseBodyAsString(); + } catch (NeutronRestApiException e) { + s_logger.error("NeutronRestApiException caught while trying to execute HTTP Method on the Neutron Controller", e); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } catch (IOException e) { + throw new NeutronRestApiException("Failed to load json response body", e); + } finally { + postMethod.releaseConnection(); + } + } + + protected void executePut(final String uri, final StringRequestEntity entity) throws NeutronRestApiException { + try { + validateCredentials(); + } catch (NeutronInvalidCredentialsException e) { + throw new NeutronRestApiException("Invalid credentials!", e); + } + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi neutronRestApi = factory.getNeutronApi(PutMethod.class); + PutMethod putMethod = (PutMethod) neutronRestApi.createMethod(url, uri); + + try { + putMethod.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); + putMethod.setRequestEntity(entity); + + String encodedCredentials = encodeCredentials(); + putMethod.setRequestHeader("Authorization", "Basic " + encodedCredentials); + + neutronRestApi.executeMethod(putMethod); + + if (putMethod.getStatusCode() != HttpStatus.SC_OK) { + String errorMessage = responseToErrorMessage(putMethod); + putMethod.releaseConnection(); + s_logger.error("Failed to update object : " + errorMessage); + throw new NeutronRestApiException("Failed to create object : " + errorMessage); + } + } catch (NeutronRestApiException e) { + s_logger.error("NeutronRestApiException caught while trying to execute HTTP Method on the Neutron Controller", e); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } finally { + putMethod.releaseConnection(); + } + } + + protected String executePut(final String uri) throws NeutronRestApiException { + try { + validateCredentials(); + } catch (NeutronInvalidCredentialsException e) { + throw new NeutronRestApiException("Invalid credentials!", e); + } + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi neutronRestApi = factory.getNeutronApi(PutMethod.class); + PutMethod putMethod = (PutMethod) neutronRestApi.createMethod(url, uri); + + try { + String encodedCredentials = encodeCredentials(); + putMethod.setRequestHeader("Authorization", "Basic " + encodedCredentials); + + neutronRestApi.executeMethod(putMethod); + + if (putMethod.getStatusCode() != HttpStatus.SC_OK) { + String errorMessage = responseToErrorMessage(putMethod); + putMethod.releaseConnection(); + s_logger.error("Failed to update object : " + errorMessage); + throw new NeutronRestApiException("Failed to create object : " + errorMessage); + } + + return putMethod.getResponseBodyAsString(); + } catch (NeutronRestApiException e) { + s_logger.error("NeutronRestApiException caught while trying to execute HTTP Method on the Neutron Controller", e); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } catch (IOException e) { + throw new NeutronRestApiException("Failed to load json response body", e); + } finally { + putMethod.releaseConnection(); + } + } + + protected void executeDelete(final String uri) throws NeutronRestApiException { + try { + validateCredentials(); + } catch (NeutronInvalidCredentialsException e) { + throw new NeutronRestApiException("Invalid credentials!", e); + } + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi neutronRestApi = factory.getNeutronApi(DeleteMethod.class); + DeleteMethod deleteMethod = (DeleteMethod) neutronRestApi.createMethod(url, uri); + + try { + deleteMethod.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE); + + String encodedCredentials = encodeCredentials(); + deleteMethod.setRequestHeader("Authorization", "Basic " + encodedCredentials); + + neutronRestApi.executeMethod(deleteMethod); + + if (deleteMethod.getStatusCode() != HttpStatus.SC_NO_CONTENT) { + String errorMessage = responseToErrorMessage(deleteMethod); + deleteMethod.releaseConnection(); + s_logger.error("Failed to update object : " + errorMessage); + throw new NeutronRestApiException("Failed to create object : " + errorMessage); + } + } catch (NeutronRestApiException e) { + s_logger.error("NeutronRestApiException caught while trying to execute HTTP Method on the Neutron Controller", e); + throw new NeutronRestApiException("API call to Neutron Controller Failed", e); + } finally { + deleteMethod.releaseConnection(); + } + } + + private void validateCredentials() throws NeutronInvalidCredentialsException { + if (username == null || username.isEmpty() || password == null || password.isEmpty()) { + throw new NeutronInvalidCredentialsException("Credentials are null or empty"); + } + } + + private String encodeCredentials() { + String authString = username + ":" + password; + byte[] authEncBytes = Base64.encodeBase64(authString.getBytes()); + String authStringEnc = new String(authEncBytes); + return authStringEnc; + } + + private String responseToErrorMessage(final HttpMethodBase method) { + assert method.isRequestSent() : "no use getting an error message unless the request is sent"; + + if (TEXT_HTML_CONTENT_TYPE.equals(method.getResponseHeader(CONTENT_TYPE).getValue())) { + // The error message is the response content + // Safety margin of 1024 characters, anything longer is probably + // useless and will clutter the logs + try { + return method.getResponseBodyAsString(BODY_RESP_MAX_LEN); + } catch (IOException e) { + s_logger.debug("Error while loading response body", e); + } + } + + // The default + return method.getStatusText(); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNetworksNorthboundAction.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNetworksNorthboundAction.java new file mode 100644 index 00000000000..3a8c187e11f --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNetworksNorthboundAction.java @@ -0,0 +1,113 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.resources; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URL; +import java.text.MessageFormat; +import java.util.Collections; + +import org.apache.commons.httpclient.methods.StringRequestEntity; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.enums.NeutronNorthboundEnum; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetwork; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworkWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworksList; + +public class NeutronNetworksNorthboundAction extends Action { + + private final Gson gsonNeutronNetwork; + + public NeutronNetworksNorthboundAction(final URL url, final String username, final String password) { + super(url, username, password); + gsonNeutronNetwork = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + } + + @SuppressWarnings("unchecked") + public T listAllNetworks() throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NETWORKS_URI.getUri(); + String bodystring = executeGet(uri, Collections. emptyMap()); + + Type returnType = new TypeToken>() { + }.getType(); + + T returnValue = (T) gsonNeutronNetwork.fromJson(bodystring, returnType); + + return returnValue; + } + + @SuppressWarnings("unchecked") + public T findNetworkById(final String networkId) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NETWORK_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, networkId); + + String bodystring = executeGet(uri, Collections. emptyMap()); + + Type returnType = new TypeToken() { + }.getType(); + + T returnValue = (T) gsonNeutronNetwork.fromJson(bodystring, returnType); + + return returnValue; + } + + @SuppressWarnings("unchecked") + public T createNeutronNetwork(final NeutronNetworkWrapper newNetworkWrapper) throws NeutronRestApiException { + try { + String uri = NeutronNorthboundEnum.NETWORKS_URI.getUri(); + StringRequestEntity entity = new StringRequestEntity(gsonNeutronNetwork.toJson(newNetworkWrapper), JSON_CONTENT_TYPE, null); + + String bodystring = executePost(uri, entity); + + T result = (T) gsonNeutronNetwork.fromJson(bodystring, TypeToken.get(NeutronNetworkWrapper.class).getType()); + + return result; + } catch (UnsupportedEncodingException e) { + throw new NeutronRestApiException("Failed to encode json request body", e); + } + } + + public void updateNeutronNetwork(final String networkId, final NeutronNetworkWrapper newNetworkWrapper) throws NeutronRestApiException { + try { + String uri = NeutronNorthboundEnum.NETWORK_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, networkId); + + StringRequestEntity entity = new StringRequestEntity(gsonNeutronNetwork.toJson(newNetworkWrapper), JSON_CONTENT_TYPE, null); + + executePut(uri, entity); + } catch (UnsupportedEncodingException e) { + throw new NeutronRestApiException("Failed to encode json request body", e); + } + } + + public void deleteNeutronNetwork(final String networkId) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NETWORK_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, networkId); + + executeDelete(uri); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNodesNorthboundAction.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNodesNorthboundAction.java new file mode 100644 index 00000000000..891ca162cb1 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronNodesNorthboundAction.java @@ -0,0 +1,89 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.resources; + +import java.lang.reflect.Type; +import java.net.URL; +import java.text.MessageFormat; +import java.util.Collections; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.enums.NeutronNorthboundEnum; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodeWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodesList; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +public class NeutronNodesNorthboundAction extends Action { + + private final Gson gsonNeutronNode; + + public NeutronNodesNorthboundAction(final URL url, final String username, final String password) { + super(url, username, password); + gsonNeutronNode = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + } + + @SuppressWarnings("unchecked") + public T listAllNodes() throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.PORTS_URI.getUri(); + String bodystring = executeGet(uri, Collections. emptyMap()); + + Type returnType = new TypeToken>() { + }.getType(); + + T returnValue = (T) gsonNeutronNode.fromJson(bodystring, returnType); + + return returnValue; + } + + public void deleteNode(final String nodeType, final String nodeId) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NETWORK_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, nodeType, nodeId); + + executeDelete(uri); + } + + @SuppressWarnings("unchecked") + public T updateNeutronNodeV1(final String nodeId, final String ipAddress, final int port) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NODE_PORT_PER_NODE_URI.getUri(); + uri = MessageFormat.format(uri, nodeId, ipAddress, String.valueOf(port)); + + String bodystring = executePut(uri); + + T result = (T) gsonNeutronNode.fromJson(bodystring, TypeToken.get(NeutronNodeWrapper.class).getType()); + + return result; + } + + @SuppressWarnings("unchecked") + public T updateNeutronNodeV2(final String nodeType, final String nodeId, final String ipAddress, final int port) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.NODE_PORT_PER_TYPE_URI.getUri(); + uri = MessageFormat.format(uri, nodeType, nodeId, ipAddress, String.valueOf(port)); + + String bodystring = executePut(uri); + + T result = (T) gsonNeutronNode.fromJson(bodystring, TypeToken.get(NeutronNodeWrapper.class).getType()); + + return result; + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronPortsNorthboundAction.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronPortsNorthboundAction.java new file mode 100644 index 00000000000..6ff16918286 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/resources/NeutronPortsNorthboundAction.java @@ -0,0 +1,111 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.resources; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.URL; +import java.text.MessageFormat; +import java.util.Collections; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.enums.NeutronNorthboundEnum; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortsList; +import org.apache.commons.httpclient.methods.StringRequestEntity; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +public class NeutronPortsNorthboundAction extends Action { + + private final Gson gsonNeutronPort; + + public NeutronPortsNorthboundAction(final URL url, final String username, final String password) { + super(url, username, password); + gsonNeutronPort = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + } + + @SuppressWarnings("unchecked") + public T listAllPorts() throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.PORTS_URI.getUri(); + String bodystring = executeGet(uri, Collections. emptyMap()); + + Type returnType = new TypeToken>() { + }.getType(); + + T returnValue = (T) gsonNeutronPort.fromJson(bodystring, returnType); + + return returnValue; + } + + @SuppressWarnings("unchecked") + public T findPortById(final String portId) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.PORTS_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, portId); + + String bodystring = executeGet(uri, Collections. emptyMap()); + + Type returnType = new TypeToken() { + }.getType(); + + T returnValue = (T) gsonNeutronPort.fromJson(bodystring, returnType); + + return returnValue; + } + + @SuppressWarnings("unchecked") + public T createNeutronPort(final NeutronPortWrapper newPortWrapper) throws NeutronRestApiException { + try { + String uri = NeutronNorthboundEnum.PORTS_URI.getUri(); + StringRequestEntity entity = new StringRequestEntity(gsonNeutronPort.toJson(newPortWrapper), JSON_CONTENT_TYPE, null); + + String bodystring = executePost(uri, entity); + + T result = (T) gsonNeutronPort.fromJson(bodystring, TypeToken.get(NeutronPortWrapper.class).getType()); + + return result; + } catch (UnsupportedEncodingException e) { + throw new NeutronRestApiException("Failed to encode json request body", e); + } + } + + public void updateNeutronPort(final String portId, final NeutronPortWrapper newPortWrapper) throws NeutronRestApiException { + try { + String uri = NeutronNorthboundEnum.PORTS_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, portId); + + StringRequestEntity entity = new StringRequestEntity(gsonNeutronPort.toJson(newPortWrapper), JSON_CONTENT_TYPE, null); + + executePut(uri, entity); + } catch (UnsupportedEncodingException e) { + throw new NeutronRestApiException("Failed to encode json request body", e); + } + } + + public void deleteNeutronPort(final String portId) throws NeutronRestApiException { + String uri = NeutronNorthboundEnum.PORTS_PARAM_URI.getUri(); + uri = MessageFormat.format(uri, portId); + + executeDelete(uri); + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/responses/OpenDaylightControllerResponse.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/responses/OpenDaylightControllerResponse.java new file mode 100644 index 00000000000..10c6f2838ca --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/api/responses/OpenDaylightControllerResponse.java @@ -0,0 +1,73 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.responses; + +import com.google.gson.annotations.SerializedName; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; +import org.apache.cloudstack.network.opendaylight.dao.OpenDaylightControllerVO; + +import com.cloud.serializer.Param; + +@EntityReference(value = OpenDaylightControllerVO.class) +public class OpenDaylightControllerResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) + @Param(description = "device id of the controller") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) + @Param(description = "the physical network to which this controller belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.NAME) + @Param(description = "the name assigned to the controller") + private String name; + + @SerializedName(ApiConstants.URL) + @Param(description = "the url of the controller api") + private String url; + + @SerializedName(ApiConstants.USERNAME) + @Param(description = "the username to authenticate to the controller") + private String username; + + public void setId(String id) { + this.id = id; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setName(String name) { + this.name = name; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setUsername(String username) { + this.username = username; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDao.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDao.java new file mode 100644 index 00000000000..87999821ef8 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDao.java @@ -0,0 +1,28 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDao; + +public interface OpenDaylightControllerMappingDao extends GenericDao { + List listByPhysicalNetwork(long physicalNetworkId); +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDaoImpl.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDaoImpl.java new file mode 100644 index 00000000000..1d0becaef56 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerMappingDaoImpl.java @@ -0,0 +1,45 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.dao; + +import java.util.List; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; + +public class OpenDaylightControllerMappingDaoImpl extends GenericDaoBase implements OpenDaylightControllerMappingDao { + private SearchBuilder physicalNetworkIdSearch; + + public OpenDaylightControllerMappingDaoImpl() { + physicalNetworkIdSearch = createSearchBuilder(); + physicalNetworkIdSearch.and("physicalNetworkId", physicalNetworkIdSearch.entity().getPhysicalNetworkId(), Op.EQ); + physicalNetworkIdSearch.done(); + } + + @Override + public List listByPhysicalNetwork(long physicalNetworkId) { + SearchCriteria sc = physicalNetworkIdSearch.create(); + sc.setParameters("physicalNetworkId", physicalNetworkId); + return search(sc, null); + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerVO.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerVO.java new file mode 100644 index 00000000000..b50cfda0e50 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/dao/OpenDaylightControllerVO.java @@ -0,0 +1,100 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.dao; + +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.apache.cloudstack.api.InternalIdentity; + +@Entity +@Table(name = "external_opendaylight_controllers") +public class OpenDaylightControllerVO implements InternalIdentity { + private static final long serialVersionUID = -575928081553194369L; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "physical_network_id") + private long physicalNetworkId; + + @Column(name = "provider_name") + private String providerName; + + @Column(name = "device_name") + private String deviceName; + + public OpenDaylightControllerVO() { + uuid = UUID.randomUUID().toString(); + } + + public OpenDaylightControllerVO(final long hostId, final long physicalNetworkId, final String providerName, final String deviceName) { + super(); + this.hostId = hostId; + this.physicalNetworkId = physicalNetworkId; + this.providerName = providerName; + this.deviceName = deviceName; + uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public long getHostId() { + return hostId; + } + + public String getProviderName() { + return providerName; + } + + public String getDeviceName() { + return deviceName; + } + +} diff --git a/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/module.properties b/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/module.properties new file mode 100644 index 00000000000..36d2331db5b --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/module.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +name=opendaylight +parent=network diff --git a/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/spring-opendaylight-context.xml b/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/spring-opendaylight-context.xml new file mode 100644 index 00000000000..76b6a920c70 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/main/resources/META-INF/cloudstack/opendaylight/spring-opendaylight-context.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronEnumsTest.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronEnumsTest.java new file mode 100644 index 00000000000..1edf93b7b64 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronEnumsTest.java @@ -0,0 +1,85 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import java.text.MessageFormat; + +import junit.framework.Assert; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.enums.NeutronNorthboundEnum; +import org.junit.Test; + +public class NeutronEnumsTest { + + @Test + public void enumsUrlFormatTest1() throws NeutronRestApiException { + String netUrl = NeutronNorthboundEnum.NETWORK_PARAM_URI.getUri(); + netUrl = MessageFormat.format(netUrl, netId); + + Assert.assertEquals(NETWORK_PARAM_URI, netUrl); + } + + @Test + public void enumsUrlFormatTest2() throws NeutronRestApiException { + String portUrl = NeutronNorthboundEnum.PORTS_PARAM_URI.getUri(); + portUrl = MessageFormat.format(portUrl, portId); + + Assert.assertEquals(PORTS_PARAM_URI, portUrl); + } + + @Test + public void enumsUrlFormatTest3() throws NeutronRestApiException { + String nodedelUrl = NeutronNorthboundEnum.NODE_PARAM_URI.getUri(); + nodedelUrl = MessageFormat.format(nodedelUrl, "test", nodeId); + + Assert.assertEquals(NODE_PARAM_URI, nodedelUrl); + } + + @Test + public void enumsUrlFormatTest4() throws NeutronRestApiException { + String nodeV1Url = NeutronNorthboundEnum.NODE_PORT_PER_NODE_URI.getUri(); + nodeV1Url = MessageFormat.format(nodeV1Url, nodeId, ip, String.valueOf(port)); + + Assert.assertEquals(NODE_PORT_PER_NODE_URI, nodeV1Url); + } + + @Test + public void enumsUrlFormatTest5() throws NeutronRestApiException { + String nodeV2Url = NeutronNorthboundEnum.NODE_PORT_PER_TYPE_URI.getUri(); + nodeV2Url = MessageFormat.format(nodeV2Url, "test", nodeId, ip, String.valueOf(port)); + + Assert.assertEquals(NODE_PORT_PER_TYPE_URI, nodeV2Url); + } + + static String NETWORK_PARAM_URI = "/controller/nb/v2/neutron/networks/0AACEED5-A688-429A-92FC-E1C9E4EEEE98"; + + static String PORTS_PARAM_URI = "/controller/nb/v2/neutron/ports/F4267875-0C85-4829-8434-901A08691C6E"; + + static String NODE_PARAM_URI = "/controller/nb/v2/connectionmanager/node/test/ca31aa7f-84c7-416d-bc00-1f84927367e0"; + static String NODE_PORT_PER_NODE_URI = "/controller/nb/v2/connectionmanager/node/ca31aa7f-84c7-416d-bc00-1f84927367e0/address/1.1.1.1/port/6400"; + static String NODE_PORT_PER_TYPE_URI = "/controller/nb/v2/connectionmanager/node/test/ca31aa7f-84c7-416d-bc00-1f84927367e0/address/1.1.1.1/port/6400"; + + static String netId = "0AACEED5-A688-429A-92FC-E1C9E4EEEE98"; + static String portId = "F4267875-0C85-4829-8434-901A08691C6E"; + static String nodeId = "ca31aa7f-84c7-416d-bc00-1f84927367e0"; + static String ip = "1.1.1.1"; + static int port = 6400; +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNetworkAdapterTest.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNetworkAdapterTest.java new file mode 100644 index 00000000000..9d0adad03ea --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNetworkAdapterTest.java @@ -0,0 +1,88 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.junit.Test; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetwork; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworkWrapper; + +public class NeutronNetworkAdapterTest { + + private final Gson gsonNeutronNetwork = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + + @Test + public void gsonNeutronNetworkMarshalingTest() throws NeutronRestApiException { + NeutronNetwork network = new NeutronNetwork(); + network.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + network.setName("test_gre"); + network.setNetworkType("test"); + network.setSegmentationId(1001); + network.setShared(true); + network.setTenantId("wilder"); + + NeutronNetworkWrapper networkWrapper = new NeutronNetworkWrapper(); + networkWrapper.setNetwork(network); + + StringRequestEntity entity; + try { + entity = new StringRequestEntity(gsonNeutronNetwork.toJson(networkWrapper), "application/json", null); + + String actual = entity.getContent(); + Assert.assertEquals(jsonString, actual); + } catch (UnsupportedEncodingException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void gsonNeutronNetworkUnmarshalingTest() throws NeutronRestApiException { + NeutronNetwork network = new NeutronNetwork(); + network.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + network.setName("test_gre"); + network.setNetworkType("test"); + network.setSegmentationId(1001); + network.setShared(true); + network.setTenantId("wilder"); + + NeutronNetworkWrapper networkWrapper = new NeutronNetworkWrapper(); + networkWrapper.setNetwork(network); + + NeutronNetworkWrapper returnValue = (NeutronNetworkWrapper) gsonNeutronNetwork.fromJson(jsonString, TypeToken.get(networkWrapper.getClass()).getType()); + + Assert.assertNotNull(returnValue); + Assert.assertEquals("test_gre", returnValue.getNetwork().getName()); + } + + static String jsonString = "{\"network\":{\"id\":\"ca31aa7f-84c7-416d-bc00-1f84927367e0\",\"name\":" + + "\"test_gre\",\"shared\":true,\"tenant_id\":\"wilder\",\"provider:network_type\":\"test\",\"provider:segmentation_id\":1001}}"; +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNodeAdapterTest.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNodeAdapterTest.java new file mode 100644 index 00000000000..d7437fe550c --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronNodeAdapterTest.java @@ -0,0 +1,74 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import java.io.UnsupportedEncodingException; + +import junit.framework.Assert; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNode; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodeWrapper; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.junit.Test; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +public class NeutronNodeAdapterTest { + + private final Gson gsonNeutronNode = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + + @Test + public void gsonNeutronPortMarshalingTest() throws NeutronRestApiException { + NeutronNode node = new NeutronNode("node-test", "test"); + NeutronNodeWrapper nodeWrapper = new NeutronNodeWrapper(node); + + StringRequestEntity entity; + try { + entity = new StringRequestEntity(gsonNeutronNode.toJson(nodeWrapper), "application/json", null); + + String actual = entity.getContent(); + Assert.assertEquals(jsonString, actual); + } catch (UnsupportedEncodingException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void gsonNeutronPortUnmarshalingTest() throws NeutronRestApiException { + NeutronNodeWrapper returnValue = (NeutronNodeWrapper) gsonNeutronNode.fromJson(jsonString, TypeToken.get(NeutronNodeWrapper.class).getType()); + + Assert.assertNotNull(returnValue); + Assert.assertEquals("node-test", returnValue.getNode().getId().toString()); + } + + @Test + public void gsonNeutronPortUnmarshalingNullTest() throws NeutronRestApiException { + String json = null; + NeutronNodeWrapper returnValue = (NeutronNodeWrapper) gsonNeutronNode.fromJson(json, TypeToken.get(NeutronNodeWrapper.class).getType()); + + Assert.assertNull(returnValue); + } + + static String jsonString = "{\"node\":{\"id\":\"node-test\",\"type\":\"test\"}}"; +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronPortAdapterTest.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronPortAdapterTest.java new file mode 100644 index 00000000000..4d88344893c --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronPortAdapterTest.java @@ -0,0 +1,95 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPort; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortWrapper; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.junit.Test; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +public class NeutronPortAdapterTest { + + private final Gson gsonNeutronPort = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); + + @Test + public void gsonNeutronPortMarshalingTest() throws NeutronRestApiException { + NeutronPort port = new NeutronPort(); + + port.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setName("test_gre"); + port.setAdminStateUp(true); + port.setDeviceId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setMacAddress("ca31aa7f-84c7-416d-bc00-1f84927367e0"); + port.setNetworkId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setStatus("ACTIVE"); + port.setTenantId("wilder"); + + NeutronPortWrapper portWrapper = new NeutronPortWrapper(); + portWrapper.setPort(port); + + StringRequestEntity entity; + try { + entity = new StringRequestEntity(gsonNeutronPort.toJson(portWrapper), "application/json", null); + + String actual = entity.getContent(); + + Assert.assertEquals(jsonString, actual); + } catch (UnsupportedEncodingException e) { + Assert.fail(e.getMessage()); + } + } + + @Test + public void gsonNeutronPortUnmarshalingTest() throws NeutronRestApiException { + NeutronPort port = new NeutronPort(); + + port.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setName("test_gre"); + port.setAdminStateUp(true); + port.setDeviceId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setMacAddress("ca31aa7f-84c7-416d-bc00-1f84927367e0"); + port.setNetworkId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + port.setStatus("ACTIVE"); + port.setTenantId("wilder"); + + NeutronPortWrapper portWrapper = new NeutronPortWrapper(); + portWrapper.setPort(port); + + NeutronPortWrapper returnValue = (NeutronPortWrapper) gsonNeutronPort.fromJson(jsonString, TypeToken.get(portWrapper.getClass()).getType()); + + Assert.assertNotNull(returnValue); + Assert.assertEquals("ca31aa7f-84c7-416d-bc00-1f84927367e0", returnValue.getPort().getMacAddress()); + } + + static String jsonString = "{\"port\":{\"id\":\"ca31aa7f-84c7-416d-bc00-1f84927367e0\",\"name\":\"test_gre\",\"tenant_id\":\"wilder\",\"network_id\":" + + "\"ca31aa7f-84c7-416d-bc00-1f84927367e0\",\"mac_address\":\"ca31aa7f-84c7-416d-bc00-1f84927367e0\",\"device_id\":\"ca31aa7f-84c7-416d-bc00-1f84927367e0\"," + + "\"admin_state_up\":true,\"status\":\"ACTIVE\"}}"; +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiIT.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiIT.java new file mode 100644 index 00000000000..89f4b41c202 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiIT.java @@ -0,0 +1,95 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import junit.framework.Assert; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworkWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworksList; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodeWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNodesList; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortWrapper; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronPortsList; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNetworksNorthboundAction; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNodesNorthboundAction; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronPortsNorthboundAction; +import org.junit.Test; + +public class NeutronRestApiIT { + + @Test + public void neutronListAllNodes() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://178.237.34.233:8080"); + + NeutronNodesNorthboundAction neutron = new NeutronNodesNorthboundAction(url, "admin", "admin"); + NeutronNodesList results = neutron.listAllNodes(); + + Assert.assertNotNull(results); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + @Test + public void neutronListAllNetworks() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://178.237.34.233:8080"); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + NeutronNetworksList results = neutron.listAllNetworks(); + + Assert.assertNotNull(results); + + List networks = results.getNetworks(); + Assert.assertNotNull(networks); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + @Test + public void neutronListAllPorts() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://178.237.34.233:8080"); + + NeutronPortsNorthboundAction neutron = new NeutronPortsNorthboundAction(url, "admin", "admin"); + NeutronPortsList results = neutron.listAllPorts(); + + Assert.assertNotNull(results); + + List networks = results.getPorts(); + Assert.assertNotNull(networks); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } +} \ No newline at end of file diff --git a/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiTest.java b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiTest.java new file mode 100644 index 00000000000..aee15b35e95 --- /dev/null +++ b/plugins/network-elements/opendaylight/src/test/java/org/apache/cloudstack/network/opendaylight/api/test/NeutronRestApiTest.java @@ -0,0 +1,254 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package org.apache.cloudstack.network.opendaylight.api.test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApi; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestApiException; +import org.apache.cloudstack.network.opendaylight.api.NeutronRestFactory; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetwork; +import org.apache.cloudstack.network.opendaylight.api.model.NeutronNetworkWrapper; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNetworksNorthboundAction; +import org.apache.cloudstack.network.opendaylight.api.resources.NeutronNodesNorthboundAction; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethodBase; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.junit.Test; + +public class NeutronRestApiTest { + + NeutronRestFactory factory = NeutronRestFactory.getInstance(); + + NeutronRestApi httpGet = factory.getNeutronApi(GetMethod.class); + NeutronRestApi httpPost = factory.getNeutronApi(PostMethod.class); + NeutronRestApi httpPut = factory.getNeutronApi(PutMethod.class); + NeutronRestApi httpDelete = factory.getNeutronApi(DeleteMethod.class); + + @Test + public void resourceHttpGetInstances() throws NeutronRestApiException { + NeutronRestApi newHttpGet = factory.getNeutronApi(GetMethod.class); + assertTrue(httpGet == newHttpGet); + } + + @Test + public void resourceHttpPostInstances() throws NeutronRestApiException { + NeutronRestApi newHttpPost = factory.getNeutronApi(PostMethod.class); + assertTrue(httpPost == newHttpPost); + } + + @Test + public void resourceHttpPutInstances() throws NeutronRestApiException { + NeutronRestApi newHttpPut = factory.getNeutronApi(PutMethod.class); + assertTrue(httpPut == newHttpPut); + } + + @Test + public void resourceHttpDeleteInstances() throws NeutronRestApiException { + NeutronRestApi newHttpDelete = factory.getNeutronApi(DeleteMethod.class); + assertTrue(httpDelete == newHttpDelete); + } + + @Test(expected = NeutronRestApiException.class) + public void neutronNetworksFail() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.listAllNetworks(); + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + @Test(expected = NeutronRestApiException.class) + public void neutronFindNetworkByIdFail() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.findNetworkById("0AACEED5-A688-429A-92FC-E1C9E4EEEE98"); + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + @Test(expected = NeutronRestApiException.class) + public void neutronNodesFail() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNodesNorthboundAction neutron = new NeutronNodesNorthboundAction(url, "admin", "admin"); + neutron.listAllNodes(); + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + /* + * Test fails because there is no controller. It's used only to test that + * the HTTP methods are correct. + */ + @Test(expected = NeutronRestApiException.class) + public void neutronHTTPDeleteMethod() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://127.0.0.1:8080"); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.deleteNeutronNetwork("0AACEED5-A688-429A-92FC-E1C9E4EEEE98"); + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + /* + * Test fails because there is no controller. It's used only to test that + * the HTTP methods are correct. + */ + @Test(expected = NeutronRestApiException.class) + public void neutronHTTPGetMethod() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.listAllNetworks(); + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + /* + * Test fails because there is no controller. It's used only to test that + * the HTTP methods are correct. + */ + @Test(expected = NeutronRestApiException.class) + public void neutronHTTPPostMethod() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNetwork network = new NeutronNetwork(); + network.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + network.setName("test_gre"); + network.setNetworkType("test"); + network.setSegmentationId(1001); + network.setShared(true); + network.setTenantId("wilder"); + + NeutronNetworkWrapper networkWrapper = new NeutronNetworkWrapper(); + networkWrapper.setNetwork(network); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.createNeutronNetwork(networkWrapper); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + /* + * Test fails because there is no controller. It's used only to test that + * the HTTP methods are correct. + */ + @Test(expected = NeutronRestApiException.class) + public void neutronHTTPPutMethod() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNetwork network = new NeutronNetwork(); + network.setId(UUID.fromString("ca31aa7f-84c7-416d-bc00-1f84927367e0")); + network.setName("test_gre"); + network.setNetworkType("test"); + network.setSegmentationId(1001); + network.setShared(true); + network.setTenantId("wilder"); + + NeutronNetworkWrapper networkWrapper = new NeutronNetworkWrapper(); + networkWrapper.setNetwork(network); + + NeutronNetworksNorthboundAction neutron = new NeutronNetworksNorthboundAction(url, "admin", "admin"); + neutron.updateNeutronNetwork("ca31aa7f-84c7-416d-bc00-1f84927367e0", networkWrapper); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + /* + * Test fails because there is no controller. It's used only to test that + * the HTTP methods are correct. + */ + @Test(expected = NeutronRestApiException.class) + public void neutronHTTPPutUriMethod() throws NeutronRestApiException { + URL url; + try { + url = new URL("http://localhost:8080"); + + NeutronNodesNorthboundAction neutron = new NeutronNodesNorthboundAction(url, "admin", "admin"); + neutron.updateNeutronNodeV1("ca31aa7f-84c7-416d-bc00-1f84927367e0", "1.1.1.1.", 6400); + + } catch (MalformedURLException e) { + Assert.fail("Should not fail here."); + } + } + + static String networkJSON = "{" + "\"networks\": [" + "{" + "\"network\": {" + "\"segmentation_id\": 100," + "\"shared\": false," + "\"name\": \"net_test\"," + + "\"network_type\": \"test\"," + "\"tenant_id\": \"t\"," + "\"id\": \"0AACEED5-A688-429A-92FC-E1C9E4EEEE98\"," + "\"status\": \"ACTIVE\"" + "}" + "}" + "]" + "}"; +} + +class NeutronRestApiMock extends NeutronRestApi { + + HttpClient client = mock(HttpClient.class); + + NeutronRestApiMock(final Class httpClazz) { + super(httpClazz); + } + + @Override + public void executeMethod(final HttpMethodBase method) throws NeutronRestApiException { + try { + client.executeMethod(method); + } catch (HttpException e) { + method.releaseConnection(); + throw new NeutronRestApiException("API call to Neutron NVP Controller Failed", e); + } catch (IOException e) { + method.releaseConnection(); + throw new NeutronRestApiException("API call to Neutron NVP Controller Failed", e); + } + } +} \ No newline at end of file diff --git a/plugins/network-elements/stratosphere-ssp/resources/META-INF/cloudstack/ssp/spring-ssp-context.xml b/plugins/network-elements/stratosphere-ssp/resources/META-INF/cloudstack/ssp/spring-ssp-context.xml index 528f3e3e38c..7e8fe6c3e6f 100644 --- a/plugins/network-elements/stratosphere-ssp/resources/META-INF/cloudstack/ssp/spring-ssp-context.xml +++ b/plugins/network-elements/stratosphere-ssp/resources/META-INF/cloudstack/ssp/spring-ssp-context.xml @@ -38,5 +38,4 @@ - diff --git a/plugins/pom.xml b/plugins/pom.xml index 17dd8af6765..85a7bbee3ee 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -32,12 +32,14 @@ deployment-planners/user-concentrated-pod deployment-planners/user-dispersing deployment-planners/implicit-dedication + ha-planners/skip-heurestics host-allocators/random dedicated-resources hypervisors/ovm hypervisors/xen hypervisors/kvm event-bus/rabbitmq + event-bus/inmemory hypervisors/baremetal hypervisors/ucs hypervisors/hyperv @@ -49,6 +51,7 @@ network-elements/bigswitch-vns network-elements/midonet network-elements/stratosphere-ssp + network-elements/opendaylight storage-allocators/random user-authenticators/ldap user-authenticators/md5 diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index e41b437689f..ae217b63595 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -24,8 +24,6 @@ import java.util.UUID; import javax.inject.Inject; -import org.apache.log4j.Logger; - import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; @@ -37,6 +35,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.StorageAction; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; @@ -51,6 +50,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; +import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.storage.ResizeVolumeAnswer; @@ -61,6 +61,7 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.configuration.Config; import com.cloud.exception.StorageUnavailableException; +import com.cloud.host.Host; import com.cloud.host.dao.HostDao; import com.cloud.storage.CreateSnapshotPayload; import com.cloud.storage.DataStoreRole; @@ -68,6 +69,7 @@ import com.cloud.storage.ResizeVolumePayload; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; @@ -148,6 +150,17 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri return null; } + @Override + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { return false; } + + @Override + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) {} + + @Override + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + return volume.getSize(); + } + @Override public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback callback) { String errMsg = null; @@ -269,10 +282,11 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri } CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO); - EndPoint ep = epSelector.select(snapshot); + EndPoint ep = this.epSelector.select(snapshot, StorageAction.TAKESNAPSHOT); Answer answer = null; - if (ep == null) { - String errMsg = "No remote endpoint to send DeleteCommand, check if host or ssvm is down?"; + + if ( ep == null ){ + String errMsg = "No remote endpoint to send createObjectCommand, check if host or ssvm is down?"; s_logger.error(errMsg); answer = new Answer(cmd, false, errMsg); } else { diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java index a8108bddd37..7da36b0cf56 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java @@ -464,8 +464,6 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore HypervisorType hType = null; if (hostPoolRecords.size() > 0) { hType = getHypervisorType(hostPoolRecords.get(0).getHostId()); - } else { - return false; } // Remove the SR associated with the Xenserver @@ -486,7 +484,7 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl implements PrimaryDataStore } } - if (!deleteFlag) { + if (!hostPoolRecords.isEmpty() && !deleteFlag) { throw new CloudRuntimeException("Failed to delete storage pool on host"); } diff --git a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java index 5e5a9340486..1f9a128727e 100644 --- a/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/sample/src/org/apache/cloudstack/storage/datastore/driver/SamplePrimaryDataStoreDriverImpl.java @@ -42,6 +42,9 @@ import org.apache.cloudstack.storage.datastore.DataObjectManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; +import com.cloud.host.Host; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.utils.exception.CloudRuntimeException; @@ -78,6 +81,17 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver return null; } + @Override + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) { return false; } + + @Override + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) {} + + @Override + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + return volume.getSize(); + } + private class CreateVolumeContext extends AsyncRpcContext { private final DataObject volume; diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index d85ed0da820..7d305e0f1b3 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -17,8 +17,10 @@ package org.apache.cloudstack.storage.datastore.driver; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.inject.Inject; @@ -44,8 +46,15 @@ import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataTO; +import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; @@ -53,22 +62,18 @@ import com.cloud.user.AccountDetailVO; import com.cloud.user.AccountDetailsDao; import com.cloud.user.AccountVO; import com.cloud.user.dao.AccountDao; +import com.cloud.utils.exception.CloudRuntimeException; public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { - @Inject - private PrimaryDataStoreDao _storagePoolDao; - @Inject - private StoragePoolDetailsDao _storagePoolDetailsDao; - @Inject - private VolumeDao _volumeDao; - @Inject - private VolumeDetailsDao _volumeDetailsDao; - @Inject - private DataCenterDao _zoneDao; - @Inject - private AccountDao _accountDao; - @Inject - private AccountDetailsDao _accountDetailsDao; + @Inject private PrimaryDataStoreDao _storagePoolDao; + @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + @Inject private VolumeDao _volumeDao; + @Inject private VolumeDetailsDao _volumeDetailsDao; + @Inject private DataCenterDao _zoneDao; + @Inject private AccountDao _accountDao; + @Inject private AccountDetailsDao _accountDetailsDao; + @Inject private ClusterDetailsDao _clusterDetailsDao; + @Inject private HostDao _hostDao; @Override public Map getCapabilities() { @@ -147,25 +152,35 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) { - AccountDetailVO accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.ACCOUNT_ID, String.valueOf(sfAccount.getId())); + AccountDetailVO accountDetail = new AccountDetailVO(csAccountId, + SolidFireUtil.ACCOUNT_ID, + String.valueOf(sfAccount.getId())); - _accountDetailsDao.persist(accountDetails); + _accountDetailsDao.persist(accountDetail); - accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_USERNAME, String.valueOf(sfAccount.getName())); + accountDetail = new AccountDetailVO(csAccountId, + SolidFireUtil.CHAP_INITIATOR_USERNAME, + String.valueOf(sfAccount.getName())); - _accountDetailsDao.persist(accountDetails); + _accountDetailsDao.persist(accountDetail); - accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_INITIATOR_SECRET, String.valueOf(sfAccount.getInitiatorSecret())); + accountDetail = new AccountDetailVO(csAccountId, + SolidFireUtil.CHAP_INITIATOR_SECRET, + String.valueOf(sfAccount.getInitiatorSecret())); - _accountDetailsDao.persist(accountDetails); + _accountDetailsDao.persist(accountDetail); - accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_USERNAME, sfAccount.getName()); + accountDetail = new AccountDetailVO(csAccountId, + SolidFireUtil.CHAP_TARGET_USERNAME, + sfAccount.getName()); - _accountDetailsDao.persist(accountDetails); + _accountDetailsDao.persist(accountDetail); - accountDetails = new AccountDetailVO(csAccountId, SolidFireUtil.CHAP_TARGET_SECRET, sfAccount.getTargetSecret()); + accountDetail = new AccountDetailVO(csAccountId, + SolidFireUtil.CHAP_TARGET_SECRET, + sfAccount.getTargetSecret()); - _accountDetailsDao.persist(accountDetails); + _accountDetailsDao.persist(accountDetail); } private class ChapInfoImpl implements ChapInfo { @@ -214,26 +229,276 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { String chapInitiatorSecret = accountDetail.getValue(); - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(volumeInfo.getPoolId(), SolidFireUtil.USE_MUTUAL_CHAP_FOR_VMWARE); + accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME); - boolean useMutualChapForVMware = new Boolean(storagePoolDetail.getValue()); + String chapTargetUsername = accountDetail.getValue(); - String chapTargetUsername = null; - String chapTargetSecret = null; + accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET); - if (useMutualChapForVMware) { - accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME); - - chapTargetUsername = accountDetail.getValue(); - - accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET); - - chapTargetSecret = accountDetail.getValue(); - } + String chapTargetSecret = accountDetail.getValue(); return new ChapInfoImpl(chapInitiatorUsername, chapInitiatorSecret, chapTargetUsername, chapTargetSecret); } + // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) + // if the VAG exists + // update the VAG to contain all IQNs of the hosts (ModifyVolumeAccessGroup) + // if the ID of volumeInfo in not in the VAG, add it (ModifyVolumeAccessGroup) + // if the VAG doesn't exist, create it with the IQNs of the hosts and the ID of volumeInfo (CreateVolumeAccessGroup) + @Override + public boolean connectVolumeToHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) + { + if (volumeInfo == null || host == null || dataStore == null) { + return false; + } + + long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); + long clusterId = host.getClusterId(); + long storagePoolId = dataStore.getId(); + + ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, getVagKey(storagePoolId)); + + String vagId = clusterDetail != null ? clusterDetail.getValue() : null; + + List hosts = _hostDao.findByClusterId(clusterId); + + if (!hostsSupport_iScsi(hosts)) { + return false; + } + + SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); + + if (vagId != null) { + SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId)); + + String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts)); + long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); + + SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), + hostIqns, volumeIds); + } + else { + long lVagId; + + try { + lVagId = SolidFireUtil.createSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), "CloudStack-" + UUID.randomUUID().toString(), + getIqnsFromHosts(hosts), new long[] { sfVolumeId }); + } + catch (Exception ex) { + String iqnInVagAlready = "Exceeded maximum number of Volume Access Groups per initiator"; + + if (!ex.getMessage().contains(iqnInVagAlready)) { + throw new CloudRuntimeException(ex.getMessage()); + } + + // getCompatibleVag throws an exception if an existing VAG can't be located + SolidFireUtil.SolidFireVag sfVag = getCompatibleVag(hosts, sfConnection); + + long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, true); + + SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), + sfVag.getInitiators(), volumeIds); + + lVagId = sfVag.getId(); + } + + clusterDetail = new ClusterDetailsVO(clusterId, getVagKey(storagePoolId), String.valueOf(lVagId)); + + _clusterDetailsDao.persist(clusterDetail); + } + + return true; + } + + // this method takes in a collection of hosts and tries to find an existing VAG that has all three of them in it + // if successful, the VAG is returned; else, a CloudRuntimeException is thrown and this issue should be corrected by an admin + private SolidFireUtil.SolidFireVag getCompatibleVag(List hosts, SolidFireConnection sfConnection) { + List sfVags = SolidFireUtil.getAllSolidFireVags(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword()); + + if (sfVags != null) { + List hostIqns = new ArrayList(); + + // where the method we're in is called, hosts should not be null + for (HostVO host : hosts) { + // where the method we're in is called, host.getStorageUrl() should not be null (it actually should start with "iqn") + hostIqns.add(host.getStorageUrl().toLowerCase()); + } + + for (SolidFireUtil.SolidFireVag sfVag : sfVags) { + List lstInitiators = getStringArrayAsLowerCaseStringList(sfVag.getInitiators()); + + // lstInitiators should not be returned from getStringArrayAsLowerCaseStringList as null + if (lstInitiators.containsAll(hostIqns)) { + return sfVag; + } + } + } + + throw new CloudRuntimeException("Unable to locate the appropriate SolidFire Volume Access Group"); + } + + private List getStringArrayAsLowerCaseStringList(String[] aString) { + List lstLowerCaseString = new ArrayList(); + + if (aString != null) { + for (String str : aString) { + if (str != null) { + lstLowerCaseString.add(str.toLowerCase()); + } + } + } + + return lstLowerCaseString; + } + + // get the VAG associated with volumeInfo's cluster, if any (ListVolumeAccessGroups) // might not exist if using CHAP + // if the VAG exists + // remove the ID of volumeInfo from the VAG (ModifyVolumeAccessGroup) + @Override + public void disconnectVolumeFromHost(VolumeInfo volumeInfo, Host host, DataStore dataStore) + { + if (volumeInfo == null || host == null || dataStore == null) { + return; + } + + long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); + long clusterId = host.getClusterId(); + long storagePoolId = dataStore.getId(); + + ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, getVagKey(storagePoolId)); + + String vagId = clusterDetail != null ? clusterDetail.getValue() : null; + + if (vagId != null) { + List hosts = _hostDao.findByClusterId(clusterId); + + SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); + + SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), Long.parseLong(vagId)); + + String[] hostIqns = getNewHostIqns(sfVag.getInitiators(), getIqnsFromHosts(hosts)); + long[] volumeIds = getNewVolumeIds(sfVag.getVolumeIds(), sfVolumeId, false); + + SolidFireUtil.modifySolidFireVag(sfConnection.getManagementVip(), sfConnection.getManagementPort(), + sfConnection.getClusterAdminUsername(), sfConnection.getClusterAdminPassword(), sfVag.getId(), + hostIqns, volumeIds); + } + } + + private boolean hostsSupport_iScsi(List hosts) { + if (hosts == null || hosts.size() == 0) { + return false; + } + + for (Host host : hosts) { + if (host == null || host.getStorageUrl() == null || host.getStorageUrl().trim().length() == 0 || !host.getStorageUrl().startsWith("iqn")) { + return false; + } + } + + return true; + } + + private String[] getNewHostIqns(String[] currentIqns, String[] newIqns) { + List lstIqns = new ArrayList(); + + if (currentIqns != null) { + for (String currentIqn : currentIqns) { + lstIqns.add(currentIqn); + } + } + + if (newIqns != null) { + for (String newIqn : newIqns) { + if (!lstIqns.contains(newIqn)) { + lstIqns.add(newIqn); + } + } + } + + return lstIqns.toArray(new String[0]); + } + + private long[] getNewVolumeIds(long[] volumeIds, long volumeIdToAddOrRemove, boolean add) { + if (add) { + return getNewVolumeIdsAdd(volumeIds, volumeIdToAddOrRemove); + } + + return getNewVolumeIdsRemove(volumeIds, volumeIdToAddOrRemove); + } + + private long[] getNewVolumeIdsAdd(long[] volumeIds, long volumeIdToAdd) { + List lstVolumeIds = new ArrayList(); + + if (volumeIds != null) { + for (long volumeId : volumeIds) { + lstVolumeIds.add(volumeId); + } + } + + if (lstVolumeIds.contains(volumeIdToAdd)) { + return volumeIds; + } + + lstVolumeIds.add(volumeIdToAdd); + + return convertArray(lstVolumeIds); + } + + private long[] getNewVolumeIdsRemove(long[] volumeIds, long volumeIdToRemove) { + List lstVolumeIds = new ArrayList(); + + if (volumeIds != null) { + for (long volumeId : volumeIds) { + lstVolumeIds.add(volumeId); + } + } + + lstVolumeIds.remove(volumeIdToRemove); + + return convertArray(lstVolumeIds); + } + + private long[] convertArray(List items) { + if (items == null) { + return new long[0]; + } + + long[] outArray = new long[items.size()]; + + for (int i = 0; i < items.size(); i++) { + Long value = items.get(i); + + outArray[i] = value; + } + + return outArray; + } + + private String getVagKey(long storagePoolId) { + return "sfVolumeAccessGroup_" + storagePoolId; + } + + private String[] getIqnsFromHosts(List hosts) { + if (hosts == null || hosts.size() == 0) { + throw new CloudRuntimeException("There do not appear to be any hosts in this cluster."); + } + + List lstIqns = new ArrayList(); + + for (Host host : hosts) { + lstIqns.add(host.getStorageUrl()); + } + + return lstIqns.toArray(new String[0]); + } + private long getDefaultMinIops(long storagePoolId) { StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS); @@ -284,8 +549,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops())); } - long volumeSize = volumeInfo.getSize(); - Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve(); + long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId)); + + long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, + getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, + NumberFormat.getInstance().format(volumeInfo.getSize()), + iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); + + return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); + } + + @Override + public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + long volumeSize = volume.getSize(); + Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); if (hypervisorSnapshotReserve != null) { if (hypervisorSnapshotReserve < 25) { @@ -295,11 +572,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f); } - long sfVolumeId = - SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, - volumeSize, true, NumberFormat.getNumberInstance().format(volumeInfo.getSize().toString()), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); - - return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); + return volumeSize; } private String getSolidFireVolumeName(String strCloudStackVolumeName) { @@ -356,11 +629,12 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } - private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) { + private SolidFireUtil.SolidFireVolume deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection) + { Long storagePoolId = volumeInfo.getPoolId(); if (storagePoolId == null) { - return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it + return null; // this volume was never assigned to a storage pool, so no SAN volume should exist for it } String mVip = sfConnection.getManagementVip(); @@ -370,7 +644,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); - SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); + return SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId); } private String getSfAccountName(String csAccountUuid, long csAccountId) { @@ -429,7 +703,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long capacityBytes = storagePool.getCapacityBytes(); long usedBytes = storagePool.getUsedBytes(); - usedBytes += volumeInfo.getSize(); + usedBytes += sfVolume.getTotalSize(); storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); @@ -447,6 +721,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(result); } + /* private void deleteSolidFireAccount(long sfAccountId, SolidFireConnection sfConnection) { String mVip = sfConnection.getManagementVip(); int mPort = sfConnection.getManagementPort(); @@ -486,6 +761,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return false; } + */ @Override public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback callback) { @@ -493,31 +769,31 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { if (dataObject.getType() == DataObjectType.VOLUME) { VolumeInfo volumeInfo = (VolumeInfo)dataObject; - AccountVO account = _accountDao.findById(volumeInfo.getAccountId()); - AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID); - long sfAccountId = Long.parseLong(accountDetails.getValue()); + // AccountVO account = _accountDao.findById(volumeInfo.getAccountId()); + // AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID); + // long sfAccountId = Long.parseLong(accountDetails.getValue()); long storagePoolId = dataStore.getId(); SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); - deleteSolidFireVolume(volumeInfo, sfConnection); + SolidFireUtil.SolidFireVolume sfVolume = deleteSolidFireVolume(volumeInfo, sfConnection); _volumeDao.deleteVolumesByInstance(volumeInfo.getId()); - // if (!sfAccountHasVolume(sfAccountId, sfConnection)) { - // // delete the account from the SolidFire SAN - // deleteSolidFireAccount(sfAccountId, sfConnection); + // if (!sfAccountHasVolume(sfAccountId, sfConnection)) { + // // delete the account from the SolidFire SAN + // deleteSolidFireAccount(sfAccountId, sfConnection); // - // // delete the info in the account_details table - // // that's related to the SolidFire account - // _accountDetailsDao.deleteDetails(account.getAccountId()); - // } + // // delete the info in the account_details table + // // that's related to the SolidFire account + // _accountDetailsDao.deleteDetails(account.getAccountId()); + // } StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); long usedBytes = storagePool.getUsedBytes(); - usedBytes -= volumeInfo.getSize(); + usedBytes -= sfVolume != null ? sfVolume.getTotalSize() : 0; storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java index d085ddc00a0..af6a77aef24 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java @@ -171,16 +171,6 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC details.put(SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS, String.valueOf(lClusterDefaultMaxIops)); details.put(SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS, String.valueOf(fClusterDefaultBurstIopsPercentOfMaxIops)); - String useMutualChapForVMware = getValue(SolidFireUtil.USE_MUTUAL_CHAP_FOR_VMWARE, url); - - if (useMutualChapForVMware == null || new Boolean(useMutualChapForVMware)) { - useMutualChapForVMware = Boolean.TRUE.toString(); - } else { - useMutualChapForVMware = Boolean.FALSE.toString(); - } - - details.put(SolidFireUtil.USE_MUTUAL_CHAP_FOR_VMWARE, useMutualChapForVMware); - // this adds a row in the cloud.storage_pool table for this SolidFire cluster return dataStoreHelper.createPrimaryDataStore(parameters); } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 5f26c97b97c..3c457ba8067 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -75,10 +75,10 @@ public class SolidFireUtil { public static final String CHAP_TARGET_USERNAME = "chapTargetUsername"; public static final String CHAP_TARGET_SECRET = "chapTargetSecret"; - public static final String USE_MUTUAL_CHAP_FOR_VMWARE = "useMutualChapForVMware"; - - public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strSfVolumeName, long lSfAccountId, - long lTotalSize, boolean bEnable512e, final String strCloudStackVolumeSize, long lMinIops, long lMaxIops, long lBurstIops) { + public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, + String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e, final String strCloudStackVolumeSize, + long lMinIops, long lMaxIops, long lBurstIops) + { final Gson gson = new GsonBuilder().create(); VolumeToCreate volumeToCreate = @@ -95,27 +95,8 @@ public class SolidFireUtil { return volumeCreateResult.result.volumeID; } - public static void deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) { - final Gson gson = new GsonBuilder().create(); - - VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId); - - String strVolumeToDeleteJson = gson.toJson(volumeToDelete); - - executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); - } - - public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) { - final Gson gson = new GsonBuilder().create(); - - VolumeToPurge volumeToPurge = new VolumeToPurge(lVolumeId); - - String strVolumeToPurgeJson = gson.toJson(volumeToPurge); - - executeJsonRpc(strVolumeToPurgeJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); - } - - public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) { + public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) + { final Gson gson = new GsonBuilder().create(); VolumeToGet volumeToGet = new VolumeToGet(lVolumeId); @@ -132,8 +113,9 @@ public class SolidFireUtil { String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId); long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId); String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId); + long lTotalSize = getVolumeTotalSize(volumeGetResult, lVolumeId); - return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus); + return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus, lTotalSize); } public static List getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) { @@ -152,12 +134,62 @@ public class SolidFireUtil { List sfVolumes = new ArrayList(); for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) { - sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status)); + sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize)); } return sfVolumes; } + public static List getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) + { + final Gson gson = new GsonBuilder().create(); + + ListDeletedVolumes listDeletedVolumes = new ListDeletedVolumes(); + + String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes); + + String strListDeletedVolumesResultJson = executeJsonRpc(strListDeletedVolumesJson, strSfMvip, iSfPort, + strSfAdmin, strSfPassword); + + VolumeGetResult volumeGetResult = gson.fromJson(strListDeletedVolumesResultJson, VolumeGetResult.class); + + verifyResult(volumeGetResult.result, strListDeletedVolumesResultJson, gson); + + List deletedVolumes = new ArrayList (); + + for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) { + deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status, volume.totalSize)); + } + + return deletedVolumes; + } + + public static SolidFireVolume deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) + { + SolidFireVolume sfVolume = getSolidFireVolume(strSfMvip, iSfPort, strSfAdmin, strSfPassword, lVolumeId); + + final Gson gson = new GsonBuilder().create(); + + VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId); + + String strVolumeToDeleteJson = gson.toJson(volumeToDelete); + + executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + + return sfVolume; + } + + public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) + { + final Gson gson = new GsonBuilder().create(); + + VolumeToPurge volumeToPurge = new VolumeToPurge(lVolumeId); + + String strVolumeToPurgeJson = gson.toJson(volumeToPurge); + + executeJsonRpc(strVolumeToPurgeJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + } + private static final String ACTIVE = "active"; public static class SolidFireVolume { @@ -166,13 +198,17 @@ public class SolidFireUtil { private final String _iqn; private final long _accountId; private final String _status; + private final long _totalSize; - public SolidFireVolume(long id, String name, String iqn, long accountId, String status) { + public SolidFireVolume(long id, String name, String iqn, + long accountId, String status, long totalSize) + { _id = id; _name = name; _iqn = "/" + iqn + "/0"; _accountId = accountId; _status = status; + _totalSize = totalSize; } public long getId() { @@ -195,6 +231,10 @@ public class SolidFireUtil { return ACTIVE.equalsIgnoreCase(_status); } + public long getTotalSize() { + return _totalSize; + } + @Override public int hashCode() { return _iqn.hashCode(); @@ -217,7 +257,9 @@ public class SolidFireUtil { SolidFireVolume sfv = (SolidFireVolume)obj; - if (_id == sfv._id && _name.equals(sfv._name) && _iqn.equals(sfv._iqn) && _accountId == sfv._accountId && isActive() == sfv.isActive()) { + if (_id == sfv._id && _name.equals(sfv._name) && + _iqn.equals(sfv._iqn) && _accountId == sfv._accountId && + isActive() == sfv.isActive() && getTotalSize() == sfv.getTotalSize()) { return true; } @@ -225,7 +267,8 @@ public class SolidFireUtil { } } - public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strAccountName) { + public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strAccountName) + { final Gson gson = new GsonBuilder().create(); AccountToAdd accountToAdd = new AccountToAdd(strAccountName); @@ -241,17 +284,8 @@ public class SolidFireUtil { return accountAddResult.result.accountID; } - public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) { - final Gson gson = new GsonBuilder().create(); - - AccountToRemove accountToRemove = new AccountToRemove(lAccountId); - - String strAccountToRemoveJson = gson.toJson(accountToRemove); - - executeJsonRpc(strAccountToRemoveJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); - } - - public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lSfAccountId) { + public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lSfAccountId) + { final Gson gson = new GsonBuilder().create(); AccountToGetById accountToGetById = new AccountToGetById(lSfAccountId); @@ -271,7 +305,8 @@ public class SolidFireUtil { return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret); } - public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strSfAccountName) { + public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strSfAccountName) + { final Gson gson = new GsonBuilder().create(); AccountToGetByName accountToGetByName = new AccountToGetByName(strSfAccountName); @@ -291,7 +326,19 @@ public class SolidFireUtil { return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret); } - public static class SolidFireAccount { + public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lAccountId) + { + final Gson gson = new GsonBuilder().create(); + + AccountToRemove accountToRemove = new AccountToRemove(lAccountId); + + String strAccountToRemoveJson = gson.toJson(accountToRemove); + + executeJsonRpc(strAccountToRemoveJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + } + + public static class SolidFireAccount + { private final long _id; private final String _name; private final String _initiatorSecret; @@ -342,7 +389,9 @@ public class SolidFireUtil { SolidFireAccount sfa = (SolidFireAccount)obj; - if (_id == sfa._id && _name.equals(sfa._name) && _initiatorSecret.equals(sfa._initiatorSecret) && _targetSecret.equals(sfa._targetSecret)) { + if (_id == sfa._id && _name.equals(sfa._name) && + _initiatorSecret.equals(sfa._initiatorSecret) && + _targetSecret.equals(sfa._targetSecret)) { return true; } @@ -350,32 +399,12 @@ public class SolidFireUtil { } } - public static List getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) { + public static long createSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strVagName, + String[] iqns, long[] volumeIds) + { final Gson gson = new GsonBuilder().create(); - ListDeletedVolumes listDeletedVolumes = new ListDeletedVolumes(); - - String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes); - - String strListDeletedVolumesResultJson = executeJsonRpc(strListDeletedVolumesJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); - - VolumeGetResult volumeGetResult = gson.fromJson(strListDeletedVolumesResultJson, VolumeGetResult.class); - - verifyResult(volumeGetResult.result, strListDeletedVolumesResultJson, gson); - - List deletedVolumes = new ArrayList(); - - for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) { - deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status)); - } - - return deletedVolumes; - } - - public static long createSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strVagName) { - final Gson gson = new GsonBuilder().create(); - - VagToCreate vagToCreate = new VagToCreate(strVagName); + VagToCreate vagToCreate = new VagToCreate(strVagName, iqns, volumeIds); String strVagCreateJson = gson.toJson(vagToCreate); @@ -388,7 +417,67 @@ public class SolidFireUtil { return vagCreateResult.result.volumeAccessGroupID; } - public static void deleteSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) { + public static void modifySolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId, + String[] iqns, long[] volumeIds) + { + final Gson gson = new GsonBuilder().create(); + + VagToModify vagToModify = new VagToModify(lVagId, iqns, volumeIds); + + String strVagModifyJson = gson.toJson(vagToModify); + + executeJsonRpc(strVagModifyJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + } + + public static SolidFireVag getSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) + { + final Gson gson = new GsonBuilder().create(); + + VagToGet vagToGet = new VagToGet(lVagId); + + String strVagToGetJson = gson.toJson(vagToGet); + + String strVagGetResultJson = executeJsonRpc(strVagToGetJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + + VagGetResult vagGetResult = gson.fromJson(strVagGetResultJson, VagGetResult.class); + + verifyResult(vagGetResult.result, strVagGetResultJson, gson); + + String[] vagIqns = getVagIqns(vagGetResult, lVagId); + long[] vagVolumeIds = getVagVolumeIds(vagGetResult, lVagId); + + return new SolidFireVag(lVagId, vagIqns, vagVolumeIds); + } + + public static List getAllSolidFireVags(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) + { + final Gson gson = new GsonBuilder().create(); + + AllVags allVags = new AllVags(); + + String strAllVagsJson = gson.toJson(allVags); + + String strAllVagsGetResultJson = executeJsonRpc(strAllVagsJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); + + VagGetResult allVagsGetResult = gson.fromJson(strAllVagsGetResultJson, VagGetResult.class); + + verifyResult(allVagsGetResult.result, strAllVagsGetResultJson, gson); + + List lstSolidFireVags = new ArrayList(); + + if (allVagsGetResult.result.volumeAccessGroups != null ) { + for (VagGetResult.Result.Vag vag : allVagsGetResult.result.volumeAccessGroups) { + SolidFireVag sfVag = new SolidFireVag(vag.volumeAccessGroupID, vag.initiators, vag.volumes); + + lstSolidFireVags.add(sfVag); + } + } + + return lstSolidFireVags; + } + + public static void deleteSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) + { final Gson gson = new GsonBuilder().create(); VagToDelete vagToDelete = new VagToDelete(lVagId); @@ -398,6 +487,64 @@ public class SolidFireUtil { executeJsonRpc(strVagToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword); } + public static class SolidFireVag + { + private final long _id; + private final String[] _initiators; + private final long[] _volumeIds; + + public SolidFireVag(long id, String[] initiators, long[] volumeIds) + { + _id = id; + _initiators = initiators; + _volumeIds = volumeIds; + } + + public long getId() + { + return _id; + } + + public String[] getInitiators() + { + return _initiators; + } + + public long[] getVolumeIds() + { + return _volumeIds; + } + + @Override + public int hashCode() { + return String.valueOf(_id).hashCode(); + } + + @Override + public String toString() { + return String.valueOf(_id); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (!obj.getClass().equals(SolidFireVag.class)) { + return false; + } + + SolidFireVag sfvag = (SolidFireVag)obj; + + if (_id == sfvag._id) { + return true; + } + + return false; + } + } + @SuppressWarnings("unused") private static final class VolumeToCreate { private final String method = "CreateVolume"; @@ -450,7 +597,59 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class VolumeToDelete { + private static final class VolumeToGet + { + private final String method = "ListActiveVolumes"; + private final VolumeToGetParams params; + + private VolumeToGet(final long lVolumeId) + { + params = new VolumeToGetParams(lVolumeId); + } + + private static final class VolumeToGetParams + { + private final long startVolumeID; + private final long limit = 1; + + private VolumeToGetParams(final long lVolumeId) + { + startVolumeID = lVolumeId; + } + } + } + + @SuppressWarnings("unused") + private static final class VolumesToGetForAccount + { + private final String method = "ListVolumesForAccount"; + private final VolumesToGetForAccountParams params; + + private VolumesToGetForAccount(final long lAccountId) + { + params = new VolumesToGetForAccountParams(lAccountId); + } + + private static final class VolumesToGetForAccountParams + { + private final long accountID; + + private VolumesToGetForAccountParams(final long lAccountId) + { + accountID = lAccountId; + } + } + } + + @SuppressWarnings("unused") + private static final class ListDeletedVolumes + { + private final String method = "ListDeletedVolumes"; + } + + @SuppressWarnings("unused") + private static final class VolumeToDelete + { private final String method = "DeleteVolume"; private final VolumeToDeleteParams params; @@ -468,12 +667,8 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class ListDeletedVolumes { - private final String method = "ListDeletedVolumes"; - } - - @SuppressWarnings("unused") - private static final class VolumeToPurge { + private static final class VolumeToPurge + { private final String method = "PurgeDeletedVolume"; private final VolumeToPurgeParams params; @@ -491,56 +686,67 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class VolumeToGet { - private final String method = "ListActiveVolumes"; - private final VolumeToGetParams params; + private static final class AccountToAdd + { + private final String method = "AddAccount"; + private final AccountToAddParams params; - private VolumeToGet(final long lVolumeId) { - params = new VolumeToGetParams(lVolumeId); + private AccountToAdd(final String strAccountName) + { + params = new AccountToAddParams(strAccountName); } - private static final class VolumeToGetParams { - private final long startVolumeID; - private final long limit = 1; + private static final class AccountToAddParams + { + private final String username; - private VolumeToGetParams(final long lVolumeId) { - startVolumeID = lVolumeId; + private AccountToAddParams(final String strAccountName) + { + username = strAccountName; } } } @SuppressWarnings("unused") - private static final class VolumesToGetForAccount { - private final String method = "ListVolumesForAccount"; - private final VolumesToGetForAccountParams params; + private static final class AccountToGetById + { + private final String method = "GetAccountByID"; + private final AccountToGetByIdParams params; - private VolumesToGetForAccount(final long lAccountId) { - params = new VolumesToGetForAccountParams(lAccountId); + private AccountToGetById(final long lAccountId) + { + params = new AccountToGetByIdParams(lAccountId); } - private static final class VolumesToGetForAccountParams { + private static final class AccountToGetByIdParams + { private final long accountID; - private VolumesToGetForAccountParams(final long lAccountId) { + private AccountToGetByIdParams(final long lAccountId) + { accountID = lAccountId; } } } @SuppressWarnings("unused") - private static final class AccountToAdd { - private final String method = "AddAccount"; - private final AccountToAddParams params; + private static final class AccountToGetByName + { + private final String method = "GetAccountByName"; + private final AccountToGetByNameParams params; - private AccountToAdd(final String strAccountName) { - params = new AccountToAddParams(strAccountName); + private AccountToGetByName(final String strUsername) + { + params = new AccountToGetByNameParams(strUsername); } - private static final class AccountToAddParams { + private static final class AccountToGetByNameParams + { private final String username; - private AccountToAddParams(final String strAccountName) { - username = strAccountName; + private AccountToGetByNameParams(final String strUsername) + { + username = strUsername; } } } @@ -564,61 +770,98 @@ public class SolidFireUtil { } @SuppressWarnings("unused") - private static final class AccountToGetById { - private final String method = "GetAccountByID"; - private final AccountToGetByIdParams params; - - private AccountToGetById(final long lAccountId) { - params = new AccountToGetByIdParams(lAccountId); - } - - private static final class AccountToGetByIdParams { - private final long accountID; - - private AccountToGetByIdParams(final long lAccountId) { - accountID = lAccountId; - } - } - } - - @SuppressWarnings("unused") - private static final class AccountToGetByName { - private final String method = "GetAccountByName"; - private final AccountToGetByNameParams params; - - private AccountToGetByName(final String strUsername) { - params = new AccountToGetByNameParams(strUsername); - } - - private static final class AccountToGetByNameParams { - private final String username; - - private AccountToGetByNameParams(final String strUsername) { - username = strUsername; - } - } - } - - @SuppressWarnings("unused") - private static final class VagToCreate { + private static final class VagToCreate + { private final String method = "CreateVolumeAccessGroup"; private final VagToCreateParams params; - private VagToCreate(final String strVagName) { - params = new VagToCreateParams(strVagName); + private VagToCreate(final String strVagName, final String[] iqns, final long[] volumeIds) + { + params = new VagToCreateParams(strVagName, iqns, volumeIds); } - private static final class VagToCreateParams { + private static final class VagToCreateParams + { private final String name; + private final String[] initiators; + private final long[] volumes; - private VagToCreateParams(final String strVagName) { + private VagToCreateParams(final String strVagName, final String[] iqns, final long[] volumeIds) + { name = strVagName; + initiators = iqns; + volumes = volumeIds; } } } @SuppressWarnings("unused") - private static final class VagToDelete { + private static final class VagToModify + { + private final String method = "ModifyVolumeAccessGroup"; + private final VagToModifyParams params; + + private VagToModify(final long lVagName, final String[] iqns, final long[] volumeIds) + { + params = new VagToModifyParams(lVagName, iqns, volumeIds); + } + + private static final class VagToModifyParams + { + private final long volumeAccessGroupID; + private final String[] initiators; + private final long[] volumes; + + private VagToModifyParams(final long lVagName, final String[] iqns, final long[] volumeIds) + { + volumeAccessGroupID = lVagName; + initiators = iqns; + volumes = volumeIds; + } + } + } + + @SuppressWarnings("unused") + private static final class VagToGet + { + private final String method = "ListVolumeAccessGroups"; + private final VagToGetParams params; + + private VagToGet(final long lVagId) + { + params = new VagToGetParams(lVagId); + } + + private static final class VagToGetParams + { + private final long startVolumeAccessGroupID; + private final long limit = 1; + + private VagToGetParams(final long lVagId) + { + startVolumeAccessGroupID = lVagId; + } + } + } + + @SuppressWarnings("unused") + private static final class AllVags + { + private final String method = "ListVolumeAccessGroups"; + private final VagToGetParams params; + + private AllVags() + { + params = new VagToGetParams(); + } + + private static final class VagToGetParams + {} + } + + @SuppressWarnings("unused") + private static final class VagToDelete + { private final String method = "DeleteVolumeAccessGroup"; private final VagToDeleteParams params; @@ -655,6 +898,7 @@ public class SolidFireUtil { private String iqn; private long accountID; private String status; + private long totalSize; } } } @@ -690,7 +934,25 @@ public class SolidFireUtil { } } - private static final class JsonError { + private static final class VagGetResult + { + private Result result; + + private static final class Result + { + private Vag[] volumeAccessGroups; + + private static final class Vag + { + private long volumeAccessGroupID; + private String[] initiators; + private long[] volumes; + } + } + } + + private static final class JsonError + { private Error error; private static final class Error { @@ -745,7 +1007,7 @@ public class SolidFireUtil { httpClient = getHttpClient(iPort); - URI uri = new URI("https://" + strMvip + ":" + iPort + "/json-rpc/1.0"); + URI uri = new URI("https://" + strMvip + ":" + iPort + "/json-rpc/5.0"); AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(strAdmin, strPassword); @@ -811,7 +1073,7 @@ public class SolidFireUtil { return volumeGetResult.result.volumes[0].name; } - throw new CloudRuntimeException("Could not determine the name of the volume, " + "but the volume was created with an ID of " + lVolumeId + "."); + throw new CloudRuntimeException("Could not determine the name of the volume for volume ID of " + lVolumeId + "."); } private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId) { @@ -819,7 +1081,7 @@ public class SolidFireUtil { return volumeGetResult.result.volumes[0].iqn; } - throw new CloudRuntimeException("Could not determine the IQN of the volume, " + "but the volume was created with an ID of " + lVolumeId + "."); + throw new CloudRuntimeException("Could not determine the IQN of the volume for volume ID of " + lVolumeId + "."); } private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId) { @@ -827,7 +1089,7 @@ public class SolidFireUtil { return volumeGetResult.result.volumes[0].accountID; } - throw new CloudRuntimeException("Could not determine the volume's account ID, " + "but the volume was created with an ID of " + lVolumeId + "."); + throw new CloudRuntimeException("Could not determine the account ID of the volume for volume ID of " + lVolumeId + "."); } private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId) { @@ -835,6 +1097,39 @@ public class SolidFireUtil { return volumeGetResult.result.volumes[0].status; } - throw new CloudRuntimeException("Could not determine the status of the volume, " + "but the volume was created with an ID of " + lVolumeId + "."); + throw new CloudRuntimeException("Could not determine the status of the volume for volume ID of " + lVolumeId + "."); + } + + private static long getVolumeTotalSize(VolumeGetResult volumeGetResult, long lVolumeId) + { + if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 && + volumeGetResult.result.volumes[0].volumeID == lVolumeId) + { + return volumeGetResult.result.volumes[0].totalSize; + } + + throw new CloudRuntimeException("Could not determine the total size of the volume for volume ID of " + lVolumeId + "."); + } + + private static String[] getVagIqns(VagGetResult vagGetResult, long lVagId) + { + if (vagGetResult.result.volumeAccessGroups != null && vagGetResult.result.volumeAccessGroups.length == 1 && + vagGetResult.result.volumeAccessGroups[0].volumeAccessGroupID == lVagId) + { + return vagGetResult.result.volumeAccessGroups[0].initiators; + } + + throw new CloudRuntimeException("Could not determine the IQNs of the volume access group for volume access group ID of " + lVagId + "."); + } + + private static long[] getVagVolumeIds(VagGetResult vagGetResult, long lVagId) + { + if (vagGetResult.result.volumeAccessGroups != null && vagGetResult.result.volumeAccessGroups.length == 1 && + vagGetResult.result.volumeAccessGroups[0].volumeAccessGroupID == lVagId) + { + return vagGetResult.result.volumeAccessGroups[0].volumes; + } + + throw new CloudRuntimeException("Could not determine the volume IDs of the volume access group for volume access group ID of " + lVagId + "."); } } diff --git a/plugins/user-authenticators/ldap/pom.xml b/plugins/user-authenticators/ldap/pom.xml index 4d1b7fa4ffa..dc4e1725029 100644 --- a/plugins/user-authenticators/ldap/pom.xml +++ b/plugins/user-authenticators/ldap/pom.xml @@ -92,7 +92,6 @@ - org.eclipse.m2e lifecycle-mapping @@ -125,7 +124,6 @@ - diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java index db6d7dd2d70..5e424dee9b4 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/command/LDAPConfigCmd.java @@ -21,11 +21,9 @@ import java.util.List; import javax.inject.Inject; -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.InsufficientCapacityException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.exception.ResourceUnavailableException; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.log4j.Logger; + import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; @@ -39,9 +37,12 @@ import org.apache.cloudstack.framework.config.impl.ConfigurationVO; import org.apache.cloudstack.ldap.LdapConfiguration; import org.apache.cloudstack.ldap.LdapConfigurationVO; import org.apache.cloudstack.ldap.LdapManager; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.log4j.Logger; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; import com.cloud.utils.Pair; @@ -67,7 +68,7 @@ public class LDAPConfigCmd extends BaseCmd { ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.LIST_ALL, type = BaseCmd.CommandType.BOOLEAN, description = "If true return current LDAP configuration") + @Parameter(name = ApiConstants.LIST_ALL, type = CommandType.BOOLEAN, description = "If true return current LDAP configuration") private Boolean listAll; @Parameter(name = ApiConstants.HOST_NAME, type = CommandType.STRING, description = "Hostname or ip address of the ldap server eg: my.ldap.com") @@ -79,13 +80,19 @@ public class LDAPConfigCmd extends BaseCmd { @Parameter(name = ApiConstants.USE_SSL, type = CommandType.BOOLEAN, description = "Check Use SSL if the external LDAP server is configured for LDAP over SSL.") private Boolean useSSL; - @Parameter(name = ApiConstants.SEARCH_BASE, type = CommandType.STRING, description = "The search base defines the starting point for the search in the directory tree Example: dc=cloud,dc=com.") + @Parameter(name = ApiConstants.SEARCH_BASE, + type = CommandType.STRING, + description = "The search base defines the starting point for the search in the directory tree Example: dc=cloud,dc=com.") private String searchBase; - @Parameter(name = ApiConstants.QUERY_FILTER, type = CommandType.STRING, description = "You specify a query filter here, which narrows down the users, who can be part of this domain.") + @Parameter(name = ApiConstants.QUERY_FILTER, + type = CommandType.STRING, + description = "You specify a query filter here, which narrows down the users, who can be part of this domain.") private String queryFilter; - @Parameter(name = ApiConstants.BIND_DN, type = CommandType.STRING, description = "Specify the distinguished name of a user with the search permission on the directory.") + @Parameter(name = ApiConstants.BIND_DN, + type = CommandType.STRING, + description = "Specify the distinguished name of a user with the search permission on the directory.") private String bindDN; @Parameter(name = ApiConstants.BIND_PASSWORD, type = CommandType.STRING, description = "Enter the password.") @@ -170,7 +177,8 @@ public class LDAPConfigCmd extends BaseCmd { ///////////////////////////////////////////////////// @Override - public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, + ResourceAllocationException { if (getListAll()) { // return the existing conf @@ -184,7 +192,8 @@ public class LDAPConfigCmd extends BaseCmd { String searchBaseConfig = _ldapConfiguration.getBaseDn(); String bindDnConfig = _ldapConfiguration.getBindPrincipal(); for (LdapConfigurationVO ldapConfigurationVO : result.first()) { - responses.add(createLDAPConfigResponse(ldapConfigurationVO.getHostname(), ldapConfigurationVO.getPort(), useSSlConfig, null, searchBaseConfig, bindDnConfig)); + responses.add(createLDAPConfigResponse(ldapConfigurationVO.getHostname(), ldapConfigurationVO.getPort(), useSSlConfig, null, searchBaseConfig, + bindDnConfig)); } } response.setResponses(responses); @@ -195,7 +204,7 @@ public class LDAPConfigCmd extends BaseCmd { } else { boolean result = updateLDAP(); if (result) { - LDAPConfigResponse lr = this.createLDAPConfigResponse(getHostname(), getPort().toString(), getUseSSL(), getQueryFilter(), getSearchBase(), getBindDN()); + LDAPConfigResponse lr = createLDAPConfigResponse(getHostname(), getPort(), getUseSSL(), getQueryFilter(), getSearchBase(), getBindDN()); lr.setResponseName(getCommandName()); setResponseObject(lr); } @@ -203,10 +212,10 @@ public class LDAPConfigCmd extends BaseCmd { } - private LDAPConfigResponse createLDAPConfigResponse(String hostname, String port, Boolean useSSL, String queryFilter, String searchBase, String bindDN) { + private LDAPConfigResponse createLDAPConfigResponse(String hostname, Integer port, Boolean useSSL, String queryFilter, String searchBase, String bindDN) { LDAPConfigResponse lr = new LDAPConfigResponse(); lr.setHostname(hostname); - lr.setPort(port); + lr.setPort(port.toString()); lr.setUseSSL(useSSL.toString()); lr.setQueryFilter(queryFilter); lr.setBindDN(bindDN); diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java index f03df42e5cb..a4e47828844 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/api/response/LdapConfigurationResponse.java @@ -16,10 +16,11 @@ // under the License. package org.apache.cloudstack.api.response; +import com.google.gson.annotations.SerializedName; + import org.apache.cloudstack.api.BaseResponse; import com.cloud.serializer.Param; -import com.google.gson.annotations.SerializedName; public class LdapConfigurationResponse extends BaseResponse { @SerializedName("hostname") @@ -28,7 +29,7 @@ public class LdapConfigurationResponse extends BaseResponse { @SerializedName("port") @Param(description = "port") - private String port; + private int port; public LdapConfigurationResponse() { super(); @@ -39,7 +40,7 @@ public class LdapConfigurationResponse extends BaseResponse { this.hostname = hostname; } - public LdapConfigurationResponse(final String hostname, final String port) { + public LdapConfigurationResponse(final String hostname, final int port) { this.hostname = hostname; this.port = port; } @@ -48,7 +49,7 @@ public class LdapConfigurationResponse extends BaseResponse { return hostname; } - public String getPort() { + public int getPort() { return port; } @@ -56,7 +57,7 @@ public class LdapConfigurationResponse extends BaseResponse { this.hostname = hostname; } - public void setPort(final String port) { + public void setPort(final int port) { this.port = port; } -} +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java index 54b35cb29f9..488e7f44485 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapConfigurationVO.java @@ -16,10 +16,6 @@ // under the License. package org.apache.cloudstack.ldap; -import org.apache.cloudstack.api.InternalIdentity; - -import com.cloud.utils.db.Encrypt; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -27,27 +23,26 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import org.apache.cloudstack.api.InternalIdentity; + @Entity @Table(name = "ldap_configuration") public class LdapConfigurationVO implements InternalIdentity { + @Column(name = "hostname") + private String hostname; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; - @Encrypt - @Column(name = "hostname") - private String hostname; - - @Encrypt @Column(name = "port") - private String port; + private int port; public LdapConfigurationVO() { } - public LdapConfigurationVO(final String hostname, final String port) { + public LdapConfigurationVO(final String hostname, final int port) { this.hostname = hostname; this.port = port; } @@ -61,11 +56,11 @@ public class LdapConfigurationVO implements InternalIdentity { return id; } - public String getPort() { + public int getPort() { return port; } public void setId(final long id) { this.id = id; } -} +} \ No newline at end of file diff --git a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java index 42b0aebef19..6d71f4f17dd 100644 --- a/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java +++ b/plugins/user-authenticators/ldap/src/org/apache/cloudstack/ldap/LdapManagerImpl.java @@ -24,6 +24,9 @@ import javax.inject.Inject; import javax.naming.NamingException; import javax.naming.directory.DirContext; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + import org.apache.cloudstack.api.LdapValidator; import org.apache.cloudstack.api.command.LDAPConfigCmd; import org.apache.cloudstack.api.command.LDAPRemoveCmd; @@ -37,8 +40,6 @@ import org.apache.cloudstack.api.command.LdapUserSearchCmd; import org.apache.cloudstack.api.response.LdapConfigurationResponse; import org.apache.cloudstack.api.response.LdapUserResponse; import org.apache.cloudstack.ldap.dao.LdapConfigurationDao; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.exception.InvalidParameterValueException; import com.cloud.utils.Pair; @@ -75,10 +76,10 @@ public class LdapManagerImpl implements LdapManager, LdapValidator { try { final String providerUrl = "ldap://" + hostname + ":" + port; _ldapContextFactory.createBindContext(providerUrl); - configuration = new LdapConfigurationVO(hostname, Integer.toString(port)); + configuration = new LdapConfigurationVO(hostname, port); _ldapConfigurationDao.persist(configuration); s_logger.info("Added new ldap server with hostname: " + hostname); - return new LdapConfigurationResponse(hostname, Integer.toString(port)); + return new LdapConfigurationResponse(hostname, port); } catch (final NamingException e) { throw new InvalidParameterValueException("Unable to bind to the given LDAP server"); } diff --git a/pom.xml b/pom.xml index f975bb0baa0..3f65bdd05ad 100644 --- a/pom.xml +++ b/pom.xml @@ -160,7 +160,7 @@ - build/checkstyle + tools/checkstyle api agent core @@ -448,27 +448,33 @@ org.apache.maven.plugins maven-checkstyle-plugin - - false + validate check - + true - ${project.basedir}/build/checkstyle/src/main/resources/tooling/checkstyle.xml + cloud-style.xml true true ${project.basedir} @@ -476,14 +482,6 @@ **\/deps\/,**\/test\/,**\/target\/,**\/bin\/,**\/*.xml,**\/*.ini,**\/*.sh,**\/*.bat,**\/awsapi\/,**\/XenServerJava\/,**\/apidoc\/ - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${cs.checkstyle.version} - maven-clean-plugin @@ -549,6 +547,19 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + [2.0,) + + copy-dependencies + + + + + + @@ -653,7 +664,6 @@ ui/lib/qunit/qunit.js ui/lib/reset.css ui/lib/require.js - waf systemvm/conf/agent.properties systemvm/conf/environment.properties systemvm/js/jquery.js @@ -753,25 +763,6 @@ - awsapi @@ -813,7 +804,6 @@ developer - diff --git a/scripts/storage/secondary/createtmplt.sh b/scripts/storage/secondary/createtmplt.sh index 05874b4be09..6b31232bc9e 100755 --- a/scripts/storage/secondary/createtmplt.sh +++ b/scripts/storage/secondary/createtmplt.sh @@ -174,7 +174,7 @@ isCifs() { #then check if the template file is from cifs using df -P filename #Currently only cifs is supported in hyperv zone. mount | grep "type cifs" > /dev/null - return $? + echo $? } if [ "$tflag$nflag$fflag$sflag" != "1111" ] @@ -204,7 +204,7 @@ rollback_if_needed $tmpltfs $? "tar archives not supported\n" if [ ${tmpltname%.vhd} != ${tmpltname} ] then - if [ isCifs -ne 0 ] ; + if [ $(isCifs) -ne 0 ] ; then if which vhd-util &>/dev/null then diff --git a/scripts/storage/secondary/createvolume.sh b/scripts/storage/secondary/createvolume.sh index f11df1d7c67..8cbccc66e19 100755 --- a/scripts/storage/secondary/createvolume.sh +++ b/scripts/storage/secondary/createvolume.sh @@ -111,6 +111,14 @@ uncompress() { return 0 } +isCifs() { + #TO:DO incase of multiple zone where cifs and nfs exists, + #then check if the template file is from cifs using df -P filename + #Currently only cifs is supported in hyperv zone. + mount | grep "type cifs" > /dev/null + echo $? +} + create_from_file() { local tmpltfs=$1 local tmpltimg=$2 @@ -196,12 +204,15 @@ rollback_if_needed $tmpltfs $? "tar archives not supported\n" if [ ${tmpltname%.vhd} != ${tmpltname} ] then - if which vhd-util &>/dev/null - then - vhd-util check -n ${tmpltimg2} > /dev/null - rollback_if_needed $tmpltfs $? "vhd check of $tmpltimg2 failed\n" - vhd-util set -n ${tmpltimg2} -f "hidden" -v "0" > /dev/null - rollback_if_needed $tmpltfs $? "vhd remove $tmpltimg2 hidden failed\n" + if [ isCifs -ne 0 ] ; + then + if which vhd-util &>/dev/null + then + vhd-util check -n ${tmpltimg2} > /dev/null + rollback_if_needed $tmpltfs $? "vhd check of $tmpltimg2 failed\n" + vhd-util set -n ${tmpltimg2} -f "hidden" -v "0" > /dev/null + rollback_if_needed $tmpltfs $? "vhd remove $tmpltimg2 hidden failed\n" + fi fi fi diff --git a/scripts/vm/hypervisor/xenserver/hostvmstats.py b/scripts/vm/hypervisor/xenserver/hostvmstats.py index 38609b15c9f..61cf2de99f2 100644 --- a/scripts/vm/hypervisor/xenserver/hostvmstats.py +++ b/scripts/vm/hypervisor/xenserver/hostvmstats.py @@ -21,8 +21,17 @@ import XenAPI import urllib import time import logging -logging.basicConfig(filename='/tmp/xapilog',level=logging.DEBUG) - +import logging.handlers + +LOG_FILENAME = '/tmp/xapilog' +logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) +stats_logger = logging.getLogger('statsLogger') +stats_logger.setLevel(logging.DEBUG) + +#handler with maxBytes=10MiB +handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=10*1024*1024, backupCount=5) +stats_logger.addHandler(handler) + def get_stats(session, collect_host_stats, consolidation_function, interval, start_time): try: @@ -41,12 +50,12 @@ def get_stats(session, collect_host_stats, consolidation_function, interval, sta url += "&interval=" + str(interval) url += "&start=" + str(int(time.time())-100) - logging.debug("Calling URL: %s",url) + stats_logger.debug("Calling URL: %s",url) sock = urllib.URLopener().open(url) xml = sock.read() sock.close() - logging.debug("Size of returned XML: %s",len(xml)) + stats_logger.debug("Size of returned XML: %s",len(xml)) return xml except Exception,e: - logging.exception("get_stats() failed") + stats_logger.exception("get_stats() failed") raise diff --git a/scripts/vm/hypervisor/xenserver/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/vmopsSnapshot index a9a5a72ea62..5fd69a633f8 100755 --- a/scripts/vm/hypervisor/xenserver/vmopsSnapshot +++ b/scripts/vm/hypervisor/xenserver/vmopsSnapshot @@ -487,6 +487,18 @@ def getVhdParent(session, args): return baseCopyUuid +def getSnapshotSize(session, args): + primaryStorageSRUuid = args['primaryStorageSRUuid'] + snapshotUuid = args['snapshotUuid'] + isISCSI = getIsTrueString(args['isISCSI']) + + primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + logging.debug("primarySRPath: " + primarySRPath) + + snapshotVHD = getVHD(snapshotUuid, isISCSI) + snapshotPath = os.path.join(primarySRPath, snapshotVHD) + physicalSize = vhdutil.getSizePhys(snapshotPath) + return str(physicalSize) def backupSnapshot(session, args): logging.debug("Called backupSnapshot with " + str(args)) @@ -515,6 +527,7 @@ def backupSnapshot(session, args): prevBackupUuid = prevBackupUuid.split("/")[-1] # Check existence of snapshot on primary storage isfile(baseCopyPath, isISCSI) + physicalSize = vhdutil.getSizePhys(baseCopyPath) if prevBackupUuid: # Check existence of prevBackupFile prevBackupVHD = getBackupVHD(prevBackupUuid) @@ -535,7 +548,7 @@ def backupSnapshot(session, args): # If there was a previous snapshot setParent(prevBackupFile, backupFile) - txt = "1#" + backupUuid + txt = "1#" + backupUuid + "#" + str(physicalSize) return txt @echo @@ -596,6 +609,6 @@ def revert_memory_snapshot(session, args): return "0" if __name__ == "__main__": - XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot}) + XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot, "getSnapshotSize":getSnapshotSize}) diff --git a/scripts/vm/hypervisor/xenserver/xenserver56/patch b/scripts/vm/hypervisor/xenserver/xenserver56/patch index 50116935789..9473bca8d5c 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56/patch @@ -29,6 +29,7 @@ NFSSR.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins +cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins hostvmstats.py=..,0755,/opt/xensource/sm systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso id_rsa.cloud=../../../systemvm,0600,/root/.ssh diff --git a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch index 55d538edf4a..c91aa73821a 100644 --- a/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch +++ b/scripts/vm/hypervisor/xenserver/xenserver56fp1/patch @@ -29,6 +29,7 @@ NFSSR.py=/opt/xensource/sm vmops=..,0755,/etc/xapi.d/plugins vmopsSnapshot=..,0755,/etc/xapi.d/plugins +cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins hostvmstats.py=..,0755,/opt/xensource/sm systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso id_rsa.cloud=../../../systemvm,0600,/root/.ssh diff --git a/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage b/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage new file mode 100644 index 00000000000..03a05b75480 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenserver62/cloud-plugin-storage @@ -0,0 +1,301 @@ +#!/usr/bin/python +# 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. + +# Version @VERSION@ +# +# A plugin for executing script needed by vmops cloud + +import os, sys, time +import XenAPIPlugin +if os.path.exists("/opt/xensource/sm"): + sys.path.extend(["/opt/xensource/sm/", "/usr/local/sbin/", "/sbin/"]) +if os.path.exists("/usr/lib/xcp/sm"): + sys.path.extend(["/usr/lib/xcp/sm/", "/usr/local/sbin/", "/sbin/"]) + +import SR, VDI, SRCommand, util, lvutil +from util import CommandException +import vhdutil +import shutil +import lvhdutil +import errno +import subprocess +import xs_errors +import cleanup +import stat +import random +import cloudstack_pluginlib as lib +import logging + +lib.setup_logging("/var/log/cloud/vmops.log") + +VHDUTIL = "vhd-util" +VHD_PREFIX = 'VHD-' +CLOUD_DIR = '/var/run/cloud_mount' + +def echo(fn): + def wrapped(*v, **k): + name = fn.__name__ + logging.debug("#### VMOPS enter %s ####" % name ) + res = fn(*v, **k) + logging.debug("#### VMOPS exit %s ####" % name ) + return res + return wrapped + +def getPrimarySRPath(primaryStorageSRUuid, isISCSI): + if isISCSI: + primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid + return os.path.join(lvhdutil.VG_LOCATION, primarySRDir) + else: + return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid) + +def getBackupVHD(UUID): + return UUID + '.' + SR.DEFAULT_TAP + +def getVHD(UUID, isISCSI): + if isISCSI: + return VHD_PREFIX + UUID + else: + return UUID + '.' + SR.DEFAULT_TAP + +def getIsTrueString(stringValue): + booleanValue = False + if (stringValue and stringValue == 'true'): + booleanValue = True + return booleanValue + +def makeUnavailable(uuid, primarySRPath, isISCSI): + if not isISCSI: + return + VHD = getVHD(uuid, isISCSI) + path = os.path.join(primarySRPath, VHD) + manageAvailability(path, '-an') + return + +def manageAvailability(path, value): + if path.__contains__("/var/run/sr-mount"): + return + logging.debug("Setting availability of " + path + " to " + value) + try: + cmd = ['/usr/sbin/lvchange', value, path] + util.pread2(cmd) + except: #CommandException, (rc, cmdListStr, stderr): + #errMsg = "CommandException thrown while executing: " + cmdListStr + " with return code: " + str(rc) + " and stderr: " + stderr + errMsg = "Unexpected exception thrown by lvchange" + logging.debug(errMsg) + if value == "-ay": + # Raise an error only if we are trying to make it available. + # Just warn if we are trying to make it unavailable after the + # snapshot operation is done. + raise xs_errors.XenError(errMsg) + return + + +def checkVolumeAvailablility(path): + try: + if not isVolumeAvailable(path): + # The VHD file is not available on XenSever. The volume is probably + # inactive or detached. + # Do lvchange -ay to make it available on XenServer + manageAvailability(path, '-ay') + except: + errMsg = "Could not determine status of ISCSI path: " + path + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + + success = False + i = 0 + while i < 6: + i = i + 1 + # Check if the vhd is actually visible by checking for the link + # set isISCSI to true + success = isVolumeAvailable(path) + if success: + logging.debug("Made vhd: " + path + " available and confirmed that it is visible") + break + + # Sleep for 10 seconds before checking again. + time.sleep(10) + + # If not visible within 1 min fail + if not success: + logging.debug("Could not make vhd: " + path + " available despite waiting for 1 minute. Does it exist?") + + return success + +def isVolumeAvailable(path): + # Check if iscsi volume is available on this XenServer. + status = "0" + try: + p = subprocess.Popen(["/bin/bash", "-c", "if [ -L " + path + " ]; then echo 1; else echo 0;fi"], stdout=subprocess.PIPE) + status = p.communicate()[0].strip("\n") + except: + errMsg = "Could not determine status of ISCSI path: " + path + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + + return (status == "1") + +def scanParent(path): + # Do a scan for the parent for ISCSI volumes + # Note that the parent need not be visible on the XenServer + parentUUID = '' + try: + lvName = os.path.basename(path) + dirname = os.path.dirname(path) + vgName = os.path.basename(dirname) + vhdInfo = vhdutil.getVHDInfoLVM(lvName, lvhdutil.extractUuid, vgName) + parentUUID = vhdInfo.parentUuid + except: + errMsg = "Could not get vhd parent of " + path + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + return parentUUID + +def getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI): + snapshotVHD = getVHD(snapshotUuid, isISCSI) + snapshotPath = os.path.join(primarySRPath, snapshotVHD) + + baseCopyUuid = '' + if isISCSI: + checkVolumeAvailablility(snapshotPath) + baseCopyUuid = scanParent(snapshotPath) + else: + baseCopyUuid = getParent(snapshotPath, isISCSI) + + logging.debug("Base copy of snapshotUuid: " + snapshotUuid + " is " + baseCopyUuid) + return baseCopyUuid + +def getParent(path, isISCSI): + parentUUID = '' + try : + if isISCSI: + parentUUID = vhdutil.getParent(path, lvhdutil.extractUuid) + else: + parentUUID = vhdutil.getParent(path, cleanup.FileVDI.extractUuid) + except: + errMsg = "Could not get vhd parent of " + path + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + return parentUUID + +def getVhdParent(session, args): + logging.debug("getParent with " + str(args)) + try: + primaryStorageSRUuid = args['primaryStorageSRUuid'] + snapshotUuid = args['snapshotUuid'] + isISCSI = getIsTrueString(args['isISCSI']) + + primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) + logging.debug("primarySRPath: " + primarySRPath) + + baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) + + return baseCopyUuid + except: + logging.debug('getVhdParent', exc_info=True) + raise xs_errors.XenError("Failed to getVhdParent") +def makedirs(path): + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError, (errno, strerror): + umount(path) + if os.path.isdir(path): + return + errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + return + +def umount(localDir): + try: + cmd = ['umount', localDir] + util.pread2(cmd) + except CommandException: + errMsg = "CommandException raised while trying to umount " + localDir + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + + logging.debug("Successfully unmounted " + localDir) + return + +@echo +def mountNfsSecondaryStorage(session, args): + remoteDir = args['remoteDir'] + localDir = args['localDir'] + mounted = False + f = open("/proc/mounts", 'r') + for line in f: + tokens = line.split(" ") + if len(tokens) > 2 and tokens[0] == remoteDir and tokens[1] == localDir: + mounted = True + + if mounted: + return "true" + + makedirs(localDir) + options = "soft,tcp,timeo=133,retrans=1" + try: + cmd = ['mount', '-o', options, remoteDir, localDir] + txt = util.pread2(cmd) + except: + txt = '' + errMsg = "Unexpected error while trying to mount " + remoteDir + " to " + localDir + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + logging.debug("Successfully mounted " + remoteDir + " to " + localDir) + + return "true" + +@echo +def umountNfsSecondaryStorage(session, args): + localDir = args['localDir'] + try: + cmd = ['umount', localDir] + util.pread2(cmd) + except CommandException: + errMsg = "CommandException raised while trying to umount " + localDir + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + try: + os.system("rmdir " + localDir) + except: + pass + logging.debug("Successfully unmounted " + localDir) + return "true" + +@echo +def makeDirectory(session, args): + path = args['path'] + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError, (errno, strerror): + if os.path.isdir(path): + return "true" + errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror + logging.debug(errMsg) + raise xs_errors.XenError(errMsg) + return "true" + +if __name__ == "__main__": + XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "mountNfsSecondaryStorage":mountNfsSecondaryStorage, + "umountNfsSecondaryStorage":umountNfsSecondaryStorage, + "makeDirectory":makeDirectory}) + + diff --git a/scripts/vm/hypervisor/xenserver/xenserver62/patch b/scripts/vm/hypervisor/xenserver/xenserver62/patch new file mode 100644 index 00000000000..70b86b4ffe5 --- /dev/null +++ b/scripts/vm/hypervisor/xenserver/xenserver62/patch @@ -0,0 +1,74 @@ +# 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. + +# This file specifies the files that need +# to be transferred over to the XenServer. +# The format of this file is as follows: +# [Name of file]=[source path],[file permission],[destination path] +# [destination path] is required. +# If [file permission] is missing, 755 is assumed. +# If [source path] is missing, it looks in the same +# directory as the patch file. +# If [source path] starts with '/', then it is absolute path. +# If [source path] starts with '~', then it is path relative to management server home directory. +# If [source path] does not start with '/' or '~', then it is relative path to the location of the patch file. +vmops=..,0755,/etc/xapi.d/plugins +vmopspremium=..,0755,/etc/xapi.d/plugins +xen-ovs-vif-flows.rules=..,0644,/etc/udev/rules.d +ovs-vif-flows.py=..,0755,/etc/xapi.d/plugins +cloudstack_plugins.conf=..,0644,/etc/xensource +cloudstack_pluginlib.py=..,0755,/etc/xapi.d/plugins +ovstunnel=..,0755,/etc/xapi.d/plugins +cloud-plugin-storage=,0755,/etc/xapi.d/plugins +hostvmstats.py=..,0755,/opt/xensource/sm +systemvm.iso=../../../../../vms,0644,/opt/xensource/packages/iso +id_rsa.cloud=../../../systemvm,0600,/root/.ssh +network_info.sh=..,0755,/opt/cloud/bin +setupxenserver.sh=..,0755,/opt/cloud/bin +make_migratable.sh=..,0755,/opt/cloud/bin +createipAlias.sh=..,0755,/opt/cloud/bin +deleteipAlias.sh=..,0755,/opt/cloud/bin +setup_iscsi.sh=..,0755,/opt/cloud/bin +pingtest.sh=../../..,0755,/opt/cloud/bin +dhcp_entry.sh=../../../../network/domr/,0755,/opt/cloud/bin +save_password_to_domr.sh=../../../../network/domr/,0755,/opt/cloud/bin +call_firewall.sh=../../../../network/domr/,0755,/opt/cloud/bin +call_loadbalancer.sh=../../../../network/domr/,0755,/opt/cloud/bin +router_proxy.sh=../../../../network/domr/,0755,/opt/cloud/bin +cloud-setup-bonding.sh=..,0755,/opt/cloud/bin +kill_copy_process.sh=..,0755,/opt/cloud/bin +setup_heartbeat_sr.sh=..,0755,/opt/cloud/bin +setup_heartbeat_file.sh=..,0755,/opt/cloud/bin +check_heartbeat.sh=..,0755,/opt/cloud/bin +xenheartbeat.sh=..,0755,/opt/cloud/bin +launch_hb.sh=..,0755,/opt/cloud/bin +vhd-util=..,0755,/opt/cloud/bin +upgrade_snapshot.sh=..,0755,/opt/cloud/bin +cloud-clean-vlan.sh=..,0755,/opt/cloud/bin +cloud-prepare-upgrade.sh=..,0755,/opt/cloud/bin +bumpUpPriority.sh=../../../../network/domr/,0755,/opt/cloud/bin +swift=..,0755,/opt/cloud/bin +swiftxen=..,0755,/etc/xapi.d/plugins +s3xen=..,0755,/etc/xapi.d/plugins +add_to_vcpus_params_live.sh=..,0755,/opt/cloud/bin +ovs-pvlan=..,0755,/etc/xapi.d/plugins +ovs-pvlan-dhcp-host.sh=../../../network,0755,/opt/cloud/bin +ovs-pvlan-vm.sh=../../../network,0755,/opt/cloud/bin +ovs-pvlan-cleanup.sh=../../../network,0755,/opt/cloud/bin +ovs-get-dhcp-iface.sh=..,0755,/opt/cloud/bin +ovs-get-bridge.sh=..,0755,/opt/cloud/bin +cloudlog=..,0644,/etc/logrotate.d diff --git a/server/pom.xml b/server/pom.xml index 7929cdf5830..86e7b76a6c2 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -36,6 +36,11 @@ cloud-framework-cluster ${project.version} + + org.apache.cloudstack + cloud-framework-security + ${project.version} + javax.servlet servlet-api @@ -50,13 +55,6 @@ cloud-framework-jobs ${project.version} - org.apache.httpcomponents httpclient @@ -137,11 +135,6 @@ - - - resources - - test/resources @@ -228,38 +221,5 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-antrun-plugin - - [1.7,) - - run - - - - - - - - - - - - diff --git a/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index d37303476a8..d4ee85e4569 100644 --- a/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -20,11 +20,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" + xmlns:util="http://www.springframework.org/schema/util" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd + http://www.springframework.org/schema/aop + http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.0.xsd" + http://www.springframework.org/schema/context/spring-context-3.0.xsd + http://www.springframework.org/schema/util + http://www.springframework.org/schema/util/spring-util-3.0.xsd" > @@ -60,6 +65,7 @@ + @@ -230,5 +236,4 @@ class="org.apache.cloudstack.region.gslb.GlobalLoadBalancingRulesServiceImpl" /> - diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 8c59ce65370..4414e3b18f0 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -779,10 +779,6 @@ public class ApiDBUtils { return s_userVmMgr.searchForUserVMs(c, s_accountDao.findById(Account.ACCOUNT_ID_SYSTEM), null, false, permittedAccounts, false, null, null).first(); } - public static List searchForStoragePools(Criteria c) { - return s_ms.searchForStoragePools(c).first(); - } - // /////////////////////////////////////////////////////////// // Manager methods // // /////////////////////////////////////////////////////////// @@ -1054,8 +1050,19 @@ public class ApiDBUtils { return s_volumeDao.getHypervisorType(volumeId); } - public static HypervisorType getHypervisorTypeFromFormat(ImageFormat format) { - return s_storageMgr.getHypervisorTypeFromFormat(format); + public static HypervisorType getHypervisorTypeFromFormat(long dcId, ImageFormat format){ + HypervisorType type = s_storageMgr.getHypervisorTypeFromFormat(format); + if (format == ImageFormat.VHD) { + // Xenserver and Hyperv both support vhd format. Additionally hyperv is only supported + // in a dc/zone if there aren't any other hypervisor types present in the zone). If the + // format type is VHD check is any xenserver clusters are present. If not, we assume it + // is a hyperv zone and update the type. + List xenClusters = s_clusterDao.listByDcHyType(dcId, HypervisorType.XenServer.toString()); + if (xenClusters.isEmpty()) { + type = HypervisorType.Hyperv; + } + } + return type; } public static List listUserStatsBy(Long accountId) { diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index d1d147f2bce..c9e5581865f 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -26,11 +26,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.StringTokenizer; import java.util.TimeZone; import javax.inject.Inject; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroup; @@ -143,11 +144,9 @@ import org.apache.cloudstack.network.lb.ApplicationLoadBalancerRule; import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpRange; import org.apache.cloudstack.region.Region; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageTypes; -import org.apache.log4j.Logger; import com.cloud.api.query.ViewResponseHelper; import com.cloud.api.query.vo.AccountJoinVO; @@ -258,7 +257,6 @@ import com.cloud.projects.Project; import com.cloud.projects.ProjectAccount; import com.cloud.projects.ProjectInvitation; import com.cloud.region.ha.GlobalLoadBalancerRule; -import com.cloud.server.Criteria; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.service.ServiceOfferingVO; @@ -269,7 +267,6 @@ import com.cloud.storage.GuestOSCategoryVO; import com.cloud.storage.ImageStore; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StoragePool; import com.cloud.storage.Upload; import com.cloud.storage.UploadVO; @@ -1670,152 +1667,6 @@ public class ApiResponseHelper implements ResponseGenerator { return ApiDBUtils.newEventResponse(vEvent); } - private List sumCapacities(List hostCapacities) { - Map totalCapacityMap = new HashMap(); - Map usedCapacityMap = new HashMap(); - - Set poolIdsToIgnore = new HashSet(); - Criteria c = new Criteria(); - // TODO: implement - List allStoragePools = ApiDBUtils.searchForStoragePools(c); - for (StoragePoolVO pool : allStoragePools) { - StoragePoolType poolType = pool.getPoolType(); - if (!(poolType.isShared())) {// All the non shared storages - // shouldn't show up in the capacity - // calculation - poolIdsToIgnore.add(pool.getId()); - } - } - - float cpuOverprovisioningFactor = ApiDBUtils.getCpuOverprovisioningFactor(); - - // collect all the capacity types, sum allocated/used and sum - // total...get one capacity number for each - for (Capacity capacity : hostCapacities) { - - // check if zone exist - DataCenter zone = ApiDBUtils.findZoneById(capacity.getDataCenterId()); - if (zone == null) { - continue; - } - - short capacityType = capacity.getCapacityType(); - - // If local storage then ignore - if ((capacityType == Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED || capacityType == Capacity.CAPACITY_TYPE_STORAGE) - && poolIdsToIgnore.contains(capacity.getHostOrPoolId())) { - continue; - } - - String key = capacity.getCapacityType() + "_" + capacity.getDataCenterId(); - String keyForPodTotal = key + "_-1"; - - boolean sumPodCapacity = false; - if (capacity.getPodId() != null) { - key += "_" + capacity.getPodId(); - sumPodCapacity = true; - } - - Long totalCapacity = totalCapacityMap.get(key); - Long usedCapacity = usedCapacityMap.get(key); - - // reset overprovisioning factor to 1 - float overprovisioningFactor = 1; - if (capacityType == Capacity.CAPACITY_TYPE_CPU) { - overprovisioningFactor = cpuOverprovisioningFactor; - } - - if (totalCapacity == null) { - totalCapacity = new Long((long)(capacity.getTotalCapacity() * overprovisioningFactor)); - } else { - totalCapacity = new Long((long)(capacity.getTotalCapacity() * overprovisioningFactor)) + totalCapacity; - } - - if (usedCapacity == null) { - usedCapacity = new Long(capacity.getUsedCapacity()); - } else { - usedCapacity = new Long(capacity.getUsedCapacity() + usedCapacity); - } - - if (capacityType == Capacity.CAPACITY_TYPE_CPU || capacityType == Capacity.CAPACITY_TYPE_MEMORY) { // Reserved - // Capacity - // accounts - // for - // stopped - // vms - // that - // have been - // stopped - // within - // an - // interval - usedCapacity += capacity.getReservedCapacity(); - } - - totalCapacityMap.put(key, totalCapacity); - usedCapacityMap.put(key, usedCapacity); - - if (sumPodCapacity) { - totalCapacity = totalCapacityMap.get(keyForPodTotal); - usedCapacity = usedCapacityMap.get(keyForPodTotal); - - overprovisioningFactor = 1; - if (capacityType == Capacity.CAPACITY_TYPE_CPU) { - overprovisioningFactor = cpuOverprovisioningFactor; - } - - if (totalCapacity == null) { - totalCapacity = new Long((long)(capacity.getTotalCapacity() * overprovisioningFactor)); - } else { - totalCapacity = new Long((long)(capacity.getTotalCapacity() * overprovisioningFactor)) + totalCapacity; - } - - if (usedCapacity == null) { - usedCapacity = new Long(capacity.getUsedCapacity()); - } else { - usedCapacity = new Long(capacity.getUsedCapacity() + usedCapacity); - } - - if (capacityType == Capacity.CAPACITY_TYPE_CPU || capacityType == Capacity.CAPACITY_TYPE_MEMORY) { // Reserved - // Capacity - // accounts - // for - // stopped - // vms - // that - // have - // been - // stopped - // within - // an - // interval - usedCapacity += capacity.getReservedCapacity(); - } - - totalCapacityMap.put(keyForPodTotal, totalCapacity); - usedCapacityMap.put(keyForPodTotal, usedCapacity); - } - } - - List summedCapacities = new ArrayList(); - for (String key : totalCapacityMap.keySet()) { - CapacityVO summedCapacity = new CapacityVO(); - - StringTokenizer st = new StringTokenizer(key, "_"); - summedCapacity.setCapacityType(Short.parseShort(st.nextToken())); - summedCapacity.setDataCenterId(Long.parseLong(st.nextToken())); - if (st.hasMoreTokens()) { - summedCapacity.setPodId(Long.parseLong(st.nextToken())); - } - - summedCapacity.setTotalCapacity(totalCapacityMap.get(key)); - summedCapacity.setUsedCapacity(usedCapacityMap.get(key)); - - summedCapacities.add(summedCapacity); - } - return summedCapacities; - } - @Override public List createCapacityResponse(List result, DecimalFormat format) { List capacityResponses = new ArrayList(); @@ -2683,6 +2534,10 @@ public class ApiResponseHelper implements ResponseGenerator { } response.setServices(services); + Provider serviceProvider = Provider.getProvider(result.getProviderName()); + boolean canEnableIndividualServices = ApiDBUtils.canElementEnableIndividualServices(serviceProvider); + response.setCanEnableIndividualServices(canEnableIndividualServices); + response.setObjectName("networkserviceprovider"); return response; } @@ -3610,6 +3465,8 @@ public class ApiResponseHelper implements ResponseGenerator { response.setIp6Address(result.getIp6Address()); } + response.setDeviceId(String.valueOf(result.getDeviceId())); + response.setIsDefault(result.isDefaultNic()); return response; } diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 91222760dc1..bdd413270db 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -176,6 +176,7 @@ import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate.TemplateFilter; +import com.cloud.template.VirtualMachineTemplate.State; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.DomainManager; @@ -424,8 +425,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { ssc.addOr("email", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("state", SearchCriteria.Op.LIKE, "%" + keyword + "%"); ssc.addOr("accountName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("type", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("accountState", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("accountType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("username", SearchCriteria.Op.SC, ssc); } @@ -2084,7 +2084,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { if (keyword != null) { SearchCriteria ssc = _imageStoreJoinDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("provider", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("providerName", SearchCriteria.Op.LIKE, "%" + keyword + "%"); sc.addAnd("name", SearchCriteria.Op.SC, ssc); } @@ -2717,6 +2717,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { TemplateFilter templateFilter = TemplateFilter.valueOf(cmd.getTemplateFilter()); Long id = cmd.getId(); Map tags = cmd.getTags(); + boolean showRemovedTmpl = cmd.getShowRemoved(); Account caller = CallContext.current().getCallingAccount(); boolean listAll = false; @@ -2741,12 +2742,12 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), - cmd.getZoneId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags); + cmd.getZoneId(), hypervisorType, showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl); } private Pair, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List permittedAccounts, - Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags) { + Account caller, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags, boolean showRemovedTmpl) { // check if zone is configured, if not, just return empty list List hypers = null; @@ -2965,7 +2966,14 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { // sc.addAnd("removed", SearchCriteria.Op.NULL); // search unique templates and find details by Ids - Pair, Integer> uniqueTmplPair = _templateJoinDao.searchAndCount(sc, searchFilter); + Pair, Integer> uniqueTmplPair = null; + if(showRemovedTmpl){ + uniqueTmplPair = _templateJoinDao.searchIncludingRemovedAndCount(sc, searchFilter); + } else { + sc.addAnd("templateState", SearchCriteria.Op.EQ, State.Active); + uniqueTmplPair = _templateJoinDao.searchAndCount(sc, searchFilter); + } + Integer count = uniqueTmplPair.second(); if (count.intValue() == 0) { // empty result @@ -2977,7 +2985,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { for (TemplateJoinVO v : uniqueTmpls) { tzIds[i++] = v.getTempZonePair(); } - List vrs = _templateJoinDao.searchByTemplateZonePair(tzIds); + List vrs = _templateJoinDao.searchByTemplateZonePair(showRemovedTmpl, tzIds); return new Pair, Integer>(vrs, count); // TODO: revisit the special logic for iso search in @@ -3001,6 +3009,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { TemplateFilter isoFilter = TemplateFilter.valueOf(cmd.getIsoFilter()); Long id = cmd.getId(); Map tags = cmd.getTags(); + boolean showRemovedISO = cmd.getShowRemoved(); Account caller = CallContext.current().getCallingAccount(); boolean listAll = false; @@ -3024,22 +3033,25 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), - cmd.getZoneId(), hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags); + cmd.getZoneId(), hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO); } @Override - public ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, - String accountName, Long domainId, boolean isRecursive, boolean listAll, Long startIndex, Long pageSize) { - Pair, Integer> result = - listAffinityGroupsInternal(affinityGroupId, affinityGroupName, affinityGroupType, vmId, accountName, domainId, isRecursive, listAll, startIndex, pageSize); + public ListResponse listAffinityGroups(Long affinityGroupId, String affinityGroupName, + String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive, + boolean listAll, Long startIndex, Long pageSize, String keyword) { + Pair, Integer> result = listAffinityGroupsInternal(affinityGroupId, + affinityGroupName, affinityGroupType, vmId, accountName, domainId, isRecursive, listAll, startIndex, + pageSize, keyword); ListResponse response = new ListResponse(); List agResponses = ViewResponseHelper.createAffinityGroupResponses(result.first()); response.setResponses(agResponses, result.second()); return response; } - public Pair, Integer> listAffinityGroupsInternal(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, - String accountName, Long domainId, boolean isRecursive, boolean listAll, Long startIndex, Long pageSize) { + public Pair, Integer> listAffinityGroupsInternal(Long affinityGroupId, + String affinityGroupName, String affinityGroupType, Long vmId, String accountName, Long domainId, + boolean isRecursive, boolean listAll, Long startIndex, Long pageSize, String keyword) { Account caller = CallContext.current().getCallingAccount(); @@ -3063,9 +3075,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); Filter searchFilter = new Filter(AffinityGroupJoinVO.class, "id", true, startIndex, pageSize); - SearchCriteria sc = - buildAffinityGroupSearchCriteria(domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType); + SearchCriteria sc = buildAffinityGroupSearchCriteria(domainId, isRecursive, + permittedAccounts, listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); Pair, Integer> uniqueGroupsPair = _affinityGroupJoinDao.searchAndCount(sc, searchFilter); // search group details by ids @@ -3084,26 +3095,26 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { if (!permittedAccounts.isEmpty()) { // add domain level affinity groups if (domainId != null) { - SearchCriteria scDomain = - buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, + new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, + affinityGroupType, keyword); vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); } else { for (Long permAcctId : permittedAccounts) { Account permittedAcct = _accountDao.findById(permAcctId); - SearchCriteria scDomain = - buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria( + null, isRecursive, new ArrayList(), + listProjectResourcesCriteria, affinityGroupId, affinityGroupName, affinityGroupType, keyword); vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, permittedAcct.getDomainId())); } } } else if (((permittedAccounts.isEmpty()) && (domainId != null) && isRecursive)) { // list all domain level affinity groups for the domain admin case - SearchCriteria scDomain = - buildAffinityGroupSearchCriteria(null, isRecursive, new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, - affinityGroupType); + SearchCriteria scDomain = buildAffinityGroupSearchCriteria(null, isRecursive, + new ArrayList(), listProjectResourcesCriteria, affinityGroupId, affinityGroupName, + affinityGroupType, keyword); vrs.addAll(listDomainLevelAffinityGroups(scDomain, searchFilter, domainId)); } @@ -3111,8 +3122,9 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { } - private SearchCriteria buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, List permittedAccounts, - ListProjectResourcesCriteria listProjectResourcesCriteria, Long affinityGroupId, String affinityGroupName, String affinityGroupType) { + private SearchCriteria buildAffinityGroupSearchCriteria(Long domainId, boolean isRecursive, + List permittedAccounts, ListProjectResourcesCriteria listProjectResourcesCriteria, + Long affinityGroupId, String affinityGroupName, String affinityGroupType, String keyword) { SearchBuilder groupSearch = _affinityGroupJoinDao.createSearchBuilder(); _accountMgr.buildACLViewSearchBuilder(groupSearch, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); @@ -3135,6 +3147,14 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); } + if (keyword != null) { + SearchCriteria ssc = _affinityGroupJoinDao.createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("type", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + return sc; } diff --git a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index ae661501ce9..f8838d84c35 100644 --- a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -81,6 +81,9 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase { List newTemplateView(VirtualMachineTemplate tmpl, long zoneId, boolean readyOnly); - List searchByTemplateZonePair(String... pairs); + List searchByTemplateZonePair( Boolean showRemoved, String... pairs); List listActiveTemplates(long storeId); + Pair, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter); + } diff --git a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index 792ac0998d7..53cb14a7ed3 100644 --- a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -47,6 +47,8 @@ import com.cloud.user.Account; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.Pair; +import com.cloud.utils.db.Filter; @Component @Local(value = {TemplateJoinDao.class}) @@ -68,6 +70,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im protected TemplateJoinDaoImpl() { tmpltIdPairSearch = createSearchBuilder(); + tmpltIdPairSearch.and("templateState", tmpltIdPairSearch.entity().getTemplateState(), SearchCriteria.Op.EQ); tmpltIdPairSearch.and("tempZonePairIN", tmpltIdPairSearch.entity().getTempZonePair(), SearchCriteria.Op.IN); tmpltIdPairSearch.done(); @@ -84,6 +87,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im activeTmpltSearch = createSearchBuilder(); activeTmpltSearch.and("store_id", activeTmpltSearch.entity().getDataStoreId(), SearchCriteria.Op.EQ); activeTmpltSearch.and("type", activeTmpltSearch.entity().getTemplateType(), SearchCriteria.Op.EQ); + activeTmpltSearch.and("templateState", activeTmpltSearch.entity().getTemplateState(), SearchCriteria.Op.EQ); activeTmpltSearch.done(); // select distinct pair (template_id, zone_id) @@ -371,7 +375,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im } @Override - public List searchByTemplateZonePair(String... idPairs) { + public List searchByTemplateZonePair(Boolean showRemoved, String... idPairs) { // set detail batch query size int DETAILS_BATCH_SIZE = 2000; String batchCfg = _configDao.getValue("detail.batch.query.size"); @@ -389,6 +393,9 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im labels[k] = idPairs[j]; } SearchCriteria sc = tmpltIdPairSearch.create(); + if (!showRemoved) { + sc.setParameters("templateState", VirtualMachineTemplate.State.Active); + } sc.setParameters("tempZonePairIN", labels); List vms = searchIncludingRemoved(sc, null, null, false); if (vms != null) { @@ -404,6 +411,9 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im labels[k] = idPairs[j]; } SearchCriteria sc = tmpltIdPairSearch.create(); + if (!showRemoved) { + sc.setParameters("templateState", VirtualMachineTemplate.State.Active); + } sc.setParameters("tempZonePairIN", labels); List vms = searchIncludingRemoved(sc, null, null, false); if (vms != null) { @@ -418,7 +428,15 @@ public class TemplateJoinDaoImpl extends GenericDaoBase im SearchCriteria sc = activeTmpltSearch.create(); sc.setParameters("store_id", storeId); sc.setParameters("type", TemplateType.USER); + sc.setParameters("templateState", VirtualMachineTemplate.State.Active); return searchIncludingRemoved(sc, null, null, false); } + @Override + public Pair, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter) { + List objects = searchIncludingRemoved(sc, filter, null, false); + Integer count = getCount(sc); + return new Pair, Integer>(objects, count); + } + } diff --git a/server/src/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java index 42a1b0f1c4a..923a2382fdd 100644 --- a/server/src/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserAccountJoinDaoImpl.java @@ -107,7 +107,7 @@ public class UserAccountJoinDaoImpl extends GenericDaoBase searchByAccountId(Long accountId) { SearchCriteria sc = vrAcctIdSearch.create(); sc.setParameters("accountId", accountId); - return searchIncludingRemoved(sc, null, null, false); + return search(sc, null, null, false); } } diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 3e218ecad48..07449a23e3d 100644 --- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -28,15 +28,14 @@ import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ApiConstants.VMDetails; import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.vo.ResourceTagJoinVO; @@ -47,9 +46,11 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.VmStats; +import com.cloud.vm.dao.UserVmDetailsDao; @Component @Local(value = {UserVmJoinDao.class}) @@ -58,6 +59,8 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem @Inject private ConfigurationDao _configDao; + @Inject + private UserVmDetailsDao _userVmDetailsDao; private final SearchBuilder VmDetailSearch; private final SearchBuilder activeVmByIsoSearch; @@ -142,6 +145,10 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setServiceOfferingId(userVm.getServiceOfferingUuid()); userVmResponse.setServiceOfferingName(userVm.getServiceOfferingName()); } + if (details.contains(VMDetails.all) || details.contains(VMDetails.diskoff)) { + userVmResponse.setDiskOfferingId(userVm.getDiskOfferingUuid()); + userVmResponse.setDiskOfferingName(userVm.getDiskOfferingName()); + } if (details.contains(VMDetails.all) || details.contains(VMDetails.servoff) || details.contains(VMDetails.stats)) { userVmResponse.setCpuNumber(userVm.getCpu()); userVmResponse.setCpuSpeed(userVm.getSpeed()); @@ -164,6 +171,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setPublicIpId(userVm.getPublicIpUuid()); userVmResponse.setPublicIp(userVm.getPublicIpAddress()); userVmResponse.setKeyPairName(userVm.getKeypairName()); + userVmResponse.setOsTypeId(userVm.getGuestOsId()); if (details.contains(VMDetails.all) || details.contains(VMDetails.stats)) { // stats calculation @@ -175,8 +183,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem userVmResponse.setNetworkKbsWrite((long)vmStats.getNetworkWriteKBs()); - if ((userVm.getHypervisorType() != null) && - (userVm.getHypervisorType().equals(HypervisorType.KVM) || userVm.getHypervisorType().equals(HypervisorType.XenServer))) { // support KVM and XenServer only util 2013.06.25 + if ((userVm.getHypervisorType() != null) && (userVm.getHypervisorType().equals(HypervisorType.KVM) || userVm.getHypervisorType().equals(HypervisorType.XenServer))) { // support KVM and XenServer only util 2013.06.25 userVmResponse.setDiskKbsRead((long)vmStats.getDiskReadKBs()); userVmResponse.setDiskKbsWrite((long)vmStats.getDiskWriteKBs()); @@ -261,10 +268,11 @@ public class UserVmJoinDaoImpl extends GenericDaoBase implem } // set resource details map - // only hypervisortoolsversion can be returned to the end user } - if (userVm.getDetailName() != null && userVm.getDetailName().equalsIgnoreCase(VmDetailConstants.HYPERVISOR_TOOLS_VERSION)) { + // only hypervisortoolsversion can be returned to the end user + UserVmDetailVO hypervisorToolsVersion = _userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.HYPERVISOR_TOOLS_VERSION); + if (hypervisorToolsVersion != null) { Map resourceDetails = new HashMap(); - resourceDetails.put(userVm.getDetailName(), userVm.getDetailValue()); + resourceDetails.put(hypervisorToolsVersion.getName(), hypervisorToolsVersion.getValue()); userVmResponse.setDetails(resourceDetails); } diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index a8631f781ed..3604883c4ea 100644 --- a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -111,7 +111,7 @@ public class VolumeJoinDaoImpl extends GenericDaoBase implem volResponse.setCreated(volume.getCreatedOnStore()); if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) - volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volume.getFormat()).toString()); + volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volume.getDataCenterId(), volume.getFormat()).toString()); if (volume.getDownloadState() != Status.DOWNLOADED) { String volumeStatus = "Processing"; if (volume.getDownloadState() == VMTemplateHostVO.Status.DOWNLOAD_IN_PROGRESS) { @@ -175,7 +175,7 @@ public class VolumeJoinDaoImpl extends GenericDaoBase implem if (volume.getHypervisorType() != null) { volResponse.setHypervisor(volume.getHypervisorType().toString()); } else { - volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volume.getFormat()).toString()); + volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volume.getDataCenterId(), volume.getFormat()).toString()); } } Long poolId = volume.getPoolId(); diff --git a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java index e295272d748..c096279888d 100644 --- a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java +++ b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java @@ -27,6 +27,7 @@ import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.template.VirtualMachineTemplate.State; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import com.cloud.hypervisor.Hypervisor.HypervisorType; @@ -200,6 +201,10 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity { @Column(name = "size") private long size; + @Column(name = "template_state") + @Enumerated(EnumType.STRING) + private State templateState; + @Column(name = "destroyed") boolean destroyed = false; @@ -532,4 +537,6 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity { public String getTempZonePair() { return tempZonePair; } + + public State getTemplateState() { return templateState; } } diff --git a/server/src/com/cloud/api/query/vo/UserVmJoinVO.java b/server/src/com/cloud/api/query/vo/UserVmJoinVO.java index 4b7294fb87d..472a8cba2af 100644 --- a/server/src/com/cloud/api/query/vo/UserVmJoinVO.java +++ b/server/src/com/cloud/api/query/vo/UserVmJoinVO.java @@ -194,6 +194,15 @@ public class UserVmJoinVO extends BaseViewVO implements ControlledViewEntity { @Column(name = "iso_display_text", length = 4096) private String isoDisplayText; + @Column(name = "disk_offering_id") + private long diskOfferingId; + + @Column(name = "disk_offering_uuid") + private String diskOfferingUuid; + + @Column(name = "disk_offering_name") + private String diskOfferingName; + @Column(name = "service_offering_id") private long serviceOfferingId; @@ -416,6 +425,18 @@ public class UserVmJoinVO extends BaseViewVO implements ControlledViewEntity { return password; } + public String getDiskOfferingName() { + return diskOfferingName; + } + + public String getDiskOfferingUuid() { + return diskOfferingUuid; + } + + public long getDiskOfferingId() { + return diskOfferingId; + } + public void setPassword(String password) { this.password = password; } diff --git a/server/src/com/cloud/api/response/ApiResponseSerializer.java b/server/src/com/cloud/api/response/ApiResponseSerializer.java index 6cdf6c6b45b..d8b728a9f3a 100644 --- a/server/src/com/cloud/api/response/ApiResponseSerializer.java +++ b/server/src/com/cloud/api/response/ApiResponseSerializer.java @@ -26,11 +26,6 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.log4j.Logger; - -import com.google.gson.Gson; -import com.google.gson.annotations.SerializedName; - import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.ResponseObject; @@ -39,6 +34,7 @@ import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.ExceptionResponse; import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; import com.cloud.api.ApiResponseGsonHelper; @@ -46,6 +42,8 @@ import com.cloud.api.ApiServer; import com.cloud.utils.encoding.URLEncoder; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionProxyObject; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; public class ApiResponseSerializer { private static final Logger s_logger = Logger.getLogger(ApiResponseSerializer.class.getName()); @@ -241,6 +239,8 @@ public class ApiResponseSerializer { if (idFieldName != null) { sb.append("<" + "uuidProperty" + ">" + idFieldName + ""); } + } else if (value instanceof String) { + sb.append("<").append(serializedName.value()).append(">").append(value).append(""); } } if (usedUuidList) { diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 7daf2779ed7..957a5d0d974 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -29,6 +29,10 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; @@ -72,6 +76,7 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.DateUtil; @@ -135,6 +140,8 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, ClusterDao _clusterDao; @Inject ConfigDepot _configDepot; + @Inject + DataStoreProviderManager _dataStoreProviderMgr; @Inject ClusterDetailsDao _clusterDetailsDao; @@ -498,12 +505,48 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, } + private long getUsedBytes(StoragePoolVO pool) { + long usedBytes = 0; + + List volumes = _volumeDao.findByPoolId(pool.getId(), null); + + if (volumes != null && volumes.size() > 0) { + DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + PrimaryDataStoreDriver primaryStoreDriver = null; + + if (storeDriver instanceof PrimaryDataStoreDriver) { + primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; + } + + for (VolumeVO volume : volumes) { + if (primaryStoreDriver != null) { + usedBytes += primaryStoreDriver.getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool); + } + else { + usedBytes += volume.getSize(); + } + } + } + + return usedBytes; + } + @Override public long getAllocatedPoolCapacity(StoragePoolVO pool, VMTemplateVO templateForVmCreation) { + long totalAllocatedSize = 0; - // Get size for all the non-destroyed volumes - Pair sizes = _volumeDao.getNonDestroyedCountAndTotalByPool(pool.getId()); - long totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume; + // if the storage pool is managed, the used bytes can be larger than the sum of the sizes of all of the non-destroyed volumes + // in this case, call getUsedBytes(StoragePoolVO) + if (pool.isManaged()) { + totalAllocatedSize = getUsedBytes(pool); + } + else { + // Get size for all the non-destroyed volumes + Pair sizes = _volumeDao.getNonDestroyedCountAndTotalByPool(pool.getId()); + + totalAllocatedSize = sizes.second() + sizes.first() * _extraBytesPerVolume; + } // Get size for VM Snapshots totalAllocatedSize = totalAllocatedSize + _volumeDao.getVMSnapshotSizeByPool(pool.getId()); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 3893c2f9ef2..0c581415f07 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -1109,6 +1109,13 @@ public enum Config { "xenserver61", "default Xen PV driver version for registered template, valid value:xenserver56,xenserver61 ", "xenserver56,xenserver61"), + XenServerHotFix("Advanced", + ManagementServer.class, + Boolean.class, + "xen.hotfix.enabled", + "false", + "Enable/Disable xenserver hot fix", + null), // VMware VmwareUseNexusVSwitch( @@ -1705,8 +1712,6 @@ public enum Config { "false", "Should be set to true, if there will be multiple NetScaler devices providing EIP service in a zone", null), - CustomDiskOfferingMinSize("Advanced", ManagementServer.class, Long.class, "custom.diskoffering.size.min", "1", "Minimum size in GB for custom disk offering", null), - CustomDiskOfferingMaxSize("Advanced", ManagementServer.class, Long.class, "custom.diskoffering.size.max", "1024", "Maximum size in GB for custom disk offering", null), ConsoleProxyServiceOffering( "Advanced", ManagementServer.class, diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 2f5704bb7a8..9d148faa6db 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -36,8 +36,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -85,6 +83,7 @@ import org.apache.cloudstack.region.dao.RegionDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.log4j.Logger; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -403,14 +402,13 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (localCidrs.length > 0) { s_logger.warn("Management network CIDR is not configured originally. Set it default to " + localCidrs[0]); - _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), - "Management network CIDR is not configured originally. Set it default to " + - localCidrs[0], ""); + _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), "Management network CIDR is not configured originally. Set it default to " + + localCidrs[0], ""); _configDao.update(Config.ManagementNetwork.key(), Config.ManagementNetwork.getCategory(), localCidrs[0]); } else { s_logger.warn("Management network CIDR is not properly configured and we are not able to find a default setting"); _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_MANAGMENT_NODE, 0, new Long(0), - "Management network CIDR is not properly configured and we are not able to find a default setting", ""); + "Management network CIDR is not properly configured and we are not able to find a default setting", ""); } } @@ -439,53 +437,53 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // global parameter updation if (scope != null && !scope.isEmpty() && !ConfigKey.Scope.Global.toString().equalsIgnoreCase(scope)) { switch (ConfigKey.Scope.valueOf(scope)) { - case Zone: - DataCenterVO zone = _zoneDao.findById(resourceId); - if (zone == null) { - throw new InvalidParameterValueException("unable to find zone by id " + resourceId); - } - _dcDetailsDao.addDetail(resourceId, name, value); - break; - case Cluster: - ClusterVO cluster = _clusterDao.findById(resourceId); - if (cluster == null) { - throw new InvalidParameterValueException("unable to find cluster by id " + resourceId); - } - ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, name); - if (clusterDetailsVO == null) { - clusterDetailsVO = new ClusterDetailsVO(resourceId, name, value); - _clusterDetailsDao.persist(clusterDetailsVO); - } else { - clusterDetailsVO.setValue(value); - _clusterDetailsDao.update(clusterDetailsVO.getId(), clusterDetailsVO); - } - break; + case Zone: + DataCenterVO zone = _zoneDao.findById(resourceId); + if (zone == null) { + throw new InvalidParameterValueException("unable to find zone by id " + resourceId); + } + _dcDetailsDao.addDetail(resourceId, name, value); + break; + case Cluster: + ClusterVO cluster = _clusterDao.findById(resourceId); + if (cluster == null) { + throw new InvalidParameterValueException("unable to find cluster by id " + resourceId); + } + ClusterDetailsVO clusterDetailsVO = _clusterDetailsDao.findDetail(resourceId, name); + if (clusterDetailsVO == null) { + clusterDetailsVO = new ClusterDetailsVO(resourceId, name, value); + _clusterDetailsDao.persist(clusterDetailsVO); + } else { + clusterDetailsVO.setValue(value); + _clusterDetailsDao.update(clusterDetailsVO.getId(), clusterDetailsVO); + } + break; - case StoragePool: - StoragePoolVO pool = _storagePoolDao.findById(resourceId); - if (pool == null) { - throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId); - } - _storagePoolDetailsDao.addDetail(resourceId, name, value); + case StoragePool: + StoragePoolVO pool = _storagePoolDao.findById(resourceId); + if (pool == null) { + throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId); + } + _storagePoolDetailsDao.addDetail(resourceId, name, value); - break; + break; - case Account: - AccountVO account = _accountDao.findById(resourceId); - if (account == null) { - throw new InvalidParameterValueException("unable to find account by id " + resourceId); - } - AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(resourceId, name); - if (accountDetailVO == null) { - accountDetailVO = new AccountDetailVO(resourceId, name, value); - _accountDetailsDao.persist(accountDetailVO); - } else { - accountDetailVO.setValue(value); - _accountDetailsDao.update(accountDetailVO.getId(), accountDetailVO); - } - break; - default: - throw new InvalidParameterValueException("Scope provided is invalid"); + case Account: + AccountVO account = _accountDao.findById(resourceId); + if (account == null) { + throw new InvalidParameterValueException("unable to find account by id " + resourceId); + } + AccountDetailVO accountDetailVO = _accountDetailsDao.findDetail(resourceId, name); + if (accountDetailVO == null) { + accountDetailVO = new AccountDetailVO(resourceId, name, value); + _accountDetailsDao.persist(accountDetailVO); + } else { + accountDetailVO.setValue(value); + _accountDetailsDao.update(accountDetailVO.getId(), accountDetailVO); + } + break; + default: + throw new InvalidParameterValueException("Scope provided is invalid"); } return value; } @@ -599,8 +597,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long clusterId = cmd.getClusterId(); Long storagepoolId = cmd.getStoragepoolId(); Long accountId = cmd.getAccountId(); - CallContext.current().setEventDetails( - " Name: " + name + " New Value: " + (((name.toLowerCase()).contains("password")) ? "*****" : (((value == null) ? "" : value)))); + CallContext.current().setEventDetails(" Name: " + name + " New Value: " + (((name.toLowerCase()).contains("password")) ? "*****" : (((value == null) ? "" : value)))); // check if config value exists ConfigurationVO config = _configDao.findByName(name); String catergory = null; @@ -781,8 +778,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (route != null) { String routeToVerify = route.trim(); if (!NetUtils.isValidCIDR(routeToVerify)) { - throw new InvalidParameterValueException("Invalid value for blacklisted route: " + route + ". Valid format is list" + - " of cidrs separated by coma. Example: 10.1.1.0/24,192.168.0.0/24"); + throw new InvalidParameterValueException("Invalid value for blacklisted route: " + route + ". Valid format is list" + + " of cidrs separated by coma. Example: 10.1.1.0/24,192.168.0.0/24"); } } } @@ -862,8 +859,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String errorMsg = table.get(2); String dbName; - if (tableName.equals("event") || tableName.equals("cloud_usage") || tableName.equals("usage_vm_instance") || tableName.equals("usage_ip_address") || - tableName.equals("usage_network") || tableName.equals("usage_job") || tableName.equals("account") || tableName.equals("user_statistics")) { + if (tableName.equals("event") || tableName.equals("cloud_usage") || tableName.equals("usage_vm_instance") || tableName.equals("usage_ip_address") + || tableName.equals("usage_network") || tableName.equals("usage_job") || tableName.equals("account") || tableName.equals("user_statistics")) { dbName = "cloud_usage"; } else { dbName = "cloud"; @@ -890,7 +887,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } private void checkPodAttributes(long podId, String podName, long zoneId, String gateway, String cidr, String startIp, String endIp, String allocationStateStr, - boolean checkForDuplicates, boolean skipGatewayOverlapCheck) { + boolean checkForDuplicates, boolean skipGatewayOverlapCheck) { if (checkForDuplicates) { // Check if the pod already exists if (validPod(podName, zoneId)) { @@ -1180,7 +1177,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override public Pod createPod(long zoneId, String name, String startIp, String endIp, String gateway, String netmask, String allocationState) { + // Check if the gateway is a valid IP address + if (!NetUtils.isValidIp(gateway)) { + throw new InvalidParameterValueException("The gateway is invalid"); + } + + if (!NetUtils.isValidNetmask(netmask)) { + throw new InvalidParameterValueException("The netmask is invalid"); + } + String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); + Long userId = CallContext.current().getCallingUserId(); if (allocationState == null) { @@ -1191,8 +1198,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB - public HostPodVO createPod(long userId, String podName, final long zoneId, String gateway, String cidr, final String startIp, String endIp, - String allocationStateStr, boolean skipGatewayOverlapCheck) { + public HostPodVO createPod(long userId, String podName, final long zoneId, String gateway, String cidr, final String startIp, String endIp, String allocationStateStr, + boolean skipGatewayOverlapCheck) { // Check if the zone is valid if (!validZone(zoneId)) { @@ -1354,7 +1361,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } private void checkZoneParameters(String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, boolean checkForDuplicates, Long domainId, - String allocationStateStr, String ip6Dns1, String ip6Dns2) { + String allocationStateStr, String ip6Dns1, String ip6Dns2) { if (checkForDuplicates) { // Check if a zone with the specified name already exists if (validZone(zoneName)) { @@ -1541,7 +1548,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String value = (String)detail.get("value"); if ((key == null) || (value == null)) { throw new InvalidParameterValueException( - "Invalid Zone Detail specified, fields 'key' and 'value' cannot be null, please specify details in the form: details[0].key=XXX&details[0].value=YYY"); + "Invalid Zone Detail specified, fields 'key' and 'value' cannot be null, please specify details in the form: details[0].key=XXX&details[0].value=YYY"); } // validate the zone detail keys are known keys /* @@ -1558,8 +1565,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati for (String dom : dnsSearchOrder) { if (!NetUtils.verifyDomainName(dom)) { throw new InvalidParameterValueException( - "Invalid network domain suffixes. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + "Invalid network domain suffixes. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } } newDetails.put(ZoneConfig.DnsSearchOrder.getName(), StringUtils.join(dnsSearchOrder, ",")); @@ -1621,8 +1628,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (networkDomain != null && !networkDomain.isEmpty()) { if (!NetUtils.verifyDomainName(networkDomain)) { throw new InvalidParameterValueException( - "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } } @@ -1683,10 +1690,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } catch (InvalidParameterValueException noStorage) { PhysicalNetworkTrafficTypeVO mgmtTraffic = _trafficTypeDao.findBy(mgmtPhyNetwork.getId(), TrafficType.Management); _networkSvc.addTrafficTypeToPhysicalNetwork(mgmtPhyNetwork.getId(), TrafficType.Storage.toString(), mgmtTraffic.getXenNetworkLabel(), - mgmtTraffic.getKvmNetworkLabel(), mgmtTraffic.getVmwareNetworkLabel(), mgmtTraffic.getSimulatorNetworkLabel(), mgmtTraffic.getVlan(), - mgmtTraffic.getHypervNetworkLabel()); - s_logger.info("No storage traffic type was specified by admin, create default storage traffic on physical network " + - mgmtPhyNetwork.getId() + " with same configure of management traffic type"); + mgmtTraffic.getKvmNetworkLabel(), mgmtTraffic.getVmwareNetworkLabel(), mgmtTraffic.getSimulatorNetworkLabel(), mgmtTraffic.getVlan(), + mgmtTraffic.getHypervNetworkLabel()); + s_logger.info("No storage traffic type was specified by admin, create default storage traffic on physical network " + mgmtPhyNetwork.getId() + + " with same configure of management traffic type"); } } catch (InvalidParameterValueException ex) { throw new InvalidParameterValueException("Cannot enable this Zone since: " + ex.getMessage()); @@ -1735,8 +1742,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB public DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, - final Long domainId, NetworkType zoneType, String allocationStateStr, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled, - String ip6Dns1, String ip6Dns2) { + final Long domainId, NetworkType zoneType, String allocationStateStr, String networkDomain, boolean isSecurityGroupEnabled, boolean isLocalStorageEnabled, + String ip6Dns1, String ip6Dns2) { // checking the following params outside checkzoneparams method as we do // not use these params for updatezone @@ -1749,8 +1756,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (networkDomain != null) { if (!NetUtils.verifyDomainName(networkDomain)) { throw new InvalidParameterValueException( - "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } } @@ -1760,8 +1767,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String zoneToken = UUID.nameUUIDFromBytes(bytes).toString(); // Create the new zone in the database - final DataCenterVO zoneFinal = - new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, networkDomain, + final DataCenterVO zoneFinal = new DataCenterVO(zoneName, null, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); if (allocationStateStr != null && !allocationStateStr.isEmpty()) { Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr); @@ -1912,8 +1918,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati isSecurityGroupEnabled = true; } - return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, - allocationState, networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); + return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, allocationState, + networkDomain, isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); } @Override @@ -1985,8 +1991,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } else if (VirtualMachine.Type.InternalLoadBalancerVm.toString().toLowerCase().equals(vmTypeString)) { vmType = VirtualMachine.Type.InternalLoadBalancerVm; } else { - throw new InvalidParameterValueException("Invalid systemVmType. Supported types are: " + VirtualMachine.Type.DomainRouter + ", " + - VirtualMachine.Type.ConsoleProxy + ", " + VirtualMachine.Type.SecondaryStorageVm); + throw new InvalidParameterValueException("Invalid systemVmType. Supported types are: " + VirtualMachine.Type.DomainRouter + ", " + VirtualMachine.Type.ConsoleProxy + + ", " + VirtualMachine.Type.SecondaryStorageVm); } } else { allowNetworkRate = true; @@ -1994,8 +2000,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } if (cmd.getNetworkRate() != null && !allowNetworkRate) { - throw new InvalidParameterValueException( - "Network rate can be specified only for non-System offering and system offerings having \"domainrouter\" systemvmtype"); + throw new InvalidParameterValueException("Network rate can be specified only for non-System offering and system offerings having \"domainrouter\" systemvmtype"); } if (cmd.getDeploymentPlanner() != null) { @@ -2009,19 +2014,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), - localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), - cmd.getDetails(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate()); + return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired, + offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), + cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate()); } - protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, - Integer speed, String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, - String hostTag, Integer networkRate, String deploymentPlanner, Map details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, - Long iopsWriteRate) { + protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed, + String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, + Integer networkRate, String deploymentPlanner, Map details, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) { tags = StringUtils.cleanupTags(tags); - ServiceOfferingVO offering = - new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, - isSystem, vmType, domainId, hostTag, deploymentPlanner); + ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, + false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner); if ((bytesReadRate != null) && (bytesReadRate > 0)) offering.setBytesReadRate(bytesReadRate); if ((bytesWriteRate != null) && (bytesWriteRate > 0)) @@ -2119,9 +2122,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, - boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops, Long bytesReadRate, Long bytesWriteRate, - Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) { + protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, + boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, + Integer hypervisorSnapshotReserve) { long diskSize = 0;// special case for custom disk offerings if (numGibibytes != null && (numGibibytes <= 0)) { throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb."); @@ -2239,8 +2242,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long iopsWriteRate = cmd.getIopsWriteRate(); Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve(); - return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, - minIops, maxIops, bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve); + return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, + maxIops, bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve); } @Override @@ -2369,7 +2372,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @DB @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, eventDescription = "creating vlan ip range", async = false) public Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, - ResourceAllocationException { + ResourceAllocationException { Long zoneId = cmd.getZoneId(); Long podId = cmd.getPodId(); String startIP = cmd.getStartIp(); @@ -2545,8 +2548,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (zone.getNetworkType() == DataCenter.NetworkType.Advanced) { if (network.getTrafficType() == TrafficType.Guest) { if (network.getGuestType() != GuestType.Shared) { - throw new InvalidParameterValueException("Can execute createVLANIpRanges on shared guest network, but type of this guest network " + network.getId() + - " is " + network.getGuestType()); + throw new InvalidParameterValueException("Can execute createVLANIpRanges on shared guest network, but type of this guest network " + network.getId() + " is " + + network.getGuestType()); } List vlans = _vlanDao.listVlansByNetworkId(network.getId()); @@ -2554,9 +2557,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati VlanVO vlan = vlans.get(0); if (vlanId == null) { vlanId = vlan.getVlanTag(); - } else if (!vlan.getVlanTag().equals(vlanId)) { - throw new InvalidParameterValueException("there is already one vlan " + vlan.getVlanTag() + " on network :" + +network.getId() + - ", only one vlan is allowed on guest network"); + } else if (!NetUtils.isSameIsolationId(vlan.getVlanTag(), vlanId)) { + throw new InvalidParameterValueException("there is already one vlan " + vlan.getVlanTag() + " on network :" + +network.getId() + + ", only one vlan is allowed on guest network"); } } sameSubnet = validateIpRange(startIP, endIP, newVlanGateway, newVlanNetmask, vlans, ipv4, ipv6, ip6Gateway, ip6Cidr, startIPv6, endIPv6, network); @@ -2588,24 +2591,23 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati checkOverlapPrivateIpRange(zoneId, startIP, endIP); } - return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, networkId, physicalNetworkId, startIPv6, endIPv6, - ip6Gateway, ip6Cidr, vlanOwner, network, sameSubnet); + return commitVlan(zoneId, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, forVirtualNetwork, networkId, physicalNetworkId, startIPv6, endIPv6, ip6Gateway, + ip6Cidr, vlanOwner, network, sameSubnet); } - private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, - final String newVlanNetmaskFinal, final String vlanId, final Boolean forVirtualNetwork, final Long networkId, final Long physicalNetworkId, - final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr, final Account vlanOwner, final Network network, - final Pair> sameSubnet) { + private Vlan commitVlan(final Long zoneId, final Long podId, final String startIP, final String endIP, final String newVlanGatewayFinal, final String newVlanNetmaskFinal, + final String vlanId, final Boolean forVirtualNetwork, final Long networkId, final Long physicalNetworkId, final String startIPv6, final String endIPv6, + final String ip6Gateway, final String ip6Cidr, final Account vlanOwner, final Network network, final Pair> sameSubnet) { return Transaction.execute(new TransactionCallback() { @Override public Vlan doInTransaction(TransactionStatus status) { String newVlanNetmask = newVlanNetmaskFinal; String newVlanGateway = newVlanGatewayFinal; - if ((sameSubnet == null || sameSubnet.first() == false) && (network.getTrafficType() == TrafficType.Guest) && - (network.getGuestType() == GuestType.Shared) && (_vlanDao.listVlansByNetworkId(networkId) != null)) { - Map dhcpCapabilities = - _networkSvc.getNetworkOfferingServiceCapabilities(_networkOfferingDao.findById(network.getNetworkOfferingId()), Service.Dhcp); + if ((sameSubnet == null || sameSubnet.first() == false) && (network.getTrafficType() == TrafficType.Guest) && (network.getGuestType() == GuestType.Shared) + && (_vlanDao.listVlansByNetworkId(networkId) != null)) { + Map dhcpCapabilities = _networkSvc.getNetworkOfferingServiceCapabilities(_networkOfferingDao.findById(network.getNetworkOfferingId()), + Service.Dhcp); String supportsMultipleSubnets = dhcpCapabilities.get(Capability.DhcpAccrossMultipleSubnets); if (supportsMultipleSubnets == null || !Boolean.valueOf(supportsMultipleSubnets)) { throw new InvalidParameterValueException("The Dhcp serivice provider for this network dose not support the dhcp across multiple subnets"); @@ -2618,8 +2620,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati newVlanGateway = sameSubnet.second().first(); newVlanNetmask = sameSubnet.second().second(); } - Vlan vlan = - createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, + Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); // create an entry in the nic_secondary table. This will be the new // gateway that will be configured on the corresponding routervm. @@ -2655,8 +2656,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return (NetUtils.isNetowrkASubsetOrSupersetOfNetworkB(cidrnew, existing_cidr)); } - public Pair> validateIpRange(String startIP, String endIP, String newVlanGateway, String newVlanNetmask, List vlans, - boolean ipv4, boolean ipv6, String ip6Gateway, String ip6Cidr, String startIPv6, String endIPv6, Network network) { + public Pair> validateIpRange(String startIP, String endIP, String newVlanGateway, String newVlanNetmask, List vlans, boolean ipv4, + boolean ipv6, String ip6Gateway, String ip6Cidr, String startIPv6, String endIPv6, Network network) { String vlanGateway = null; String vlanNetmask = null; boolean sameSubnet = false; @@ -2670,8 +2671,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (val == NetUtils.supersetOrSubset.isSuperset) { // this means that new cidr is a superset of the // existing subnet. - throw new InvalidParameterValueException("The subnet you are trying to add is a superset of the existing subnet having gateway" + - vlan.getVlanGateway() + " and netmask " + vlan.getVlanNetmask()); + throw new InvalidParameterValueException("The subnet you are trying to add is a superset of the existing subnet having gateway" + vlan.getVlanGateway() + + " and netmask " + vlan.getVlanNetmask()); } else if (val == NetUtils.supersetOrSubset.neitherSubetNorSuperset) { // this implies the user is trying to add a new subnet // which is not a superset or subset of this subnet. @@ -2679,8 +2680,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati continue; } else if (val == NetUtils.supersetOrSubset.isSubset) { // this means he is trying to add to the same subnet. - throw new InvalidParameterValueException("The subnet you are trying to add is a subset of the existing subnet having gateway" + - vlan.getVlanGateway() + " and netmask " + vlan.getVlanNetmask()); + throw new InvalidParameterValueException("The subnet you are trying to add is a subset of the existing subnet having gateway" + vlan.getVlanGateway() + + " and netmask " + vlan.getVlanNetmask()); } else if (val == NetUtils.supersetOrSubset.sameSubnet) { sameSubnet = true; //check if the gateway provided by the user is same as that of the subnet. @@ -2705,8 +2706,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } if (newVlanGateway == null && newVlanNetmask == null && sameSubnet == false) { - throw new InvalidParameterValueException( - "The ip range dose not belong to any of the existing subnets, Provide the netmask and gateway if you want to add new subnet"); + throw new InvalidParameterValueException("The ip range dose not belong to any of the existing subnets, Provide the netmask and gateway if you want to add new subnet"); } Pair vlanDetails = null; @@ -2727,7 +2727,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) { + String vlanGateway, String vlanNetmask, String vlanId, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) { Network network = _networkModel.getNetwork(networkId); boolean ipv4 = false, ipv6 = false; @@ -2769,8 +2769,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // pod vlans can be created in basic zone only if (zone.getNetworkType() != NetworkType.Basic || network.getTrafficType() != TrafficType.Guest) { - throw new InvalidParameterValueException("Pod id can be specified only for the networks of type " + TrafficType.Guest + " in zone of type " + - NetworkType.Basic); + throw new InvalidParameterValueException("Pod id can be specified only for the networks of type " + TrafficType.Guest + " in zone of type " + NetworkType.Basic); } } @@ -2790,15 +2789,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (vlanId != null) { // if vlan is specified, throw an error if it's not equal to // network's vlanId - if (networkVlanId != null && !networkVlanId.equalsIgnoreCase(vlanId)) { + if (networkVlanId != null && !NetUtils.isSameIsolationId(networkVlanId, vlanId)) { throw new InvalidParameterValueException("Vlan doesn't match vlan of the network"); } } else { vlanId = networkVlanId; } } else if (network.getTrafficType() == TrafficType.Public && vlanId == null) { - // vlan id is required for public network - throw new InvalidParameterValueException("Vlan id is required when add ip range to the public network"); + throw new InvalidParameterValueException("Unable to determine vlan id or untagged vlan for public network"); } if (vlanId == null) { @@ -2836,8 +2834,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String newCidr = NetUtils.getCidrFromGatewayAndNetmask(vlanGateway, vlanNetmask); //Make sure start and end ips are with in the range of cidr calculated for this gateway and netmask { - if (!NetUtils.isIpWithtInCidrRange(vlanGateway, newCidr) || !NetUtils.isIpWithtInCidrRange(startIP, newCidr) || - !NetUtils.isIpWithtInCidrRange(endIP, newCidr)) { + if (!NetUtils.isIpWithtInCidrRange(vlanGateway, newCidr) || !NetUtils.isIpWithtInCidrRange(startIP, newCidr) || !NetUtils.isIpWithtInCidrRange(endIP, newCidr)) { throw new InvalidParameterValueException("Please specify a valid IP range or valid netmask or valid gateway"); } @@ -2847,8 +2844,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String guestNetworkCidr = zone.getGuestNetworkCidr(); if (guestNetworkCidr != null) { if (NetUtils.isNetworksOverlap(newCidr, guestNetworkCidr)) { - throw new InvalidParameterValueException("The new IP range you have specified has overlapped with the guest network in zone: " + zone.getName() + - ". Please specify a different gateway/netmask."); + throw new InvalidParameterValueException("The new IP range you have specified has overlapped with the guest network in zone: " + zone.getName() + + ". Please specify a different gateway/netmask."); } } @@ -2876,13 +2873,13 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati continue; } // from here, subnet overlaps - if (!vlanId.equals(vlan.getVlanTag())) { - throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " in zone " + zone.getName() + - " has overlapped with the subnet. Please specify a different gateway/netmask."); + if (!NetUtils.isSameIsolationId(vlanId, vlan.getVlanTag())) { + throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " in zone " + zone.getName() + + " has overlapped with the subnet. Please specify a different gateway/netmask."); } if (vlan.getNetworkId() != networkId) { - throw new InvalidParameterValueException("This subnet is overlapped with subnet in other network " + vlan.getNetworkId() + " in zone " + - zone.getName() + " . Please specify a different gateway/netmask."); + throw new InvalidParameterValueException("This subnet is overlapped with subnet in other network " + vlan.getNetworkId() + " in zone " + zone.getName() + + " . Please specify a different gateway/netmask."); } String[] otherVlanIpRange = vlan.getIpRange().split("\\-"); @@ -2894,12 +2891,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati //extend IP range if (!vlanGateway.equals(otherVlanGateway) || !vlanNetmask.equals(vlan.getVlanNetmask())) { - throw new InvalidParameterValueException("The IP range has already been added with gateway " + otherVlanGateway + " ,and netmask " + - otherVlanNetmask + ", Please specify the gateway/netmask if you want to extend ip range"); + throw new InvalidParameterValueException("The IP range has already been added with gateway " + otherVlanGateway + " ,and netmask " + otherVlanNetmask + + ", Please specify the gateway/netmask if you want to extend ip range"); } if (NetUtils.ipRangesOverlap(startIP, endIP, otherVlanStartIP, otherVlanEndIP)) { - throw new InvalidParameterValueException("The IP range already has IPs that overlap with the new range." - + " Please specify a different start IP/end IP."); + throw new InvalidParameterValueException("The IP range already has IPs that overlap with the new range." + " Please specify a different start IP/end IP."); } } } @@ -2916,15 +2912,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (vlan.getIp6Gateway() == null) { continue; } - if (vlanId.equals(vlan.getVlanTag())) { + if (NetUtils.isSameIsolationId(vlanId, vlan.getVlanTag())) { if (NetUtils.isIp6RangeOverlap(ipv6Range, vlan.getIp6Range())) { - throw new InvalidParameterValueException("The IPv6 range with tag: " + vlan.getVlanTag() + - " already has IPs that overlap with the new range. Please specify a different start IP/end IP."); + throw new InvalidParameterValueException("The IPv6 range with tag: " + vlan.getVlanTag() + + " already has IPs that overlap with the new range. Please specify a different start IP/end IP."); } if (!vlanIp6Gateway.equals(vlan.getIp6Gateway())) { - throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + - vlan.getIp6Gateway() + ". Please specify a different tag."); + throw new InvalidParameterValueException("The IP range with tag: " + vlan.getVlanTag() + " has already been added with gateway " + vlan.getIp6Gateway() + + ". Please specify a different tag."); } } } @@ -2932,8 +2928,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // Check if the vlan is being used if (_zoneDao.findVnet(zoneId, physicalNetworkId, vlanId).size() > 0) { - throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + - zone.getName()); + throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + + zone.getName()); } String ipRange = null; @@ -2946,21 +2942,19 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } // Everything was fine, so persist the VLAN - VlanVO vlan = - commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, vlanIp6Gateway, vlanIp6Cidr, + VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, vlanOwner, vlanIp6Gateway, vlanIp6Cidr, ipv4, zone, vlanType, ipv6Range, ipRange); return vlan; } - private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, - final String endIP, final String vlanGateway, final String vlanNetmask, final String vlanId, final Account vlanOwner, final String vlanIp6Gateway, - final String vlanIp6Cidr, final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange) { + private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, final String endIP, + final String vlanGateway, final String vlanNetmask, final String vlanId, final Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr, + final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange) { return Transaction.execute(new TransactionCallback() { @Override public VlanVO doInTransaction(TransactionStatus status) { - VlanVO vlan = - new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId, vlanIp6Gateway, vlanIp6Cidr, ipv6Range); + VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId, vlanIp6Gateway, vlanIp6Cidr, ipv6Range); s_logger.debug("Saving vlan range " + vlan); vlan = _vlanDao.persist(vlan); @@ -2982,8 +2976,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // range List ips = _publicIpAddressDao.listByVlanId(vlan.getId()); for (IPAddressVO ip : ips) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), - ip.getAddress().toString(), ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), + ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); } // increment resource count for dedicated public ip's _resourceLimitMgr.incrementResourceCount(vlanOwner.getId(), ResourceType.public_ip, new Long(ips.size())); @@ -3031,19 +3025,19 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } for (IPAddressVO ip : ips) { if (ip.isOneToOneNat()) { - throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + - " belonging to the range is used for static nat purposes. Cleanup the rules first"); + throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + + " belonging to the range is used for static nat purposes. Cleanup the rules first"); } if (ip.isSourceNat()) { - throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + - " belonging to the range is a source nat ip for the network id=" + ip.getSourceNetworkId() + - ". IP range with the source nat ip address can be removed either as a part of Network, or account removal"); + throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + + " belonging to the range is a source nat ip for the network id=" + ip.getSourceNetworkId() + + ". IP range with the source nat ip address can be removed either as a part of Network, or account removal"); } if (_firewallDao.countRulesByIpId(ip.getId()) > 0) { - throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + - " belonging to the range has firewall rules applied. Cleanup the rules first"); + throw new InvalidParameterValueException("Can't delete account specific vlan " + vlanDbId + " as ip " + ip + + " belonging to the range has firewall rules applied. Cleanup the rules first"); } // release public ip address here success = success && _ipAddrMgr.disassociatePublicIpAddress(ip.getId(), userId, caller); @@ -3052,8 +3046,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati s_logger.warn("Some ip addresses failed to be released as a part of vlan " + vlanDbId + " removal"); } else { for (IPAddressVO ip : ips) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_RELEASE, acctVln.get(0).getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress() - .toString(), ip.isSourceNat(), vlanRange.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_RELEASE, acctVln.get(0).getId(), ip.getDataCenterId(), ip.getId(), + ip.getAddress().toString(), ip.isSourceNat(), vlanRange.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); } } } finally { @@ -3063,8 +3057,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati NicIpAliasVO ipAlias = _nicIpAliasDao.findByGatewayAndNetworkIdAndState(vlanRange.getVlanGateway(), vlanRange.getNetworkId(), NicIpAlias.state.active); //check if the ipalias belongs to the vlan range being deleted. if (ipAlias != null && vlanDbId == _publicIpAddressDao.findByIpAndSourceNetworkId(vlanRange.getNetworkId(), ipAlias.getIp4Address()).getVlanId()) { - throw new InvalidParameterValueException("Cannot delete vlan range " + vlanDbId + " as " + ipAlias.getIp4Address() + - "is being used for providing dhcp service in this subnet. Delete all VMs in this subnet and try again"); + throw new InvalidParameterValueException("Cannot delete vlan range " + vlanDbId + " as " + ipAlias.getIp4Address() + + "is being used for providing dhcp service in this subnet. Delete all VMs in this subnet and try again"); } allocIpCount = _publicIpAddressDao.countIPs(vlanRange.getDataCenterId(), vlanDbId, true); if (allocIpCount > 0) { @@ -3159,8 +3153,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // generate usage event for dedication of every ip address in the range for (IPAddressVO ip : ips) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), - ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, vlanOwner.getId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), ip.isSourceNat(), + vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); } // increment resource count for dedicated public ip's @@ -3230,8 +3224,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // generate usage events to remove dedication for every ip in the range that has been disassociated for (IPAddressVO ip : ips) { if (!ipsInUse.contains(ip)) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_RELEASE, acctVln.get(0).getAccountId(), ip.getDataCenterId(), ip.getId(), ip.getAddress() - .toString(), ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_RELEASE, acctVln.get(0).getAccountId(), ip.getDataCenterId(), ip.getId(), ip.getAddress().toString(), + ip.isSourceNat(), vlan.getVlanType().toString(), ip.getSystem(), ip.getClass().getName(), ip.getUuid()); } } // decrement resource count for dedicated public ip's @@ -3294,15 +3288,15 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati private void checkConflictsWithPortableIpRange(long zoneId, String vlanId, String vlanGateway, String vlanNetmask, String startIP, String endIP) { // check and throw exception if there is portable IP range that overlaps with public ip range being configured if (checkOverlapPortableIpRange(_regionDao.getRegionId(), startIP, endIP)) { - throw new InvalidParameterValueException("Ip range: " + startIP + "-" + endIP + " overlaps with a portable" + " IP range already configured in the region " + - _regionDao.getRegionId()); + throw new InvalidParameterValueException("Ip range: " + startIP + "-" + endIP + " overlaps with a portable" + " IP range already configured in the region " + + _regionDao.getRegionId()); } // verify and throw exception if the VLAN Id is used by any portable IP range List existingPortableIPRanges = _portableIpRangeDao.listByRegionId(_regionDao.getRegionId()); if (existingPortableIPRanges != null && !existingPortableIPRanges.isEmpty()) { for (PortableIpRangeVO portableIpRange : existingPortableIPRanges) { - if (portableIpRange.getVlanTag().equalsIgnoreCase(vlanId)) { + if (NetUtils.isSameIsolationId(portableIpRange.getVlanTag(), vlanId)) { throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for portable ip range in this region"); } } @@ -3377,10 +3371,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (cidrSubnet.equals(guestSubnet)) { if (podName.equals("newPod")) { throw new InvalidParameterValueException( - "The subnet of the pod you are adding conflicts with the subnet of the Guest IP Network. Please specify a different CIDR."); + "The subnet of the pod you are adding conflicts with the subnet of the Guest IP Network. Please specify a different CIDR."); } else { - throw new InvalidParameterValueException("Warning: The subnet of pod " + podName + " in zone " + zoneName + - " conflicts with the subnet of the Guest IP Network. Please change either the pod's CIDR or the Guest IP Network's subnet, and re-run install-vmops-management."); + throw new InvalidParameterValueException( + "Warning: The subnet of pod " + + podName + + " in zone " + + zoneName + + " conflicts with the subnet of the Guest IP Network. Please change either the pod's CIDR or the Guest IP Network's subnet, and re-run install-vmops-management."); } } } @@ -3408,11 +3406,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (cidrSubnet.equals(otherCidrSubnet)) { String otherPodName = getPodName(otherPodId.longValue()); if (podName.equals("newPod")) { - throw new InvalidParameterValueException("The subnet of the pod you are adding conflicts with the subnet of pod " + otherPodName + " in zone " + - zoneName + ". Please specify a different CIDR."); + throw new InvalidParameterValueException("The subnet of the pod you are adding conflicts with the subnet of pod " + otherPodName + " in zone " + zoneName + + ". Please specify a different CIDR."); } else { - throw new InvalidParameterValueException("Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName + - " have conflicting CIDR subnets. Please change the CIDR of one of these pods."); + throw new InvalidParameterValueException("Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName + + " have conflicting CIDR subnets. Please change the CIDR of one of these pods."); } } } @@ -3629,8 +3627,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // Allow to specify more than 1 provider per service only if // the service is LB if (!serviceStr.equalsIgnoreCase(Service.Lb.getName()) && svcPrv.get(serviceStr) != null && svcPrv.get(serviceStr).size() > 1) { - throw new InvalidParameterValueException("In the current release only one provider can be " - + "specified for the service if the service is not LB"); + throw new InvalidParameterValueException("In the current release only one provider can be " + "specified for the service if the service is not LB"); } for (String prvNameStr : svcPrv.get(serviceStr)) { // check if provider is supported @@ -3720,10 +3717,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati firewallProviderSet.add(firewallProvider); serviceProviderMap.put(Service.Firewall, firewallProviderSet); if (!(firewallProvider.getName().equals(Provider.JuniperSRX.getName()) || firewallProvider.getName().equals(Provider.PaloAlto.getName()) || firewallProvider.getName() - .equals(Provider.VirtualRouter.getName())) && - egressDefaultPolicy == false) { - throw new InvalidParameterValueException("Firewall egress with default policy " + egressDefaultPolicy + " is not supported by the provider " + - firewallProvider.getName()); + .equals(Provider.VirtualRouter.getName())) && egressDefaultPolicy == false) { + throw new InvalidParameterValueException("Firewall egress with default policy " + egressDefaultPolicy + " is not supported by the provider " + + firewallProvider.getName()); } } @@ -3744,8 +3740,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - NetworkOffering offering = - createNetworkOffering(name, displayText, trafficType, tags, specifyVlan, availability, networkRate, serviceProviderMap, false, guestType, false, + NetworkOffering offering = createNetworkOffering(name, displayText, trafficType, tags, specifyVlan, availability, networkRate, serviceProviderMap, false, guestType, false, serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive); CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); return offering; @@ -3754,8 +3749,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati void validateLoadBalancerServiceCapabilities(Map lbServiceCapabilityMap) { if (lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { if (lbServiceCapabilityMap.keySet().size() > 3 || !lbServiceCapabilityMap.containsKey(Capability.SupportedLBIsolation)) { - throw new InvalidParameterValueException("Only " + Capability.SupportedLBIsolation.getName() + ", " + Capability.ElasticLb.getName() + ", " + - Capability.InlineMode.getName() + " capabilities can be sepcified for LB service"); + throw new InvalidParameterValueException("Only " + Capability.SupportedLBIsolation.getName() + ", " + Capability.ElasticLb.getName() + ", " + + Capability.InlineMode.getName() + " capabilities can be sepcified for LB service"); } for (Capability cap : lbServiceCapabilityMap.keySet()) { @@ -3785,8 +3780,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Unknown specified value for " + Capability.LbSchemes.getName()); } } else { - throw new InvalidParameterValueException("Only " + Capability.SupportedLBIsolation.getName() + ", " + Capability.ElasticLb.getName() + ", " + - Capability.InlineMode.getName() + ", " + Capability.LbSchemes.getName() + " capabilities can be sepcified for LB service"); + throw new InvalidParameterValueException("Only " + Capability.SupportedLBIsolation.getName() + ", " + Capability.ElasticLb.getName() + ", " + + Capability.InlineMode.getName() + ", " + Capability.LbSchemes.getName() + " capabilities can be sepcified for LB service"); } } } @@ -3795,8 +3790,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati void validateSourceNatServiceCapablities(Map sourceNatServiceCapabilityMap) { if (sourceNatServiceCapabilityMap != null && !sourceNatServiceCapabilityMap.isEmpty()) { if (sourceNatServiceCapabilityMap.keySet().size() > 2) { - throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter + - " capabilities can be sepcified for source nat service"); + throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter + + " capabilities can be sepcified for source nat service"); } for (Capability capability : sourceNatServiceCapabilityMap.keySet()) { @@ -3805,8 +3800,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati boolean perAccount = value.contains("peraccount"); boolean perZone = value.contains("perzone"); if ((perAccount && perZone) || (!perAccount && !perZone)) { - throw new InvalidParameterValueException("Either peraccount or perzone source NAT type can be specified for " + - Capability.SupportedSourceNatTypes.getName()); + throw new InvalidParameterValueException("Either peraccount or perzone source NAT type can be specified for " + + Capability.SupportedSourceNatTypes.getName()); } } else if (capability == Capability.RedundantRouter) { boolean enabled = value.contains("true"); @@ -3815,8 +3810,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Unknown specified value for " + Capability.RedundantRouter.getName()); } } else { - throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter + - " capabilities can be sepcified for source nat service"); + throw new InvalidParameterValueException("Only " + Capability.SupportedSourceNatTypes.getName() + " and " + Capability.RedundantRouter + + " capabilities can be sepcified for source nat service"); } } } @@ -3836,13 +3831,13 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } else if (capability == Capability.AssociatePublicIP) { associatePublicIP = value.contains("true"); } else { - throw new InvalidParameterValueException("Only " + Capability.ElasticIp.getName() + " and " + Capability.AssociatePublicIP.getName() + - " capabilitiy can be sepcified for static nat service"); + throw new InvalidParameterValueException("Only " + Capability.ElasticIp.getName() + " and " + Capability.AssociatePublicIP.getName() + + " capabilitiy can be sepcified for static nat service"); } } if ((!eipEnabled) && associatePublicIP) { - throw new InvalidParameterValueException("Capability " + Capability.AssociatePublicIP.getName() + " can only be set when capability " + - Capability.ElasticIp.getName() + " is true"); + throw new InvalidParameterValueException("Capability " + Capability.AssociatePublicIP.getName() + " can only be set when capability " + + Capability.ElasticIp.getName() + " is true"); } } } @@ -3850,9 +3845,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB public NetworkOfferingVO createNetworkOffering(String name, String displayText, TrafficType trafficType, String tags, boolean specifyVlan, Availability availability, - Integer networkRate, final Map> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, - boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, - final Map details, boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive) { + Integer networkRate, final Map> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, + boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, + final Map details, boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive) { String multicastRateStr = _configDao.getValue("multicast.throttling.rate"); int multicastRate = ((multicastRateStr == null) ? 10 : Integer.parseInt(multicastRateStr)); @@ -3887,15 +3882,14 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (availability == NetworkOffering.Availability.Required) { boolean canOffBeRequired = (type == GuestType.Isolated && serviceProviderMap.containsKey(Service.SourceNat)); if (!canOffBeRequired) { - throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + - GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); + throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + + GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); } // only one network offering in the system can be Required List offerings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (!offerings.isEmpty()) { - throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + - Availability.Required); + throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + Availability.Required); } } @@ -3953,8 +3947,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if ((sourceNatServiceCapabilityMap != null) && (!sourceNatServiceCapabilityMap.isEmpty())) { String sourceNatType = sourceNatServiceCapabilityMap.get(Capability.SupportedSourceNatTypes); if (sourceNatType != null) { - _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, - sourceNatType); + _networkModel.checkCapabilityForProvider(serviceProviderMap.get(Service.SourceNat), Service.SourceNat, Capability.SupportedSourceNatTypes, sourceNatType); sharedSourceNat = sourceNatType.contains("perzone"); } @@ -3983,10 +3976,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati publicLb = true; } - final NetworkOfferingVO offeringFinal = - new NetworkOfferingVO(name, displayText, trafficType, systemOnly, specifyVlan, networkRate, multicastRate, isDefault, availability, tags, type, conserveMode, - dedicatedLb, sharedSourceNat, redundantRouter, elasticIp, elasticLb, specifyIpRanges, inline, isPersistent, associatePublicIp, publicLb, internalLb, - egressDefaultPolicy); + final NetworkOfferingVO offeringFinal = new NetworkOfferingVO(name, displayText, trafficType, systemOnly, specifyVlan, networkRate, multicastRate, isDefault, availability, + tags, type, conserveMode, dedicatedLb, sharedSourceNat, redundantRouter, elasticIp, elasticLb, specifyIpRanges, inline, isPersistent, associatePublicIp, publicLb, + internalLb, egressDefaultPolicy); if (serviceOfferingId != null) { offeringFinal.setServiceOfferingId(serviceOfferingId); @@ -4061,8 +4053,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } if (lbProvider == null) { - throw new InvalidParameterValueException("Invalid value " + details.get(detail) + " for the detail " + detail + - ". The provider is not supported by the network offering"); + throw new InvalidParameterValueException("Invalid value " + details.get(detail) + " for the detail " + detail + + ". The provider is not supported by the network offering"); } // 2) validate if the provider supports the scheme @@ -4328,8 +4320,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // though) int networkCount = _networkDao.getNetworkCountByNetworkOffId(offeringId); if (networkCount > 0) { - throw new InvalidParameterValueException("Can't delete network offering " + offeringId + " as its used by " + networkCount + " networks. " + - "To make the network offering unavaiable, disable it"); + throw new InvalidParameterValueException("Can't delete network offering " + offeringId + " as its used by " + networkCount + " networks. " + + "To make the network offering unavaiable, disable it"); } if (_networkOfferingDao.remove(offeringId)) { @@ -4401,19 +4393,18 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("Invalid value for Availability. Supported types: " + Availability.Required + ", " + Availability.Optional); } else { if (availability == NetworkOffering.Availability.Required) { - boolean canOffBeRequired = - (offeringToUpdate.getGuestType() == GuestType.Isolated && _networkModel.areServicesSupportedByNetworkOffering(offeringToUpdate.getId(), - Service.SourceNat)); + boolean canOffBeRequired = (offeringToUpdate.getGuestType() == GuestType.Isolated && _networkModel.areServicesSupportedByNetworkOffering( + offeringToUpdate.getId(), Service.SourceNat)); if (!canOffBeRequired) { - throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + - GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); + throw new InvalidParameterValueException("Availability can be " + NetworkOffering.Availability.Required + " only for networkOfferings of type " + + GuestType.Isolated + " and with " + Service.SourceNat.getName() + " enabled"); } // only one network offering in the system can be Required List offerings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (!offerings.isEmpty() && offerings.get(0).getId() != offeringToUpdate.getId()) { - throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + - Availability.Required); + throw new InvalidParameterValueException("System already has network offering id=" + offerings.get(0).getId() + " with availability " + + Availability.Required); } } offering.setAvailability(availability); @@ -4653,17 +4644,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (!NetUtils.sameSubnet(startIP, gateway, netmask)) { throw new InvalidParameterValueException("Please ensure that your start IP is in the same subnet as " - + "your portable IP range's gateway and as per the IP range's netmask."); + + "your portable IP range's gateway and as per the IP range's netmask."); } if (!NetUtils.sameSubnet(endIP, gateway, netmask)) { throw new InvalidParameterValueException("Please ensure that your end IP is in the same subnet as " - + "your portable IP range's gateway and as per the IP range's netmask."); + + "your portable IP range's gateway and as per the IP range's netmask."); } if (checkOverlapPortableIpRange(regionId, startIP, endIP)) { - throw new InvalidParameterValueException("Ip range: " + startIP + "-" + endIP + " overlaps with a portable" + " IP range already configured in the region " + - regionId); + throw new InvalidParameterValueException("Ip range: " + startIP + "-" + endIP + " overlaps with a portable" + " IP range already configured in the region " + regionId); } if (vlanId == null) { @@ -4678,8 +4668,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati for (DataCenterVO zone : zones) { // check if there is zone vlan with same id if (_vlanDao.findByZoneAndVlanId(zone.getId(), vlanId) != null) - throw new InvalidParameterValueException("Found a VLAN id " + vlanId + " already existing in" + " zone " + zone.getUuid() + - " that conflicts with VLAN id of the portable ip range being configured"); + throw new InvalidParameterValueException("Found a VLAN id " + vlanId + " already existing in" + " zone " + zone.getUuid() + + " that conflicts with VLAN id of the portable ip range being configured"); //check if there is a public ip range that overlaps with portable ip range being created checkOverlapPublicIpRange(zone.getId(), startIP, endIP); } diff --git a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java index 0f4b27a40b6..20366095048 100755 --- a/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/AgentBasedConsoleProxyManager.java @@ -25,6 +25,7 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import com.cloud.agent.AgentManager; import com.cloud.agent.api.GetVncPortAnswer; @@ -33,7 +34,6 @@ import com.cloud.agent.api.StartupProxyCommand; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyInfo; -import com.cloud.keystore.KeystoreManager; import com.cloud.server.ManagementServer; import com.cloud.utils.NumbersUtil; import com.cloud.utils.component.ManagerBase; diff --git a/server/src/com/cloud/consoleproxy/AgentHookBase.java b/server/src/com/cloud/consoleproxy/AgentHookBase.java index 62777f5b405..57fa43a6aaf 100644 --- a/server/src/com/cloud/consoleproxy/AgentHookBase.java +++ b/server/src/com/cloud/consoleproxy/AgentHookBase.java @@ -26,6 +26,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import com.cloud.agent.AgentManager; import com.cloud.agent.api.AgentControlAnswer; @@ -44,7 +45,6 @@ import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; -import com.cloud.keystore.KeystoreManager; import com.cloud.server.ManagementServer; import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor; import com.cloud.servlet.ConsoleProxyServlet; diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 4b35b0e578d..466ebc28beb 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -36,6 +36,9 @@ import com.google.gson.GsonBuilder; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; +import org.apache.cloudstack.framework.security.keystore.KeystoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -80,9 +83,6 @@ import com.cloud.info.ConsoleProxyStatus; import com.cloud.info.RunningHostCountInfo; import com.cloud.info.RunningHostInfoAgregator; import com.cloud.info.RunningHostInfoAgregator.ZoneHostInfo; -import com.cloud.keystore.KeystoreDao; -import com.cloud.keystore.KeystoreManager; -import com.cloud.keystore.KeystoreVO; import com.cloud.network.Network; import com.cloud.network.NetworkModel; import com.cloud.network.Networks.TrafficType; @@ -549,7 +549,7 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy } if (proxy.getState() == VirtualMachine.State.Stopped) { - _itMgr.advanceStart(proxy.getUuid(), null); + _itMgr.advanceStart(proxy.getUuid(), null, null); proxy = _consoleProxyDao.findById(proxy.getId()); } @@ -1440,6 +1440,11 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy controlNic = managementNic; } + // verify ssh access on management nic for system vm running on HyperV + if(profile.getHypervisorType() == HypervisorType.Hyperv) { + controlNic = managementNic; + } + CheckSshCommand check = new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922); cmds.addCommand("checkSsh", check); diff --git a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java index de1b34f7b2d..27bdcbe47fb 100755 --- a/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java +++ b/server/src/com/cloud/consoleproxy/StaticConsoleProxyManager.java @@ -27,6 +27,8 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupProxyCommand; @@ -34,8 +36,6 @@ import com.cloud.host.Host.Type; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.info.ConsoleProxyInfo; -import com.cloud.keystore.KeystoreDao; -import com.cloud.keystore.KeystoreManager; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceStateAdapter; import com.cloud.resource.ServerResource; diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index d2699eb3693..8cad796d966 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -233,8 +233,8 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } @Override - public DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, - AffinityConflictException { + public DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner) + throws InsufficientServerCapacityException, AffinityConflictException { // call affinitygroup chain VirtualMachine vm = vmProfile.getVirtualMachine(); @@ -265,19 +265,20 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } ServiceOffering offering = vmProfile.getServiceOffering(); - String plannerName = offering.getDeploymentPlanner(); - if (plannerName == null) { - if (vm.getHypervisorType() == HypervisorType.BareMetal) { - plannerName = "BareMetalPlanner"; - } else { - plannerName = _configDao.getValue(Config.VmDeploymentPlanner.key()); + if(planner == null){ + String plannerName = offering.getDeploymentPlanner(); + if (plannerName == null) { + if (vm.getHypervisorType() == HypervisorType.BareMetal) { + plannerName = "BareMetalPlanner"; + } else { + plannerName = _configDao.getValue(Config.VmDeploymentPlanner.key()); + } } - } - DeploymentPlanner planner = null; - for (DeploymentPlanner plannerInList : _planners) { - if (plannerName.equals(plannerInList.getName())) { - planner = plannerInList; - break; + for (DeploymentPlanner plannerInList : _planners) { + if (plannerName.equals(plannerInList.getName())) { + planner = plannerInList; + break; + } } } @@ -474,7 +475,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy // check if zone is dedicated. if yes check if vm owner has acess to it. DedicatedResourceVO dedicatedZone = _dedicatedDao.findByZoneId(dc.getId()); - if (dedicatedZone != null) { + if (dedicatedZone != null && !_accountMgr.isRootAdmin(vmProfile.getOwner().getType())) { long accountDomainId = vmProfile.getOwner().getDomainId(); long accountId = vmProfile.getOwner().getAccountId(); diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java index b59fceff36b..1c79a67e780 100755 --- a/server/src/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/com/cloud/deploy/FirstFitPlanner.java @@ -259,7 +259,15 @@ public class FirstFitPlanner extends PlannerBase implements DeploymentClusterPla return capacityList; } - private void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, VirtualMachineProfile vmProfile, DeploymentPlan plan) { + /** + * This method should remove the clusters crossing capacity threshold to avoid further vm allocation on it. + * @param clusterListForVmAllocation + * @param avoid + * @param vmProfile + * @param plan + */ + protected void removeClustersCrossingThreshold(List clusterListForVmAllocation, ExcludeList avoid, + VirtualMachineProfile vmProfile, DeploymentPlan plan) { List capacityList = getCapacitiesForCheckingThreshold(); List clustersCrossingThreshold = new ArrayList(); diff --git a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java index 8e86bbcd24f..3963f29ec81 100755 --- a/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java +++ b/server/src/com/cloud/ha/HighAvailabilityManagerImpl.java @@ -32,6 +32,7 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.apache.log4j.NDC; +import com.cloud.deploy.HAPlanner; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContext; @@ -141,6 +142,16 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai this.fenceBuilders = fenceBuilders; } + List _haPlanners; + public List getHaPlanners() { + return _haPlanners; + } + + public void setHaPlanners(List haPlanners) { + this._haPlanners = haPlanners; + } + + @Inject AgentManager _agentMgr; @Inject @@ -340,6 +351,7 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai try { _itMgr.advanceStop(vm.getUuid(), true); + vm = _instanceDao.findByUuid(vm.getUuid()); } catch (ResourceUnavailableException e) { assert false : "How do we hit this when force is true?"; throw new CloudRuntimeException("Caught exception even though it should be handled.", e); @@ -548,7 +560,14 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai if (_haTag != null) { params.put(VirtualMachineProfile.Param.HaTag, _haTag); } - _itMgr.advanceStart(vm.getUuid(), params); + + try{ + // First try starting the vm with its original planner, if it doesn't succeed send HAPlanner as its an emergency. + _itMgr.advanceStart(vm.getUuid(), params, null); + }catch (InsufficientCapacityException e){ + s_logger.warn("Failed to deploy vm " + vmId + " with original planner, sending HAPlanner"); + _itMgr.advanceStart(vm.getUuid(), params, _haPlanners.get(0)); + } VMInstanceVO started = _instanceDao.findById(vm.getId()); if (started != null && started.getState() == VirtualMachine.State.Running) { @@ -591,8 +610,14 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements HighAvai _haDao.update(work.getId(), work); VMInstanceVO vm = _instanceDao.findById(vmId); - - _itMgr.migrateAway(vm.getUuid(), srcHostId); + // First try starting the vm with its original planner, if it doesn't succeed send HAPlanner as its an emergency. + boolean result = false; + try { + _itMgr.migrateAway(vm.getUuid(), srcHostId, null); + }catch (InsufficientServerCapacityException e) { + s_logger.warn("Failed to deploy vm " + vmId + " with original planner, sending HAPlanner"); + _itMgr.migrateAway(vm.getUuid(), srcHostId, _haPlanners.get(0)); + } return null; } catch (InsufficientServerCapacityException e) { s_logger.warn("Insufficient capacity for migrating a VM."); diff --git a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java index aa0fa590a47..7fb79fa1578 100644 --- a/server/src/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/com/cloud/hypervisor/HypervisorGuruBase.java @@ -150,4 +150,9 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis public List finalizeExpungeNics(VirtualMachine vm, List nics) { return null; } + + @Override + public List finalizeExpungeVolumes(VirtualMachine vm) { + return null; + } } diff --git a/server/src/com/cloud/metadata/ResourceMetaDataManagerImpl.java b/server/src/com/cloud/metadata/ResourceMetaDataManagerImpl.java index 4cd98402a56..c7906f52d16 100644 --- a/server/src/com/cloud/metadata/ResourceMetaDataManagerImpl.java +++ b/server/src/com/cloud/metadata/ResourceMetaDataManagerImpl.java @@ -26,6 +26,7 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.api.ResourceDetail; import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; +import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.apache.cloudstack.resourcedetail.dao.FirewallRuleDetailsDao; import org.apache.cloudstack.resourcedetail.dao.NetworkACLItemDetailsDao; import org.apache.cloudstack.resourcedetail.dao.NetworkACLListDetailsDao; @@ -33,6 +34,7 @@ import org.apache.cloudstack.resourcedetail.dao.RemoteAccessVpnDetailsDao; import org.apache.cloudstack.resourcedetail.dao.Site2SiteCustomerGatewayDetailsDao; import org.apache.cloudstack.resourcedetail.dao.Site2SiteVpnConnectionDetailsDao; import org.apache.cloudstack.resourcedetail.dao.Site2SiteVpnGatewayDetailsDao; +import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao; import org.apache.cloudstack.resourcedetail.dao.UserIpAddressDetailsDao; import org.apache.cloudstack.resourcedetail.dao.VpcDetailsDao; import org.apache.cloudstack.resourcedetail.dao.VpcGatewayDetailsDao; @@ -101,6 +103,10 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource Site2SiteCustomerGatewayDetailsDao _customerGatewayDetailsDao; @Inject Site2SiteVpnConnectionDetailsDao _vpnConnectionDetailsDao; + @Inject + DiskOfferingDetailsDao _diskOfferingDetailsDao; + @Inject + UserDetailsDao _userDetailsDao; private static Map> s_daoMap = new HashMap>(); @@ -126,6 +132,8 @@ public class ResourceMetaDataManagerImpl extends ManagerBase implements Resource s_daoMap.put(ResourceObjectType.VpnGateway, _vpnGatewayDetailsDao); s_daoMap.put(ResourceObjectType.CustomerGateway, _customerGatewayDetailsDao); s_daoMap.put(ResourceObjectType.VpnConnection, _vpnConnectionDetailsDao); + s_daoMap.put(ResourceObjectType.DiskOffering, _diskOfferingDetailsDao); + s_daoMap.put(ResourceObjectType.User, _userDetailsDao); return true; } diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 26cf15a5bd4..6596074ad34 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -28,8 +28,6 @@ import java.util.UUID; import javax.inject.Inject; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.context.CallContext; @@ -41,6 +39,7 @@ import org.apache.cloudstack.region.PortableIp; import org.apache.cloudstack.region.PortableIpDao; import org.apache.cloudstack.region.PortableIpVO; import org.apache.cloudstack.region.Region; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.alert.AlertManager; @@ -389,9 +388,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage SearchBuilder podVlanMapSB = _podVlanMapDao.createSearchBuilder(); podVlanMapSB.and("podId", podVlanMapSB.entity().getPodId(), Op.EQ); AssignIpAddressFromPodVlanSearch.join("podVlanMapSB", podVlanMapSB, podVlanMapSB.entity().getVlanDbId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), - JoinType.INNER); - AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), - JoinType.INNER); + JoinType.INNER); + AssignIpAddressFromPodVlanSearch.join("vlan", podVlanSearch, podVlanSearch.entity().getId(), AssignIpAddressFromPodVlanSearch.entity().getVlanId(), JoinType.INNER); AssignIpAddressFromPodVlanSearch.done(); Network.State.getStateMachine().registerListener(new NetworkStateListener(_usageEventDao, _networksDao)); @@ -402,7 +400,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage } private IpAddress allocateIP(Account ipOwner, boolean isSystem, long zoneId) throws ResourceAllocationException, InsufficientAddressCapacityException, - ConcurrentOperationException { + ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); long callerUserId = CallContext.current().getCallingUserId(); // check permissions @@ -465,7 +463,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override public boolean applyRules(List rules, FirewallRule.Purpose purpose, NetworkRuleApplier applier, boolean continueOnError) - throws ResourceUnavailableException { + throws ResourceUnavailableException { if (rules == null || rules.size() == 0) { s_logger.debug("There are no rules to forward to the network elements"); return true; @@ -640,20 +638,19 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override public PublicIp assignPublicIpAddress(long dcId, Long podId, Account owner, VlanType type, Long networkId, String requestedIp, boolean isSystem) - throws InsufficientAddressCapacityException { + throws InsufficientAddressCapacityException { return fetchNewPublicIp(dcId, podId, null, owner, type, networkId, false, true, requestedIp, isSystem, null); } @Override - public PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List vlanDbIds, Long networkId, String requestedIp, - boolean isSystem) throws InsufficientAddressCapacityException { + public PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List vlanDbIds, Long networkId, String requestedIp, boolean isSystem) + throws InsufficientAddressCapacityException { return fetchNewPublicIp(dcId, podId, vlanDbIds, owner, type, networkId, false, true, requestedIp, isSystem, null); } @DB - public PublicIp fetchNewPublicIp(final long dcId, final Long podId, final List vlanDbIds, final Account owner, final VlanType vlanUse, - final Long guestNetworkId, final boolean sourceNat, final boolean assign, final String requestedIp, final boolean isSystem, final Long vpcId) - throws InsufficientAddressCapacityException { + public PublicIp fetchNewPublicIp(final long dcId, final Long podId, final List vlanDbIds, final Account owner, final VlanType vlanUse, final Long guestNetworkId, + final boolean sourceNat, final boolean assign, final String requestedIp, final boolean isSystem, final Long vpcId) throws InsufficientAddressCapacityException { IPAddressVO addr = Transaction.execute(new TransactionCallbackWithException() { @Override public IPAddressVO doInTransaction(TransactionStatus status) throws InsufficientAddressCapacityException { @@ -796,8 +793,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override public void markPublicIpAsAllocated(final IPAddressVO addr) { - assert (addr.getState() == IpAddress.State.Allocating || addr.getState() == IpAddress.State.Free) : "Unable to transition from state " + addr.getState() + - " to " + IpAddress.State.Allocated; + assert (addr.getState() == IpAddress.State.Allocating || addr.getState() == IpAddress.State.Free) : "Unable to transition from state " + addr.getState() + " to " + + IpAddress.State.Allocated; Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -813,8 +810,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage String guestType = vlan.getVlanType().toString(); if (!isIpDedicated(addr)) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(), addr.getDataCenterId(), addr.getId(), addr.getAddress() - .toString(), addr.isSourceNat(), guestType, addr.getSystem(), addr.getClass().getName(), addr.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NET_IP_ASSIGN, owner.getId(), addr.getDataCenterId(), addr.getId(), addr.getAddress().toString(), + addr.isSourceNat(), guestType, addr.getSystem(), addr.getClass().getName(), addr.getUuid()); } if (updateIpResourceCount(addr)) { @@ -835,7 +832,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override public PublicIp assignSourceNatIpAddressToGuestNetwork(Account owner, Network guestNetwork) throws InsufficientAddressCapacityException, ConcurrentOperationException { assert (guestNetwork.getTrafficType() != null) : "You're asking for a source nat but your network " - + "can't participate in source nat. What do you have to say for yourself?"; + + "can't participate in source nat. What do you have to say for yourself?"; long dcId = guestNetwork.getDataCenterId(); IPAddressVO sourceNatIp = getExistingSourceNatInNetwork(owner.getId(), guestNetwork.getId()); @@ -853,7 +850,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @DB @Override public PublicIp assignDedicateIpAddress(Account owner, final Long guestNtwkId, final Long vpcId, final long dcId, final boolean isSourceNat) - throws ConcurrentOperationException, InsufficientAddressCapacityException { + throws ConcurrentOperationException, InsufficientAddressCapacityException { final long ownerId = owner.getId(); @@ -932,7 +929,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage // but still be associated with the account. Its up to caller of this function to decide when to invoke IPAssociation @Override public boolean applyIpAssociations(Network network, boolean postApplyRules, boolean continueOnError, List publicIps) - throws ResourceUnavailableException { + throws ResourceUnavailableException { boolean success = true; Map> ipToServices = _networkModel.getIpToServices(publicIps, postApplyRules, true); @@ -976,8 +973,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @DB @Override - public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone) - throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException { + public IpAddress allocateIp(final Account ipOwner, final boolean isSystem, Account caller, long callerUserId, final DataCenter zone) throws ConcurrentOperationException, + ResourceAllocationException, InsufficientAddressCapacityException { final VlanType vlanType = VlanType.VirtualNetwork; final boolean assign = false; @@ -1012,8 +1009,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage PublicIp ip = fetchNewPublicIp(zone.getId(), null, null, ipOwner, vlanType, null, false, assign, null, isSystem, null); if (ip == null) { - InsufficientAddressCapacityException ex = - new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zone.getId()); + InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available public IP addresses", DataCenter.class, zone + .getId()); ex.addProxyObject(ApiDBUtils.findZoneById(zone.getId()).getUuid()); throw ex; } @@ -1040,8 +1037,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override @DB - public IpAddress allocatePortableIp(final Account ipOwner, Account caller, final long dcId, final Long networkId, final Long vpcID) - throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException { + public IpAddress allocatePortableIp(final Account ipOwner, Account caller, final long dcId, final Long networkId, final Long vpcID) throws ConcurrentOperationException, + ResourceAllocationException, InsufficientAddressCapacityException { GlobalLock portableIpLock = GlobalLock.getInternLock("PortablePublicIpRange"); IPAddressVO ipaddr; @@ -1056,8 +1053,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage List portableIpVOs = _portableIpDao.listByRegionIdAndState(1, PortableIp.State.Free); if (portableIpVOs == null || portableIpVOs.isEmpty()) { - InsufficientAddressCapacityException ex = - new InsufficientAddressCapacityException("Unable to find available portable IP addresses", Region.class, new Long(1)); + InsufficientAddressCapacityException ex = new InsufficientAddressCapacityException("Unable to find available portable IP addresses", Region.class, + new Long(1)); throw ex; } @@ -1077,22 +1074,20 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage long physicalNetworkId = _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(dcId, TrafficType.Public).getId(); Network network = _networkModel.getSystemNetworkByZoneAndTrafficType(dcId, TrafficType.Public); String range = allocatedPortableIp.getAddress() + "-" + allocatedPortableIp.getAddress(); - VlanVO vlan = - new VlanVO(VlanType.VirtualNetwork, allocatedPortableIp.getVlan(), allocatedPortableIp.getGateway(), allocatedPortableIp.getNetmask(), dcId, + VlanVO vlan = new VlanVO(VlanType.VirtualNetwork, allocatedPortableIp.getVlan(), allocatedPortableIp.getGateway(), allocatedPortableIp.getNetmask(), dcId, range, network.getId(), physicalNetworkId, null, null, null); vlan = _vlanDao.persist(vlan); // provision the portable IP in to user_ip_address table - IPAddressVO ipaddr = - new IPAddressVO(new Ip(allocatedPortableIp.getAddress()), dcId, networkId, vpcID, physicalNetworkId, network.getId(), vlan.getId(), true); + IPAddressVO ipaddr = new IPAddressVO(new Ip(allocatedPortableIp.getAddress()), dcId, networkId, vpcID, physicalNetworkId, network.getId(), vlan.getId(), true); ipaddr.setState(State.Allocated); ipaddr.setAllocatedTime(new Date()); ipaddr.setAllocatedInDomainId(ipOwner.getDomainId()); ipaddr.setAllocatedToAccountId(ipOwner.getId()); ipaddr = _ipAddressDao.persist(ipaddr); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_PORTABLE_IP_ASSIGN, ipaddr.getId(), ipaddr.getDataCenterId(), ipaddr.getId(), ipaddr.getAddress() - .toString(), ipaddr.isSourceNat(), null, ipaddr.getSystem(), ipaddr.getClass().getName(), ipaddr.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_PORTABLE_IP_ASSIGN, ipaddr.getId(), ipaddr.getDataCenterId(), ipaddr.getId(), + ipaddr.getAddress().toString(), ipaddr.isSourceNat(), null, ipaddr.getSystem(), ipaddr.getClass().getName(), ipaddr.getUuid()); return ipaddr; } @@ -1105,8 +1100,14 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage } protected IPAddressVO getExistingSourceNatInNetwork(long ownerId, Long networkId) { - - List addrs = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, networkId, true); + List addrs; + Network guestNetwork = _networksDao.findById(networkId); + if (guestNetwork.getGuestType() == GuestType.Shared) { + // ignore the account id for the shared network + addrs = _networkModel.listPublicIpsAssignedToGuestNtwk(networkId, true); + } else { + addrs = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, networkId, true); + } IPAddressVO sourceNatIp = null; if (addrs.isEmpty()) { @@ -1129,7 +1130,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @DB @Override public IPAddressVO associateIPToGuestNetwork(long ipId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, - InsufficientAddressCapacityException, ConcurrentOperationException { + InsufficientAddressCapacityException, ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); Account owner = null; @@ -1147,7 +1148,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage _accountMgr.checkAccess(CallContext.current().getCallingAccount(), AccessType.UseNetwork, false, network); } else { throw new InvalidParameterValueException("IP can be associated with guest network of 'shared' type only if " - + "network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network"); + + "network services Source Nat, Static Nat, Port Forwarding, Load balancing, firewall are enabled in the network"); } } } else { @@ -1192,15 +1193,15 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage if (zone.getNetworkType() == NetworkType.Advanced) { // In Advance zone allow to do IP assoc only for Isolated networks with source nat service enabled if (network.getGuestType() == GuestType.Isolated && !(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced + " ip address can be associated only to the network of guest type " + - GuestType.Isolated + " with the " + Service.SourceNat.getName() + " enabled"); + throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced + " ip address can be associated only to the network of guest type " + + GuestType.Isolated + " with the " + Service.SourceNat.getName() + " enabled"); } // In Advance zone allow to do IP assoc only for shared networks with source nat/static nat/lb/pf services enabled if (network.getGuestType() == GuestType.Shared && !isSharedNetworkOfferingWithServices(network.getNetworkOfferingId())) { - throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced + " ip address can be associated with network of guest type " + - GuestType.Shared + "only if at " + "least one of the services " + Service.SourceNat.getName() + "/" + Service.StaticNat.getName() + "/" + - Service.Lb.getName() + "/" + Service.PortForwarding.getName() + " is enabled"); + throw new InvalidParameterValueException("In zone of type " + NetworkType.Advanced + " ip address can be associated with network of guest type " + GuestType.Shared + + "only if at " + "least one of the services " + Service.SourceNat.getName() + "/" + Service.StaticNat.getName() + "/" + Service.Lb.getName() + "/" + + Service.PortForwarding.getName() + " is enabled"); } } @@ -1253,27 +1254,27 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage protected boolean isSharedNetworkOfferingWithServices(long networkOfferingId) { NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); - if ((networkOffering.getGuestType() == Network.GuestType.Shared) && - (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) || - _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering( - networkOfferingId, Service.Lb))) { + if ((networkOffering.getGuestType() == Network.GuestType.Shared) + && (_networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) + || _networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || _networkModel.areServicesSupportedByNetworkOffering( + networkOfferingId, Service.Lb))) { return true; } return false; } @Override - public IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, - ResourceUnavailableException, InsufficientAddressCapacityException, ConcurrentOperationException { + public IPAddressVO associatePortableIPToGuestNetwork(long ipAddrId, long networkId, boolean releaseOnFailure) throws ResourceAllocationException, ResourceUnavailableException, + InsufficientAddressCapacityException, ConcurrentOperationException { return associateIPToGuestNetwork(ipAddrId, networkId, releaseOnFailure); } @DB @Override public IPAddressVO disassociatePortableIPToGuestNetwork(long ipId, long networkId) throws ResourceAllocationException, ResourceUnavailableException, - InsufficientAddressCapacityException, ConcurrentOperationException { + InsufficientAddressCapacityException, ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); Account owner = null; @@ -1380,7 +1381,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @DB @Override public void transferPortableIP(final long ipAddrId, long currentNetworkId, long newNetworkId) throws ResourceAllocationException, ResourceUnavailableException, - InsufficientAddressCapacityException, ConcurrentOperationException { + InsufficientAddressCapacityException, ConcurrentOperationException { Network srcNetwork = _networksDao.findById(currentNetworkId); if (srcNetwork == null) { @@ -1448,7 +1449,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage // trigger an action event for the transfer of portable IP across the networks, so that external entities // monitoring for this event can initiate the route advertisement for the availability of IP from the zoe ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, Domain.ROOT_DOMAIN, EventTypes.EVENT_PORTABLE_IP_TRANSFER, - "Portable IP associated is transferred from network " + currentNetworkId + " to " + newNetworkId); + "Portable IP associated is transferred from network " + currentNetworkId + " to " + newNetworkId); } protected List getIsolatedNetworksWithSourceNATOwnedByAccountInZone(long zoneId, Account owner) { @@ -1459,7 +1460,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override @DB public boolean associateIpAddressListToAccount(long userId, final long accountId, final long zoneId, final Long vlanId, final Network guestNetworkFinal) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException { + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException { final Account owner = _accountMgr.getActiveAccountById(accountId); if (guestNetworkFinal != null && guestNetworkFinal.getTrafficType() != TrafficType.Guest) { @@ -1471,7 +1472,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage pair = Transaction.execute(new TransactionCallbackWithException, Network>, Exception>() { @Override public Ternary, Network> doInTransaction(TransactionStatus status) throws InsufficientCapacityException, - ResourceAllocationException { + ResourceAllocationException { boolean createNetwork = false; Network guestNetwork = guestNetworkFinal; @@ -1483,41 +1484,37 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage guestNetwork = networks.get(0); } else { throw new InvalidParameterValueException("Error, more than 1 Guest Isolated Networks with SourceNAT " - + "service enabled found for this account, cannot assosiate the IP range, please provide the network ID"); + + "service enabled found for this account, cannot assosiate the IP range, please provide the network ID"); } } // create new Virtual network (Isolated with SourceNAT) for the user if it doesn't exist List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { - throw new CloudRuntimeException("Unable to find network offering with availability=" + Availability.Required + - " to automatically create the network as part of createVlanIpRange"); + throw new CloudRuntimeException("Unable to find network offering with availability=" + Availability.Required + + " to automatically create the network as part of createVlanIpRange"); } if (createNetwork) { if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { - long physicalNetworkId = - _networkModel.findPhysicalNetworkId(zoneId, requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); + long physicalNetworkId = _networkModel.findPhysicalNetworkId(zoneId, requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + - requiredOfferings.get(0).getTags()); + throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + + requiredOfferings.get(0).getTags()); } - s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + - " as a part of createVlanIpRange process"); - guestNetwork = - _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", - owner.getAccountName() + "-network", null, null, null, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, - null, true, null); + s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + + " as a part of createVlanIpRange process"); + guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + + "-network", null, null, null, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null); if (guestNetwork == null) { s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId); - throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " + - "service enabled as a part of createVlanIpRange, for the account " + accountId + "in zone " + zoneId); + throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " + + "service enabled as a part of createVlanIpRange, for the account " + accountId + "in zone " + zoneId); } } else { - throw new CloudRuntimeException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + - NetworkOffering.State.Enabled); + throw new CloudRuntimeException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); } } @@ -1580,8 +1577,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage guestNetwork = implementedNetwork.second(); } catch (Exception ex) { s_logger.warn("Failed to implement network " + guestNetwork + " elements and resources as a part of" + " network provision due to ", ex); - CloudRuntimeException e = - new CloudRuntimeException("Failed to implement network (with specified id)" + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id)" + " elements and resources as a part of network provision for persistent network"); e.addProxyObject(guestNetwork.getUuid(), "networkId"); throw e; @@ -1615,8 +1611,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage String guestType = vlan.getVlanType().toString(); if (!isIpDedicated(ip)) { String eventType = ip.isPortable() ? EventTypes.EVENT_PORTABLE_IP_RELEASE : EventTypes.EVENT_NET_IP_RELEASE; - UsageEventUtils.publishUsageEvent(eventType, ip.getAllocatedToAccountId(), ip.getDataCenterId(), addrId, ip.getAddress().addr(), - ip.isSourceNat(), guestType, ip.getSystem(), ip.getClass().getName(), ip.getUuid()); + UsageEventUtils.publishUsageEvent(eventType, ip.getAllocatedToAccountId(), ip.getDataCenterId(), addrId, ip.getAddress().addr(), ip.isSourceNat(), + guestType, ip.getSystem(), ip.getClass().getName(), ip.getUuid()); } } @@ -1811,7 +1807,7 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage @Override @DB public void allocateDirectIp(final NicProfile nic, final DataCenter dc, final VirtualMachineProfile vm, final Network network, final String requestedIpv4, - final String requestedIpv6) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { + final String requestedIpv6) throws InsufficientVirtualNetworkCapcityException, InsufficientAddressCapacityException { Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws InsufficientAddressCapacityException { @@ -1892,8 +1888,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage } @Override - public String allocatePublicIpForGuestNic(Long networkId, DataCenter dc, Pod pod, Account owner, String requestedIp) throws InsufficientAddressCapacityException { - PublicIp ip = assignPublicIpAddress(dc.getId(), null, owner, VlanType.DirectAttached, networkId, requestedIp, false); + public String allocatePublicIpForGuestNic(Network network, Long podId, Account owner, String requestedIp) throws InsufficientAddressCapacityException { + PublicIp ip = assignPublicIpAddress(network.getDataCenterId(), podId, owner, VlanType.DirectAttached, network.getId(), requestedIp, false); if (ip == null) { s_logger.debug("There is no free public ip address"); return null; @@ -1903,16 +1899,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage } @Override - public String allocateGuestIP(Account ipOwner, boolean isSystem, long zoneId, Long networkId, String requestedIp) throws InsufficientAddressCapacityException { - String ipaddr = null; - Account caller = CallContext.current().getCallingAccount(); - // check permissions - Network network = _networksDao.findById(networkId); - - _accountMgr.checkAccess(caller, null, false, network); - - ipaddr = acquireGuestIpAddress(network, requestedIp); - return ipaddr; + public String allocateGuestIP(Network network, String requestedIp) throws InsufficientAddressCapacityException { + return acquireGuestIpAddress(network, requestedIp); } @Override diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index 746faa8b6b0..52a08e1480e 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -523,6 +523,19 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { return _ipAddressDao.search(sc, null); } + @Override + public List listPublicIpsAssignedToGuestNtwk(long associatedNetworkId, Boolean sourceNat) { + SearchCriteria sc = IpAddressSearch.create(); + sc.setParameters("associatedWithNetworkId", associatedNetworkId); + + if (sourceNat != null) { + sc.addAnd("sourceNat", SearchCriteria.Op.EQ, sourceNat); + } + sc.setJoinParameters("virtualNetworkVlanSB", "vlanType", VlanType.VirtualNetwork); + + return _ipAddressDao.search(sc, null); + } + @Override public List listPublicIpsAssignedToAccount(long accountId, long dcId, Boolean sourceNat) { SearchCriteria sc = IpAddressSearch.create(); diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index b7ffe269533..fb6f3fd451a 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -19,7 +19,6 @@ package com.cloud.network; import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; - import java.net.UnknownHostException; import java.security.InvalidParameterException; import java.sql.PreparedStatement; @@ -41,8 +40,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; @@ -56,15 +53,16 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; +import org.apache.log4j.Logger; import com.cloud.api.ApiDBUtils; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; +import com.cloud.configuration.Resource; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenterVnetVO; -import com.cloud.dc.Pod; import com.cloud.dc.Vlan.VlanType; import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; @@ -425,8 +423,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new InvalidParameterException("There are multiple services used ip " + ip.getAddress() + "."); } if (service != null && !((Service)services.toArray()[0] == service || service.equals(Service.Firewall))) { - throw new InvalidParameterException("The IP " + ip.getAddress() + " is already used as " + ((Service)services.toArray()[0]).getName() + " rather than " + - service.getName()); + throw new InvalidParameterException("The IP " + ip.getAddress() + " is already used as " + ((Service)services.toArray()[0]).getName() + " rather than " + + service.getName()); } return true; } @@ -516,7 +514,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "allocating Ip", create = true) public IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId) throws ResourceAllocationException, InsufficientAddressCapacityException, - ConcurrentOperationException { + ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); long callerUserId = CallContext.current().getCallingUserId(); @@ -542,7 +540,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return _ipAddrMgr.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"); + + " with Firewall/Source Nat/Static Nat/Port Forwarding/Load balancing services enabled"); } } } @@ -556,7 +554,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_ASSIGN, eventDescription = "allocating portable public Ip", create = true) public IpAddress allocatePortableIP(Account ipOwner, int regionId, Long zoneId, Long networkId, Long vpcId) throws ResourceAllocationException, - InsufficientAddressCapacityException, ConcurrentOperationException { + InsufficientAddressCapacityException, ConcurrentOperationException { Account caller = CallContext.current().getCallingAccount(); long callerUserId = CallContext.current().getCallingUserId(); DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); @@ -585,7 +583,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return _ipAddrMgr.allocatePortableIp(ipOwner, caller, zoneId, networkId, null); } 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"); + + " with Firewall/Source Nat/Static Nat/Port Forwarding/Load balancing services enabled"); } } } @@ -641,14 +639,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } @Override - public NicSecondaryIp allocateSecondaryGuestIP(Account ipOwner, long zoneId, final Long nicId, final Long networkId, String requestedIp) - throws InsufficientAddressCapacityException { - - String ipaddr = null; - - if (networkId == null) { - throw new InvalidParameterValueException("Invalid network id is given"); - } + public NicSecondaryIp allocateSecondaryGuestIP(final long nicId, String requestedIp) throws InsufficientAddressCapacityException { Account caller = CallContext.current().getCallingAccount(); @@ -666,47 +657,42 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (vm == null) { throw new InvalidParameterValueException("There is no vm with the nic"); } + + final long networkId = nicVO.getNetworkId(); + final Account ipOwner = _accountMgr.getAccount(vm.getAccountId()); + // verify permissions - _accountMgr.checkAccess(ipOwner, null, true, vm); + _accountMgr.checkAccess(caller, null, true, vm); Network network = _networksDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("Invalid network id is given"); } - final Long accountId = ipOwner.getAccountId(); - final Long domainId = ipOwner.getDomainId(); - - // Validate network offering - NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(network.getNetworkOfferingId()); - - DataCenter dc = _dcDao.findById(network.getDataCenterId()); - - DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); - if (zone == null) { - throw new InvalidParameterValueException("Invalid zone Id is given"); - } s_logger.debug("Calling the ip allocation ..."); - if (dc.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Isolated) { + String ipaddr = null; + //Isolated network can exist in Basic zone only, so no need to verify the zone type + if (network.getGuestType() == Network.GuestType.Isolated) { try { - ipaddr = _ipAddrMgr.allocateGuestIP(ipOwner, false, zoneId, networkId, requestedIp); + ipaddr = _ipAddrMgr.allocateGuestIP(network, requestedIp); } catch (InsufficientAddressCapacityException e) { throw new InvalidParameterValueException("Allocating guest ip for nic failed"); } - } else if (dc.getNetworkType() == NetworkType.Basic || ntwkOff.getGuestType() == Network.GuestType.Shared) { - //handle the basic networks here - VMInstanceVO vmi = (VMInstanceVO)vm; - Long podId = vmi.getPodIdToDeployIn(); - if (podId == null) { - throw new InvalidParameterValueException("vm pod id is null"); - } - Pod pod = _hostPodDao.findById(podId); - if (pod == null) { - throw new InvalidParameterValueException("vm pod is null"); + } else if (network.getGuestType() == Network.GuestType.Shared) { + //for basic zone, need to provide the podId to ensure proper ip alloation + Long podId = null; + DataCenter dc = _dcDao.findById(network.getDataCenterId()); + + if (dc.getNetworkType() == NetworkType.Basic) { + VMInstanceVO vmi = (VMInstanceVO)vm; + podId = vmi.getPodIdToDeployIn(); + if (podId == null) { + throw new InvalidParameterValueException("vm pod id is null in Basic zone; can't decide the range for ip allocation"); + } } try { - ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(networkId, dc, pod, caller, requestedIp); + ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(network, podId, ipOwner, requestedIp); if (ipaddr == null) { throw new InvalidParameterValueException("Allocating ip to guest nic " + nicId + " failed"); } @@ -735,7 +721,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { s_logger.debug("Setting nic_secondary_ip table ..."); Long vmId = nicVO.getInstanceId(); - NicSecondaryIpVO secondaryIpVO = new NicSecondaryIpVO(nicId, addrFinal, vmId, accountId, domainId, networkId); + NicSecondaryIpVO secondaryIpVO = new NicSecondaryIpVO(nicId, addrFinal, vmId, ipOwner.getId(), ipOwner.getDomainId(), networkId); _nicSecondaryIpDao.persist(secondaryIpVO); return secondaryIpVO.getId(); } @@ -756,12 +742,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // Verify input parameters NicSecondaryIpVO secIpVO = _nicSecondaryIpDao.findById(ipAddressId); if (secIpVO == null) { - throw new InvalidParameterValueException("Unable to find ip address by id"); + throw new InvalidParameterValueException("Unable to find secondary ip address by id"); } VirtualMachine vm = _userVmDao.findById(secIpVO.getVmId()); if (vm == null) { - throw new InvalidParameterValueException("There is no vm with the nic"); + throw new InvalidParameterValueException("There is no vm with the given secondary ip"); } // verify permissions _accountMgr.checkAccess(caller, null, true, vm); @@ -790,7 +776,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new InvalidParameterValueException("Invalid zone Id is given"); } - s_logger.debug("Calling the ip allocation ..."); + s_logger.debug("Calling secondary ip " + secIpVO.getIp4Address() + " release "); if (dc.getNetworkType() == NetworkType.Advanced && network.getGuestType() == Network.GuestType.Isolated) { //check PF or static NAT is configured on this ip address String secondaryIp = secIpVO.getIp4Address(); @@ -808,11 +794,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { IPAddressVO publicIpVO = _ipAddressDao.findByVmIp(secondaryIp); if (publicIpVO != null) { s_logger.debug("VM nic IP " + secondaryIp + " is associated with the static NAT rule public IP address id " + publicIpVO.getId()); - throw new InvalidParameterValueException("Can' remove the ip " + secondaryIp + "is associate with static NAT rule public IP address id " + - publicIpVO.getId()); + throw new InvalidParameterValueException("Can' remove the ip " + secondaryIp + "is associate with static NAT rule public IP address id " + publicIpVO.getId()); } } else if (dc.getNetworkType() == NetworkType.Basic || ntwkOff.getGuestType() == Network.GuestType.Shared) { - final IPAddressVO ip = _ipAddressDao.findByIpAndNetworkId(secIpVO.getNetworkId(), secIpVO.getIp4Address()); + final IPAddressVO ip = _ipAddressDao.findByIpAndSourceNetworkId(secIpVO.getNetworkId(), secIpVO.getIp4Address()); if (ip != null) { Transaction.execute(new TransactionCallbackNoReturn() { @Override @@ -1066,8 +1051,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } } else if (ntwkOff.getGuestType() == GuestType.Shared) { if (!(aclType == ACLType.Domain || aclType == ACLType.Account)) { - throw new InvalidParameterValueException("AclType should be " + ACLType.Domain + " or " + ACLType.Account + " for network of type " + - Network.GuestType.Shared); + throw new InvalidParameterValueException("AclType should be " + ACLType.Domain + " or " + ACLType.Account + " for network of type " + Network.GuestType.Shared); } } } else { @@ -1097,8 +1081,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (domainId != null) { if (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Shared) { - throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + - Network.GuestType.Shared); + throw new InvalidParameterValueException("Domain level networks are supported just for traffic type " + TrafficType.Guest + " and guest type " + + Network.GuestType.Shared); } DomainVO domain = _domainDao.findById(domainId); @@ -1196,11 +1180,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } // Regular user can create Guest Isolated Source Nat enabled network only - if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL && - (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Isolated && - areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { - throw new InvalidParameterValueException("Regular user can create a network only from the network" + " offering having traffic type " + TrafficType.Guest + - " and network type " + Network.GuestType.Isolated + " with a service " + Service.SourceNat.getName() + " enabled"); + if (caller.getType() == Account.ACCOUNT_TYPE_NORMAL + && (ntwkOff.getTrafficType() != TrafficType.Guest || ntwkOff.getGuestType() != Network.GuestType.Isolated + && areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))) { + throw new InvalidParameterValueException("Regular user can create a network only from the network" + " offering having traffic type " + TrafficType.Guest + + " and network type " + Network.GuestType.Isolated + " with a service " + Service.SourceNat.getName() + " enabled"); } // Don't allow to specify vlan if the caller is not ROOT admin @@ -1261,10 +1245,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw ex; } - Network network = - commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, name, displayText, caller, physicalNetworkId, zoneId, domainId, - isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, isolatedPvlan, ntwkOff, pNtwk, aclType, owner, - cidr, createVlan); + Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, name, displayText, caller, physicalNetworkId, zoneId, domainId, + isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, isolatedPvlan, ntwkOff, pNtwk, aclType, owner, cidr, + createVlan); // if the network offering has persistent set to true, implement the network if (ntwkOff.getIsPersistent()) { @@ -1293,12 +1276,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return network; } - private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, - final String networkDomain, final String vlanId, final String name, final String displayText, final Account caller, final Long physicalNetworkId, - final Long zoneId, final Long domainId, final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, - final String endIPv6, final String ip6Gateway, final String ip6Cidr, final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, - final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType, final Account ownerFinal, final String cidr, final boolean createVlan) - throws InsufficientCapacityException, ResourceAllocationException { + private Network commitNetwork(final Long networkOfferingId, final String gateway, final String startIP, final String endIP, final String netmask, final String networkDomain, + final String vlanId, final String name, final String displayText, final Account caller, final Long physicalNetworkId, final Long zoneId, final Long domainId, + final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, + final String ip6Cidr, final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final NetworkOfferingVO ntwkOff, final PhysicalNetwork pNtwk, + final ACLType aclType, final Account ownerFinal, final String cidr, final boolean createVlan) throws InsufficientCapacityException, ResourceAllocationException { try { return Transaction.execute(new TransactionCallbackWithException() { @Override @@ -1342,9 +1324,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } } } - network = - _vpcMgr.createVpcGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, - zoneId, aclType, subdomainAccess, vpcId, aclId, caller, displayNetwork); + network = _vpcMgr.createVpcGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, + aclType, subdomainAccess, vpcId, aclId, caller, displayNetwork); } else { if (_configMgr.isOfferingForVpc(ntwkOff)) { throw new InvalidParameterValueException("Network offering can be used for VPC networks only"); @@ -1353,15 +1334,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new InvalidParameterValueException("Internal Lb can be enabled on vpc networks only"); } - network = - _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, - zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan); + network = _networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, + aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan); } if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN && createVlan) { // Create vlan ip range - _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, null, startIP, endIP, gateway, netmask, - vlanId, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, null, startIP, endIP, gateway, netmask, vlanId, + null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); } return network; } @@ -1375,7 +1355,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } @Override - public List searchForNetworks(ListNetworksCmd cmd) { + public Pair, Integer> searchForNetworks(ListNetworksCmd cmd) { Long id = cmd.getId(); String keyword = cmd.getKeyword(); Long zoneId = cmd.getZoneId(); @@ -1530,35 +1510,34 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (!permittedAccounts.isEmpty()) { //get account level networks networksToReturn.addAll(listAccountSpecificNetworks( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, - restartRequired, specifyIpRanges, vpcId, tags), searchFilter, permittedAccounts)); + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter, permittedAccounts)); //get domain level networks if (domainId != null) { networksToReturn.addAll(listDomainLevelNetworks( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, true, restartRequired, - specifyIpRanges, vpcId, tags), searchFilter, domainId, false)); + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, true, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter, domainId, false)); } } else { //add account specific networks networksToReturn.addAll(listAccountSpecificNetworksByDomainPath( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, - restartRequired, specifyIpRanges, vpcId, tags), searchFilter, path, isRecursive)); + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter, path, isRecursive)); //add domain specific networks of domain + parent domains networksToReturn.addAll(listDomainSpecificNetworksByDomainPath( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, - restartRequired, specifyIpRanges, vpcId, tags), searchFilter, path, isRecursive)); + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, skipProjectNetworks, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter, path, isRecursive)); //add networks of subdomains if (domainId == null) { networksToReturn.addAll(listDomainLevelNetworks( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, true, restartRequired, - specifyIpRanges, vpcId, tags), searchFilter, caller.getDomainId(), true)); + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, aclType, true, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter, caller.getDomainId(), true)); } } } else { - networksToReturn = - _networksDao.search( - buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, null, skipProjectNetworks, - restartRequired, specifyIpRanges, vpcId, tags), searchFilter); + networksToReturn = _networksDao.search( + buildNetworkSearchCriteria(sb, keyword, id, isSystem, zoneId, guestIpType, trafficType, physicalNetworkId, null, skipProjectNetworks, restartRequired, + specifyIpRanges, vpcId, tags), searchFilter); } if (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !networksToReturn.isEmpty()) { @@ -1598,37 +1577,34 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { //Now apply pagination //Most likely pageSize will never exceed int value, and we need integer to partition the listToReturn boolean notNull = cmd.getStartIndex() != null && cmd.getPageSizeVal() != null; - if (notNull && cmd.getStartIndex() <= Integer.MAX_VALUE && cmd.getStartIndex() >= Integer.MIN_VALUE && - cmd.getPageSizeVal() <= Integer.MAX_VALUE && cmd.getPageSizeVal() >= Integer.MIN_VALUE) { - int startIndex = cmd.getStartIndex().intValue() == 0 ? 0 : cmd.getStartIndex().intValue() - 1; + if (notNull && cmd.getStartIndex() <= Integer.MAX_VALUE && cmd.getStartIndex() >= Integer.MIN_VALUE && cmd.getPageSizeVal() <= Integer.MAX_VALUE + && cmd.getPageSizeVal() >= Integer.MIN_VALUE) { + int index = cmd.getStartIndex().intValue() == 0 ? 0 : cmd.getStartIndex().intValue() / cmd.getPageSizeVal().intValue(); List wPagination = new ArrayList(); List> partitions = partitionNetworks(networksToReturn, cmd.getPageSizeVal().intValue()); - if (startIndex < partitions.size()) { - wPagination = partitions.get(startIndex); + if (index < partitions.size()) { + wPagination = partitions.get(index); } - return wPagination; + return new Pair, Integer>(wPagination, networksToReturn.size()); } - return networksToReturn; + return new Pair, Integer>(networksToReturn, networksToReturn.size()); } - private static List> partitionNetworks(List originalList, - int chunkSize) { + private static List> partitionNetworks(List originalList, int chunkSize) { List> listOfChunks = new ArrayList>(); for (int i = 0; i < originalList.size() / chunkSize; i++) { - listOfChunks.add(originalList.subList(i * chunkSize, i * chunkSize - + chunkSize)); + listOfChunks.add(originalList.subList(i * chunkSize, i * chunkSize + chunkSize)); } if (originalList.size() % chunkSize != 0) { - listOfChunks.add(originalList.subList(originalList.size() - - originalList.size() % chunkSize, originalList.size())); + listOfChunks.add(originalList.subList(originalList.size() - originalList.size() % chunkSize, originalList.size())); } return listOfChunks; } private SearchCriteria buildNetworkSearchCriteria(SearchBuilder sb, String keyword, Long id, Boolean isSystem, Long zoneId, String guestIpType, - String trafficType, Long physicalNetworkId, String aclType, boolean skipProjectNetworks, Boolean restartRequired, Boolean specifyIpRanges, Long vpcId, - Map tags) { + String trafficType, Long physicalNetworkId, String aclType, boolean skipProjectNetworks, Boolean restartRequired, Boolean specifyIpRanges, Long vpcId, + Map tags) { SearchCriteria sc = sb.create(); @@ -1820,8 +1796,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_NETWORK_RESTART, eventDescription = "restarting network", async = true) - public boolean restartNetwork(RestartNetworkCmd cmd, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { + public boolean restartNetwork(RestartNetworkCmd cmd, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // This method restarts all network elements belonging to the network and re-applies all the rules Long networkId = cmd.getNetworkId(); @@ -1838,8 +1813,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // Don't allow to restart network if it's not in Implemented/Setup state if (!(network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup)) { - throw new InvalidParameterValueException("Network is not in the right state to be restarted. Correct states are: " + Network.State.Implemented + ", " + - Network.State.Setup); + throw new InvalidParameterValueException("Network is not in the right state to be restarted. Correct states are: " + Network.State.Implemented + ", " + + Network.State.Setup); } if (network.getBroadcastDomainType() == BroadcastDomainType.Lswitch) { @@ -1899,8 +1874,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (elementCapabilities == null || !elementCapabilities.containsKey(service)) { // TBD: We should be sending providerId and not the offering object itself. - throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() + - " implementing Provider=" + provider); + throw new UnsupportedServiceException("Service " + service.getName() + " is not supported by the element=" + element.getName() + " implementing Provider=" + + provider); } serviceCapabilities = elementCapabilities.get(service); } @@ -1925,10 +1900,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { protected boolean isSharedNetworkOfferingWithServices(long networkOfferingId) { NetworkOfferingVO networkOffering = _networkOfferingDao.findById(networkOfferingId); - if ((networkOffering.getGuestType() == Network.GuestType.Shared) && - (areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) || areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) || - areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) || - areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || areServicesSupportedByNetworkOffering(networkOfferingId, Service.Lb))) { + if ((networkOffering.getGuestType() == Network.GuestType.Shared) + && (areServicesSupportedByNetworkOffering(networkOfferingId, Service.SourceNat) || areServicesSupportedByNetworkOffering(networkOfferingId, Service.StaticNat) + || areServicesSupportedByNetworkOffering(networkOfferingId, Service.Firewall) + || areServicesSupportedByNetworkOffering(networkOfferingId, Service.PortForwarding) || areServicesSupportedByNetworkOffering(networkOfferingId, Service.Lb))) { return true; } return false; @@ -1943,8 +1918,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } private boolean checkForNonStoppedVmInNetwork(long networkId) { - List vms = - _userVmDao.listByNetworkIdAndStates(networkId, VirtualMachine.State.Starting, VirtualMachine.State.Running, VirtualMachine.State.Migrating, + List vms = _userVmDao.listByNetworkIdAndStates(networkId, VirtualMachine.State.Starting, VirtualMachine.State.Running, VirtualMachine.State.Migrating, VirtualMachine.State.Stopping); return vms.isEmpty(); } @@ -1953,7 +1927,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @DB @ActionEvent(eventType = EventTypes.EVENT_NETWORK_UPDATE, eventDescription = "updating network", async = true) public Network updateGuestNetwork(final long networkId, String name, String displayText, Account callerAccount, User callerUser, String domainSuffix, - final Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork) { + final Long networkOfferingId, Boolean changeCidr, String guestVmCidr, Boolean displayNetwork) { boolean restartNetwork = false; @@ -1998,10 +1972,18 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { network.setDisplayText(displayText); } - if (displayNetwork != null) { + // display flag is not null and has changed + if (displayNetwork != null && displayNetwork != network.getDisplayNetwork()) { if (!_accountMgr.isRootAdmin(callerAccount.getType())) { throw new PermissionDeniedException("Only admin allowed to update displaynetwork parameter"); } + + // Update resource count if it needs to be updated + NetworkOffering networkOffering = _networkOfferingDao.findById(network.getNetworkOfferingId()); + if (_networkMgr.resourceCountNeedsUpdate(networkOffering, network.getAclType())) { + _resourceLimitMgr.changeResourceCount(network.getAccountId(), Resource.ResourceType.network, displayNetwork); + } + network.setDisplayNetwork(displayNetwork); } @@ -2024,8 +2006,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // network offering should be in Enabled state if (networkOffering.getState() != NetworkOffering.State.Enabled) { - InvalidParameterValueException ex = - new InvalidParameterValueException("Network offering with specified id is not in " + NetworkOffering.State.Enabled + " state, can't upgrade to it"); + InvalidParameterValueException ex = new InvalidParameterValueException("Network offering with specified id is not in " + NetworkOffering.State.Enabled + + " state, can't upgrade to it"); ex.addProxyObject(networkOffering.getUuid(), "networkOfferingId"); throw ex; } @@ -2046,33 +2028,31 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } if (changeCidr) { if (!checkForNonStoppedVmInNetwork(network.getId())) { - InvalidParameterValueException ex = - new InvalidParameterValueException("All user vm of network of specified id should be stopped before changing CIDR!"); + InvalidParameterValueException ex = new InvalidParameterValueException("All user vm of network of specified id should be stopped before changing CIDR!"); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } } // check if the network is upgradable if (!canUpgrade(network, oldNetworkOfferingId, networkOfferingId)) { - throw new InvalidParameterValueException("Can't upgrade from network offering " + oldNtwkOff.getUuid() + " to " + networkOffering.getUuid() + - "; check logs for more information"); + throw new InvalidParameterValueException("Can't upgrade from network offering " + oldNtwkOff.getUuid() + " to " + networkOffering.getUuid() + + "; check logs for more information"); } restartNetwork = true; networkOfferingChanged = true; } } - final Map newSvcProviders = - networkOfferingChanged ? _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), - network.getPhysicalNetworkId()) : new HashMap(); + final Map newSvcProviders = networkOfferingChanged ? _networkMgr.finalizeServicesAndProvidersForNetwork( + _entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId()) : new HashMap(); // don't allow to modify network domain if the service is not supported if (domainSuffix != null) { // validate network domain if (!NetUtils.verifyDomainName(domainSuffix)) { throw new InvalidParameterValueException( - "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } long offeringId = oldNetworkOfferingId; @@ -2108,8 +2088,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { throw new InvalidParameterValueException("Cannot specify this nework offering change and guestVmCidr at same time. Specify only one."); } if (!(network.getState() == Network.State.Implemented)) { - throw new InvalidParameterValueException("The network must be in " + Network.State.Implemented + " state. IP Reservation cannot be applied in " + - network.getState() + " state"); + throw new InvalidParameterValueException("The network must be in " + Network.State.Implemented + " state. IP Reservation cannot be applied in " + + network.getState() + " state"); } if (!NetUtils.isValidCIDR(guestVmCidr)) { throw new InvalidParameterValueException("Invalid format of Guest VM CIDR."); @@ -2122,13 +2102,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // But in case networkCidr is a non null value (IP reservation already exists), it implies network cidr is networkCidr if (networkCidr != null) { if (!NetUtils.isNetworkAWithinNetworkB(guestVmCidr, networkCidr)) { - throw new InvalidParameterValueException("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + - networkCidr); + throw new InvalidParameterValueException("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + + networkCidr); } } else { if (!NetUtils.isNetworkAWithinNetworkB(guestVmCidr, network.getCidr())) { - throw new InvalidParameterValueException("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + - network.getCidr()); + throw new InvalidParameterValueException("Invalid value of Guest VM CIDR. For IP Reservation, Guest VM CIDR should be a subset of network CIDR : " + + network.getCidr()); } } @@ -2158,15 +2138,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // the IP ranges exactly matches, in these special cases make sure no Reservation gets applied if (network.getNetworkCidr() == null) { if (NetUtils.isSameIpRange(guestVmCidr, network.getCidr()) && !guestVmCidr.equals(network.getCidr())) { - throw new InvalidParameterValueException("The Start IP and End IP of guestvmcidr: " + guestVmCidr + " and CIDR: " + network.getCidr() + - " are same, " + "even though both the cidrs appear to be different. As a precaution no IP Reservation will be applied."); + throw new InvalidParameterValueException("The Start IP and End IP of guestvmcidr: " + guestVmCidr + " and CIDR: " + network.getCidr() + " are same, " + + "even though both the cidrs appear to be different. As a precaution no IP Reservation will be applied."); } } else { if (NetUtils.isSameIpRange(guestVmCidr, network.getNetworkCidr()) && !guestVmCidr.equals(network.getNetworkCidr())) { - throw new InvalidParameterValueException("The Start IP and End IP of guestvmcidr: " + guestVmCidr + " and Network CIDR: " + network.getNetworkCidr() + - " are same, " + - "even though both the cidrs appear to be different. As a precaution IP Reservation will not be affected. If you want to reset IP Reservation, " + - "specify guestVmCidr to be: " + network.getNetworkCidr()); + throw new InvalidParameterValueException("The Start IP and End IP of guestvmcidr: " + guestVmCidr + " and Network CIDR: " + network.getNetworkCidr() + + " are same, " + + "even though both the cidrs appear to be different. As a precaution IP Reservation will not be affected. If you want to reset IP Reservation, " + + "specify guestVmCidr to be: " + network.getNetworkCidr()); } } @@ -2191,8 +2171,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { ReservationContext context = new ReservationContextImpl(null, null, callerUser, callerAccount); // 1) Shutdown all the elements and cleanup all the rules. Don't allow to shutdown network in intermediate // states - Shutdown and Implementing - boolean validStateToShutdown = - (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated); + boolean validStateToShutdown = (network.getState() == Network.State.Implemented || network.getState() == Network.State.Setup || network.getState() == Network.State.Allocated); if (restartNetwork) { if (validStateToShutdown) { if (!changeCidr) { @@ -2200,8 +2179,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (!_networkMgr.shutdownNetworkElementsAndResources(context, true, network)) { s_logger.warn("Failed to shutdown the network elements and resources as a part of network restart: " + network); - CloudRuntimeException ex = - new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network of specified id"); + CloudRuntimeException ex = new CloudRuntimeException("Failed to shutdown the network elements and resources as a part of update to network of specified id"); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -2211,8 +2189,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { //check if network has reservation if (NetUtils.isNetworkAWithinNetworkB(network.getCidr(), network.getNetworkCidr())) { - s_logger.warn("Existing IP reservation will become ineffective for the network with id = " + networkId + - " You need to reapply reservation after network reimplementation."); + s_logger.warn("Existing IP reservation will become ineffective for the network with id = " + networkId + + " You need to reapply reservation after network reimplementation."); //set cidr to the newtork cidr network.setCidr(network.getNetworkCidr()); //set networkCidr to null to bring network back to no IP reservation state @@ -2227,10 +2205,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } } } else { - CloudRuntimeException ex = - new CloudRuntimeException( - "Failed to shutdown the network elements and resources as a part of update to network with specified id; network is in wrong state: " + - network.getState()); + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to shutdown the network elements and resources as a part of update to network with specified id; network is in wrong state: " + network.getState()); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -2241,8 +2217,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Network.State networkState = _networksDao.findById(networkId).getState(); boolean validStateToImplement = (networkState == Network.State.Implemented || networkState == Network.State.Setup || networkState == Network.State.Allocated); if (restartNetwork && !validStateToImplement) { - CloudRuntimeException ex = - new CloudRuntimeException( + CloudRuntimeException ex = new CloudRuntimeException( "Failed to implement the network elements and resources as a part of update to network with specified id; network is in wrong state: " + networkState); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; @@ -2269,16 +2244,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { long isDefault = (nic.isDefaultNic()) ? 1 : 0; String nicIdString = Long.toString(nic.getId()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, - oldNetworkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); + oldNetworkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), nicIdString, - networkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); + networkOfferingId, null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); } } }); } else { network.setNetworkOfferingId(networkOfferingId); _networksDao.update(networkId, network, - _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())); + _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId())); } } else { _networksDao.update(networkId, network); @@ -2297,8 +2272,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } } catch (Exception ex) { s_logger.warn("Failed to implement network " + network + " elements and resources as a part of network update due to ", ex); - CloudRuntimeException e = - new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of network update"); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified id) elements and resources as a part of network update"); e.addProxyObject(network.getUuid(), "networkId"); throw e; } @@ -2314,8 +2288,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { _networkMgr.implementNetwork(network.getId(), dest, context); } catch (Exception ex) { s_logger.warn("Failed to implement network " + network + " elements and resources as a part o" + "f network update due to ", ex); - CloudRuntimeException e = - new CloudRuntimeException("Failed to implement network (with specified" + " id) elements and resources as a part of network update"); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network (with specified" + " id) elements and resources as a part of network update"); e.addProxyObject(network.getUuid(), "networkId"); throw e; } @@ -2357,8 +2330,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } // security group service should be the same - if (areServicesSupportedByNetworkOffering(oldNetworkOfferingId, Service.SecurityGroup) != areServicesSupportedByNetworkOffering(newNetworkOfferingId, - Service.SecurityGroup)) { + if (areServicesSupportedByNetworkOffering(oldNetworkOfferingId, Service.SecurityGroup) != areServicesSupportedByNetworkOffering(newNetworkOfferingId, Service.SecurityGroup)) { s_logger.debug("Offerings " + newNetworkOfferingId + " and " + oldNetworkOfferingId + " have different securityGroupProperty, can't upgrade"); return false; } @@ -2372,8 +2344,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // tags should be the same if (newNetworkOffering.getTags() != null) { if (oldNetworkOffering.getTags() == null) { - s_logger.debug("New network offering id=" + newNetworkOfferingId + " has tags and old network offering id=" + oldNetworkOfferingId + - " doesn't, can't upgrade"); + s_logger.debug("New network offering id=" + newNetworkOfferingId + " has tags and old network offering id=" + oldNetworkOfferingId + " doesn't, can't upgrade"); return false; } if (!oldNetworkOffering.getTags().equalsIgnoreCase(newNetworkOffering.getTags())) { @@ -2429,7 +2400,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @DB @ActionEvent(eventType = EventTypes.EVENT_PHYSICAL_NETWORK_CREATE, eventDescription = "Creating Physical Network", create = true) public PhysicalNetwork createPhysicalNetwork(final Long zoneId, final String vnetRange, final String networkSpeed, final List isolationMethods, - String broadcastDomainRangeStr, final Long domainId, final List tags, final String name) { + String broadcastDomainRangeStr, final Long domainId, final List tags, final String name) { // Check if zone exists if (zoneId == null) { @@ -2451,8 +2422,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (zoneType == NetworkType.Basic) { if (!_physicalNetworkDao.listByZone(zoneId).isEmpty()) { // TBD: Send uuid instead of zoneId; may have to hardcode tablename in call to addProxyObject(). - throw new CloudRuntimeException("Cannot add the physical network to basic zone id: " + zoneId + - ", there is a physical network already existing in this basic Zone"); + throw new CloudRuntimeException("Cannot add the physical network to basic zone id: " + zoneId + ", there is a physical network already existing in this basic Zone"); } } if (tags != null && tags.size() > 1) { @@ -2466,8 +2436,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (vnetRange != null) { // Verify zone type if (zoneType == NetworkType.Basic || (zoneType == NetworkType.Advanced && zone.isSecurityGroupEnabled())) { - throw new InvalidParameterValueException("Can't add vnet range to the physical network in the zone that supports " + zoneType + - " network, Security Group enabled: " + zone.isSecurityGroupEnabled()); + throw new InvalidParameterValueException("Can't add vnet range to the physical network in the zone that supports " + zoneType + + " network, Security Group enabled: " + zone.isSecurityGroupEnabled()); } } @@ -2583,8 +2553,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } if (newVnetRange != null) { if (zone.getNetworkType() == NetworkType.Basic || (zone.getNetworkType() == NetworkType.Advanced && zone.isSecurityGroupEnabled())) { - throw new InvalidParameterValueException("Can't add vnet range to the physical network in the zone that supports " + zone.getNetworkType() + - " network, Security Group enabled: " + zone.isSecurityGroupEnabled()); + throw new InvalidParameterValueException("Can't add vnet range to the physical network in the zone that supports " + zone.getNetworkType() + + " network, Security Group enabled: " + zone.isSecurityGroupEnabled()); } } @@ -2672,14 +2642,14 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override public void doInTransactionWithoutResult(TransactionStatus status) { if (addVnetsFinal != null) { - s_logger.debug("Adding vnet range " + addVnetsFinal.toString() + " for the physicalNetwork id= " + network.getId() + " and zone id=" + - network.getDataCenterId() + " as a part of updatePhysicalNetwork call"); + s_logger.debug("Adding vnet range " + addVnetsFinal.toString() + " for the physicalNetwork id= " + network.getId() + " and zone id=" + + network.getDataCenterId() + " as a part of updatePhysicalNetwork call"); //add vnet takes a list of strings to be added. each string is a vnet. _dcDao.addVnet(network.getDataCenterId(), network.getId(), addVnetsFinal); } if (removeVnetsFinal != null) { - s_logger.debug("removing vnet range " + removeVnetsFinal.toString() + " for the physicalNetwork id= " + network.getId() + " and zone id=" + - network.getDataCenterId() + " as a part of updatePhysicalNetwork call"); + s_logger.debug("removing vnet range " + removeVnetsFinal.toString() + " for the physicalNetwork id= " + network.getId() + " and zone id=" + + network.getDataCenterId() + " as a part of updatePhysicalNetwork call"); //deleteVnets takes a list of strings to be removed. each string is a vnet. _datacneterVnet.deleteVnets(TransactionLegacy.currentTxn(), network.getDataCenterId(), network.getId(), removeVnetsFinal); } @@ -2733,8 +2703,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { return vlanTokens; } if (VnetRange.length < 2) { - throw new InvalidParameterValueException( - "Please provide valid vnet range. vnet range should be a coma seperated list of vlan ranges. example 500-500,600-601" + rangeMessage); + throw new InvalidParameterValueException("Please provide valid vnet range. vnet range should be a coma seperated list of vlan ranges. example 500-500,600-601" + + rangeMessage); } if (VnetRange[0] == null || VnetRange[1] == null) { @@ -2746,8 +2716,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { EndVnet = Integer.parseInt(VnetRange[1]); } catch (NumberFormatException e) { s_logger.warn("Unable to parse vnet range:", e); - throw new InvalidParameterValueException("Please provide valid vnet range. The vnet range should be a coma seperated list example 2001-2012,3000-3005." + - rangeMessage); + throw new InvalidParameterValueException("Please provide valid vnet range. The vnet range should be a coma seperated list example 2001-2012,3000-3005." + + rangeMessage); } if (StartVnet < minVnet || EndVnet > maxVnet) { throw new InvalidParameterValueException("Vnet range has to be" + rangeMessage); @@ -2833,8 +2803,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Integer dedicatedStartVlan = Integer.parseInt(vlans[0]); Integer dedicatedEndVlan = Integer.parseInt(vlans[1]); if ((start >= dedicatedStartVlan && start <= dedicatedEndVlan) || (end >= dedicatedStartVlan && end <= dedicatedEndVlan)) { - throw new InvalidParameterValueException("Vnet range " + map.getGuestVlanRange() + " is dedicated" + " to an account. The specified range " + start + - "-" + end + " overlaps with the dedicated range " + " Please release the overlapping dedicated range before deleting the range"); + throw new InvalidParameterValueException("Vnet range " + map.getGuestVlanRange() + " is dedicated" + " to an account. The specified range " + start + "-" + end + + " overlaps with the dedicated range " + " Please release the overlapping dedicated range before deleting the range"); } } } @@ -3047,13 +3017,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // Verify guest vlans in the range don't belong to a network of a different account for (int i = startVlan; i <= endVlan; i++) { - List allocatedVlans = - _datacneterVnet.listAllocatedVnetsInRange(physicalNetwork.getDataCenterId(), physicalNetwork.getId(), startVlan, endVlan); + List allocatedVlans = _datacneterVnet.listAllocatedVnetsInRange(physicalNetwork.getDataCenterId(), physicalNetwork.getId(), startVlan, endVlan); if (allocatedVlans != null && !allocatedVlans.isEmpty()) { for (DataCenterVnetVO allocatedVlan : allocatedVlans) { if (allocatedVlan.getAccountId() != vlanOwner.getAccountId()) { - throw new InvalidParameterValueException("Guest vlan from this range " + allocatedVlan.getVnet() + " is allocated to a different account." + - " Can only dedicate a range which has no allocated vlans or has vlans allocated to the same account "); + throw new InvalidParameterValueException("Guest vlan from this range " + allocatedVlan.getVnet() + " is allocated to a different account." + + " Can only dedicate a range which has no allocated vlans or has vlans allocated to the same account "); } } } @@ -3195,8 +3164,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (zoneId != null) { SearchBuilder physicalnetworkSearch = _physicalNetworkDao.createSearchBuilder(); physicalnetworkSearch.and("zoneId", physicalnetworkSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ); - sb.join("physicalnetworkSearch", physicalnetworkSearch, sb.entity().getPhysicalNetworkId(), physicalnetworkSearch.entity().getId(), - JoinBuilder.JoinType.INNER); + sb.join("physicalnetworkSearch", physicalnetworkSearch, sb.entity().getPhysicalNetworkId(), physicalnetworkSearch.entity().getId(), JoinBuilder.JoinType.INNER); } SearchCriteria sc = sb.create(); @@ -3269,8 +3237,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_SERVICE_PROVIDER_CREATE, eventDescription = "Creating Physical Network ServiceProvider", create = true) - public PhysicalNetworkServiceProvider addProviderToPhysicalNetwork(Long physicalNetworkId, String providerName, Long destinationPhysicalNetworkId, - List enabledServices) { + public PhysicalNetworkServiceProvider addProviderToPhysicalNetwork(Long physicalNetworkId, String providerName, Long destinationPhysicalNetworkId, List enabledServices) { // verify input parameters PhysicalNetworkVO network = _physicalNetworkDao.findById(physicalNetworkId); @@ -3312,8 +3279,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (enabledServices != null) { if (!element.canEnableIndividualServices()) { if (enabledServices.size() != element.getCapabilities().keySet().size()) { - throw new InvalidParameterValueException( - "Cannot enable subset of Services, Please specify the complete list of Services for this Service Provider '" + providerName + "'"); + throw new InvalidParameterValueException("Cannot enable subset of Services, Please specify the complete list of Services for this Service Provider '" + + providerName + "'"); } } @@ -3363,7 +3330,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override public Pair, Integer> listNetworkServiceProviders(Long physicalNetworkId, String name, String state, Long startIndex, - Long pageSize) { + Long pageSize) { Filter searchFilter = new Filter(PhysicalNetworkServiceProviderVO.class, "id", false, startIndex, pageSize); SearchBuilder sb = _pNSPDao.createSearchBuilder(); @@ -3412,25 +3379,25 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (state != null) { if (s_logger.isDebugEnabled()) { - s_logger.debug("trying to update the state of the service provider id=" + id + " on physical network: " + provider.getPhysicalNetworkId() + - " to state: " + stateStr); + s_logger.debug("trying to update the state of the service provider id=" + id + " on physical network: " + provider.getPhysicalNetworkId() + " to state: " + + stateStr); } switch (state) { - case Enabled: - if (element != null && element.isReady(provider)) { - provider.setState(PhysicalNetworkServiceProvider.State.Enabled); - update = true; - } else { - throw new CloudRuntimeException("Provider is not ready, cannot Enable the provider, please configure the provider first"); - } - break; - case Disabled: - // do we need to do anything for the provider instances before disabling? - provider.setState(PhysicalNetworkServiceProvider.State.Disabled); + case Enabled: + if (element != null && element.isReady(provider)) { + provider.setState(PhysicalNetworkServiceProvider.State.Enabled); update = true; - break; - case Shutdown: - throw new InvalidParameterValueException("Updating the provider state to 'Shutdown' is not supported"); + } else { + throw new CloudRuntimeException("Provider is not ready, cannot Enable the provider, please configure the provider first"); + } + break; + case Disabled: + // do we need to do anything for the provider instances before disabling? + provider.setState(PhysicalNetworkServiceProvider.State.Disabled); + update = true; + break; + case Shutdown: + throw new InvalidParameterValueException("Updating the provider state to 'Shutdown' is not supported"); } } @@ -3473,7 +3440,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { List networks = _networksDao.listByPhysicalNetworkAndProvider(provider.getPhysicalNetworkId(), provider.getProviderName()); if (networks != null && !networks.isEmpty()) { throw new CloudRuntimeException( - "Provider is not deletable because there are active networks using this provider, please upgrade these networks to new network offerings"); + "Provider is not deletable because there are active networks using this provider, please upgrade these networks to new network offerings"); } User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId()); @@ -3532,8 +3499,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (pNtwks.size() > 1) { if (tag == null) { - throw new InvalidParameterValueException("More than one physical networks exist in zone id=" + zoneId + - " and no tags are specified in order to make a choice"); + throw new InvalidParameterValueException("More than one physical networks exist in zone id=" + zoneId + " and no tags are specified in order to make a choice"); } Long pNtwkId = null; @@ -3556,8 +3522,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_TRAFFIC_TYPE_CREATE, eventDescription = "Creating Physical Network TrafficType", create = true) - public PhysicalNetworkTrafficType addTrafficTypeToPhysicalNetwork(Long physicalNetworkId, String trafficTypeStr, String xenLabel, String kvmLabel, - String vmwareLabel, String simulatorLabel, String vlan, String hypervLabel) { + public PhysicalNetworkTrafficType addTrafficTypeToPhysicalNetwork(Long physicalNetworkId, String trafficTypeStr, String xenLabel, String kvmLabel, String vmwareLabel, + String simulatorLabel, String vlan, String hypervLabel) { // verify input parameters PhysicalNetworkVO network = _physicalNetworkDao.findById(physicalNetworkId); @@ -3583,19 +3549,18 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (TrafficType.isSystemNetwork(trafficType) || TrafficType.Public.equals(trafficType) || TrafficType.Storage.equals(trafficType)) { if (!_physicalNetworkDao.listByZoneAndTrafficType(network.getDataCenterId(), trafficType).isEmpty()) { - throw new CloudRuntimeException("Fail to add the traffic type to physical network because Zone already has a physical network with this traffic type: " + - trafficType); + throw new CloudRuntimeException("Fail to add the traffic type to physical network because Zone already has a physical network with this traffic type: " + + trafficType); } } if (TrafficType.Storage.equals(trafficType)) { List ssvms = _stnwMgr.getSSVMWithNoStorageNetwork(network.getDataCenterId()); if (!ssvms.isEmpty()) { - StringBuilder sb = - new StringBuilder( - "Cannot add " + - trafficType + - " traffic type as there are below secondary storage vm still running. Please stop them all and add Storage traffic type again, then destory them all to allow CloudStack recreate them with storage network(If you have added storage network ip range)"); + StringBuilder sb = new StringBuilder( + "Cannot add " + + trafficType + + " traffic type as there are below secondary storage vm still running. Please stop them all and add Storage traffic type again, then destory them all to allow CloudStack recreate them with storage network(If you have added storage network ip range)"); sb.append("SSVMs:"); for (SecondaryStorageVmVO ssvm : ssvms) { sb.append(ssvm.getInstanceName()).append(":").append(ssvm.getState()); @@ -3609,8 +3574,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (xenLabel == null) { xenLabel = getDefaultXenNetworkLabel(trafficType); } - PhysicalNetworkTrafficTypeVO pNetworktrafficType = - new PhysicalNetworkTrafficTypeVO(physicalNetworkId, trafficType, xenLabel, kvmLabel, vmwareLabel, simulatorLabel, vlan, hypervLabel); + PhysicalNetworkTrafficTypeVO pNetworktrafficType = new PhysicalNetworkTrafficTypeVO(physicalNetworkId, trafficType, xenLabel, kvmLabel, vmwareLabel, simulatorLabel, + vlan, hypervLabel); pNetworktrafficType = _pNTrafficTypeDao.persist(pNetworktrafficType); return pNetworktrafficType; @@ -3624,24 +3589,24 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { private String getDefaultXenNetworkLabel(TrafficType trafficType) { String xenLabel = null; switch (trafficType) { - case Public: - xenLabel = _configDao.getValue(Config.XenPublicNetwork.key()); - break; - case Guest: - xenLabel = _configDao.getValue(Config.XenGuestNetwork.key()); - break; - case Storage: - xenLabel = _configDao.getValue(Config.XenStorageNetwork1.key()); - break; - case Management: - xenLabel = _configDao.getValue(Config.XenPrivateNetwork.key()); - break; - case Control: - xenLabel = "cloud_link_local_network"; - break; - case Vpn: - case None: - break; + case Public: + xenLabel = _configDao.getValue(Config.XenPublicNetwork.key()); + break; + case Guest: + xenLabel = _configDao.getValue(Config.XenGuestNetwork.key()); + break; + case Storage: + xenLabel = _configDao.getValue(Config.XenStorageNetwork1.key()); + break; + case Management: + xenLabel = _configDao.getValue(Config.XenPrivateNetwork.key()); + break; + case Control: + xenLabel = "cloud_link_local_network"; + break; + case Vpn: + case None: + break; } return xenLabel; } @@ -3704,14 +3669,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { // check if there are any networks associated to this physical network with this traffic type if (TrafficType.Guest.equals(trafficType.getTrafficType())) { if (!_networksDao.listByPhysicalNetworkTrafficType(trafficType.getPhysicalNetworkId(), trafficType.getTrafficType()).isEmpty()) { - throw new CloudRuntimeException("The Traffic Type is not deletable because there are existing networks with this traffic type:" + - trafficType.getTrafficType()); + throw new CloudRuntimeException("The Traffic Type is not deletable because there are existing networks with this traffic type:" + trafficType.getTrafficType()); } } else if (TrafficType.Storage.equals(trafficType.getTrafficType())) { PhysicalNetworkVO pn = _physicalNetworkDao.findById(trafficType.getPhysicalNetworkId()); if (_stnwMgr.isAnyStorageIpInUseInZone(pn.getDataCenterId())) { - throw new CloudRuntimeException("The Traffic Type is not deletable because there are still some storage network ip addresses in use:" + - trafficType.getTrafficType()); + throw new CloudRuntimeException("The Traffic Type is not deletable because there are still some storage network ip addresses in use:" + + trafficType.getTrafficType()); } } return _pNTrafficTypeDao.remove(id); @@ -3732,17 +3696,15 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override //TODO: duplicated in NetworkModel - public - NetworkVO getExclusiveGuestNetwork(long zoneId) { + public NetworkVO getExclusiveGuestNetwork(long zoneId) { List networks = _networksDao.listBy(Account.ACCOUNT_ID_SYSTEM, zoneId, GuestType.Shared, TrafficType.Guest); if (networks == null || networks.isEmpty()) { - throw new InvalidParameterValueException("Unable to find network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared + " in zone " + - zoneId); + throw new InvalidParameterValueException("Unable to find network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared + " in zone " + zoneId); } if (networks.size() > 1) { - throw new InvalidParameterValueException("Found more than 1 network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared + - " in zone " + zoneId); + throw new InvalidParameterValueException("Found more than 1 network with trafficType " + TrafficType.Guest + " and guestType " + GuestType.Shared + " in zone " + + zoneId); } @@ -3772,8 +3734,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } OvsProviderVO element = _ovsProviderDao.findByNspId(nsp.getId()); if (element != null) { - s_logger.debug("There is already a Ovs element with service provider id " - + nsp.getId()); + s_logger.debug("There is already a Ovs element with service provider id " + nsp.getId()); return nsp; } element = new OvsProviderVO(nsp.getId()); @@ -3873,8 +3834,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "associating Ip", async = true) - public IpAddress associateIPToNetwork(long ipId, long networkId) throws InsufficientAddressCapacityException, ResourceAllocationException, - ResourceUnavailableException, ConcurrentOperationException { + public IpAddress associateIPToNetwork(long ipId, long networkId) throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException, + ConcurrentOperationException { Network network = _networksDao.findById(networkId); if (network == null) { @@ -3882,8 +3843,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } if (network.getVpcId() != null) { - throw new InvalidParameterValueException("Can't assign ip to the network directly when network belongs" - + " to VPC.Specify vpcId to associate ip address to VPC"); + throw new InvalidParameterValueException("Can't assign ip to the network directly when network belongs" + " to VPC.Specify vpcId to associate ip address to VPC"); } return _ipAddrMgr.associateIPToGuestNetwork(ipId, networkId, true); @@ -3891,9 +3851,9 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @DB - public Network createPrivateNetwork(final String networkName, final String displayText, long physicalNetworkId, String broadcastUriString, final String startIp, - String endIp, final String gateway, String netmask, final long networkOwnerId, final Long vpcId, final Boolean sourceNat, final Long networkOfferingId) - throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { + public Network createPrivateNetwork(final String networkName, final String displayText, long physicalNetworkId, String broadcastUriString, final String startIp, String endIp, + final String gateway, String netmask, final long networkOwnerId, final Long vpcId, final Boolean sourceNat, final Long networkOfferingId) + throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { final Account owner = _accountMgr.getAccount(networkOwnerId); @@ -3955,24 +3915,23 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { Network privateNetwork = _networksDao.getPrivateNetwork(uriString, cidr, networkOwnerId, pNtwk.getDataCenterId(), networkOfferingId); if (privateNetwork == null) { //create Guest network - privateNetwork = - _networkMgr.createGuestNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, null, owner, null, pNtwk, + privateNetwork = _networkMgr.createGuestNetwork(ntwkOffFinal.getId(), networkName, displayText, gateway, cidr, uriString, null, owner, null, pNtwk, pNtwk.getDataCenterId(), ACLType.Account, null, vpcId, null, null, true, null); s_logger.debug("Created private network " + privateNetwork); } else { s_logger.debug("Private network already exists: " + privateNetwork); //Do not allow multiple private gateways with same Vlan within a VPC if (vpcId.equals(privateNetwork.getVpcId())) { - throw new InvalidParameterValueException("Private network for the vlan: " + uriString + " and cidr " + cidr + " already exists " + - "for Vpc " + vpcId + " in zone " + _entityMgr.findById(DataCenter.class, pNtwk.getDataCenterId()).getName()); + throw new InvalidParameterValueException("Private network for the vlan: " + uriString + " and cidr " + cidr + " already exists " + "for Vpc " + vpcId + + " in zone " + _entityMgr.findById(DataCenter.class, pNtwk.getDataCenterId()).getName()); } } //add entry to private_ip_address table PrivateIpVO privateIp = _privateIpDao.findByIpAndSourceNetworkIdAndVpcId(privateNetwork.getId(), startIp, vpcId); if (privateIp != null) { - throw new InvalidParameterValueException("Private ip address " + startIp + " already used for private gateway" + " in zone " + - _entityMgr.findById(DataCenter.class, pNtwk.getDataCenterId()).getName()); + throw new InvalidParameterValueException("Private ip address " + startIp + " already used for private gateway" + " in zone " + + _entityMgr.findById(DataCenter.class, pNtwk.getDataCenterId()).getName()); } Long mac = dc.getMacAddress(); @@ -4016,17 +3975,18 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { public List listNics(ListNicsCmd cmd) { Account caller = CallContext.current().getCallingAccount(); Long nicId = cmd.getNicId(); - Long vmId = cmd.getVmId(); + long vmId = cmd.getVmId(); + Long networkId = cmd.getNetworkId(); UserVmVO userVm = _userVmDao.findById(vmId); if (userVm == null) { InvalidParameterValueException ex = new InvalidParameterValueException("Virtual mahine id does not exist"); - ex.addProxyObject(vmId.toString(), "vmId"); + ex.addProxyObject(Long.valueOf(vmId).toString(), "vmId"); throw ex; } _accountMgr.checkAccess(caller, null, true, userVm); - return _networkMgr.listVmNics(vmId, nicId); + return _networkMgr.listVmNics(vmId, nicId, networkId); } public List getNetworkGurus() { diff --git a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java index f535e2e959c..5695a2c9fcd 100644 --- a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java @@ -353,6 +353,9 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc s_logger.debug("Failed to apply network acl id " + gateway.getNetworkACLId() + " on gateway "); return false; } + } else { + s_logger.debug ("Failed to setup private gateway "+ gateway); + return false; } return true; } @@ -413,10 +416,15 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc return true; } - if (!_vpcRouterMgr.applyNetworkACLs(config, rules, routers, false)) { - throw new CloudRuntimeException("Failed to apply network acl rules in network " + config.getId()); - } else { - return true; + try { + if (!_vpcRouterMgr.applyNetworkACLs(config, rules, routers, false)) { + return false; + } else { + return true; + } + } catch (Exception ex) { + s_logger.debug("Failed to apply network acl in network " + config.getId()); + return false; } } else { return true; diff --git a/server/src/com/cloud/network/guru/ControlNetworkGuru.java b/server/src/com/cloud/network/guru/ControlNetworkGuru.java index 1de12b7ea19..a6e21801586 100755 --- a/server/src/com/cloud/network/guru/ControlNetworkGuru.java +++ b/server/src/com/cloud/network/guru/ControlNetworkGuru.java @@ -173,8 +173,8 @@ public class ControlNetworkGuru extends PodBasedNetworkGuru implements NetworkGu @Override public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { assert nic.getTrafficType() == TrafficType.Control; - - if (vm.getHypervisorType() == HypervisorType.VMware && isRouterVm(vm)) { + HypervisorType hType = vm.getHypervisorType(); + if ( ( (hType == HypervisorType.VMware) || (hType == HypervisorType.Hyperv) )&& isRouterVm(vm)) { long dcId = vm.getVirtualMachine().getDataCenterId(); DataCenterVO dcVo = _dcDao.findById(dcId); if (dcVo.getNetworkType() != NetworkType.Basic) { diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index c2902b1c7f8..82332b731b2 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -665,6 +665,10 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V static final ConfigKey UseExternalDnsServers = new ConfigKey(Boolean.class, "use.external.dns", "Advanced", "false", "Bypass internal dns, use external dns1 and dns2", true, ConfigKey.Scope.Zone, null); + static final ConfigKey routerVersionCheckEnabled = new ConfigKey("Advanced", Boolean.class, "router.version.check", "true", + "If true, router minimum required version is checked before sending command", false); + + @Override public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -2044,7 +2048,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V } else if (nic.getTrafficType() == TrafficType.Control) { controlNic = nic; // DOMR control command is sent over management server in VMware - if (dest.getHost().getHypervisorType() == HypervisorType.VMware) { + if (dest.getHost().getHypervisorType() == HypervisorType.VMware || dest.getHost().getHypervisorType() == HypervisorType.Hyperv) { s_logger.info("Check if we need to add management server explicit route to DomR. pod cidr: " + dest.getPod().getCidrAddress() + "/" + dest.getPod().getCidrSize() + ", pod gateway: " + dest.getPod().getGateway() + ", management host: " + ClusterManager.ManagementHostIPAdr.value()); @@ -2137,7 +2141,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V boolean useExtDns = !dnsProvided; /* For backward compatibility */ - useExtDns = UseExternalDnsServers.valueIn(dc.getId()); + useExtDns = useExtDns || UseExternalDnsServers.valueIn(dc.getId()); if (useExtDns) { buf.append(" useextdns=true"); @@ -2571,7 +2575,16 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V protected ArrayList getPublicIpsToApply(VirtualRouter router, Provider provider, Long guestNetworkId, com.cloud.network.IpAddress.State... skipInStates) { long ownerId = router.getAccountId(); - final List userIps = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, guestNetworkId, null); + final List userIps; + + Network guestNetwork = _networkDao.findById(guestNetworkId); + if (guestNetwork.getGuestType() == GuestType.Shared) { + // ignore the account id for the shared network + userIps = _networkModel.listPublicIpsAssignedToGuestNtwk(guestNetworkId, null); + } else { + userIps = _networkModel.listPublicIpsAssignedToGuestNtwk(ownerId, guestNetworkId, null); + } + List allPublicIps = new ArrayList(); if (userIps != null && !userIps.isEmpty()) { boolean addIp = true; @@ -2755,7 +2768,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V throws StorageUnavailableException, InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Starting router " + router); try { - _itMgr.advanceStart(router.getUuid(), params, planToDeploy); + _itMgr.advanceStart(router.getUuid(), params, planToDeploy, null); } catch (OperationTimedoutException e) { throw new ResourceUnavailableException("Starting router " + router + " failed! " + e.toString(), DataCenter.class, router.getDataCenterId()); } @@ -3512,8 +3525,9 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V } protected boolean sendCommandsToRouter(final VirtualRouter router, Commands cmds) throws AgentUnavailableException { - if (!checkRouterVersion(router)) { - throw new CloudRuntimeException("Router requires upgrade. Unable to send command to router:" + router.getId()); + if(!checkRouterVersion(router)){ + s_logger.debug("Router requires upgrade. Unable to send command to router:" + router.getId()); + throw new CloudRuntimeException("Unable to send command. Upgrade in progress. Please contact administrator."); } Answer[] answers = null; try { @@ -3795,6 +3809,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V s_logger.debug("Router " + router.getInstanceName() + " is stop pending, so not sending apply " + typeString + " commands to the backend"); continue; } + try { result = applier.execute(network, router); connectedRouters.add(router); @@ -3952,8 +3967,10 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List nics = _nicDao.listByVmId(routerId); for (NicVO n : nics) { NetworkVO nc = _networkDao.findById(n.getNetworkId()); - if (nc.getTrafficType() == TrafficType.Control) { + if (nc != null && nc.getTrafficType() == TrafficType.Control) { routerControlIpAddress = n.getIp4Address(); + // router will have only one control ip + break; } } @@ -4099,10 +4116,10 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V if(account == null){ throw new InvalidParameterValueException("Account :"+accountName+" does not exist in domain: "+domainId); } - routers = _routerDao.listBy(account.getId()); + routers = _routerDao.listRunningByAccountId(account.getId()); } else { //List by domainId, account name not specified - routers = _routerDao.listByDomain(domainId); + routers = _routerDao.listRunningByDomain(domainId); } params++; } @@ -4110,19 +4127,19 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V Long clusterId = cmd.getClusterId(); if (clusterId != null) { params++; - routers = _routerDao.listByClusterId(clusterId); + routers = _routerDao.listRunningByClusterId(clusterId); } Long podId = cmd.getPodId(); if (podId != null) { params++; - routers = _routerDao.listByPodId(podId); + routers = _routerDao.listRunningByPodId(podId); } Long zoneId = cmd.getZoneId(); if (zoneId != null) { params++; - routers = _routerDao.listByDataCenter(zoneId); + routers = _routerDao.listRunningByDataCenter(zoneId); } if (params > 1) { @@ -4138,7 +4155,14 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V //Checks if the router is at the required version // Compares MS version and router version - private boolean checkRouterVersion(VirtualRouter router) { + protected boolean checkRouterVersion(VirtualRouter router){ + if(!routerVersionCheckEnabled.value()){ + //Router version check is disabled. + return true; + } + if(router.getTemplateVersion() == null){ + return false; + } String trimmedVersion = Version.trimRouterVersion(router.getTemplateVersion()); return (Version.compare(trimmedVersion, MinVRVersion) >= 0); } @@ -4178,6 +4202,6 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {UseExternalDnsServers}; + return new ConfigKey[] {UseExternalDnsServers, routerVersionCheckEnabled}; } } diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index c3099c7754a..6e4b771073f 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -789,7 +789,7 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian // should this be a vlan id or a broadcast uri??? String vlanTag = BroadcastDomainType.getValue(network.getBroadcastUri()); String netmask = NetUtils.getCidrNetmask(network.getCidr()); - PrivateIpAddress ip = new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, guestNic.getMacAddress()); + PrivateIpAddress ip = new PrivateIpAddress(ipVO, network.getBroadcastUri().toString(), network.getGateway(), netmask, guestNic.getMacAddress()); List privateIps = new ArrayList(1); privateIps.add(ip); @@ -891,6 +891,10 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian Network network = _networkModel.getNetwork(gateway.getNetworkId()); NicProfile requested = createPrivateNicProfileForGateway(gateway); + if (!checkRouterVersion(router)) { + s_logger.warn("Router requires upgrade. Unable to send command to router: " + router.getId()); + return false; + } NicProfile guestNic = _itMgr.addVmToNetwork(router, network, requested); //setup source nat @@ -932,18 +936,23 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian // or maybe conditional; in case of vlan ... in case of lswitch String vlanTag = BroadcastDomainType.getValue(network.getBroadcastUri()); String netmask = NetUtils.getCidrNetmask(network.getCidr()); - PrivateIpAddress ip = new PrivateIpAddress(ipVO, vlanTag, network.getGateway(), netmask, privateNic.getMacAddress()); + PrivateIpAddress ip = new PrivateIpAddress(ipVO, network.getBroadcastUri().toString(), network.getGateway(), netmask, privateNic.getMacAddress()); List privateIps = new ArrayList(1); privateIps.add(ip); Commands cmds = new Commands(Command.OnError.Stop); createVpcAssociatePrivateIPCommands(router, privateIps, cmds, add); - if (sendCommandsToRouter(router, cmds)) { - s_logger.debug("Successfully applied ip association for ip " + ip + " in vpc network " + network); - return true; - } else { - s_logger.warn("Failed to associate ip address " + ip + " in vpc network " + network); + try{ + if (sendCommandsToRouter(router, cmds)) { + s_logger.debug("Successfully applied ip association for ip " + ip + " in vpc network " + network); + return true; + } else { + s_logger.warn("Failed to associate ip address " + ip + " in vpc network " + network); + return false; + } + }catch (Exception ex) { + s_logger.warn("Failed to send " + (add ?"add ":"delete ") + " private network " + network + " commands to rotuer "); return false; } } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { diff --git a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java index d60d2f3d8bf..cd684407c2e 100755 --- a/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java +++ b/server/src/com/cloud/network/security/SecurityGroupManagerImpl.java @@ -40,9 +40,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.commons.codec.digest.DigestUtils; -import org.apache.log4j.Logger; - import org.apache.cloudstack.api.command.user.securitygroup.AuthorizeSecurityGroupEgressCmd; import org.apache.cloudstack.api.command.user.securitygroup.AuthorizeSecurityGroupIngressCmd; import org.apache.cloudstack.api.command.user.securitygroup.CreateSecurityGroupCmd; @@ -54,6 +51,8 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.NetworkRulesSystemVmCommand; @@ -122,8 +121,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; @Local(value = {SecurityGroupManager.class, SecurityGroupService.class}) -public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGroupManager, SecurityGroupService, - StateListener { +public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGroupManager, SecurityGroupService, StateListener { public static final Logger s_logger = Logger.getLogger(SecurityGroupManagerImpl.class); @Inject @@ -464,8 +462,8 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro // For each group, find the security rules that allow the group for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao //Add usage events for security group assign - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), mapVO.getSecurityGroupId(), - vm.getClass().getName(), vm.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), mapVO.getSecurityGroupId(), vm + .getClass().getName(), vm.getUuid()); List allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId()); // For each security rule that allows a group that the vm belongs to, find the group it belongs to @@ -480,8 +478,8 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro // For each group, find the security rules rules that allow the group for (SecurityGroupVMMapVO mapVO : groupsForVm) {// FIXME: use custom sql in the dao //Add usage events for security group remove - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), mapVO.getSecurityGroupId(), - vm.getClass().getName(), vm.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SECURITY_GROUP_REMOVE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), mapVO.getSecurityGroupId(), vm + .getClass().getName(), vm.getUuid()); List allowingRules = _securityGroupRuleDao.listByAllowedSecurityGroupId(mapVO.getSecurityGroupId()); // For each security rule that allows a group that the vm belongs to, find the group it belongs to @@ -505,27 +503,27 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro } protected SecurityGroupRulesCmd generateRulesetCmd(String vmName, String guestIp, String guestMac, Long vmId, String signature, long seqnum, - Map> ingressRules, Map> egressRules, List secIps) { + Map> ingressRules, Map> egressRules, List secIps) { List ingressResult = new ArrayList(); List egressResult = new ArrayList(); for (PortAndProto pAp : ingressRules.keySet()) { Set cidrs = ingressRules.get(pAp); if (cidrs.size() > 0) { - IpPortAndProto ipPortAndProto = - new SecurityGroupRulesCmd.IpPortAndProto(pAp.getProto(), pAp.getStartPort(), pAp.getEndPort(), cidrs.toArray(new String[cidrs.size()])); + IpPortAndProto ipPortAndProto = new SecurityGroupRulesCmd.IpPortAndProto(pAp.getProto(), pAp.getStartPort(), pAp.getEndPort(), cidrs.toArray(new String[cidrs + .size()])); ingressResult.add(ipPortAndProto); } } for (PortAndProto pAp : egressRules.keySet()) { Set cidrs = egressRules.get(pAp); if (cidrs.size() > 0) { - IpPortAndProto ipPortAndProto = - new SecurityGroupRulesCmd.IpPortAndProto(pAp.getProto(), pAp.getStartPort(), pAp.getEndPort(), cidrs.toArray(new String[cidrs.size()])); + IpPortAndProto ipPortAndProto = new SecurityGroupRulesCmd.IpPortAndProto(pAp.getProto(), pAp.getStartPort(), pAp.getEndPort(), cidrs.toArray(new String[cidrs + .size()])); egressResult.add(ipPortAndProto); } } return new SecurityGroupRulesCmd(guestIp, guestMac, vmName, vmId, signature, seqnum, ingressResult.toArray(new IpPortAndProto[ingressResult.size()]), - egressResult.toArray(new IpPortAndProto[egressResult.size()]), secIps); + egressResult.toArray(new IpPortAndProto[egressResult.size()]), secIps); } protected void handleVmStopped(VMInstanceVO vm) { @@ -590,7 +588,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro } private List authorizeSecurityGroupRule(final Long securityGroupId, String protocol, Integer startPort, Integer endPort, Integer icmpType, - Integer icmpCode, final List cidrList, Map groupList, final SecurityRuleType ruleType) { + Integer icmpCode, final List cidrList, Map groupList, final SecurityRuleType ruleType) { Integer startPortOrType = null; Integer endPortOrCode = null; @@ -685,19 +683,19 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro if ((group == null) || (authorizedAccountName == null)) { throw new InvalidParameterValueException( - "Invalid user group specified, fields 'group' and 'account' cannot be null, please specify groups in the form: userGroupList[0].group=XXX&userGroupList[0].account=YYY"); + "Invalid user group specified, fields 'group' and 'account' cannot be null, please specify groups in the form: userGroupList[0].group=XXX&userGroupList[0].account=YYY"); } Account authorizedAccount = _accountDao.findActiveAccount(authorizedAccountName, domainId); if (authorizedAccount == null) { - throw new InvalidParameterValueException("Nonexistent account: " + authorizedAccountName + " when trying to authorize security group rule for " + - securityGroupId + ":" + protocol + ":" + startPortOrType + ":" + endPortOrCode); + throw new InvalidParameterValueException("Nonexistent account: " + authorizedAccountName + " when trying to authorize security group rule for " + + securityGroupId + ":" + protocol + ":" + startPortOrType + ":" + endPortOrCode); } SecurityGroupVO groupVO = _securityGroupDao.findByAccountAndName(authorizedAccount.getId(), group); if (groupVO == null) { - throw new InvalidParameterValueException("Nonexistent group " + group + " for account " + authorizedAccountName + "/" + domainId + - " is given, unable to authorize security group rule."); + throw new InvalidParameterValueException("Nonexistent group " + group + " for account " + authorizedAccountName + "/" + domainId + + " is given, unable to authorize security group rule."); } // Check permissions @@ -737,21 +735,19 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro throw new ConcurrentAccessException("Failed to acquire lock on security group: " + ngId); } } - SecurityGroupRuleVO securityGroupRule = - _securityGroupRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocolFinal, startPortOrTypeFinal, endPortOrCodeFinal, - ngVO.getId()); + SecurityGroupRuleVO securityGroupRule = _securityGroupRuleDao.findByProtoPortsAndAllowedGroupId(securityGroup.getId(), protocolFinal, startPortOrTypeFinal, + endPortOrCodeFinal, ngVO.getId()); if ((securityGroupRule != null) && (securityGroupRule.getRuleType() == ruleType)) { continue; // rule already exists. } - securityGroupRule = - new SecurityGroupRuleVO(ruleType, securityGroup.getId(), startPortOrTypeFinal, endPortOrCodeFinal, protocolFinal, ngVO.getId()); + securityGroupRule = new SecurityGroupRuleVO(ruleType, securityGroup.getId(), startPortOrTypeFinal, endPortOrCodeFinal, protocolFinal, ngVO.getId()); securityGroupRule = _securityGroupRuleDao.persist(securityGroupRule); newRules.add(securityGroupRule); } if (cidrList != null) { for (String cidr : cidrList) { - SecurityGroupRuleVO securityGroupRule = - _securityGroupRuleDao.findByProtoPortsAndCidr(securityGroup.getId(), protocolFinal, startPortOrTypeFinal, endPortOrCodeFinal, cidr); + SecurityGroupRuleVO securityGroupRule = _securityGroupRuleDao.findByProtoPortsAndCidr(securityGroup.getId(), protocolFinal, startPortOrTypeFinal, + endPortOrCodeFinal, cidr); if ((securityGroupRule != null) && (securityGroupRule.getRuleType() == ruleType)) { continue; } @@ -893,8 +889,8 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro _serverId = ManagementServerNode.getManagementServerId(); - s_logger.info("SecurityGroupManager: num worker threads=" + _numWorkerThreads + ", time between cleanups=" + _timeBetweenCleanups + " global lock timeout=" + - _globalWorkLockTimeout); + s_logger.info("SecurityGroupManager: num worker threads=" + _numWorkerThreads + ", time between cleanups=" + _timeBetweenCleanups + " global lock timeout=" + + _globalWorkLockTimeout); createThreadPools(); return true; @@ -928,7 +924,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro Account accVO = _accountDao.findById(accountId); if (accVO != null) { return createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, accVO.getDomainId(), accVO.getId(), - accVO.getAccountName()); + accVO.getAccountName()); } } return groupVO; @@ -1002,8 +998,7 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro nicSecIps = _nicSecIpDao.getSecondaryIpAddressesForNic(nic.getId()); } } - SecurityGroupRulesCmd cmd = - generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), + SecurityGroupRulesCmd cmd = generateRulesetCmd(vm.getInstanceName(), vm.getPrivateIpAddress(), vm.getPrivateMacAddress(), vm.getId(), generateRulesetSignature(ingressRules, egressRules), seqnum, ingressRules, egressRules, nicSecIps); Commands cmds = new Commands(cmd); try { @@ -1053,8 +1048,8 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro SecurityGroupVO ngrpLock = _securityGroupDao.lockRow(securityGroup.getId(), false); if (ngrpLock == null) { s_logger.warn("Failed to acquire lock on network group id=" + securityGroup.getId() + " name=" + securityGroup.getName()); - throw new ConcurrentModificationException("Failed to acquire lock on network group id=" + securityGroup.getId() + " name=" + - securityGroup.getName()); + throw new ConcurrentModificationException("Failed to acquire lock on network group id=" + securityGroup.getId() + " name=" + + securityGroup.getName()); } if (_securityGroupVMMapDao.findByVmIdGroupId(userVmId, securityGroup.getId()) == null) { SecurityGroupVMMapVO groupVmMapVO = new SecurityGroupVMMapVO(securityGroup.getId(), userVmId); @@ -1334,17 +1329,22 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro } @Override - public boolean securityGroupRulesForVmSecIp(Long nicId, Long networkId, String secondaryIp, boolean ruleAction) { + public boolean securityGroupRulesForVmSecIp(long nicId, String secondaryIp, boolean ruleAction) { + Account caller = CallContext.current().getCallingAccount(); - String vmMac = null; - String vmName = null; - - if (secondaryIp == null || nicId == null || networkId == null) { - throw new InvalidParameterValueException("Vm nicId or networkId or secondaryIp can't be null"); + if (secondaryIp == null) { + throw new InvalidParameterValueException("Vm secondaryIp can't be null"); } NicVO nic = _nicDao.findById(nicId); - Long vmId = nic.getInstanceId(); + long vmId = nic.getInstanceId(); + UserVm vm = _userVMDao.findById(vmId); + if (vm == null || vm.getType() != VirtualMachine.Type.User) { + throw new InvalidParameterValueException("Can't configure the SG ipset, arprules rules for the non existing or non user vm"); + } + + // Verify permissions + _accountMgr.checkAccess(caller, null, false, vm); // Validate parameters List vmSgGrps = getSecurityGroupsForVm(vmId); @@ -1353,28 +1353,17 @@ public class SecurityGroupManagerImpl extends ManagerBase implements SecurityGro return true; } - Account caller = CallContext.current().getCallingAccount(); - for (SecurityGroupVO securityGroup : vmSgGrps) { Account owner = _accountMgr.getAccount(securityGroup.getAccountId()); if (owner == null) { throw new InvalidParameterValueException("Unable to find security group owner by id=" + securityGroup.getAccountId()); } - // Verify permissions - _accountMgr.checkAccess(caller, null, true, securityGroup); } - UserVm vm = _userVMDao.findById(vmId); - if (vm.getType() != VirtualMachine.Type.User) { - throw new InvalidParameterValueException("Can't configure the SG ipset, arprules rules for the non user vm"); - } - - if (vm != null) { - vmMac = vm.getPrivateMacAddress(); - vmName = vm.getInstanceName(); - if (vmMac == null || vmName == null) { - throw new InvalidParameterValueException("vm name or vm mac can't be null"); - } + String vmMac = vm.getPrivateMacAddress(); + String vmName = vm.getInstanceName(); + if (vmMac == null || vmName == null) { + throw new InvalidParameterValueException("vm name or vm mac can't be null"); } //create command for the to add ip in ipset and arptables rules diff --git a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java index a0decf3e58a..979d407697a 100644 --- a/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java +++ b/server/src/com/cloud/network/vpc/NetworkACLManagerImpl.java @@ -377,8 +377,12 @@ public class NetworkACLManagerImpl extends ManagerBase implements NetworkACLMana throw new CloudRuntimeException("Failed to initialize vpc elements"); } - for (VpcProvider provider : vpcElements) { - return provider.applyACLItemsToPrivateGw(gateway, rules); + try{ + for (VpcProvider provider : vpcElements) { + return provider.applyACLItemsToPrivateGw(gateway, rules); + } + } catch(Exception ex) { + s_logger.debug("Failed to apply acl to private gateway " + gateway); } return false; } diff --git a/server/src/com/cloud/network/vpc/NetworkACLServiceImpl.java b/server/src/com/cloud/network/vpc/NetworkACLServiceImpl.java index fbcd461b759..e5a59ffd153 100644 --- a/server/src/com/cloud/network/vpc/NetworkACLServiceImpl.java +++ b/server/src/com/cloud/network/vpc/NetworkACLServiceImpl.java @@ -116,6 +116,7 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ String name = cmd.getName(); Long networkId = cmd.getNetworkId(); Long vpcId = cmd.getVpcId(); + String keyword = cmd.getKeyword(); SearchBuilder sb = _networkACLDao.createSearchBuilder(); sb.and("id", sb.entity().getId(), Op.EQ); sb.and("name", sb.entity().getName(), Op.EQ); @@ -130,7 +131,15 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ } SearchCriteria sc = sb.create(); - if (id != null) { + + if (keyword != null) { + SearchCriteria ssc = _networkACLDao.createSearchCriteria(); + ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%"); + sc.addAnd("name", SearchCriteria.Op.SC, ssc); + } + + if(id != null){ sc.setParameters("id", id); } @@ -579,8 +588,16 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ @Override public boolean revokeNetworkACLItem(long ruleId) { NetworkACLItemVO aclItem = _networkACLItemDao.findById(ruleId); - if (aclItem != null) { - if ((aclItem.getAclId() == NetworkACL.DEFAULT_ALLOW) || (aclItem.getAclId() == NetworkACL.DEFAULT_DENY)) { + if(aclItem != null){ + NetworkACL acl = _networkAclMgr.getNetworkACL(aclItem.getAclId()); + + Vpc vpc = _entityMgr.findById(Vpc.class, acl.getVpcId()); + + Account caller = CallContext.current().getCallingAccount(); + + _accountMgr.checkAccess(caller, null, true, vpc); + + if((aclItem.getAclId() == NetworkACL.DEFAULT_ALLOW) || (aclItem.getAclId() == NetworkACL.DEFAULT_DENY)){ throw new InvalidParameterValueException("ACL Items in default ACL cannot be deleted"); } } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index cb3d9afc7c3..30751895e51 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -1474,7 +1474,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (!success) { if (destroyOnFailure) { s_logger.debug("Destroying private gateway " + vo + " that failed to start"); - if (deleteVpcPrivateGateway(gatewayId)) { + // calling deleting from db because on createprivategateway fail, destroyPrivateGateway is already called + if (deletePrivateGatewayFromTheDB(getVpcPrivateGateway(gatewayId))) { s_logger.warn("Successfully destroyed vpc " + vo + " that failed to start"); } else { s_logger.warn("Failed to destroy vpc " + vo + " that failed to start"); @@ -1518,6 +1519,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis s_logger.debug("Private gateway " + gateway + " was applied succesfully on the backend"); } else { s_logger.warn("Private gateway " + gateway + " failed to apply on the backend"); + gatewayVO.setState(VpcGateway.State.Ready); + _vpcGatewayDao.update(gatewayVO.getId(), gatewayVO); + s_logger.debug("Marked gateway " + gatewayVO + " with state " + VpcGateway.State.Ready); + return false; } } diff --git a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java index 9fe3ad64bcf..ef4cccdab9b 100755 --- a/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java +++ b/server/src/com/cloud/network/vpn/RemoteAccessVpnManagerImpl.java @@ -529,7 +529,7 @@ public class RemoteAccessVpnManagerImpl extends ManagerBase implements RemoteAcc } } } - } catch (ResourceUnavailableException e) { + } catch (Exception e) { s_logger.warn("Unable to apply vpn users ", e); success = false; diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index c961ce16a1a..c9e2e7ace56 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -1232,6 +1232,26 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, } } + @Override + public boolean checkAndMaintain(final long hostId) { + boolean hostInMaintenance = false; + HostVO host = _hostDao.findById(hostId); + + try { + if (host.getType() != Host.Type.Storage) { + List vos = _vmDao.listByHostId(hostId); + List vosMigrating = _vmDao.listVmsMigratingFromHost(hostId); + if (vos.isEmpty() && vosMigrating.isEmpty()) { + resourceStateTransitTo(host, ResourceState.Event.InternalEnterMaintenance, _nodeId); + hostInMaintenance = true; + } + } + } catch (NoTransitionException e) { + s_logger.debug("Cannot transmit host " + host.getId() + "to Maintenance state", e); + } + return hostInMaintenance; + } + @Override public Host updateHost(UpdateHostCmd cmd) throws NoTransitionException { Long hostId = cmd.getId(); @@ -1254,23 +1274,29 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, if (guestOSCategoryId != null) { // Verify that the guest OS Category exists - if (guestOSCategoryId > 0) { - if (_guestOSCategoryDao.findById(guestOSCategoryId) == null) { - throw new InvalidParameterValueException("Please specify a valid guest OS category."); - } + if (!(guestOSCategoryId > 0) || _guestOSCategoryDao.findById(guestOSCategoryId) == null) { + throw new InvalidParameterValueException("Please specify a valid guest OS category."); } GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); - Map hostDetails = _hostDetailsDao.findDetails(hostId); + DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id"); - if (guestOSCategory != null) { - // Save a new entry for guest.os.category.id - hostDetails.put("guest.os.category.id", String.valueOf(guestOSCategory.getId())); + if (guestOSCategory != null && !GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) { + // Create/Update an entry for guest.os.category.id + if (guestOSDetail != null) { + guestOSDetail.setValue(String.valueOf(guestOSCategory.getId())); + _hostDetailsDao.update(guestOSDetail.getId(), guestOSDetail); + } else { + Map detail = new HashMap(); + detail.put("guest.os.category.id", String.valueOf(guestOSCategory.getId())); + _hostDetailsDao.persist(hostId, detail); + } } else { // Delete any existing entry for guest.os.category.id - hostDetails.remove("guest.os.category.id"); + if (guestOSDetail != null) { + _hostDetailsDao.remove(guestOSDetail.getId()); + } } - _hostDetailsDao.persist(hostId, hostDetails); } List hostTags = cmd.getHostTags(); diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 98912ace596..3a7251b2095 100755 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -35,8 +35,11 @@ import org.springframework.stereotype.Component; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; @@ -68,6 +71,8 @@ import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.SnapshotDao; @@ -149,8 +154,11 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim private TemplateDataStoreDao _vmTemplateStoreDao; @Inject private VlanDao _vlanDao; + @Inject + private SnapshotDataStoreDao _snapshotDataStoreDao; protected GenericSearchBuilder templateSizeSearch; + protected GenericSearchBuilder snapshotSizeSearch; protected SearchBuilder ResourceCountSearch; ScheduledExecutorService _rcExecutor; @@ -189,6 +197,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim templateSizeSearch.join("templates", join1, templateSizeSearch.entity().getTemplateId(), join1.entity().getId(), JoinBuilder.JoinType.INNER); templateSizeSearch.done(); + snapshotSizeSearch = _snapshotDataStoreDao.createSearchBuilder(SumCount.class); + snapshotSizeSearch.select("sum", Func.SUM, snapshotSizeSearch.entity().getSize()); + snapshotSizeSearch.and("state", snapshotSizeSearch.entity().getState(), Op.EQ); + snapshotSizeSearch.and("storeRole", snapshotSizeSearch.entity().getRole(), Op.EQ); + SearchBuilder join2 = _snapshotDao.createSearchBuilder(); + join2.and("accountId", join2.entity().getAccountId(), Op.EQ); + snapshotSizeSearch.join("snapshots", join2, snapshotSizeSearch.entity().getSnapshotId(), join2.entity().getId(), JoinBuilder.JoinType.INNER); + snapshotSizeSearch.done(); + _resourceCountCheckInterval = NumbersUtil.parseInt(_configDao.getValue(Config.ResourceCountCheckInterval.key()), 0); if (_resourceCountCheckInterval > 0) { _rcExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ResourceCountChecker")); @@ -879,6 +896,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim join1.and("accountId", join1.entity().getAccountId(), Op.EQ); join1.and("type", join1.entity().getType(), Op.EQ); join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + join1.and("displayVm", join1.entity().isDisplayVm(), Op.EQ); cpuSearch.join("offerings", join1, cpuSearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); cpuSearch.done(); @@ -886,6 +904,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim sc.setJoinParameters("offerings", "accountId", accountId); sc.setJoinParameters("offerings", "type", VirtualMachine.Type.User); sc.setJoinParameters("offerings", "state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + sc.setJoinParameters("offerings", "displayVm", 1); List cpus = _serviceOfferingDao.customSearch(sc, null); if (cpus != null) { return cpus.get(0).sum; @@ -901,6 +920,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim join1.and("accountId", join1.entity().getAccountId(), Op.EQ); join1.and("type", join1.entity().getType(), Op.EQ); join1.and("state", join1.entity().getState(), SearchCriteria.Op.NIN); + join1.and("displayVm", join1.entity().isDisplayVm(), Op.EQ); memorySearch.join("offerings", join1, memorySearch.entity().getId(), join1.entity().getServiceOfferingId(), JoinBuilder.JoinType.INNER); memorySearch.done(); @@ -908,6 +928,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim sc.setJoinParameters("offerings", "accountId", accountId); sc.setJoinParameters("offerings", "type", VirtualMachine.Type.User); sc.setJoinParameters("offerings", "state", new Object[] {State.Destroyed, State.Error, State.Expunging}); + sc.setJoinParameters("offerings", "displayVm", 1); List memory = _serviceOfferingDao.customSearch(sc, null); if (memory != null) { return memory.get(0).sum; @@ -918,7 +939,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim public long calculateSecondaryStorageForAccount(long accountId) { long totalVolumesSize = _volumeDao.secondaryStorageUsedForAccount(accountId); - long totalSnapshotsSize = _snapshotDao.secondaryStorageUsedForAccount(accountId); + long totalSnapshotsSize = 0; long totalTemplatesSize = 0; SearchCriteria sc = templateSizeSearch.create(); @@ -930,6 +951,14 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim totalTemplatesSize = templates.get(0).sum; } + SearchCriteria sc2 = snapshotSizeSearch.create(); + sc2.setParameters("state", ObjectInDataStoreStateMachine.State.Ready); + sc2.setParameters("storeRole", DataStoreRole.Image); + sc2.setJoinParameters("snapshots", "accountId", accountId); + List snapshots = _snapshotDataStoreDao.customSearch(sc2, null); + if (snapshots != null) { + totalSnapshotsSize = snapshots.get(0).sum; + } return totalVolumesSize + totalSnapshotsSize + totalTemplatesSize; } @@ -954,14 +983,17 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim return _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type); } + private boolean isDisplayFlagOn(Boolean displayResource){ + + // 1. If its null assume displayResource = 1 + // 2. If its not null then send true if displayResource = 1 + return (displayResource == null) || (displayResource != null && displayResource); + } + @Override public void checkResourceLimit(Account account, ResourceType type, Boolean displayResource, long... count) throws ResourceAllocationException { - // By default its always on. - // TODO boilerplate code. - boolean displayflag = (displayResource == null) || (displayResource != null && displayResource); - - if (displayflag) { + if (isDisplayFlagOn(displayResource)) { checkResourceLimit(account, type, count); } } @@ -969,9 +1001,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Override public void incrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) { - // 1. If its null assume displayResource = 1 - // 2. If its not null then increment if displayResource = 1 - if (displayResource == null || (displayResource != null && displayResource)) { + if (isDisplayFlagOn(displayResource)) { incrementResourceCount(accountId, type, delta); } } @@ -979,9 +1009,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim @Override public void decrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta) { - // 1. If its null assume displayResource = 1 - // 2. If its not null then decrement if displayResource = 1 - if (displayResource == null || (displayResource != null && displayResource)) { + if (isDisplayFlagOn(displayResource)) { decrementResourceCount(accountId, type, delta); } } @@ -995,7 +1023,6 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim // Increment because the display is turned on. if (displayResource) { - // checkResourceLimit((Account)_accountDao.findById(accountId), type, delta); incrementResourceCount(accountId, type, delta); } else { decrementResourceCount(accountId, type, delta); diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 6cddc7162b0..b46708d4633 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -32,6 +32,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Set; import java.util.Map; import java.util.Properties; import java.util.UUID; @@ -801,7 +802,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio public List getConfigListByScope(String scope, Long resourceId) { // Getting the list of parameters defined at the scope - List> configList = _configDepot.getConfigListByScope(scope); + Set> configList = _configDepot.getConfigListByScope(scope); List configVOList = new ArrayList(); for (ConfigKey param : configList) { ConfigurationVO configVo = _configDao.findByName(param.toString()); diff --git a/server/src/com/cloud/server/ManagementServer.java b/server/src/com/cloud/server/ManagementServer.java index 5a751e8c6c4..b93e0276584 100755 --- a/server/src/com/cloud/server/ManagementServer.java +++ b/server/src/com/cloud/server/ManagementServer.java @@ -16,10 +16,6 @@ // under the License. package com.cloud.server; -import java.util.List; - -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; - import com.cloud.host.HostVO; import com.cloud.storage.GuestOSVO; import com.cloud.utils.Pair; @@ -65,8 +61,6 @@ public interface ManagementServer extends ManagementService, PluggableService { public long getMemoryOrCpuCapacityByHost(Long hostId, short capacityType); - Pair, Integer> searchForStoragePools(Criteria c); - String getHashKey(); String getEncryptionKey(); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 8bc095dfc70..a015b4b0c2f 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -42,11 +42,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.service.ServiceOfferingVO; -import com.cloud.service.dao.ServiceOfferingDao; -import org.apache.commons.codec.binary.Base64; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroupProcessor; @@ -165,7 +160,7 @@ import org.apache.cloudstack.api.command.admin.storage.ListSecondaryStagingStore import org.apache.cloudstack.api.command.admin.storage.ListStoragePoolsCmd; import org.apache.cloudstack.api.command.admin.storage.ListStorageProvidersCmd; import org.apache.cloudstack.api.command.admin.storage.PreparePrimaryStorageForMaintenanceCmd; -import org.apache.cloudstack.api.command.admin.storage.PrepareSecondaryStorageForMigrationCmd; +import org.apache.cloudstack.api.command.admin.storage.UpdateCloudToUseObjectStoreCmd; import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import org.apache.cloudstack.api.command.admin.swift.AddSwiftCmd; import org.apache.cloudstack.api.command.admin.swift.ListSwiftsCmd; @@ -443,15 +438,19 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.config.impl.ConfigurationVO; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.GetVncPortAnswer; @@ -518,7 +517,6 @@ import com.cloud.hypervisor.HypervisorCapabilities; import com.cloud.hypervisor.HypervisorCapabilitiesVO; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.info.ConsoleProxyInfo; -import com.cloud.keystore.KeystoreManager; import com.cloud.network.IpAddress; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; @@ -534,6 +532,8 @@ import com.cloud.projects.ProjectManager; import com.cloud.resource.ResourceManager; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.auth.UserAuthenticator; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSCategoryVO; @@ -714,6 +714,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject ConfigurationServer _configServer; @Inject + ConfigDepot _configDepot; + @Inject UserVmManager _userVmMgr; @Inject VolumeDataFactory _volFactory; @@ -749,7 +751,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } public void setPlanners(List planners) { - this._planners = planners; + _planners = planners; } @Inject @@ -1039,8 +1041,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe Object resourceState = cmd.getResourceState(); Object haHosts = cmd.getHaHost(); - Pair, Integer> result = - searchForServers(cmd.getStartIndex(), cmd.getPageSizeVal(), name, type, state, zoneId, pod, cluster, id, keyword, resourceState, haHosts, null, null); + Pair, Integer> result = searchForServers(cmd.getStartIndex(), cmd.getPageSizeVal(), name, type, state, zoneId, pod, cluster, id, keyword, resourceState, + haHosts, null, null); return new Pair, Integer>(result.first(), result.second()); } @@ -1070,14 +1072,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw ex; } - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) - && !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm) - && !vm.getHypervisorType().equals(HypervisorType.Hyperv)) { + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) + && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv)) { if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is not XenServer/VMware/KVM/OVM/Hyperv, cannot migrate this VM."); } - throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support " + - "XenServer/VMware/KVM/Ovm/Hyperv only"); + throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support " + "XenServer/VMware/KVM/Ovm/Hyperv only"); } long srcHostId = vm.getHostId(); @@ -1096,8 +1096,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe boolean canMigrateWithStorage = false; if (vm.getType() == VirtualMachine.Type.User) { - HypervisorCapabilitiesVO capabilities = - _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); + HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); if (capabilities != null) { canMigrateWithStorage = capabilities.isStorageMotionSupported(); } @@ -1127,8 +1126,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe DataCenterDeployment plan = null; boolean zoneWideStoragePool = false; if (canMigrateWithStorage) { - allHostsPair = - searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), null, null, null, null, null, null, srcHost.getHypervisorType(), + allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), null, null, null, null, null, null, srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); allHosts = allHostsPair.first(); allHosts.remove(srcHost); @@ -1358,7 +1356,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } private Pair, Integer> searchForServers(Long startIndex, Long pageSize, Object name, Object type, Object state, Object zone, Object pod, Object cluster, - Object id, Object keyword, Object resourceState, Object haHosts, Object hypervisorType, Object hypervisorVersion) { + Object id, Object keyword, Object resourceState, Object haHosts, Object hypervisorType, Object hypervisorVersion) { Filter searchFilter = new Filter(HostVO.class, "id", Boolean.TRUE, startIndex, pageSize); SearchBuilder sb = _hostDao.createSearchBuilder(); @@ -1649,15 +1647,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw new InvalidParameterValueException("cannot handle multiple IDs, provide only one ID corresponding to the scope"); } - if (scope != null && !scope.isEmpty()) { - // getting the list of parameters at requested scope - if (id == null) { - throw new InvalidParameterValueException("Invalid id null, id is needed corresponding to the scope"); - } - List configList = _configServer.getConfigListByScope(scope, id); - return new Pair, Integer>(configList, configList.size()); - } - if (keyword != null) { SearchCriteria ssc = _configDao.createSearchCriteria(); ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -1681,7 +1670,28 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // hidden configurations are not displayed using the search API sc.addAnd("category", SearchCriteria.Op.NEQ, "Hidden"); + if (scope != null && !scope.isEmpty()) { + // getting the list of parameters at requested scope + if (id == null) { + throw new InvalidParameterValueException("Invalid id null, id is needed corresponding to the scope"); + } + sc.addAnd("scope", SearchCriteria.Op.EQ, scope); + } + Pair, Integer> result = _configDao.searchAndCount(sc, searchFilter); + + if (scope != null && !scope.isEmpty()) { + // Populate values corresponding the resource id + List configVOList = new ArrayList(); + for (ConfigurationVO param : result.first()) { + ConfigurationVO configVo = _configDao.findByName(param.getName()); + configVo.setValue(_configDepot.get(param.getName()).valueIn(id).toString()); + configVOList.add(configVo); + } + + return new Pair, Integer>(configVOList, configVOList.size()); + } + return new Pair, Integer>(result.first(), result.second()); } @@ -1823,9 +1833,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw new PermissionDeniedException("Parameter isrouting can only be specified by a Root Admin, permission denied"); } } - boolean updateNeeded = - !(name == null && displayText == null && format == null && guestOSId == null && passwordEnabled == null && bootable == null && sortKey == null && - isDynamicallyScalable == null && isRoutingTemplate == null); + boolean updateNeeded = !(name == null && displayText == null && format == null && guestOSId == null && passwordEnabled == null && bootable == null && sortKey == null + && isDynamicallyScalable == null && isRoutingTemplate == null); if (!updateNeeded) { return template; } @@ -1920,10 +1929,10 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (isAllocated) { Account caller = CallContext.current().getCallingAccount(); - Ternary domainIdRecursiveListProject = - new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); - _accountMgr.buildACLSearchParameters(caller, cmd.getId(), cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, - cmd.listAll(), false); + Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), + cmd.isRecursive(), null); + _accountMgr.buildACLSearchParameters(caller, cmd.getId(), cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), + false); domainId = domainIdRecursiveListProject.first(); isRecursive = domainIdRecursiveListProject.second(); listProjectResourcesCriteria = domainIdRecursiveListProject.third(); @@ -2103,8 +2112,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return _consoleProxyMgr.startProxy(instanceId); } - private ConsoleProxyVO stopConsoleProxy(VMInstanceVO systemVm, boolean isForced) throws ResourceUnavailableException, OperationTimedoutException, - ConcurrentOperationException { + private ConsoleProxyVO stopConsoleProxy(VMInstanceVO systemVm, boolean isForced) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { _itMgr.advanceStop(systemVm.getUuid(), isForced); return _consoleProxyDao.findById(systemVm.getId()); @@ -2188,8 +2196,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe boolean sameDomain = (domains.size() == 1 && domains.get(0).getId() == domainId); if (!domains.isEmpty() && !sameDomain) { - InvalidParameterValueException ex = - new InvalidParameterValueException("Failed to update specified domain id with name '" + domainName + "' since it already exists in the system"); + InvalidParameterValueException ex = new InvalidParameterValueException("Failed to update specified domain id with name '" + domainName + + "' since it already exists in the system"); ex.addProxyObject(domain.getUuid(), "domainId"); throw ex; } @@ -2199,8 +2207,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (networkDomain != null && !networkDomain.isEmpty()) { if (!NetUtils.verifyDomainName(networkDomain)) { throw new InvalidParameterValueException( - "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'); can't start or end with \"-\""); + "Invalid network domain. Total length shouldn't exceed 190 chars. Each domain label must be between 1 and 63 characters long, can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + + "and the hyphen ('-'); can't start or end with \"-\""); } } @@ -2316,20 +2324,17 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe List summedCapacities = new ArrayList(); if (zoneId == null && podId == null) {// Group by Zone, capacity type - List summedCapacitiesAtZone = - _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 1, cmd.getPageSizeVal()); + List summedCapacitiesAtZone = _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 1, cmd.getPageSizeVal()); if (summedCapacitiesAtZone != null) { summedCapacities.addAll(summedCapacitiesAtZone); } } else if (podId == null) {// Group by Pod, capacity type - List summedCapacitiesAtPod = - _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 2, cmd.getPageSizeVal()); + List summedCapacitiesAtPod = _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 2, cmd.getPageSizeVal()); if (summedCapacitiesAtPod != null) { summedCapacities.addAll(summedCapacitiesAtPod); } } else { // Group by Cluster, capacity type - List summedCapacitiesAtCluster = - _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 3, cmd.getPageSizeVal()); + List summedCapacitiesAtCluster = _capacityDao.listCapacitiesGroupedByLevelAndType(capacityType, zoneId, podId, clusterId, 3, cmd.getPageSizeVal()); if (summedCapacitiesAtCluster != null) { summedCapacities.addAll(summedCapacitiesAtCluster); } @@ -2364,8 +2369,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe summedCapacities = summedCapacities.subList(0, summedCapacities.size() < cmd.getPageSizeVal() ? summedCapacities.size() : pageSize); for (SummedCapacity summedCapacity : summedCapacities) { - CapacityVO capacity = - new CapacityVO(summedCapacity.getDataCenterId(), summedCapacity.getPodId(), summedCapacity.getClusterId(), summedCapacity.getCapacityType(), + CapacityVO capacity = new CapacityVO(summedCapacity.getDataCenterId(), summedCapacity.getPodId(), summedCapacity.getClusterId(), summedCapacity.getCapacityType(), summedCapacity.getPercentUsed()); capacity.setUsedCapacity(summedCapacity.getUsedCapacity() + summedCapacity.getReservedCapacity()); capacity.setTotalCapacity(summedCapacity.getTotalCapacity()); @@ -2388,9 +2392,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } else { capacity.setUsedPercentage(0); } - SummedCapacity summedCapacity = - new SummedCapacity(capacity.getUsedCapacity(), capacity.getTotalCapacity(), capacity.getUsedPercentage(), capacity.getCapacityType(), - capacity.getDataCenterId(), capacity.getPodId(), capacity.getClusterId()); + SummedCapacity summedCapacity = new SummedCapacity(capacity.getUsedCapacity(), capacity.getTotalCapacity(), capacity.getUsedPercentage(), + capacity.getCapacityType(), capacity.getDataCenterId(), capacity.getPodId(), capacity.getClusterId()); list.add(summedCapacity); } else { List dcList = _dcDao.listEnabledZones(); @@ -2401,9 +2404,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } else { capacity.setUsedPercentage(0); } - SummedCapacity summedCapacity = - new SummedCapacity(capacity.getUsedCapacity(), capacity.getTotalCapacity(), capacity.getUsedPercentage(), capacity.getCapacityType(), - capacity.getDataCenterId(), capacity.getPodId(), capacity.getClusterId()); + SummedCapacity summedCapacity = new SummedCapacity(capacity.getUsedCapacity(), capacity.getTotalCapacity(), capacity.getUsedPercentage(), + capacity.getCapacityType(), capacity.getDataCenterId(), capacity.getPodId(), capacity.getClusterId()); list.add(summedCapacity); }// End of for } @@ -2430,8 +2432,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe List capacities = new ArrayList(); for (SummedCapacity summedCapacity : summedCapacities) { - CapacityVO capacity = - new CapacityVO(null, summedCapacity.getDataCenterId(), podId, clusterId, summedCapacity.getUsedCapacity() + summedCapacity.getReservedCapacity(), + CapacityVO capacity = new CapacityVO(null, summedCapacity.getDataCenterId(), podId, clusterId, summedCapacity.getUsedCapacity() + summedCapacity.getReservedCapacity(), summedCapacity.getTotalCapacity(), summedCapacity.getCapacityType()); capacities.add(capacity); } @@ -2476,8 +2477,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } public static boolean isAdmin(short accountType) { - return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) || - (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); + return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || (accountType == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); } @Override @@ -2827,7 +2827,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(CreateSecondaryStagingStoreCmd.class); cmdList.add(ListSecondaryStagingStoresCmd.class); cmdList.add(DeleteSecondaryStagingStoreCmd.class); - cmdList.add(PrepareSecondaryStorageForMigrationCmd.class); + cmdList.add(UpdateCloudToUseObjectStoreCmd.class); cmdList.add(CreateApplicationLoadBalancerCmd.class); cmdList.add(ListApplicationLoadBalancersCmd.class); cmdList.add(DeleteApplicationLoadBalancerCmd.class); @@ -2939,64 +2939,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } - @Override - public Pair, Integer> searchForStoragePools(Criteria c) { - Filter searchFilter = new Filter(StoragePoolVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit()); - SearchCriteria sc = _poolDao.createSearchCriteria(); - - Object id = c.getCriteria(Criteria.ID); - Object name = c.getCriteria(Criteria.NAME); - Object host = c.getCriteria(Criteria.HOST); - Object path = c.getCriteria(Criteria.PATH); - Object zone = c.getCriteria(Criteria.DATACENTERID); - Object pod = c.getCriteria(Criteria.PODID); - Object cluster = c.getCriteria(Criteria.CLUSTERID); - Object address = c.getCriteria(Criteria.ADDRESS); - Object keyword = c.getCriteria(Criteria.KEYWORD); - - if (keyword != null) { - SearchCriteria ssc = _poolDao.createSearchCriteria(); - ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - ssc.addOr("poolType", SearchCriteria.Op.LIKE, "%" + keyword + "%"); - - sc.addAnd("name", SearchCriteria.Op.SC, ssc); - } - - if (id != null) { - sc.addAnd("id", SearchCriteria.Op.EQ, id); - } - - if (name != null) { - sc.addAnd("name", SearchCriteria.Op.LIKE, "%" + name + "%"); - } - if (host != null) { - sc.addAnd("host", SearchCriteria.Op.EQ, host); - } - if (path != null) { - sc.addAnd("path", SearchCriteria.Op.EQ, path); - } - if (zone != null) { - sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zone); - } - if (pod != null) { - sc.addAnd("podId", SearchCriteria.Op.EQ, pod); - } - if (address != null) { - sc.addAnd("hostAddress", SearchCriteria.Op.EQ, address); - } - if (cluster != null) { - sc.addAnd("clusterId", SearchCriteria.Op.EQ, cluster); - } - - return _poolDao.searchAndCount(sc, searchFilter); - } - private SecondaryStorageVmVO startSecondaryStorageVm(long instanceId) { return _secStorageVmMgr.startSecStorageVm(instanceId); } private SecondaryStorageVmVO stopSecondaryStorageVm(VMInstanceVO systemVm, boolean isForced) throws ResourceUnavailableException, OperationTimedoutException, - ConcurrentOperationException { + ConcurrentOperationException { _itMgr.advanceStop(systemVm.getUuid(), isForced); return _secStorageVmDao.findById(systemVm.getId()); @@ -3270,7 +3218,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } - long diskOffMaxSize = Long.valueOf(_configDao.getValue(Config.CustomDiskOfferingMaxSize.key())); + long diskOffMaxSize = _volumeMgr.CustomDiskOfferingMaxSize.value(); KVMSnapshotEnabled = Boolean.parseBoolean(_configDao.getValue("KVM.snapshot.enabled")); boolean userPublicTemplateEnabled = TemplateManager.AllowPublicUserTemplates.valueIn(caller.getId()); @@ -3448,8 +3396,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // although we may have race conditioning here, database transaction serialization should // give us the same key if (_hashKey == null) { - _hashKey = - _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), getBase64EncodedRandomKey(128), Config.HashKey.getDescription()); + _hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), Config.HashKey.getCategory(), getBase64EncodedRandomKey(128), Config.HashKey.getDescription()); } return _hashKey; } @@ -3457,8 +3404,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Override public String getEncryptionKey() { if (_encryptionKey == null) { - _encryptionKey = - _configDao.getValueAndInitIfNotExist(Config.EncryptionKey.key(), Config.EncryptionKey.getCategory(), getBase64EncodedRandomKey(128), + _encryptionKey = _configDao.getValueAndInitIfNotExist(Config.EncryptionKey.key(), Config.EncryptionKey.getCategory(), getBase64EncodedRandomKey(128), Config.EncryptionKey.getDescription()); } return _encryptionKey; @@ -3467,8 +3413,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Override public String getEncryptionIV() { if (_encryptionIV == null) { - _encryptionIV = - _configDao.getValueAndInitIfNotExist(Config.EncryptionIV.key(), Config.EncryptionIV.getCategory(), getBase64EncodedRandomKey(128), + _encryptionIV = _configDao.getValueAndInitIfNotExist(Config.EncryptionIV.key(), Config.EncryptionIV.getCategory(), getBase64EncodedRandomKey(128), Config.EncryptionIV.getDescription()); } return _encryptionIV; @@ -3540,9 +3485,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), cmd.getName()); if (s == null) { - InvalidParameterValueException ex = - new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName() + - " in specified domain id"); + InvalidParameterValueException ex = new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + + owner.getAccountName() + " in specified domain id"); DomainVO domain = ApiDBUtils.findDomainById(owner.getDomainId()); String domainUuid = String.valueOf(owner.getDomainId()); if (domain != null) { @@ -3563,10 +3507,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe Account caller = CallContext.current().getCallingAccount(); List permittedAccounts = new ArrayList(); - Ternary domainIdRecursiveListProject = - new Ternary(cmd.getDomainId(), cmd.isRecursive(), null); - _accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), - false); + Ternary domainIdRecursiveListProject = new Ternary(cmd.getDomainId(), + cmd.isRecursive(), null); + _accountMgr.buildACLSearchParameters(caller, null, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); @@ -3645,8 +3588,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe _userVmDao.loadDetails(vm); String password = vm.getDetail("Encrypted.Password"); if (password == null || password.equals("")) { - InvalidParameterValueException ex = - new InvalidParameterValueException("No password for VM with specified id found. " + InvalidParameterValueException ex = new InvalidParameterValueException("No password for VM with specified id found. " + "If VM is created from password enabled template and SSH keypair is assigned to VM then only password can be retrieved."); ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; @@ -3724,7 +3666,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Override public Pair, Integer> listHypervisorCapabilities(Long id, HypervisorType hypervisorType, String keyword, Long startIndex, - Long pageSizeVal) { + Long pageSizeVal) { Filter searchFilter = new Filter(HypervisorCapabilitiesVO.class, "id", true, startIndex, pageSizeVal); SearchCriteria sc = _hypervisorCapabilitiesDao.createSearchCriteria(); @@ -3783,13 +3725,13 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "Upgrading system VM", async = true) public VirtualMachine upgradeSystemVM(ScaleSystemVMCmd cmd) throws ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException, - ConcurrentOperationException { + ConcurrentOperationException { VMInstanceVO vmInstance = _vmInstanceDao.findById(cmd.getId()); if (vmInstance.getHypervisorType() == HypervisorType.XenServer && vmInstance.getState().equals(State.Running)) { throw new InvalidParameterValueException("Dynamic Scaling operation is not permitted for this hypervisor on system vm"); } - boolean result = _userVmMgr.upgradeVirtualMachine(cmd.getId(), cmd.getServiceOfferingId(), cmd.getCustomParameters()); + boolean result = _userVmMgr.upgradeVirtualMachine(cmd.getId(), cmd.getServiceOfferingId(), cmd.getDetails()); if (result) { VirtualMachine vm = _vmInstanceDao.findById(cmd.getId()); return vm; @@ -3802,7 +3744,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public VirtualMachine upgradeSystemVM(UpgradeSystemVMCmd cmd) { Long systemVmId = cmd.getId(); Long serviceOfferingId = cmd.getServiceOfferingId(); - return upgradeStoppedSystemVm(systemVmId, serviceOfferingId, cmd.getCustomParameters()); + return upgradeStoppedSystemVm(systemVmId, serviceOfferingId, cmd.getDetails()); } diff --git a/server/src/com/cloud/servlet/ConsoleProxyClientParam.java b/server/src/com/cloud/servlet/ConsoleProxyClientParam.java index 7ea17c8d3ef..e9793373c16 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyClientParam.java +++ b/server/src/com/cloud/servlet/ConsoleProxyClientParam.java @@ -23,7 +23,7 @@ public class ConsoleProxyClientParam { private String clientHostPassword; private String clientTag; private String ticket; - + private String locale; private String clientTunnelUrl; private String clientTunnelSession; @@ -89,6 +89,14 @@ public class ConsoleProxyClientParam { this.clientTunnelSession = clientTunnelSession; } + public String getLocale() { + return this.locale; + } + + public void setLocale(String locale) { + this.locale = locale; + } + public String getAjaxSessionId() { return this.ajaxSessionId; } diff --git a/server/src/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/com/cloud/servlet/ConsoleProxyServlet.java index 06e542d110e..0359a45c8b6 100644 --- a/server/src/com/cloud/servlet/ConsoleProxyServlet.java +++ b/server/src/com/cloud/servlet/ConsoleProxyServlet.java @@ -57,8 +57,10 @@ import com.cloud.utils.Pair; import com.cloud.utils.Ternary; import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.dao.UserVmDetailsDao; /** * Thumbnail access : /console?cmd=thumbnail&vm=xxx&w=xxx&h=xxx @@ -82,6 +84,8 @@ public class ConsoleProxyServlet extends HttpServlet { IdentityService _identityService; @Inject EntityManager _entityMgr; + @Inject + UserVmDetailsDao _userVmDetailsDao; static ManagementServer s_ms; @@ -391,6 +395,7 @@ public class ConsoleProxyServlet extends HttpServlet { Ternary parsedHostInfo = parseHostInfo(portInfo.first()); + UserVmDetailVO details = _userVmDetailsDao.findDetail(vm.getId(), "keyboard"); String sid = vm.getVncPassword(); String tag = vm.getUuid(); String ticket = genAccessTicket(host, String.valueOf(portInfo.second()), sid, tag); @@ -401,7 +406,10 @@ public class ConsoleProxyServlet extends HttpServlet { param.setClientHostPassword(sid); param.setClientTag(tag); param.setTicket(ticket); - if (parsedHostInfo.second() != null && parsedHostInfo.third() != null) { + if (details != null) { + param.setLocale(details.getValue()); + } + if (parsedHostInfo.second() != null && parsedHostInfo.third() != null) { param.setClientTunnelUrl(parsedHostInfo.second()); param.setClientTunnelSession(parsedHostInfo.third()); } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index 831fac8ba44..52c2e7e0e4e 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -44,7 +44,6 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; -import org.apache.cloudstack.api.command.admin.storage.AddImageStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; @@ -55,6 +54,7 @@ import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; @@ -63,6 +63,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; @@ -270,7 +271,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } public void setDiscoverers(List discoverers) { - this._discoverers = discoverers; + _discoverers = discoverers; } protected SearchBuilder HostTemplateStatesSearch; @@ -658,12 +659,12 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C lifeCycle.attachZone(store, zoneScope, hypervisorType); } } catch (Exception e) { - s_logger.debug("Failed to add data store", e); + s_logger.debug("Failed to add data store: "+e.getMessage(), e); // clean up the db if (store != null) { lifeCycle.deleteDataStore(store); } - throw new CloudRuntimeException("Failed to add data store", e); + throw new CloudRuntimeException("Failed to add data store: "+e.getMessage(), e); } return (PrimaryDataStoreInfo)dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Primary); @@ -1226,28 +1227,6 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return (PrimaryDataStoreInfo)dataStoreMgr.getDataStore(primaryStorage.getId(), DataStoreRole.Primary); } - @Override - @DB - public ImageStore prepareSecondaryStorageForObjectStoreMigration(Long storeId) throws ResourceUnavailableException, InsufficientCapacityException { - // Verify that image store exists - ImageStoreVO store = _imageStoreDao.findById(storeId); - if (store == null) { - throw new InvalidParameterValueException("Image store with id " + storeId + " doesn't exist"); - } else if (!store.getProviderName().equals(DataStoreProvider.NFS_IMAGE)) { - throw new InvalidParameterValueException("We only support migrate NFS secondary storage to use object store!"); - } - _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), store.getDataCenterId()); - - DataStoreProvider provider = dataStoreProviderMgr.getDataStoreProvider(store.getProviderName()); - DataStoreLifeCycle lifeCycle = provider.getDataStoreLifeCycle(); - DataStore secStore = dataStoreMgr.getDataStore(storeId, DataStoreRole.Image); - lifeCycle.migrateToObjectStore(secStore); - // update store_role in template_store_ref and snapshot_store_ref to ImageCache - _templateStoreDao.updateStoreRoleToCachce(storeId); - _snapshotStoreDao.updateStoreRoleToCache(storeId); - // converted to an image cache store - return (ImageStore)_dataStoreMgr.getDataStore(storeId, DataStoreRole.ImageCache); - } protected class StorageGarbageCollector extends ManagedContextRunnable { @@ -1506,11 +1485,13 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (requestedVolumes == null || requestedVolumes.isEmpty() || pool == null) { return false; } - // Only Solidfire type primary storage is using/setting Iops. - // This check will fix to return the storage has enough Iops when capacityIops is set to NULL for any PS Storage provider - if (pool.getCapacityIops() == null) { + + // Only SolidFire-type primary storage is using/setting IOPS. + // This check returns true for storage that does not specify IOPS. + if (pool.getCapacityIops() == null ) { return true; } + long currentIops = 0; List volumesInPool = _volumeDao.findByPoolId(pool.getId(), null); @@ -1565,7 +1546,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } } if (volume.getState() != Volume.State.Ready) { - totalAskingSize = totalAskingSize + volume.getSize(); + totalAskingSize = totalAskingSize + getVolumeSizeIncludingHvSsReserve(volume, pool); } } @@ -1604,6 +1585,19 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return true; } + private long getVolumeSizeIncludingHvSsReserve(Volume volume, StoragePool pool) { + DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); + DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); + + if (storeDriver instanceof PrimaryDataStoreDriver) { + PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; + + return primaryStoreDriver.getVolumeSizeIncludingHypervisorSnapshotReserve(volume, pool); + } + + return volume.getSize(); + } + @Override public void createCapacityEntry(long poolId) { StoragePoolVO storage = _storagePoolDao.findById(poolId); @@ -1635,8 +1629,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } @Override - public ImageStore discoverImageStore(AddImageStoreCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException { - String providerName = cmd.getProviderName(); + public ImageStore discoverImageStore(String name, String url, String providerName, Long dcId, Map details) throws IllegalArgumentException, DiscoveryException, + InvalidParameterValueException { DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(providerName); if (storeProvider == null) { @@ -1647,13 +1641,19 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C providerName = storeProvider.getName(); // ignored passed provider name and use default image store provider name } - Long dcId = cmd.getZoneId(); - Map details = cmd.getDetails(); ScopeType scopeType = ScopeType.ZONE; if (dcId == null) { scopeType = ScopeType.REGION; } + if (name == null) { + name = url; + } + ImageStoreVO imageStore = _imageStoreDao.findByName(name); + if (imageStore != null) { + throw new InvalidParameterValueException("The image store with name " + name + " already exists, try creating with another name"); + } + // check if scope is supported by store provider if (!((ImageStoreProvider)storeProvider).isScopeSupported(scopeType)) { throw new InvalidParameterValueException("Image store provider " + providerName + " does not support scope " + scopeType); @@ -1686,8 +1686,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C Map params = new HashMap(); params.put("zoneId", dcId); - params.put("url", cmd.getUrl()); - params.put("name", cmd.getName()); + params.put("url", url); + params.put("name", name); params.put("details", details); params.put("scope", scopeType); params.put("providerName", storeProvider.getName()); @@ -1698,8 +1698,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C try { store = lifeCycle.initialize(params); } catch (Exception e) { - s_logger.debug("Failed to add data store", e); - throw new CloudRuntimeException("Failed to add data store", e); + s_logger.debug("Failed to add data store: " + e.getMessage(), e); + throw new CloudRuntimeException("Failed to add data store: " + e.getMessage(), e); } if (((ImageStoreProvider)storeProvider).needDownloadSysTemplate()) { @@ -1722,6 +1722,41 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C return (ImageStore)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.Image); } + @Override + public ImageStore migrateToObjectStore(String name, String url, String providerName, Map details) throws IllegalArgumentException, DiscoveryException, + InvalidParameterValueException { + // check if current cloud is ready to migrate, we only support cloud with only NFS secondary storages + List imgStores = _imageStoreDao.listImageStores(); + List nfsStores = new ArrayList(); + if (imgStores != null && imgStores.size() > 0) { + for (ImageStoreVO store : imgStores) { + if (!store.getProviderName().equals(DataStoreProvider.NFS_IMAGE)) { + throw new InvalidParameterValueException("We only support migrate NFS secondary storage to use object store!"); + } else { + nfsStores.add(store); + } + } + } + // convert all NFS secondary storage to staging store + if (nfsStores != null && nfsStores.size() > 0) { + for (ImageStoreVO store : nfsStores) { + long storeId = store.getId(); + + _accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(), store.getDataCenterId()); + + DataStoreProvider provider = dataStoreProviderMgr.getDataStoreProvider(store.getProviderName()); + DataStoreLifeCycle lifeCycle = provider.getDataStoreLifeCycle(); + DataStore secStore = dataStoreMgr.getDataStore(storeId, DataStoreRole.Image); + lifeCycle.migrateToObjectStore(secStore); + // update store_role in template_store_ref and snapshot_store_ref to ImageCache + _templateStoreDao.updateStoreRoleToCachce(storeId); + _snapshotStoreDao.updateStoreRoleToCache(storeId); + } + } + // add object store + return discoverImageStore(name, url, providerName, null, details); + } + private void duplicateCacheStoreRecordsToRegionStore(long storeId) { _templateStoreDao.duplicateCacheRecordsOnRegionStore(storeId); _snapshotStoreDao.duplicateCacheRecordsOnRegionStore(storeId); @@ -1862,8 +1897,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C try { store = lifeCycle.initialize(params); } catch (Exception e) { - s_logger.debug("Failed to add data store", e); - throw new CloudRuntimeException("Failed to add data store", e); + s_logger.debug("Failed to add data store: "+e.getMessage(), e); + throw new CloudRuntimeException("Failed to add data store: "+e.getMessage(), e); } return (ImageStore)_dataStoreMgr.getDataStore(store.getId(), DataStoreRole.ImageCache); diff --git a/server/src/com/cloud/storage/StoragePoolAutomationImpl.java b/server/src/com/cloud/storage/StoragePoolAutomationImpl.java index c76c6477418..8becd75ef26 100644 --- a/server/src/com/cloud/storage/StoragePoolAutomationImpl.java +++ b/server/src/com/cloud/storage/StoragePoolAutomationImpl.java @@ -219,7 +219,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { if (restart) { - vmMgr.advanceStart(consoleProxy.getUuid(), null); + vmMgr.advanceStart(consoleProxy.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); @@ -245,7 +245,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { _storagePoolWorkDao.update(work.getId(), work); if (restart) { - vmMgr.advanceStart(secStrgVm.getUuid(), null); + vmMgr.advanceStart(secStrgVm.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); @@ -262,7 +262,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { _storagePoolWorkDao.update(work.getId(), work); if (restart) { - vmMgr.advanceStart(domR.getUuid(), null); + vmMgr.advanceStart(domR.getUuid(), null, null); // update work status work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); @@ -330,17 +330,21 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { // proxy if (vmInstance.getType().equals(VirtualMachine.Type.ConsoleProxy)) { - ConsoleProxyVO consoleProxy = _consoleProxyDao.findById(vmInstance.getId()); - vmMgr.advanceStart(consoleProxy.getUuid(), null); + ConsoleProxyVO consoleProxy = _consoleProxyDao + .findById(vmInstance.getId()); + vmMgr.advanceStart(consoleProxy.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } // if the instance is of type ssvm, call the ssvm manager - if (vmInstance.getType().equals(VirtualMachine.Type.SecondaryStorageVm)) { - SecondaryStorageVmVO ssVm = _secStrgDao.findById(vmInstance.getId()); - vmMgr.advanceStart(ssVm.getUuid(), null); + if (vmInstance.getType().equals( + VirtualMachine.Type.SecondaryStorageVm)) { + SecondaryStorageVmVO ssVm = _secStrgDao.findById(vmInstance + .getId()); + vmMgr.advanceStart(ssVm.getUuid(), null, null); + // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); @@ -349,7 +353,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { // if the instance is of type ssvm, call the ssvm manager if (vmInstance.getType().equals(VirtualMachine.Type.DomainRouter)) { DomainRouterVO domR = _domrDao.findById(vmInstance.getId()); - vmMgr.advanceStart(domR.getUuid(), null); + vmMgr.advanceStart(domR.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); @@ -359,7 +363,7 @@ public class StoragePoolAutomationImpl implements StoragePoolAutomation { if (vmInstance.getType().equals(VirtualMachine.Type.User)) { UserVmVO userVm = userVmDao.findById(vmInstance.getId()); - vmMgr.advanceStart(userVm.getUuid(), null); // update work queue + vmMgr.advanceStart(userVm.getUuid(), null, null); // update work queue work.setStartedAfterMaintenance(true); _storagePoolWorkDao.update(work.getId(), work); } diff --git a/server/src/com/cloud/storage/VmWorkAttachVolume.java b/server/src/com/cloud/storage/VmWorkAttachVolume.java new file mode 100644 index 00000000000..3cdfbb52a2d --- /dev/null +++ b/server/src/com/cloud/storage/VmWorkAttachVolume.java @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage; + +import com.cloud.vm.VmWork; + +public class VmWorkAttachVolume extends VmWork { + private static final long serialVersionUID = 553291814854451740L; + + private Long volumeId; + private Long deviceId; + + public VmWorkAttachVolume(long userId, long accountId, long vmId, String handlerName, Long volumeId, Long deviceId) { + super(userId, accountId, vmId, handlerName); + this.volumeId = volumeId; + this.deviceId = deviceId; + } + + public Long getVolumeId() { + return volumeId; + } + + public Long getDeviceId() { + return deviceId; + } +} diff --git a/server/src/com/cloud/storage/VmWorkDetachVolume.java b/server/src/com/cloud/storage/VmWorkDetachVolume.java new file mode 100644 index 00000000000..18262d254aa --- /dev/null +++ b/server/src/com/cloud/storage/VmWorkDetachVolume.java @@ -0,0 +1,34 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage; + +import com.cloud.vm.VmWork; + +public class VmWorkDetachVolume extends VmWork { + private static final long serialVersionUID = -8722243207385263101L; + + private Long volumeId; + + public VmWorkDetachVolume(long userId, long accountId, long vmId, String handlerName, Long volumeId) { + super(userId, accountId, vmId, handlerName); + this.volumeId = volumeId; + } + + public Long getVolumeId() { + return volumeId; + } +} diff --git a/server/src/com/cloud/storage/VmWorkMigrateVolume.java b/server/src/com/cloud/storage/VmWorkMigrateVolume.java new file mode 100644 index 00000000000..c83e02df3ba --- /dev/null +++ b/server/src/com/cloud/storage/VmWorkMigrateVolume.java @@ -0,0 +1,46 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.storage; + +import com.cloud.vm.VmWork; + +public class VmWorkMigrateVolume extends VmWork { + private static final long serialVersionUID = -565778516928408602L; + + private long volumeId; + private long destPoolId; + private boolean liveMigrate; + + public VmWorkMigrateVolume(long userId, long accountId, long vmId, String handlerName, long volumeId, long destPoolId, boolean liveMigrate) { + super(userId, accountId, vmId, handlerName); + this.volumeId = volumeId; + this.destPoolId = destPoolId; + this.liveMigrate = liveMigrate; + } + + public long getVolumeId() { + return volumeId; + } + + public long getDestPoolId() { + return destPoolId; + } + + public boolean isLiveMigrate() { + return liveMigrate; + } +} diff --git a/server/src/com/cloud/storage/VmWorkResizeVolume.java b/server/src/com/cloud/storage/VmWorkResizeVolume.java new file mode 100644 index 00000000000..3ccaecd2429 --- /dev/null +++ b/server/src/com/cloud/storage/VmWorkResizeVolume.java @@ -0,0 +1,61 @@ +// 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.storage; + +import com.cloud.vm.VmWork; + +public class VmWorkResizeVolume extends VmWork { + private static final long serialVersionUID = 6112366316907642498L; + + private long volumeId; + private long currentSize; + private long newSize; + private Long newServiceOfferingId; + private boolean shrinkOk; + + public VmWorkResizeVolume(long userId, long accountId, long vmId, String handlerName, + long volumeId, long currentSize, long newSize, Long newServiceOfferingId, boolean shrinkOk) { + + super(userId, accountId, vmId, handlerName); + + this.volumeId = volumeId; + this.currentSize = currentSize; + this.newSize = newSize; + this.newServiceOfferingId = newServiceOfferingId; + this.shrinkOk = shrinkOk; + } + + public long getVolumeId() { + return volumeId; + } + + public long getCurrentSize() { + return currentSize; + } + + public long getNewSize() { + return newSize; + } + + public Long getNewServiceOfferingId() { + return newServiceOfferingId; + } + + public boolean isShrinkOk() { + return shrinkOk; + } +} diff --git a/server/src/com/cloud/storage/VmWorkTakeVolumeSnapshot.java b/server/src/com/cloud/storage/VmWorkTakeVolumeSnapshot.java new file mode 100644 index 00000000000..8b238080bce --- /dev/null +++ b/server/src/com/cloud/storage/VmWorkTakeVolumeSnapshot.java @@ -0,0 +1,54 @@ +// 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.storage; + +import com.cloud.vm.VmWork; + +public class VmWorkTakeVolumeSnapshot extends VmWork { + + private static final long serialVersionUID = 341816293003023823L; + + private Long volumeId; + private Long policyId; + private Long snapshotId; + private boolean quiesceVm; + + public VmWorkTakeVolumeSnapshot(long userId, long accountId, long vmId, String handlerName, + Long volumeId, Long policyId, Long snapshotId, boolean quiesceVm) { + super(userId, accountId, vmId, handlerName); + this.volumeId = volumeId; + this.policyId = policyId; + this.snapshotId = snapshotId; + this.quiesceVm = quiesceVm; + } + + public Long getVolumeId() { + return volumeId; + } + + public Long getPolicyId() { + return policyId; + } + + public Long getSnapshotId() { + return snapshotId; + } + + public boolean isQuiesceVm() { + return quiesceVm; + } +} diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index d215858dfb4..ac0c438aab9 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -26,10 +26,8 @@ import java.util.concurrent.ExecutionException; import javax.inject.Inject; -import com.cloud.uuididentity.UUIDManager; import org.apache.log4j.Logger; -import org.apache.cloudstack.api.BaseCmd; 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.DetachVolumeCmd; @@ -47,6 +45,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; @@ -54,10 +53,16 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.Outcome; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; +import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.storage.command.AttachAnswer; import org.apache.cloudstack.storage.command.AttachCommand; import org.apache.cloudstack.storage.command.DettachCommand; @@ -115,7 +120,6 @@ import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotPolicyDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.StoragePoolWorkDao; -import com.cloud.storage.dao.UploadDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; @@ -125,18 +129,20 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.snapshot.SnapshotApiService; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.storage.snapshot.SnapshotScheduler; -import com.cloud.storage.upload.UploadMonitor; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.TemplateManager; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.ResourceLimitService; +import com.cloud.user.User; import com.cloud.user.VmDiskStatisticsVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; import com.cloud.user.dao.VmDiskStatisticsDao; import com.cloud.utils.EnumUtils; import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.Predicate; import com.cloud.utils.UriUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; @@ -147,12 +153,18 @@ import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; import com.cloud.utils.fsm.StateMachine2; +import com.cloud.uuididentity.UUIDManager; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; +import com.cloud.vm.VmWork; +import com.cloud.vm.VmWorkConstants; +import com.cloud.vm.VmWorkJobHandler; +import com.cloud.vm.VmWorkJobHandlerProxy; +import com.cloud.vm.VmWorkSerializer; import com.cloud.vm.dao.ConsoleProxyDao; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.SecondaryStorageVmDao; @@ -161,8 +173,11 @@ import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; -public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService { +public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService, VmWorkJobHandler { private final static Logger s_logger = Logger.getLogger(VolumeApiServiceImpl.class); + + public static final String VM_WORK_JOB_HANDLER = VolumeApiServiceImpl.class.getSimpleName(); + @Inject VolumeOrchestrationService _volumeMgr; @@ -300,9 +315,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject SnapshotApiService snapshotMgr; @Inject - UploadMonitor _uploadMonitor; - @Inject - UploadDao _uploadDao; + SnapshotService snapshotSrv; @Inject UUIDManager _uuidMgr; @@ -310,8 +323,18 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; @Inject StorageManager storageMgr; - private int _customDiskOfferingMinSize = 1; - private final int _customDiskOfferingMaxSize = 1024; + + @Inject + protected AsyncJobManager _jobMgr; + + VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); + + // TODO + static final ConfigKey VmJobEnabled = new ConfigKey("Advanced", Boolean.class, "vm.job.enabled", "false", + "True to enable new VM sync model. false to use the old way", false); + static final ConfigKey VmJobCheckInterval = new ConfigKey("Advanced", Long.class, "vm.job.check.interval", "3000", + "Interval in milliseconds to check if the job is complete", false); + private long _maxVolumeSizeInGb; private final StateMachine2 _volStateMachine; @@ -381,25 +404,23 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (userSpecifiedName == null) { userSpecifiedName = getRandomVolumeName(); } - if ((!url.toLowerCase().endsWith("vhd")) && (!url.toLowerCase().endsWith("vhd.zip")) && (!url.toLowerCase().endsWith("vhd.bz2")) && - (!url.toLowerCase().endsWith("vhdx")) && (!url.toLowerCase().endsWith("vhdx.zip")) && - (!url.toLowerCase().endsWith("vhdx.gz")) && (!url.toLowerCase().endsWith("vhdx.bz2")) && - (!url.toLowerCase().endsWith("vhd.gz")) && (!url.toLowerCase().endsWith("qcow2")) && (!url.toLowerCase().endsWith("qcow2.zip")) && - (!url.toLowerCase().endsWith("qcow2.bz2")) && (!url.toLowerCase().endsWith("qcow2.gz")) && (!url.toLowerCase().endsWith("ova")) && - (!url.toLowerCase().endsWith("ova.zip")) && (!url.toLowerCase().endsWith("ova.bz2")) && (!url.toLowerCase().endsWith("ova.gz")) && - (!url.toLowerCase().endsWith("img")) && (!url.toLowerCase().endsWith("raw"))) { + if ((!url.toLowerCase().endsWith("vhd")) && (!url.toLowerCase().endsWith("vhd.zip")) && (!url.toLowerCase().endsWith("vhd.bz2")) && (!url.toLowerCase().endsWith("vhdx")) + && (!url.toLowerCase().endsWith("vhdx.zip")) && (!url.toLowerCase().endsWith("vhdx.gz")) && (!url.toLowerCase().endsWith("vhdx.bz2")) + && (!url.toLowerCase().endsWith("vhd.gz")) && (!url.toLowerCase().endsWith("qcow2")) && (!url.toLowerCase().endsWith("qcow2.zip")) + && (!url.toLowerCase().endsWith("qcow2.bz2")) && (!url.toLowerCase().endsWith("qcow2.gz")) && (!url.toLowerCase().endsWith("ova")) + && (!url.toLowerCase().endsWith("ova.zip")) && (!url.toLowerCase().endsWith("ova.bz2")) && (!url.toLowerCase().endsWith("ova.gz")) + && (!url.toLowerCase().endsWith("img")) && (!url.toLowerCase().endsWith("raw"))) { throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase()); } - if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase() - .endsWith("vhd.gz"))) || - (format.equalsIgnoreCase("vhdx") && (!url.toLowerCase().endsWith(".vhdx") && !url.toLowerCase().endsWith("vhdx.zip") && - !url.toLowerCase().endsWith("vhdx.bz2") && !url.toLowerCase() - .endsWith("vhdx.gz"))) || - (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && - !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz"))) || - (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase() - .endsWith("ova.gz"))) || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith(".img") && !url.toLowerCase().endsWith("raw")))) { + if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url + .toLowerCase().endsWith("vhd.gz"))) + || (format.equalsIgnoreCase("vhdx") && (!url.toLowerCase().endsWith(".vhdx") && !url.toLowerCase().endsWith("vhdx.zip") && !url.toLowerCase().endsWith("vhdx.bz2") && !url + .toLowerCase().endsWith("vhdx.gz"))) + || (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") + && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz"))) + || (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url + .toLowerCase().endsWith("ova.gz"))) || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith(".img") && !url.toLowerCase().endsWith("raw")))) { throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase()); } UriUtils.validateUrl(url); @@ -511,9 +532,12 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (size == null) { throw new InvalidParameterValueException("This disk offering requires a custom size specified"); } - if ((sizeInGB < _customDiskOfferingMinSize) || (sizeInGB > _customDiskOfferingMaxSize)) { - throw new InvalidParameterValueException("Volume size: " + sizeInGB + "GB is out of allowed range. Max: " + _customDiskOfferingMaxSize + " Min:" + - _customDiskOfferingMinSize); + Long customDiskOfferingMaxSize = _volumeMgr.CustomDiskOfferingMaxSize.value(); + Long customDiskOfferingMinSize = _volumeMgr.CustomDiskOfferingMinSize.value(); + + if ((sizeInGB < customDiskOfferingMinSize) || (sizeInGB > customDiskOfferingMaxSize)) { + throw new InvalidParameterValueException("Volume size: " + sizeInGB + "GB is out of allowed range. Max: " + customDiskOfferingMaxSize + " Min:" + + customDiskOfferingMinSize); } } @@ -572,14 +596,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } if (snapshotCheck.getState() != Snapshot.State.BackedUp) { - throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.State.BackedUp + - " state yet and can't be used for volume creation"); + throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.State.BackedUp + " state yet and can't be used for volume creation"); } parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId()); diskOfferingId = snapshotCheck.getDiskOfferingId(); diskOffering = _diskOfferingDao.findById(diskOfferingId); - zoneId = snapshotCheck.getDataCenterId(); + if (zoneId == null) { + // if zoneId is not provided, we default to create volume in the same zone as the snapshot zone. + zoneId = snapshotCheck.getDataCenterId(); + } size = snapshotCheck.getSize(); // ; disk offering is used for tags // purposes @@ -632,15 +658,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic userSpecifiedName = getRandomVolumeName(); } - VolumeVO volume = commitVolume(cmd, caller, ownerId, displayVolumeEnabled, zoneId, diskOfferingId, size, minIops, - maxIops, parentVolume, userSpecifiedName, _uuidMgr.generateUuid(Volume.class, cmd.getCustomId())); + VolumeVO volume = commitVolume(cmd, caller, ownerId, displayVolumeEnabled, zoneId, diskOfferingId, size, minIops, maxIops, parentVolume, userSpecifiedName, + _uuidMgr.generateUuid(Volume.class, cmd.getCustomId())); return volume; } private VolumeVO commitVolume(final CreateVolumeCmd cmd, final Account caller, final long ownerId, final Boolean displayVolumeEnabled, final Long zoneId, - final Long diskOfferingId, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, - final String userSpecifiedName, final String uuid) { + final Long diskOfferingId, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, final String userSpecifiedName, final String uuid) { return Transaction.execute(new TransactionCallback() { @Override public VolumeVO doInTransaction(TransactionStatus status) { @@ -670,7 +695,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (cmd.getSnapshotId() == null) { // for volume created from snapshot, create usage event after volume creation UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - diskOfferingId, null, size, Volume.class.getName(), volume.getUuid()); + diskOfferingId, null, size, Volume.class.getName(), volume.getUuid()); } CallContext.current().setEventDetails("Volume Id: " + volume.getId()); @@ -735,24 +760,27 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (!created) { s_logger.trace("Decrementing volume resource count for account id=" + volume.getAccountId() + " as volume failed to create on the backend"); _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, cmd.getDisplayVolume()); - _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, cmd.getDisplayVolume(), new Long(volume.getSize())); + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), ResourceType.primary_storage.getOrdinal()); } } } - protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId, Long vmId) - throws StorageUnavailableException { + protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId, Long vmId) throws StorageUnavailableException { VolumeInfo createdVolume = null; SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + long snapshotVolId = snapshot.getVolumeId(); UserVmVO vm = null; if (vmId != null) { vm = _userVmDao.findById(vmId); } + + // sync old snapshots to region store if necessary + createdVolume = _volumeMgr.createVolumeFromSnapshot(volume, snapshot, vm); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(), - createdVolume.getName(), createdVolume.getDiskOfferingId(), null, createdVolume.getSize(), Volume.class.getName(), createdVolume.getUuid()); + createdVolume.getName(), createdVolume.getDiskOfferingId(), null, createdVolume.getSize(), Volume.class.getName(), createdVolume.getUuid()); return _volsDao.findById(createdVolume.getId()); } @@ -784,8 +812,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } /* Only works for KVM/Xen for now */ - if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer && - _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware) { + if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer + && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware) { throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize"); } @@ -868,16 +896,53 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic * the actual disk size */ if (currentSize > newSize && !shrinkOk) { - throw new InvalidParameterValueException("Going from existing size of " + currentSize + " to size of " + newSize + - " would shrink the volume, need to sign off by supplying the shrinkok parameter with value of true"); + throw new InvalidParameterValueException("Going from existing size of " + currentSize + " to size of " + newSize + + " would shrink the volume, need to sign off by supplying the shrinkok parameter with value of true"); } if (!shrinkOk) { /* Check resource limit for this account on primary storage resource */ - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), new Long(newSize - - currentSize)); + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), new Long(newSize + - currentSize)); } + if (userVm != null) { + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + } else { + Outcome outcome = resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, newSize, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + + Volume vol = null; + try { + vol = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + return volume; + } + } + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, + newDiskOffering != null ? cmd.getNewDiskOfferingId() : null, shrinkOk); + } + + private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long newSize, Long newDiskOfferingId, boolean shrinkOk) { + VolumeVO volume = _volsDao.findById(volumeId); + UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); /* * get a list of hosts to send the commands to, try the system the * associated vm is running on first, then the last known place it ran. @@ -915,13 +980,13 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic volume = _volsDao.findById(volume.getId()); - if (newDiskOffering != null) { - volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); + if (newDiskOfferingId != null) { + volume.setDiskOfferingId(newDiskOfferingId); } _volsDao.update(volume.getId(), volume); // Log usage event for volumes belonging user VM's only UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); + volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); /* Update resource count for the account on primary storage resource */ if (!shrinkOk) { @@ -982,15 +1047,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic /* If volume is in primary storage, decrement primary storage count else decrement secondary storage count (in case of upload volume). */ if (volume.getFolder() != null || volume.getPath() != null || volume.getState() == Volume.State.Allocated) { - _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplayVolume(), - new Long(volume.getSize())); + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), ResourceType.primary_storage.getOrdinal()); } else { _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), ResourceType.secondary_storage.getOrdinal()); } // Log usage event for volumes belonging user VM's only UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - Volume.class.getName(), volume.getUuid()); + Volume.class.getName(), volume.getUuid()); } } // Mark volume as removed if volume has not been created on primary or secondary @@ -1034,9 +1098,35 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Override public Volume attachVolumeToVM(AttachVolumeCmd command) { - Long vmId = command.getVirtualMachineId(); - Long volumeId = command.getId(); - Long deviceId = command.getDeviceId(); + + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateAttachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId()); + } else { + Outcome outcome = attachVolumeToVmThroughJobQueue(command.getVirtualMachineId(), command.getId(), command.getDeviceId()); + + Volume vol = null; + try { + vol = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + return vol; + } + } + + private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) { return attachVolumeToVM(vmId, volumeId, deviceId); } @@ -1084,8 +1174,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic List existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK); int maxDataVolumesSupported = getMaxDataVolumesSupported(vm); if (existingDataVolumes.size() >= maxDataVolumesSupported) { - throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + - "). Please specify another VM."); + throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). Please specify another VM."); } // Check that the VM and the volume are in the same zone @@ -1141,7 +1230,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic deviceId = getDeviceId(vmId, deviceId); VolumeInfo volumeOnPrimaryStorage = volume; - if (volume.getState().equals(Volume.State.Allocated) || volume.getState() == Volume.State.Uploaded) { + //don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet + List existingRootVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT); + boolean rootVolumeCreatedOnPrimary = true; + if (existingRootVolumes.get(0).getState().equals(Volume.State.Allocated)) { + rootVolumeCreatedOnPrimary = false; + } + + if ((volume.getState().equals(Volume.State.Allocated) && rootVolumeCreatedOnPrimary) || volume.getState() == Volume.State.Uploaded) { try { volumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, rootVolumeOfVm, volume, rootDiskHyperType); } catch (NoTransitionException e) { @@ -1157,15 +1253,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (moveVolumeNeeded) { PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)volumeOnPrimaryStorage.getDataStore(); if (primaryStore.isLocal()) { - throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() + - " as migration of local data volume is not allowed"); + throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() + + " as migration of local data volume is not allowed"); } StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); try { - volumeOnPrimaryStorage = - _volumeMgr.moveVolume(volumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), - dataDiskHyperType); + volumeOnPrimaryStorage = _volumeMgr.moveVolume(volumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), + vmRootVolumePool.getClusterId(), dataDiskHyperType); } catch (ConcurrentOperationException e) { s_logger.debug("move volume failed", e); throw new CloudRuntimeException("move volume failed", e); @@ -1185,7 +1280,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); - _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, Long.toString(volumeId)); } VolumeVO newVol = _volumeDao.findById(volumeOnPrimaryStorage.getId()); @@ -1203,6 +1297,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic volume.setPath(path); } + if (displayVolume != null) { + volume.setDisplayVolume(displayVolume); + } + if (state != null) { try { Volume.State volumeState = Volume.State.valueOf(state); @@ -1240,9 +1338,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { Account caller = CallContext.current().getCallingAccount(); - if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || - (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null)) || - (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null))) { + if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) + || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null)) + || (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null))) { throw new InvalidParameterValueException("Please provide either a volume id, or a tuple(device id, instance id)"); } @@ -1302,11 +1400,42 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); - _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId.toString()); } - String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName(); - boolean sendCommand = (vm.getState() == State.Running); + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateDetachVolumeFromVM(vmId, volumeId); + } else { + Outcome outcome = detachVolumeFromVmThroughJobQueue(vmId, volumeId); + + Volume vol = null; + try { + vol = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + return vol; + } + } + + private Volume orchestrateDetachVolumeFromVM(long vmId, long volumeId) { + + Volume volume = _volumeDao.findById(volumeId); + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + String errorMsg = "Failed to detach volume " + volume.getName() + " from VM " + vm.getHostName(); + boolean sendCommand = vm.getState() == State.Running; Long hostId = vm.getHostId(); @@ -1320,11 +1449,20 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } + HostVO host = null; + StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); + + if (hostId != null) { + host = _hostDao.findById(hostId); + + if (host != null && host.getHypervisorType() == HypervisorType.XenServer && volumePool.isManaged()) { + sendCommand = true; + } + } + Answer answer = null; if (sendCommand) { - StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); - DataTO volTO = volFactory.getVolume(volume.getId()).getTO(); DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getPath(), volume.getVolumeType()); @@ -1344,10 +1482,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } + DataStore dataStore = dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary); + if (!sendCommand || (answer != null && answer.getResult())) { // Mark the volume as detached _volsDao.detachVolume(volume.getId()); + volService.disconnectVolumeFromHost(volFactory.getVolume(volume.getId()), host, dataStore); + return _volsDao.findById(volumeId); } else { @@ -1423,6 +1565,48 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported"); } + if (vm != null) { + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateMigrateVolume(vol.getId(), destPool.getId(), liveMigrateVolume); + } else { + Outcome outcome = migrateVolumeThroughJobQueue(vm.getId(), vol.getId(), destPool.getId(), liveMigrateVolume); + + try { + outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + // retrieve the migrated new volume from job result + if (jobResult != null && jobResult instanceof Long) { + return _entityMgr.findById(VolumeVO.class, ((Long)jobResult).longValue()); + } + return null; + } + } + + return orchestrateMigrateVolume(vol.getId(), destPool.getId(), liveMigrateVolume); + } + + private Volume orchestrateMigrateVolume(long volumeId, long destPoolId, boolean liveMigrateVolume) { + VolumeVO vol = _volsDao.findById(volumeId); + assert (vol != null); + StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destPoolId, DataStoreRole.Primary); + assert (destPool != null); + Volume newVol = null; if (liveMigrateVolume) { newVol = liveMigrateVolume(vol, destPool); @@ -1458,14 +1642,71 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Override public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm) throws ResourceAllocationException { + VolumeInfo volume = volFactory.getVolume(volumeId); if (volume == null) { throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); } if (volume.getState() != Volume.State.Ready) { - throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + - ". Cannot take snapshot."); + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); + } + + VMInstanceVO vm = null; + if (volume.getInstanceId() != null) + vm = _vmInstanceDao.findById(volume.getInstanceId()); + + if (vm != null) { + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateTakeVolumeSnapshot(volumeId, policyId, snapshotId, account, quiescevm); + } else { + Outcome outcome = takeVolumeSnapshotThroughJobQueue(vm.getId(), volumeId, policyId, snapshotId, account.getId(), quiescevm); + + try { + outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof ResourceAllocationException) + throw (ResourceAllocationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + return _snapshotDao.findById(snapshotId); + } + } else { + CreateSnapshotPayload payload = new CreateSnapshotPayload(); + payload.setSnapshotId(snapshotId); + payload.setSnapshotPolicyId(policyId); + payload.setAccount(account); + payload.setQuiescevm(quiescevm); + volume.addPayload(payload); + return volService.takeSnapshot(volume); + } + } + + private Snapshot orchestrateTakeVolumeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm) + throws ResourceAllocationException { + + VolumeInfo volume = volFactory.getVolume(volumeId); + + if (volume == null) { + throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); + } + + if (volume.getState() != Volume.State.Ready) { + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); } CreateSnapshotPayload payload = new CreateSnapshotPayload(); @@ -1495,8 +1736,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } if (volume.getState() != Volume.State.Ready) { - throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + - ". Cannot take snapshot."); + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); } if (volume.getTemplateId() != null) { @@ -1546,8 +1786,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic // instance is stopped if (volume.getInstanceId() != null && ApiDBUtils.findVMInstanceById(volume.getInstanceId()).getState() != State.Stopped) { s_logger.debug("Invalid state of the volume with ID: " + volumeId + ". It should be either detached or the VM should be in stopped state."); - PermissionDeniedException ex = - new PermissionDeniedException("Invalid state of the volume with specified ID. It should be either detached or the VM should be in stopped state."); + PermissionDeniedException ex = new PermissionDeniedException( + "Invalid state of the volume with specified ID. It should be either detached or the VM should be in stopped state."); ex.addProxyObject(volume.getUuid(), "volumeId"); throw ex; } @@ -1675,8 +1915,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic if (storeForDataStoreScope.getScopeId().equals(vmClusterId)) { return false; } - } else if (storeForDataStoreScope.getScopeType() == ScopeType.HOST && - (storeForRootStoreScope.getScopeType() == ScopeType.CLUSTER || storeForRootStoreScope.getScopeType() == ScopeType.ZONE)) { + } else if (storeForDataStoreScope.getScopeType() == ScopeType.HOST + && (storeForRootStoreScope.getScopeType() == ScopeType.CLUSTER || storeForRootStoreScope.getScopeType() == ScopeType.ZONE)) { Long hostId = _vmInstanceDao.findById(rootVolumeOfVm.getInstanceId()).getHostId(); if (storeForDataStoreScope.getScopeId().equals(hostId)) { return false; @@ -1689,26 +1929,52 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, Long deviceId) { - String errorMsg = "Failed to attach volume: " + volumeToAttach.getName() + " to VM: " + vm.getHostName(); - boolean sendCommand = (vm.getState() == State.Running); + String errorMsg = "Failed to attach volume " + volumeToAttach.getName() + " to VM " + vm.getHostName(); + boolean sendCommand = vm.getState() == State.Running; AttachAnswer answer = null; Long hostId = vm.getHostId(); + if (hostId == null) { hostId = vm.getLastHostId(); + HostVO host = _hostDao.findById(hostId); + if (host != null && host.getHypervisorType() == HypervisorType.VMware) { sendCommand = true; } } - StoragePoolVO volumeToAttachStoragePool = null; + HostVO host = null; + StoragePoolVO volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId()); + + if (hostId != null) { + host = _hostDao.findById(hostId); + + if (host != null && host.getHypervisorType() == HypervisorType.XenServer && volumeToAttachStoragePool.isManaged()) { + sendCommand = true; + } + } + + DataStore dataStore = dataStoreMgr.getDataStore(volumeToAttachStoragePool.getId(), DataStoreRole.Primary); + + boolean queryForChap = true; + + if (host != null) { + try { + // if connectVolumeToHost returns true, then we do not want to use CHAP because the volume is already connected to the host(s) + queryForChap = !volService.connectVolumeToHost(volFactory.getVolume(volumeToAttach.getId()), host, dataStore); + } + catch (Exception e) { + volService.disconnectVolumeFromHost(volFactory.getVolume(volumeToAttach.getId()), host, dataStore); + + throw new CloudRuntimeException(e.getMessage()); + } + } if (sendCommand) { - volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId()); - - HostVO host = _hostDao.findById(hostId); - - if (host.getHypervisorType() == HypervisorType.KVM && volumeToAttachStoragePool.isManaged() && volumeToAttach.getPath() == null) { + if (host.getHypervisorType() == HypervisorType.KVM && + volumeToAttachStoragePool.isManaged() && + volumeToAttach.getPath() == null) { volumeToAttach.setPath(volumeToAttach.get_iScsiName()); _volsDao.update(volumeToAttach.getId(), volumeToAttach); @@ -1719,9 +1985,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName()); - VolumeInfo volumeInfo = volFactory.getVolume(volumeToAttach.getId()); - DataStore dataStore = dataStoreMgr.getDataStore(volumeToAttachStoragePool.getId(), DataStoreRole.Primary); - ChapInfo chapInfo = volService.getChapInfo(volumeInfo, dataStore); + ChapInfo chapInfo = queryForChap ? volService.getChapInfo(volFactory.getVolume(volumeToAttach.getId()), dataStore) : null; Map details = new HashMap(); @@ -1743,6 +2007,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic try { answer = (AttachAnswer)_agentMgr.send(hostId, cmd); } catch (Exception e) { + volService.disconnectVolumeFromHost(volFactory.getVolume(volumeToAttach.getId()), host, dataStore); + throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); } } @@ -1779,6 +2045,9 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic errorMsg += "; " + details; } } + + volService.disconnectVolumeFromHost(volFactory.getVolume(volumeToAttach.getId()), host, dataStore); + throw new CloudRuntimeException(errorMsg); } } @@ -1833,8 +2102,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Override public boolean configure(String name, Map params) { - String _customDiskOfferingMinSizeStr = _configDao.getValue(Config.CustomDiskOfferingMinSize.toString()); - _customDiskOfferingMinSize = NumbersUtil.parseInt(_customDiskOfferingMinSizeStr, Integer.parseInt(Config.CustomDiskOfferingMinSize.getDefaultValue())); String maxVolumeSizeInGbString = _configDao.getValue(Config.MaxVolumeSize.toString()); _maxVolumeSizeInGb = NumbersUtil.parseLong(maxVolumeSizeInGbString, 2000); @@ -1850,4 +2117,312 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic _storagePoolAllocators = storagePoolAllocators; } + public class VmJobVolumeOutcome extends OutcomeImpl { + private long _volumeId; + + public VmJobVolumeOutcome(final AsyncJob job, final long volumeId) { + super(Volume.class, job, VmJobCheckInterval.value(), new Predicate() { + @Override + public boolean checkCondition() { + AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId()); + assert (jobVo != null); + if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) + return true; + + return false; + } + }, AsyncJob.Topics.JOB_STATE); + _volumeId = volumeId; + } + + @Override + protected Volume retrieve() { + return _volumeDao.findById(_volumeId); + } + } + + public class VmJobSnapshotOutcome extends OutcomeImpl { + private long _snapshotId; + + public VmJobSnapshotOutcome(final AsyncJob job, final long snapshotId) { + super(Snapshot.class, job, VmJobCheckInterval.value(), new Predicate() { + @Override + public boolean checkCondition() { + AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId()); + assert (jobVo != null); + if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) + return true; + + return false; + } + }, AsyncJob.Topics.JOB_STATE); + _snapshotId = snapshotId; + } + + @Override + protected Snapshot retrieve() { + return _snapshotDao.findById(_snapshotId); + } + } + + public Outcome attachVolumeToVmThroughJobQueue(final Long vmId, final Long volumeId, final Long deviceId) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkAttachVolume.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkAttachVolume workInfo = new VmWorkAttachVolume(callingUser.getId(), callingAccount.getId(), vm.getId(), VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, + deviceId); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + AsyncJobVO jobVo = _jobMgr.getAsyncJob(workJob.getId()); + s_logger.debug("New job " + workJob.getId() + ", result field: " + jobVo.getResult()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVolumeOutcome((VmWorkJobVO)result[0], + volumeId); + } + + public Outcome detachVolumeFromVmThroughJobQueue(final Long vmId, final Long volumeId) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkDetachVolume.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkDetachVolume workInfo = new VmWorkDetachVolume(callingUser.getId(), callingAccount.getId(), vm.getId(), VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVolumeOutcome((VmWorkJobVO)result[0], + volumeId); + } + + public Outcome resizeVolumeThroughJobQueue(final Long vmId, final long volumeId, + final long currentSize, final long newSize, final Long newServiceOfferingId, final boolean shrinkOk) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkResizeVolume.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkResizeVolume workInfo = new VmWorkResizeVolume(callingUser.getId(), callingAccount.getId(), vm.getId(), + VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, currentSize, newSize, newServiceOfferingId, shrinkOk); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVolumeOutcome((VmWorkJobVO)result[0], + volumeId); + } + + public Outcome migrateVolumeThroughJobQueue(final Long vmId, final long volumeId, + final long destPoolId, final boolean liveMigrate) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkMigrateVolume.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkMigrateVolume workInfo = new VmWorkMigrateVolume(callingUser.getId(), callingAccount.getId(), vm.getId(), + VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, destPoolId, liveMigrate); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVolumeOutcome((VmWorkJobVO)result[0], + volumeId); + } + + public Outcome takeVolumeSnapshotThroughJobQueue(final Long vmId, final Long volumeId, + final Long policyId, final Long snapshotId, final Long accountId, final boolean quiesceVm) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkTakeVolumeSnapshot.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkTakeVolumeSnapshot workInfo = new VmWorkTakeVolumeSnapshot( + callingUser.getId(), accountId != null ? accountId : callingAccount.getId(), vm.getId(), + VolumeApiServiceImpl.VM_WORK_JOB_HANDLER, volumeId, policyId, snapshotId, quiesceVm); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobSnapshotOutcome((VmWorkJobVO)result[0], + snapshotId); + } + + private Pair orchestrateAttachVolumeToVM(VmWorkAttachVolume work) throws Exception { + orchestrateAttachVolumeToVM(work.getVmId(), work.getVolumeId(), work.getDeviceId()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateDetachVolumeFromVM(VmWorkDetachVolume work) throws Exception { + orchestrateDetachVolumeFromVM(work.getVmId(), work.getVolumeId()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateResizeVolume(VmWorkResizeVolume work) throws Exception { + orchestrateResizeVolume(work.getVolumeId(), work.getCurrentSize(), work.getNewSize(), + work.getNewServiceOfferingId(), work.isShrinkOk()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + private Pair orchestrateMigrateVolume(VmWorkMigrateVolume work) throws Exception { + Volume newVol = orchestrateMigrateVolume(work.getVolumeId(), work.getDestPoolId(), work.isLiveMigrate()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Long(newVol.getId()))); + } + + private Pair orchestrateTakeVolumeSnapshot(VmWorkTakeVolumeSnapshot work) throws Exception { + Account account = _accountDao.findById(work.getAccountId()); + orchestrateTakeVolumeSnapshot(work.getVolumeId(), work.getPolicyId(), work.getSnapshotId(), + account, work.isQuiesceVm()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(work.getSnapshotId())); + } + + @Override + public Pair handleVmWorkJob(VmWork work) throws Exception { + return _jobHandlerProxy.handleVmWorkJob(work); + } } diff --git a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java index 0c5d8f086e2..9f6b5fb9d3e 100755 --- a/server/src/com/cloud/storage/listener/StoragePoolMonitor.java +++ b/server/src/com/cloud/storage/listener/StoragePoolMonitor.java @@ -74,9 +74,9 @@ public class StoragePoolMonitor implements Listener { public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) throws ConnectionException { if (cmd instanceof StartupRoutingCommand) { StartupRoutingCommand scCmd = (StartupRoutingCommand)cmd; - if (scCmd.getHypervisorType() == HypervisorType.XenServer || scCmd.getHypervisorType() == HypervisorType.KVM || - scCmd.getHypervisorType() == HypervisorType.VMware || scCmd.getHypervisorType() == HypervisorType.Simulator || - scCmd.getHypervisorType() == HypervisorType.Ovm) { + if (scCmd.getHypervisorType() == HypervisorType.XenServer || scCmd.getHypervisorType() == HypervisorType.KVM || + scCmd.getHypervisorType() == HypervisorType.VMware || scCmd.getHypervisorType() == HypervisorType.Simulator || + scCmd.getHypervisorType() == HypervisorType.Ovm || scCmd.getHypervisorType() == HypervisorType.Hyperv) { List pools = _poolDao.listBy(host.getDataCenterId(), host.getPodId(), host.getClusterId(), ScopeType.CLUSTER); List zoneStoragePoolsByTags = _poolDao.findZoneWideStoragePoolsByTags(host.getDataCenterId(), null); List zoneStoragePoolsByHypervisor = _poolDao.findZoneWideStoragePoolsByHypervisor(host.getDataCenterId(), scCmd.getHypervisorType()); diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java index 8decfd223b5..f42bca09b91 100755 --- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java +++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java @@ -38,6 +38,7 @@ 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.ZoneScope; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.security.keystore.KeystoreManager; import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; @@ -50,7 +51,6 @@ import com.cloud.agent.api.RebootCommand; import com.cloud.agent.api.SecStorageFirewallCfgCommand; import com.cloud.agent.api.SecStorageSetupAnswer; import com.cloud.agent.api.SecStorageSetupCommand; -import com.cloud.agent.api.SecStorageSetupCommand.Certificates; import com.cloud.agent.api.SecStorageVMSetupCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupSecondaryStorageCommand; @@ -81,7 +81,6 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.info.RunningHostCountInfo; import com.cloud.info.RunningHostInfoAgregator; import com.cloud.info.RunningHostInfoAgregator.ZoneHostInfo; -import com.cloud.keystore.KeystoreManager; import com.cloud.network.Network; import com.cloud.network.NetworkModel; import com.cloud.network.Networks.TrafficType; @@ -258,7 +257,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar public SecondaryStorageVmVO startSecStorageVm(long secStorageVmId) { try { SecondaryStorageVmVO secStorageVm = _secStorageVmDao.findById(secStorageVmId); - _itMgr.advanceStart(secStorageVm.getUuid(), null); + _itMgr.advanceStart(secStorageVm.getUuid(), null, null); return _secStorageVmDao.findById(secStorageVm.getId()); } catch (StorageUnavailableException e) { s_logger.warn("Exception while trying to start secondary storage vm", e); @@ -304,7 +303,7 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar if (!_useSSlCopy) { setupCmd = new SecStorageSetupCommand(ssStore.getTO(), secUrl, null); } else { - Certificates certs = _keystoreMgr.getCertificates(ConsoleProxyManager.CERTIFICATE_NAME); + KeystoreManager.Certificates certs = _keystoreMgr.getCertificates(ConsoleProxyManager.CERTIFICATE_NAME); setupCmd = new SecStorageSetupCommand(ssStore.getTO(), secUrl, certs); } @@ -1141,6 +1140,11 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar controlNic = managementNic; } + // verify ssh access on management nic for system vm running on HyperV + if(profile.getHypervisorType() == HypervisorType.Hyperv) { + controlNic = managementNic; + } + CheckSshCommand check = new CheckSshCommand(profile.getInstanceName(), controlNic.getIp4Address(), 3922); cmds.addCommand("checkSsh", check); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index c19e3ec7343..a9eae7db1d5 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -322,16 +322,10 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); } - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); } catch (Exception e) { s_logger.debug("Failed to create snapshot", e); - if (backup) { - _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, new Long(volume.getSize())); - } else { - _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.primary_storage, new Long(volume.getSize())); - } throw new CloudRuntimeException("Failed to create snapshot", e); } @@ -427,6 +421,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, s_logger.error("Unable to find snaphot strategy to handle snapshot with id '" + snapshotId + "'"); return false; } + SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image); try { boolean result = snapshotStrategy.deleteSnapshot(snapshotId); @@ -435,8 +430,10 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), snapshotCheck.getDataCenterId(), snapshotId, snapshotCheck.getName(), null, null, 0L, snapshotCheck.getClass().getName(), snapshotCheck.getUuid()); } - _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot); - _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotCheck.getSize())); + if (snapshotCheck.getState() != Snapshot.State.Error && snapshotCheck.getState() != Snapshot.State.Destroyed) + _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot); + if (snapshotCheck.getState() == Snapshot.State.BackedUp) + _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, new Long(snapshotStoreRef.getSize())); } return result; } catch (Exception e) { @@ -621,11 +618,12 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, s_logger.error("Unable to find snaphot strategy to handle snapshot with id '" + snapshot.getId() + "'"); continue; } + SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image); if (snapshotStrategy.deleteSnapshot(snapshot.getId())) { if (snapshot.getRecurringType() == Type.MANUAL) { _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.snapshot); - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, new Long(snapshot.getSize())); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, new Long(snapshotStoreRef.getSize())); } // Log event after successful deletion @@ -953,17 +951,16 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + SnapshotDataStoreVO snapshotStoreRef = _snapshotStoreDao.findBySnapshot(snapshotId, DataStoreRole.Image); + // Correct the resource count of snapshot in case of delta snapshots. + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, new Long(volume.getSize() - snapshotStoreRef.getSize())); } catch (Exception e) { s_logger.debug("post process snapshot failed", e); } } catch (Exception e) { s_logger.debug("Failed to create snapshot", e); - if (backup) { - _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, new Long(volume.getSize())); - } else { - _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.primary_storage, new Long(volume.getSize())); - } + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, new Long(volume.getSize())); throw new CloudRuntimeException("Failed to create snapshot", e); } return snapshot; @@ -1068,11 +1065,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, try { _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); - if (backup) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize())); - } else { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize())); - } + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize())); } catch (ResourceAllocationException e) { if (snapshotType != Type.MANUAL) { String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots"; @@ -1111,11 +1104,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (snapshot == null) { throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId()); } - if (backup) { - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, new Long(volume.getSize())); - } else { - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize())); - } + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.snapshot); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, new Long(volume.getSize())); return snapshot; } diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java index 3dd90ff6a82..a202ad28517 100644 --- a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java +++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java @@ -57,6 +57,7 @@ import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.TaggedResourceService; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; @@ -64,6 +65,7 @@ import com.cloud.tags.dao.ResourceTagDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.DomainManager; +import com.cloud.user.dao.UserDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; @@ -147,6 +149,10 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso Site2SiteCustomerGatewayDao _customerGatewayDao; @Inject Site2SiteVpnConnectionDao _vpnConnectionDao; + @Inject + UserDao _userDao; + @Inject + DiskOfferingDao _diskOffDao; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -176,6 +182,8 @@ public class TaggedResourceManagerImpl extends ManagerBase implements TaggedReso s_daoMap.put(ResourceObjectType.VpnGateway, _vpnGatewayDao); s_daoMap.put(ResourceObjectType.CustomerGateway, _customerGatewayDao); s_daoMap.put(ResourceObjectType.VpnConnection, _vpnConnectionDao); + s_daoMap.put(ResourceObjectType.User, _userDao); + s_daoMap.put(ResourceObjectType.DiskOffering, _diskOffDao); return true; } diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java index 74d1ac85b4f..25e79db2bbe 100755 --- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java @@ -151,21 +151,54 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase { (!url.toLowerCase().endsWith("qcow2.bz2")) && (!url.toLowerCase().endsWith("qcow2.gz")) && (!url.toLowerCase().endsWith("ova")) && (!url.toLowerCase().endsWith("ova.zip")) && (!url.toLowerCase().endsWith("ova.bz2")) && (!url.toLowerCase().endsWith("ova.gz")) && (!url.toLowerCase().endsWith("tar")) && (!url.toLowerCase().endsWith("tar.zip")) && (!url.toLowerCase().endsWith("tar.bz2")) && - (!url.toLowerCase().endsWith("tar.gz")) && (!url.toLowerCase().endsWith("img")) && (!url.toLowerCase().endsWith("raw"))) { + (!url.toLowerCase().endsWith("tar.gz")) && (!url.toLowerCase().endsWith("vmdk")) && (!url.toLowerCase().endsWith("vmdk.gz")) && + (!url.toLowerCase().endsWith("vmdk.zip")) && (!url.toLowerCase().endsWith("vmdk.bz2")) && (!url.toLowerCase().endsWith("img")) && + (!url.toLowerCase().endsWith("img.gz")) && (!url.toLowerCase().endsWith("img.zip")) && (!url.toLowerCase().endsWith("img.bz2")) && + (!url.toLowerCase().endsWith("raw")) && (!url.toLowerCase().endsWith("raw.gz")) && (!url.toLowerCase().endsWith("raw.bz2")) && + (!url.toLowerCase().endsWith("raw.zip"))) { throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase()); } - if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith("vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase() - .endsWith("vhd.gz"))) || - (format.equalsIgnoreCase("vhdx") && (!url.toLowerCase().endsWith("vhdx") && !url.toLowerCase().endsWith("vhdx.zip") && - !url.toLowerCase().endsWith("vhdx.bz2") && !url.toLowerCase() - .endsWith("vhdx.gz"))) || - (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith("qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && - !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz"))) || - (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith("ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase() - .endsWith("ova.gz"))) || - (format.equalsIgnoreCase("tar") && (!url.toLowerCase().endsWith("tar") && !url.toLowerCase().endsWith("tar.zip") && !url.toLowerCase().endsWith("tar.bz2") && !url.toLowerCase() - .endsWith("tar.gz"))) || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith("img") && !url.toLowerCase().endsWith("raw")))) { + if ((format.equalsIgnoreCase("vhd") + && (!url.toLowerCase().endsWith("vhd") + && !url.toLowerCase().endsWith("vhd.zip") + && !url.toLowerCase().endsWith("vhd.bz2") + && !url.toLowerCase().endsWith("vhd.gz"))) + || (format.equalsIgnoreCase("vhdx") + && (!url.toLowerCase().endsWith("vhdx") + && !url.toLowerCase().endsWith("vhdx.zip") + && !url.toLowerCase().endsWith("vhdx.bz2") + && !url.toLowerCase().endsWith("vhdx.gz"))) + || (format.equalsIgnoreCase("qcow2") + && (!url.toLowerCase().endsWith("qcow2") + && !url.toLowerCase().endsWith("qcow2.zip") + && !url.toLowerCase().endsWith("qcow2.bz2") + && !url.toLowerCase().endsWith("qcow2.gz"))) + || (format.equalsIgnoreCase("ova") + && (!url.toLowerCase().endsWith("ova") + && !url.toLowerCase().endsWith("ova.zip") + && !url.toLowerCase().endsWith("ova.bz2") + && !url.toLowerCase().endsWith("ova.gz"))) + || (format.equalsIgnoreCase("tar") + && (!url.toLowerCase().endsWith("tar") + && !url.toLowerCase().endsWith("tar.zip") + && !url.toLowerCase().endsWith("tar.bz2") + && !url.toLowerCase().endsWith("tar.gz"))) + || (format.equalsIgnoreCase("raw") + && (!url.toLowerCase().endsWith("img") + && !url.toLowerCase().endsWith("img.zip") + && !url.toLowerCase().endsWith("img.bz2") + && !url.toLowerCase().endsWith("img.gz") + && !url.toLowerCase().endsWith("raw") + && !url.toLowerCase().endsWith("raw.bz2") + && !url.toLowerCase().endsWith("raw.zip") + && !url.toLowerCase().endsWith("raw.gz"))) + || (format.equalsIgnoreCase("vmdk") + && (!url.toLowerCase().endsWith("vmdk") + && !url.toLowerCase().endsWith("vmdk.zip") + && !url.toLowerCase().endsWith("vmdk.bz2") + && !url.toLowerCase().endsWith("vmdk.gz"))) + ) { throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase()); } diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index accb1812d2f..ac641a2010c 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -689,38 +689,47 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, Account caller = CallContext.current().getCallingAccount(); // Verify parameters - if (sourceZoneId.equals(destZoneId)) { - throw new InvalidParameterValueException("Please specify different source and destination zones."); - } - - DataCenterVO sourceZone = _dcDao.findById(sourceZoneId); - if (sourceZone == null) { - throw new InvalidParameterValueException("Please specify a valid source zone."); - } - - DataCenterVO dstZone = _dcDao.findById(destZoneId); - if (dstZone == null) { - throw new InvalidParameterValueException("Please specify a valid destination zone."); - } - VMTemplateVO template = _tmpltDao.findById(templateId); if (template == null || template.getRemoved() != null) { throw new InvalidParameterValueException("Unable to find template with id"); } - DataStore srcSecStore = getImageStore(sourceZoneId, templateId); + DataStore srcSecStore = null; + if (sourceZoneId != null) { + // template is on zone-wide secondary storage + srcSecStore = getImageStore(sourceZoneId, templateId); + } else { + // template is on region store + srcSecStore = getImageStore(templateId); + } + if (srcSecStore == null) { - throw new InvalidParameterValueException("There is no template " + templateId + " in zone " + sourceZoneId); + throw new InvalidParameterValueException("There is no template " + templateId + " ready on image store."); } if (template.isCrossZones()) { - //TODO: we may need UI still enable CopyTemplate in case of cross zone template to trigger sync to region store. // sync template from cache store to region store if it is not there, for cases where we are going to migrate existing NFS to S3. _tmpltSvr.syncTemplateToRegionStore(templateId, srcSecStore); s_logger.debug("Template " + templateId + " is cross-zone, don't need to copy"); return template; } + if (sourceZoneId != null) { + if (sourceZoneId.equals(destZoneId)) { + throw new InvalidParameterValueException("Please specify different source and destination zones."); + } + + DataCenterVO sourceZone = _dcDao.findById(sourceZoneId); + if (sourceZone == null) { + throw new InvalidParameterValueException("Please specify a valid source zone."); + } + } + + DataCenterVO dstZone = _dcDao.findById(destZoneId); + if (dstZone == null) { + throw new InvalidParameterValueException("Please specify a valid destination zone."); + } + DataStore dstSecStore = getImageStore(destZoneId, templateId); if (dstSecStore != null) { s_logger.debug("There is template " + templateId + " in secondary storage " + dstSecStore.getName() + " in zone " + destZoneId + " , don't need to copy"); @@ -1386,8 +1395,14 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, throw new CloudRuntimeException("Failed to create template" + result.getResult()); } - VMTemplateZoneVO templateZone = new VMTemplateZoneVO(zoneId, templateId, new Date()); - _tmpltZoneDao.persist(templateZone); + // create entries in template_zone_ref table + if (_dataStoreMgr.isRegionStore(store)) { + // template created on region store + _tmpltSvr.associateTemplateToZone(templateId, null); + } else { + VMTemplateZoneVO templateZone = new VMTemplateZoneVO(zoneId, templateId, new Date()); + _tmpltZoneDao.persist(templateZone); + } privateTemplate = _tmpltDao.findById(templateId); if (snapshotId != null) { @@ -1692,6 +1707,18 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, return null; } + // get the region wide image store where a template is READY on, + // just pick one is enough. + @Override + public DataStore getImageStore(long tmpltId) { + TemplateDataStoreVO tmpltStore = _tmplStoreDao.findReadyByTemplate(tmpltId, DataStoreRole.Image); + if (tmpltStore != null) { + return _dataStoreMgr.getDataStore(tmpltStore.getDataStoreId(), DataStoreRole.Image); + } + + return null; + } + @Override public Long getTemplateSize(long templateId, long zoneId) { TemplateDataStoreVO templateStoreRef = _tmplStoreDao.findByTemplateZoneDownloadStatus(templateId, zoneId, VMTemplateStorageResourceAssoc.Status.DOWNLOADED); diff --git a/server/src/com/cloud/user/AccountManagerImpl.java b/server/src/com/cloud/user/AccountManagerImpl.java index 8504ee11705..20a6242cfd6 100755 --- a/server/src/com/cloud/user/AccountManagerImpl.java +++ b/server/src/com/cloud/user/AccountManagerImpl.java @@ -750,11 +750,14 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M // release account specific acquired portable IP's. Since all the portable IP's must have been already // disassociated with VPC/guest network (due to deletion), so just mark portable IP as free. - List portableIpsToRelease = _ipAddressDao.listByAccount(accountId); - for (IpAddress ip : portableIpsToRelease) { - s_logger.debug("Releasing portable ip " + ip + " as a part of account id=" + accountId + " cleanup"); - _ipAddrMgr.releasePortableIpAddress(ip.getId()); + List ipsToRelease = _ipAddressDao.listByAccount(accountId); + for (IpAddress ip : ipsToRelease) { + if (ip.isPortable()) { + s_logger.debug("Releasing portable ip " + ip + " as a part of account id=" + accountId + " cleanup"); + _ipAddrMgr.releasePortableIpAddress(ip.getId()); + } } + // release dedication if any List dedicatedResources = _dedicatedDao.listByAccountId(accountId); if (dedicatedResources != null && !dedicatedResources.isEmpty()) { diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 604349153ce..950e7e37094 100755 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -46,7 +46,7 @@ import com.cloud.utils.Pair; public interface UserVmManager extends UserVmService { static final String EnableDynamicallyScaleVmCK = "enable.dynamic.scale.vm"; static final ConfigKey EnableDynamicallyScaleVm = new ConfigKey("Advanced", Boolean.class, EnableDynamicallyScaleVmCK, "false", - "Enables/Diables dynamically scaling a vm", true, ConfigKey.Scope.Zone); + "Enables/Disables dynamically scaling a vm", true, ConfigKey.Scope.Zone); static final int MAX_USER_DATA_LENGTH_BYTES = 2048; diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 3ad49d89dc6..e9cccc4fe36 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -34,9 +34,6 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.commons.codec.binary.Base64; -import org.apache.log4j.Logger; - import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -82,6 +79,8 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.commons.codec.binary.Base64; +import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -371,6 +370,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject protected NicDao _nicDao; @Inject + protected ServiceOfferingDao _offerringDao; + @Inject protected VpcDao _vpcDao; @Inject protected RulesManager _rulesMgr; @@ -485,22 +486,22 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return _vmDao.listByHostId(hostId); } - protected void resourceLimitCheck(Account owner, Long cpu, Long memory) throws ResourceAllocationException { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, cpu); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, memory); + protected void resourceLimitCheck(Account owner, Boolean displayVm, Long cpu, Long memory) throws ResourceAllocationException { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm, displayVm); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, displayVm, cpu); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, displayVm, memory); } - protected void resourceCountIncrement(long accountId, Long cpu, Long memory) { - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.cpu, cpu); - _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.memory, memory); + protected void resourceCountIncrement(long accountId, Boolean displayVm, Long cpu, Long memory) { + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.user_vm, displayVm); + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.cpu, displayVm, cpu); + _resourceLimitMgr.incrementResourceCount(accountId, ResourceType.memory, displayVm, memory); } - protected void resourceCountDecrement(long accountId, Long cpu, Long memory) { - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.user_vm); - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.cpu, cpu); - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, memory); + protected void resourceCountDecrement(long accountId, Boolean displayVm, Long cpu, Long memory) { + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.user_vm, displayVm); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.cpu, displayVm, cpu); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.memory, displayVm, memory); } @Override @@ -560,8 +561,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); - NicProfile defaultNicProfile = - new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), + NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork)); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); vmProfile.setParameter(VirtualMachineProfile.Param.VmPassword, password); @@ -629,8 +629,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir SSHKeyPairVO s = _sshKeyPairDao.findByName(owner.getAccountId(), owner.getDomainId(), cmd.getName()); if (s == null) { - throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName() + - " in specified domain id"); + throw new InvalidParameterValueException("A key pair with name '" + cmd.getName() + "' does not exist for account " + owner.getAccountName() + + " in specified domain id"); } _accountMgr.checkAccess(caller, null, true, userVm); @@ -668,9 +668,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } Network defaultNetwork = _networkDao.findById(defaultNic.getNetworkId()); - NicProfile defaultNicProfile = - new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), _networkModel.getNetworkTag( - template.getHypervisorType(), defaultNetwork)); + NicProfile defaultNicProfile = new NicProfile(defaultNic, defaultNetwork, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork), + _networkModel.getNetworkTag(template.getHypervisorType(), defaultNetwork)); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vmInstance); @@ -759,8 +758,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir * TODO: cleanup eventually - Refactored API call */ // This method will be deprecated as we use ScaleVMCmd for both stopped VMs and running VMs - public - UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationException { + public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationException { Long vmId = cmd.getId(); Long svcOffId = cmd.getServiceOfferingId(); Account caller = CallContext.current().getCallingAccount(); @@ -771,18 +769,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vmInstance == null) { throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); } else if (!(vmInstance.getState().equals(State.Stopped))) { - throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + " in state " + vmInstance.getState() + - "; make sure the virtual machine is stopped"); + throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + " in state " + vmInstance.getState() + + "; make sure the virtual machine is stopped"); } _accountMgr.checkAccess(caller, null, true, vmInstance); // Check resource limits for CPU and Memory. - Map customParameters = cmd.getCustomParameters(); + Map customParameters = cmd.getDetails(); ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId); if (newServiceOffering.isDynamic()) { newServiceOffering.setDynamicFlag(true); - validateCustomParameters(newServiceOffering, cmd.getCustomParameters()); + validateCustomParameters(newServiceOffering, cmd.getDetails()); newServiceOffering = _offeringDao.getcomputeOffering(newServiceOffering, customParameters); } ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); @@ -836,7 +834,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory)); } - generateUsageEvent(newServiceOffering, cmd.getCustomParameters(), _vmDao.findById(vmId), EventTypes.EVENT_VM_UPGRADE); + // Generate usage event for VM upgrade + generateUsageEvent(newServiceOffering, cmd.getDetails(), _vmDao.findById(vmId), EventTypes.EVENT_VM_UPGRADE); return _vmDao.findById(vmInstance.getId()); } @@ -850,8 +849,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Invalid cpu cores value, specify a value between 1 and 2147483647"); } } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) { - throw new InvalidParameterValueException("The cpu cores of this offering id:" + serviceOffering.getId() + - " is not customizable. This is predefined in the template."); + throw new InvalidParameterValueException("The cpu cores of this offering id:" + serviceOffering.getId() + + " is not customizable. This is predefined in the template."); } if (serviceOffering.getSpeed() == null) { @@ -860,8 +859,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Invalid cpu speed value, specify a value between 1 and 2147483647"); } } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) { - throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getId() + - " is not customizable. This is predefined in the template."); + throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getId() + + " is not customizable. This is predefined in the template."); } if (serviceOffering.getRamSize() == null) { @@ -870,8 +869,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Invalid memory value, specify a value between 32 and 2147483647 MB"); } } else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) { - throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getId() + - " is not customizable. This is predefined in the template."); + throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getId() + " is not customizable. This is predefined in the template."); } } else { throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering"); @@ -967,8 +965,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (network == null) { throw new InvalidParameterValueException("unable to find a network with id " + networkId); } - if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain) && - !(network.getAclType() == ACLType.Account && network.getAccountId() == vmInstance.getAccountId())) { + if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain) + && !(network.getAclType() == ACLType.Account && network.getAccountId() == vmInstance.getAccountId())) { throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vmId: " + vmId); } List allNics = _nicDao.listByVmId(vmInstance.getId()); @@ -1144,8 +1142,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (existing == null) { s_logger.warn("Failed to update default nic, no nic profile found for existing default network"); - throw new CloudRuntimeException( - "Failed to find a nic profile for the existing default network. This is bad and probably means some sort of configuration corruption"); + throw new CloudRuntimeException("Failed to find a nic profile for the existing default network. This is bad and probably means some sort of configuration corruption"); } Network oldDefaultNetwork = null; @@ -1190,45 +1187,44 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir String nicIdString = Long.toString(nic.getId()); long newNetworkOfferingId = network.getNetworkOfferingId(); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), - oldNicIdString, oldNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); + oldNicIdString, oldNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString, + newNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), nicIdString, + newNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), - nicIdString, newNetworkOfferingId, null, 1L, VirtualMachine.class.getName(), vmInstance.getUuid()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), - nicIdString, newNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), - oldNicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); + oldNicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); return _vmDao.findById(vmInstance.getId()); } - throw new CloudRuntimeException("something strange happened, new default network(" + newdefault.getId() + ") is not null, and is not equal to the network(" + - nic.getNetworkId() + ") of the chosen nic"); + throw new CloudRuntimeException("something strange happened, new default network(" + newdefault.getId() + ") is not null, and is not equal to the network(" + + nic.getNetworkId() + ") of the chosen nic"); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_UPGRADE, eventDescription = "Upgrading VM", async = true) public UserVm upgradeVirtualMachine(ScaleVMCmd cmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, - VirtualMachineMigrationException { + VirtualMachineMigrationException { Long vmId = cmd.getId(); Long newServiceOfferingId = cmd.getServiceOfferingId(); CallContext.current().setEventDetails("Vm Id: " + vmId); - boolean result = upgradeVirtualMachine(vmId, newServiceOfferingId, cmd.getCustomParameters()); + boolean result = upgradeVirtualMachine(vmId, newServiceOfferingId, cmd.getDetails()); if (result) { UserVmVO vmInstance = _vmDao.findById(vmId); if (vmInstance.getState().equals(State.Stopped)) { // Generate usage event for VM upgrade - generateUsageEvent(_serviceOfferingDao.findById(newServiceOfferingId), cmd.getCustomParameters(), vmInstance, EventTypes.EVENT_VM_UPGRADE); + generateUsageEvent(_offeringDao.findById(newServiceOfferingId), cmd.getDetails(), _vmDao.findById(vmId), EventTypes.EVENT_VM_UPGRADE); } if (vmInstance.getState().equals(State.Running)) { // Generate usage event for Dynamic scaling of VM - generateUsageEvent(_serviceOfferingDao.findById(newServiceOfferingId), cmd.getCustomParameters(), vmInstance, EventTypes.EVENT_VM_UPGRADE); + generateUsageEvent(_offeringDao.findById(newServiceOfferingId), cmd.getDetails(), _vmDao.findById(vmId), EventTypes.EVENT_VM_UPGRADE); } return vmInstance; } else { throw new CloudRuntimeException("Failed to scale the VM"); } - } @Override @@ -1268,7 +1264,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId, Map customParameters) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { // Verify input parameters VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); @@ -1284,7 +1280,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } private boolean upgradeRunningVirtualMachine(Long vmId, Long newServiceOfferingId, Map customParameters) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { Account caller = CallContext.current().getCallingAccount(); VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); @@ -1316,11 +1312,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir int cpuDiff = newCpu * newSpeed - currentCpu * currentSpeed; // Don't allow to scale when (Any of the new values less than current values) OR (All current and new values are same) - if ((newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu) || - (newSpeed == currentSpeed && newMemory == currentMemory && newCpu == currentCpu)) { - throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering(speed=" + newSpeed + ",cpu=" + newCpu + ",memory=," + - newMemory + ")" + " should have at least one value(cpu/ram) greater than old value and no resource value less than older(speed=" + currentSpeed + - ",cpu=" + currentCpu + ",memory=," + currentMemory + ")"); + if ((newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu) || (newSpeed == currentSpeed && newMemory == currentMemory && newCpu == currentCpu)) { + throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering(speed=" + newSpeed + ",cpu=" + newCpu + ",memory=," + newMemory + + ")" + " should have at least one value(cpu/ram) greater than old value and no resource value less than older(speed=" + currentSpeed + ",cpu=" + currentCpu + + ",memory=," + currentMemory + ")"); } // Check resource limits @@ -1370,9 +1365,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // #1 Check existing host has capacity if (!excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId()))) { existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed) - && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, - (memoryDiff) * 1024L * 1024L, false, _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU), - _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false); + && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, (memoryDiff) * 1024L * 1024L, false, + _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU), + _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false); excludes.addHost(vmInstance.getHostId()); } @@ -1538,7 +1533,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // First check that the maximum number of UserVMs, CPU and Memory limit for the given // accountId will not be exceeded - resourceLimitCheck(account, new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); + resourceLimitCheck(account, vm.isDisplayVm(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); _haMgr.cancelDestroy(vm, vm.getHostId()); @@ -1565,13 +1560,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir offeringId = offering.getId(); } } - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), - volume.getName(), offeringId, templateId, volume.getSize(), Volume.class.getName(), volume.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + offeringId, templateId, volume.getSize(), Volume.class.getName(), volume.getUuid()); } } //Update Resource Count for the given account - resourceCountIncrement(account.getId(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); + resourceCountIncrement(account.getId(), vm.isDisplayVm(), new Long(serviceOffering.getCpu()), new Long(serviceOffering.getRamSize())); } }); @@ -1618,7 +1613,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _itMgr.registerGuru(VirtualMachine.Type.User, this); - VirtualMachine.State.getStateMachine().registerListener(new UserVmStateListener(_usageEventDao, _networkDao, _nicDao)); + VirtualMachine.State.getStateMachine().registerListener(new UserVmStateListener(_usageEventDao, _networkDao, _nicDao, _offeringDao)); String value = _configDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()); _instanceNameFlag = (value == null) ? false : Boolean.parseBoolean(value); @@ -1663,7 +1658,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // 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())); + _resourceLimitMgr.recalculateResourceCount(vm.getAccountId(), vm.getDomainId(), ResourceType.primary_storage.getOrdinal()); } // Only if vm is not expunged already, cleanup it's resources @@ -1782,7 +1777,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); // Update Resource Count for the given account - resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize())); } } } @@ -1846,10 +1841,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, vmInstance); - if (isDisplayVmEnabled != null) { + //If the flag is specified and is changed + if (isDisplayVmEnabled != null && isDisplayVmEnabled != vmInstance.isDisplayVm()) { if (!_accountMgr.isRootAdmin(caller.getType())) { throw new PermissionDeniedException("Cannot update parameter displayvm, only admin permitted "); } + ServiceOffering offering = _serviceOfferingDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.user_vm, isDisplayVmEnabled); + _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.cpu, isDisplayVmEnabled, new Long(offering.getCpu())); + _resourceLimitMgr.changeResourceCount(vmInstance.getAccountId(), ResourceType.memory, isDisplayVmEnabled, new Long(offering.getRamSize())); } return updateVirtualMachine(id, displayName, group, ha, isDisplayVmEnabled, osTypeId, userData, isDynamicallyScalable, cmd.getHttpMethod(), cmd.getCustomId()); @@ -1857,7 +1857,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData, - Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId) throws ResourceUnavailableException, InsufficientCapacityException { + Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId) throws ResourceUnavailableException, InsufficientCapacityException { UserVmVO vm = _vmDao.findById(id); if (vm == null) { throw new CloudRuntimeException("Unable to find virual machine with id " + id); @@ -1939,8 +1939,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir for (Nic nic : nics) { Network network = _networkDao.findById(nic.getNetworkId()); - NicProfile nicProfile = - new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag( + NicProfile nicProfile = new NicProfile(nic, network, null, null, null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag( template.getHypervisorType(), network)); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); @@ -1960,8 +1959,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true) - public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { + public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { return startVirtualMachine(cmd.getId(), cmd.getHostId(), null).first(); } @@ -2205,11 +2203,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) - public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, - HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, - List affinityGroupIdList, Map customParametes, String customId) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, + String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, + Map customParametes, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2244,26 +2242,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (s_logger.isDebugEnabled()) { s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); } - defaultGroup = - _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } } } - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, - httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId); + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, + userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, - HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, - List affinityGroupIdList, Map customParameters, String customId) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, + List affinityGroupIdList, Map customParameters, String customId) throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2352,25 +2349,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (s_logger.isDebugEnabled()) { s_logger.debug("Couldn't find default security group for the account " + owner + " so creating a new one"); } - defaultGroup = - _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, owner.getDomainId(), owner.getId(), owner.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } } } - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, - httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId); + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, + userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId); } @Override @ActionEvent(eventType = EventTypes.EVENT_VM_CREATE, eventDescription = "deploying Vm", create = true) - public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, - Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, - String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, - List affinityGroupIdList, Map customParametrs, String customId) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, + String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, + String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List affinityGroupIdList, + Map customParametrs, String customId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2392,27 +2388,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required + - " to automatically create the network as a part of vm creation"); + throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required + + " to automatically create the network as a part of vm creation"); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = _networkModel.listNetworksForAccount(owner.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks.isEmpty()) { - long physicalNetworkId = - _networkModel.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); + long physicalNetworkId = _networkModel.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + - requiredOfferings.get(0).getTags()); + throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + + requiredOfferings.get(0).getTags()); } - s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + - " as a part of deployVM process"); - Network newNetwork = - _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, - null, null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null); + s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); + Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", + null, null, null, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null); defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { throw new InvalidParameterValueException("More than 1 default Isolated networks are found for account " + owner + "; please specify networkIds"); @@ -2420,8 +2413,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } } else { - throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + - NetworkOffering.State.Enabled); + throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); } networkList.add(defaultNetwork); @@ -2436,8 +2428,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Only ISOs, XenServer, KVM, and VmWare template types are // supported for vpc networks if (template.getFormat() != ImageFormat.ISO && !vpcSupportedHTypes.contains(template.getHypervisorType())) { - throw new InvalidParameterValueException("Can't create vm from template with hypervisor " + template.getHypervisorType() + " in vpc network " + - network); + throw new InvalidParameterValueException("Can't create vm from template with hypervisor " + template.getHypervisorType() + " in vpc network " + network); } else if (template.getFormat() == ImageFormat.ISO && !vpcSupportedHTypes.contains(hypervisor)) { // Only XenServer, KVM, and VMware hypervisors are supported // for vpc networks @@ -2457,23 +2448,23 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, - userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId); + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData, + sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId); } public void checkNameForRFCCompliance(String name) { if (!NetUtils.verifyDomainNameLabel(name, true)) { throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " - + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); + + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); } } @DB - protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, - Account owner, Long diskOfferingId, Long diskSize, List networkList, List securityGroupIdList, String group, HTTPMethod httpmethod, - String userData, String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, IpAddresses defaultIps, - Boolean isDisplayVmEnabled, String keyboard, List affinityGroupIdList, Map customParameters, String customId) - throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { + protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate tmplt, String hostName, String displayName, Account owner, + Long diskOfferingId, Long diskSize, List networkList, List securityGroupIdList, String group, HTTPMethod httpmethod, String userData, + String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, IpAddresses defaultIps, Boolean isDisplayVmEnabled, String keyboard, + List affinityGroupIdList, Map customParameters, String customId) throws InsufficientCapacityException, ResourceUnavailableException, + ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, true, owner); @@ -2521,9 +2512,21 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir size = tmp; } if (diskOfferingId != null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); + if (diskOffering != null && diskOffering.isCustomized()) { + if (diskSize == null) { + throw new InvalidParameterValueException("This disk offering requires a custom size specified"); + } + Long customDiskOfferingMaxSize = volumeMgr.CustomDiskOfferingMaxSize.value(); + Long customDiskOfferingMinSize = volumeMgr.CustomDiskOfferingMinSize.value(); + if ((diskSize < customDiskOfferingMinSize) || (diskSize > customDiskOfferingMaxSize)) { + throw new InvalidParameterValueException("VM Creation failed. Volume size: " + diskSize + "GB is out of allowed range. Max: " + customDiskOfferingMaxSize + + " Min:" + customDiskOfferingMinSize); + } + } size += _diskOfferingDao.findById(diskOfferingId).getDiskSize(); } - resourceLimitCheck(owner, new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceLimitCheck(owner, isDisplayVmEnabled, new Long(offering.getCpu()), new Long(offering.getRamSize())); _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2)); _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, size); @@ -2548,8 +2551,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (ag == null) { throw new InvalidParameterValueException("Unable to find affinity group " + ag); } else if (!_affinityGroupService.isAffinityGroupProcessorAvailable(ag.getType())) { - throw new InvalidParameterValueException("Affinity group type is not supported for group: " + ag + " ,type: " + ag.getType() + - " , Please try again after removing the affinity group"); + throw new InvalidParameterValueException("Affinity group type is not supported for group: " + ag + " ,type: " + ag.getType() + + " , Please try again after removing the affinity group"); } else { // verify permissions if (ag.getAclType() == ACLType.Domain) { @@ -2629,8 +2632,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (network.getDataCenterId() != zone.getId()) { throw new InvalidParameterValueException("Network id=" + network.getId() + " doesn't belong to zone " + zone.getId()); } - if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain) && - !(network.getAclType() == ACLType.Account && network.getAccountId() == accountId)) { + if (!(network.getGuestType() == Network.GuestType.Shared && network.getAclType() == ACLType.Domain) + && !(network.getAclType() == ACLType.Account && network.getAccountId() == accountId)) { throw new InvalidParameterValueException("only shared network or isolated network with the same account_id can be added to vm"); } IpAddresses requestedIpPair = null; @@ -2746,8 +2749,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List hostNames = _vmInstanceDao.listDistinctHostNames(ntwkId); // * verify that there are no duplicates if (hostNames.contains(hostName)) { - throw new InvalidParameterValueException("The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain.getKey() + - "; network=" + _networkModel.getNetwork(ntwkId)); + throw new InvalidParameterValueException("The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain.getKey() + "; network=" + + _networkModel.getNetwork(ntwkId)); } } } @@ -2759,8 +2762,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir hypervisorType = template.getHypervisorType(); } - UserVmVO vm = - commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, hypervisor, caller, isDisplayVmEnabled, keyboard, accountId, + UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, hypervisor, caller, isDisplayVmEnabled, keyboard, accountId, offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters); // Assign instance to the group @@ -2789,16 +2791,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } private UserVmVO commitUserVm(final DataCenter zone, final VirtualMachineTemplate template, final String hostName, final String displayName, final Account owner, - final Long diskOfferingId, final Long diskSize, final String userData, final HypervisorType hypervisor, final Account caller, final Boolean isDisplayVmEnabled, - final String keyboard, final long accountId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, - final LinkedHashMap networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, - final Map customParameters) throws InsufficientCapacityException { + final Long diskOfferingId, final Long diskSize, final String userData, final HypervisorType hypervisor, final Account caller, final Boolean isDisplayVmEnabled, + final String keyboard, final long accountId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, + final LinkedHashMap networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, + final Map customParameters) throws InsufficientCapacityException { return Transaction.execute(new TransactionCallbackWithException() { @Override public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException { - UserVmVO vm = - new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(), - offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), offering.getId(), userData, hostName, diskOfferingId); + UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.getOfferHA(), offering + .getLimitCpuUse(), owner.getDomainId(), owner.getId(), offering.getId(), userData, hostName, diskOfferingId); vm.setUuid(uuidName); vm.setDynamicallyScalable(template.isDynamicallyScalable()); if (sshPublicKey != null) { @@ -2881,12 +2882,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (isIso) { _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), Long.toString(owner.getAccountId()), vm.getIsoId().toString(), hostName, displayName, - hypervisor.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, - networkNicMap, plan); + hypervisor.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, + networkNicMap, plan); } else { - _orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, - hypervisor.name(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, - rootDiskSize); + _orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, hypervisor.name(), + offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize); } if (s_logger.isDebugEnabled()) { @@ -2895,17 +2895,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir CallContext.current().setEventDetails("Vm Id: " + vm.getId()); if (!offering.isDynamic()) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), - vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString(), - VirtualMachine.class.getName(), vm.getUuid()); - } - else { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), - template.getId(), hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), customParameters); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), + hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid()); + } else { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), + hypervisorType.toString(), VirtualMachine.class.getName(), vm.getUuid(), customParameters); } //Update Resource Count for the given account - resourceCountIncrement(accountId, new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountIncrement(accountId, isDisplayVmEnabled, new Long(offering.getCpu()), new Long(offering.getRamSize())); return vm; } }); @@ -2913,14 +2911,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private void generateUsageEvent(ServiceOfferingVO serviceOffering, Map customParameters, UserVmVO vm, String eventType) { if (!serviceOffering.isDynamic()) { - UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), - vm.getHostName(), serviceOffering.getId(), vm.getTemplateId(), vm.getHypervisorType().toString(), - VirtualMachine.class.getName(), vm.getUuid()); - } - else { - UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), - vm.getHostName(), serviceOffering.getId(), vm.getTemplateId(), vm.getHypervisorType().toString(), - VirtualMachine.class.getName(), vm.getUuid(), customParameters); + UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), serviceOffering.getId(), vm.getTemplateId(), vm + .getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); + } else { + UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), serviceOffering.getId(), vm.getTemplateId(), vm + .getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), customParameters); } } @@ -2962,7 +2957,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } protected UserVm startVirtualMachine(DeployVMCmd cmd, Map additonalParams) throws ResourceUnavailableException, - InsufficientCapacityException, ConcurrentOperationException { + InsufficientCapacityException, ConcurrentOperationException { long vmId = cmd.getEntityId(); Long hostId = cmd.getHostId(); @@ -3064,6 +3059,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (network.getTrafficType() == TrafficType.Guest || network.getTrafficType() == TrafficType.Public) { userVm.setPrivateIpAddress(nic.getIp4Address()); userVm.setPrivateMacAddress(nic.getMacAddress()); + _vmDao.update(userVm.getId(), userVm); } } @@ -3116,7 +3112,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir NetworkVO network = _networkDao.findById(nic.getNetworkId()); long isDefault = (nic.isDefaultNic()) ? 1 : 0; UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_ASSIGN, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), Long.toString(nic.getId()), - network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); + network.getNetworkOfferingId(), null, isDefault, VirtualMachine.class.getName(), vm.getUuid()); if (network.getTrafficType() == TrafficType.Guest) { originalIp = nic.getIp4Address(); guestNic = nic; @@ -3221,8 +3217,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir assert (offering.getAssociatePublicIP() == true) : "User VM should not have system owned public IP associated with it when offering configured not to associate public IP."; _rulesMgr.disableStaticNat(ip.getId(), ctx.getCallingAccount(), ctx.getCallingUserId(), true); } catch (Exception ex) { - s_logger.warn("Failed to disable static nat and release system ip " + ip + " as a part of vm " + profile.getVirtualMachine() + " stop due to exception ", - ex); + s_logger.warn("Failed to disable static nat and release system ip " + ip + " as a part of vm " + profile.getVirtualMachine() + " stop due to exception ", ex); } } @@ -3243,9 +3238,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public Pair> - startVirtualMachine(long vmId, Long hostId, Map additionalParams) throws ConcurrentOperationException, - ResourceUnavailableException, InsufficientCapacityException { + public Pair> startVirtualMachine(long vmId, Long hostId, Map additionalParams) + throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // Input validation Account callerAccount = CallContext.current().getCallingAccount(); UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId()); @@ -3285,8 +3279,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } // check if vm is security group enabled - if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() && - !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { + if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty() + && !_securityGroupMgr.isVmMappedToDefaultSecurityGroup(vmId) && _networkModel.canAddDefaultSecurityGroup()) { // if vm is not mapped to security group, create a mapping if (s_logger.isDebugEnabled()) { s_logger.debug("Vm " + vm + " is security group enabled, but not mapped to default security group; creating the mapping automatically"); @@ -3365,8 +3359,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public UserVm destroyVm(long vmId) throws ResourceUnavailableException, - ConcurrentOperationException { + public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { // Account caller = CallContext.current().getCallingAccount(); // Long userId = CallContext.current().getCallingUserId(); Long userId = 2L; @@ -3404,7 +3397,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir for (VolumeVO volume : volumes) { if (volume.getVolumeType().equals(Volume.Type.ROOT)) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - Volume.class.getName(), volume.getUuid()); + Volume.class.getName(), volume.getUuid()); } } @@ -3413,7 +3406,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); //Update Resource Count for the given account - resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize())); } return _vmDao.findById(vmId); } else { @@ -3444,8 +3437,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } if (diskStatsAnswer != null) { if (!diskStatsAnswer.getResult()) { - s_logger.warn("Error while collecting disk stats vm: " + userVm.getHostName() + " from host: " + host.getName() + "; details: " + - diskStatsAnswer.getDetails()); + s_logger.warn("Error while collecting disk stats vm: " + userVm.getHostName() + " from host: " + host.getName() + "; details: " + diskStatsAnswer.getDetails()); return; } try { @@ -3467,8 +3459,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if ((volumes == null) || (volumes.size() == 0)) break; VolumeVO volume = volumes.get(0); - VmDiskStatisticsVO previousVmDiskStats = - _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), volume.getId()); + VmDiskStatisticsVO previousVmDiskStats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), volume.getId()); VmDiskStatisticsVO vmDiskStat_lock = _vmDiskStatsDao.lock(userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), volume.getId()); if ((vmDiskStat.getIORead() == 0) && (vmDiskStat.getIOWrite() == 0) && (vmDiskStat.getBytesRead() == 0) && (vmDiskStat.getBytesWrite() == 0)) { @@ -3477,52 +3468,50 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } if (vmDiskStat_lock == null) { - s_logger.warn("unable to find vm disk stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId() + - " and volumeId:" + volume.getId()); + s_logger.warn("unable to find vm disk stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId() + " and volumeId:" + + volume.getId()); continue; } - if (previousVmDiskStats != null && - ((previousVmDiskStats.getCurrentIORead() != vmDiskStat_lock.getCurrentIORead()) || ((previousVmDiskStats.getCurrentIOWrite() != vmDiskStat_lock.getCurrentIOWrite()) || - (previousVmDiskStats.getCurrentBytesRead() != vmDiskStat_lock.getCurrentBytesRead()) || (previousVmDiskStats.getCurrentBytesWrite() != vmDiskStat_lock.getCurrentBytesWrite())))) { - s_logger.debug("vm disk stats changed from the time GetVmDiskStatsCommand was sent. " + "Ignoring current answer. Host: " + - host.getName() + " . VM: " + vmDiskStat.getVmName() + " IO Read: " + vmDiskStat.getIORead() + " IO Write: " + - vmDiskStat.getIOWrite() + " Bytes Read: " + vmDiskStat.getBytesRead() + " Bytes Write: " + vmDiskStat.getBytesWrite()); + if (previousVmDiskStats != null + && ((previousVmDiskStats.getCurrentIORead() != vmDiskStat_lock.getCurrentIORead()) || ((previousVmDiskStats.getCurrentIOWrite() != vmDiskStat_lock + .getCurrentIOWrite()) || (previousVmDiskStats.getCurrentBytesRead() != vmDiskStat_lock.getCurrentBytesRead()) || (previousVmDiskStats + .getCurrentBytesWrite() != vmDiskStat_lock.getCurrentBytesWrite())))) { + s_logger.debug("vm disk stats changed from the time GetVmDiskStatsCommand was sent. " + "Ignoring current answer. Host: " + host.getName() + + " . VM: " + vmDiskStat.getVmName() + " IO Read: " + vmDiskStat.getIORead() + " IO Write: " + vmDiskStat.getIOWrite() + " Bytes Read: " + + vmDiskStat.getBytesRead() + " Bytes Write: " + vmDiskStat.getBytesWrite()); continue; } if (vmDiskStat_lock.getCurrentIORead() > vmDiskStat.getIORead()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Read # of IO that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + - host.getName() + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getIORead() + " Stored: " + - vmDiskStat_lock.getCurrentIORead()); + s_logger.debug("Read # of IO that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + host.getName() + + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getIORead() + " Stored: " + vmDiskStat_lock.getCurrentIORead()); } vmDiskStat_lock.setNetIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead()); } vmDiskStat_lock.setCurrentIORead(vmDiskStat.getIORead()); if (vmDiskStat_lock.getCurrentIOWrite() > vmDiskStat.getIOWrite()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Write # of IO that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + - host.getName() + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getIOWrite() + " Stored: " + - vmDiskStat_lock.getCurrentIOWrite()); + s_logger.debug("Write # of IO that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + host.getName() + + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getIOWrite() + " Stored: " + vmDiskStat_lock.getCurrentIOWrite()); } vmDiskStat_lock.setNetIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite()); } vmDiskStat_lock.setCurrentIOWrite(vmDiskStat.getIOWrite()); if (vmDiskStat_lock.getCurrentBytesRead() > vmDiskStat.getBytesRead()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Read # of Bytes that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + - host.getName() + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getBytesRead() + " Stored: " + - vmDiskStat_lock.getCurrentBytesRead()); + s_logger.debug("Read # of Bytes that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + host.getName() + + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getBytesRead() + " Stored: " + vmDiskStat_lock.getCurrentBytesRead()); } vmDiskStat_lock.setNetBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead()); } vmDiskStat_lock.setCurrentBytesRead(vmDiskStat.getBytesRead()); if (vmDiskStat_lock.getCurrentBytesWrite() > vmDiskStat.getBytesWrite()) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Write # of Bytes that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + - host.getName() + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getBytesWrite() + " Stored: " + - vmDiskStat_lock.getCurrentBytesWrite()); + s_logger.debug("Write # of Bytes that's less than the last one. " + "Assuming something went wrong and persisting it. Host: " + host.getName() + + " . VM: " + vmDiskStat.getVmName() + " Reported: " + vmDiskStat.getBytesWrite() + " Stored: " + + vmDiskStat_lock.getCurrentBytesWrite()); } vmDiskStat_lock.setNetBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite()); } @@ -3586,8 +3575,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } @Override - public Pair, Integer> searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, List permittedAccounts, - boolean listAll, ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags) { + public Pair, Integer> searchForUserVMs(Criteria c, Account caller, Long domainId, boolean isRecursive, List permittedAccounts, boolean listAll, + ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags) { Filter searchFilter = new Filter(UserVmJoinVO.class, c.getOrderBy(), c.getAscending(), c.getOffset(), c.getLimit()); //first search distinct vm id by using query criteria and pagination @@ -3777,7 +3766,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, - StorageUnavailableException, ResourceAllocationException { + StorageUnavailableException, ResourceAllocationException { // TODO Auto-generated method stub return null; } @@ -3820,8 +3809,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir HypervisorType destHypervisorType = _clusterDao.findById(destPool.getClusterId()).getHypervisorType(); if (vm.getHypervisorType() != destHypervisorType) { - throw new InvalidParameterValueException("hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + - vm.getHypervisorType().toString()); + throw new InvalidParameterValueException("hypervisor is not compatible: dest: " + destHypervisorType.toString() + ", vm: " + vm.getHypervisorType().toString()); } _itMgr.storageMigration(vm.getUuid(), destPool); return _vmDao.findById(vm.getId()); @@ -3848,8 +3836,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) - public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, - ManagementServerException, VirtualMachineMigrationException { + public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, + VirtualMachineMigrationException { // access check - only root admin can migrate VM Account caller = CallContext.current().getCallingAccount(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { @@ -3872,18 +3860,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ex.addProxyObject(vm.getUuid(), "vmId"); throw ex; } - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) - && !vm.getHypervisorType().equals(HypervisorType.VMware) - && !vm.getHypervisorType().equals(HypervisorType.KVM) - && !vm.getHypervisorType().equals(HypervisorType.Ovm) - && !vm.getHypervisorType().equals(HypervisorType.Hyperv) - && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) + && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv) + && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { if (s_logger.isDebugEnabled()) { - s_logger.debug(vm - + " is not XenServer/VMware/KVM/Ovm/Hyperv, cannot migrate this VM."); + s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv, cannot migrate this VM."); } - throw new InvalidParameterValueException( - "Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv only"); + throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv only"); } if (isVMUsingLocalStorage(vm)) { @@ -3901,8 +3884,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // check if host is UP if (destinationHost.getState() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { - throw new InvalidParameterValueException("Cannot migrate VM, destination host is not in correct state, has status: " + destinationHost.getState() + - ", state: " + destinationHost.getResourceState()); + throw new InvalidParameterValueException("Cannot migrate VM, destination host is not in correct state, has status: " + destinationHost.getState() + ", state: " + + destinationHost.getResourceState()); } if (vm.getType() != VirtualMachine.Type.User) { @@ -3928,11 +3911,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() + - " already has max Running VMs(count includes system VMs), cannot migrate to this host"); + s_logger.debug("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() + + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); } - throw new VirtualMachineMigrationException("Destination host, hostId: " + destinationHost.getId() + - " already has max Running VMs(count includes system VMs), cannot migrate to this host"); + throw new VirtualMachineMigrationException("Destination host, hostId: " + destinationHost.getId() + + " already has max Running VMs(count includes system VMs), cannot migrate to this host"); } UserVmVO uservm = _vmDao.findById(vmId); @@ -4012,16 +3995,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //if hosts are dedicated to different account/domains, raise an alert if (srcExplDedicated && destExplDedicated) { if ((accountOfDedicatedHost(srcHost) != null) && (accountOfDedicatedHost(srcHost) != accountOfDedicatedHost(destHost))) { - String msg = - "VM is being migrated from host " + srcHost.getName() + " explicitly dedicated to account " + accountOfDedicatedHost(srcHost) + " to host " + - destHost.getName() + " explicitly dedicated to account " + accountOfDedicatedHost(destHost); + String msg = "VM is being migrated from host " + srcHost.getName() + " explicitly dedicated to account " + accountOfDedicatedHost(srcHost) + " to host " + + destHost.getName() + " explicitly dedicated to account " + accountOfDedicatedHost(destHost); _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); s_logger.warn(msg); } if ((domainOfDedicatedHost(srcHost) != null) && (domainOfDedicatedHost(srcHost) != domainOfDedicatedHost(destHost))) { - String msg = - "VM is being migrated from host " + srcHost.getName() + " explicitly dedicated to domain " + domainOfDedicatedHost(srcHost) + " to host " + - destHost.getName() + " explicitly dedicated to domain " + domainOfDedicatedHost(destHost); + String msg = "VM is being migrated from host " + srcHost.getName() + " explicitly dedicated to domain " + domainOfDedicatedHost(srcHost) + " to host " + + destHost.getName() + " explicitly dedicated to domain " + domainOfDedicatedHost(destHost); _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); s_logger.warn(msg); } @@ -4045,9 +4026,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (!isServiceOfferingUsingPlannerInPreferredMode(vm.getServiceOfferingId())) { //Check if all vms on destination host are created using strict implicit mode if (!checkIfAllVmsCreatedInStrictMode(accountOfVm, vmsOnDest)) { - msg = - "VM of account " + accountOfVm + " with strict implicit deployment planner being migrated to host " + destHost.getName() + - " not having all vms strict implicitly dedicated to account " + accountOfVm; + msg = "VM of account " + accountOfVm + " with strict implicit deployment planner being migrated to host " + destHost.getName() + + " not having all vms strict implicitly dedicated to account " + accountOfVm; } } else { //If vm is deployed using preferred implicit planner, check if all vms on destination host must be @@ -4055,9 +4035,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir for (VMInstanceVO vmsDest : vmsOnDest) { ServiceOfferingVO destPlanner = _offeringDao.findById(vm.getId(), vmsDest.getServiceOfferingId()); if (!((destPlanner.getDeploymentPlanner() != null && destPlanner.getDeploymentPlanner().equals("ImplicitDedicationPlanner")) && vmsDest.getAccountId() == accountOfVm)) { - msg = - "VM of account " + accountOfVm + " with preffered implicit deployment planner being migrated to host " + destHost.getName() + - " not having all vms implicitly dedicated to account " + accountOfVm; + msg = "VM of account " + accountOfVm + " with preffered implicit deployment planner being migrated to host " + destHost.getName() + + " not having all vms implicitly dedicated to account " + accountOfVm; } } } @@ -4135,8 +4114,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir createdByImplicitStrict = false; break; } else if (isServiceOfferingUsingPlannerInPreferredMode(vm.getServiceOfferingId()) || vm.getAccountId() != accountId) { - s_logger.info("Host " + vm.getHostId() + " found to be running a vm created by an implicit planner" + - " in preferred mode, or running vms of other account"); + s_logger.info("Host " + vm.getHostId() + " found to be running a vm created by an implicit planner" + " in preferred mode, or running vms of other account"); createdByImplicitStrict = false; break; } @@ -4164,7 +4142,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map volumeToPool) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { // Access check - only root administrator can migrate VM. Account caller = CallContext.current().getCallingAccount(); if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { @@ -4188,9 +4166,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw ex; } - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && - !vm.getHypervisorType().equals(HypervisorType.KVM) && !vm.getHypervisorType().equals(HypervisorType.Ovm) && - !vm.getHypervisorType().equals(HypervisorType.Simulator)) { + if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) + && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" + " XenServer/VMware/KVM only"); } @@ -4198,27 +4175,25 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Host srcHost = _resourceMgr.getHost(srcHostId); // Check if src and destination hosts are valid and migrating to same host if (destinationHost.getId() == srcHostId) { - throw new InvalidParameterValueException("Cannot migrate VM, VM is already present on this host, please" - + " specify valid destination host to migrate the VM"); + throw new InvalidParameterValueException("Cannot migrate VM, VM is already present on this host, please" + " specify valid destination host to migrate the VM"); } // Check if the source and destination hosts are of the same type and support storage motion. if (!(srcHost.getHypervisorType().equals(destinationHost.getHypervisorType()) && srcHost.getHypervisorVersion().equals(destinationHost.getHypervisorVersion()))) { - throw new CloudRuntimeException("The source and destination hosts are not of the same type and version. " + "Source hypervisor type and version: " + - srcHost.getHypervisorType().toString() + " " + srcHost.getHypervisorVersion() + ", Destination hypervisor type and version: " + - destinationHost.getHypervisorType().toString() + " " + destinationHost.getHypervisorVersion()); + throw new CloudRuntimeException("The source and destination hosts are not of the same type and version. " + "Source hypervisor type and version: " + + srcHost.getHypervisorType().toString() + " " + srcHost.getHypervisorVersion() + ", Destination hypervisor type and version: " + + destinationHost.getHypervisorType().toString() + " " + destinationHost.getHypervisorVersion()); } HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(srcHost.getHypervisorType(), srcHost.getHypervisorVersion()); if (!capabilities.isStorageMotionSupported()) { - throw new CloudRuntimeException("Migration with storage isn't supported on hypervisor " + srcHost.getHypervisorType() + " of version " + - srcHost.getHypervisorVersion()); + throw new CloudRuntimeException("Migration with storage isn't supported on hypervisor " + srcHost.getHypervisorType() + " of version " + srcHost.getHypervisorVersion()); } // Check if destination host is up. if (destinationHost.getState() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { - throw new CloudRuntimeException("Cannot migrate VM, destination host is not in correct state, has " + "status: " + destinationHost.getState() + ", state: " + - destinationHost.getResourceState()); + throw new CloudRuntimeException("Cannot migrate VM, destination host is not in correct state, has " + "status: " + destinationHost.getState() + ", state: " + + destinationHost.getResourceState()); } List vmVolumes = _volsDao.findUsableVolumesForInstance(vm.getId()); @@ -4227,8 +4202,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (volumeToPool.isEmpty()) { // If the destination host is in the same cluster and volumes do not have to be migrated across pools // then fail the call. migrateVirtualMachine api should have been used. - throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost + " to destination host " + destinationHost + - " doesn't involve migrating the volumes."); + throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost + " to destination host " + destinationHost + + " doesn't involve migrating the volumes."); } } @@ -4244,8 +4219,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } else { // Verify the volume given belongs to the vm. if (!vmVolumes.contains(volume)) { - throw new InvalidParameterValueException("There volume " + volume + " doesn't belong to " + "the virtual machine " + vm + - " that has to be migrated"); + throw new InvalidParameterValueException("There volume " + volume + " doesn't belong to " + "the virtual machine " + vm + " that has to be migrated"); } volToPoolObjectMap.put(volume, pool); } @@ -4262,8 +4236,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Check max guest vm limit for the destinationHost. HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) { - throw new VirtualMachineMigrationException("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() + - " already has max running vms (count includes system VMs). Cannot" + " migrate to this host"); + throw new VirtualMachineMigrationException("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() + + " already has max running vms (count includes system VMs). Cannot" + " migrate to this host"); } checkHostsDedication(vm, srcHostId, destinationHost.getId()); @@ -4275,8 +4249,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @DB @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MOVE, eventDescription = "move VM to another user", async = false) - public UserVm moveVMToUser(final AssignVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { + public UserVm moveVMToUser(final AssignVMCmd cmd) throws ResourceAllocationException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { // VERIFICATIONS and VALIDATIONS // VV 1: verify the two users @@ -4364,7 +4337,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir removeInstanceFromInstanceGroup(cmd.getVmId()); // VV 2: check if account/domain is with in resource limits to create a new vm - resourceLimitCheck(newAccount, new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceLimitCheck(newAccount, vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize())); // VV 3: check if volumes and primary storage space are with in resource limits _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, _volsDao.findByInstance(cmd.getVmId()).size()); @@ -4389,11 +4362,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public void doInTransactionWithoutResult(TransactionStatus status) { //generate destroy vm event for usage - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), - vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), + vm.getTemplateId(), vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); // update resource counts for old account - resourceCountDecrement(oldAccount.getAccountId(), new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountDecrement(oldAccount.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize())); // OWNERSHIP STEP 1: update the vm owner vm.setAccountId(newAccount.getAccountId()); @@ -4403,7 +4376,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // OS 2: update volume for (VolumeVO volume : volumes) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - Volume.class.getName(), volume.getUuid()); + Volume.class.getName(), volume.getUuid()); _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.volume); _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize())); volume.setAccountId(newAccount.getAccountId()); @@ -4412,7 +4385,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.volume); _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize())); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); + volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); //snapshots: mark these removed in db List snapshots = _snapshotDao.listByVolumeIdIncludingRemoved(volume.getId()); for (SnapshotVO snapshot : snapshots) { @@ -4421,11 +4394,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } //update resource count of new account - resourceCountIncrement(newAccount.getAccountId(), new Long(offering.getCpu()), new Long(offering.getRamSize())); + resourceCountIncrement(newAccount.getAccountId(), vm.isDisplayVm(), new Long(offering.getCpu()), new Long(offering.getRamSize())); //generate usage events to account for this change - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), - vm.getServiceOfferingId(), vm.getTemplateId(), vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), + vm.getTemplateId(), vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid()); } }); @@ -4487,8 +4460,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (s_logger.isDebugEnabled()) { s_logger.debug("Couldn't find default security group for the account " + newAccount + " so creating a new one"); } - defaultGroup = - _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, + defaultGroup = _securityGroupMgr.createSecurityGroup(SecurityGroupManager.DEFAULT_GROUP_NAME, SecurityGroupManager.DEFAULT_GROUP_DESCRIPTION, newAccount.getDomainId(), newAccount.getId(), newAccount.getAccountName()); securityGroupIdList.add(defaultGroup.getId()); } @@ -4534,8 +4506,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // don't allow to use system networks NetworkOffering networkOffering = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (networkOffering.isSystemOnly()) { - InvalidParameterValueException ex = - new InvalidParameterValueException("Specified Network id is system only and can't be used for vm deployment"); + InvalidParameterValueException ex = new InvalidParameterValueException("Specified Network id is system only and can't be used for vm deployment"); ex.addProxyObject(network.getUuid(), "networkId"); throw ex; } @@ -4545,27 +4516,26 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir NetworkVO defaultNetwork = null; List requiredOfferings = _networkOfferingDao.listByAvailability(Availability.Required, false); if (requiredOfferings.size() < 1) { - throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required + - " to automatically create the network as a part of vm creation"); + throw new InvalidParameterValueException("Unable to find network offering with availability=" + Availability.Required + + " to automatically create the network as a part of vm creation"); } if (requiredOfferings.get(0).getState() == NetworkOffering.State.Enabled) { // get Virtual networks List virtualNetworks = _networkModel.listNetworksForAccount(newAccount.getId(), zone.getId(), Network.GuestType.Isolated); if (virtualNetworks.isEmpty()) { - long physicalNetworkId = - _networkModel.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0).getTrafficType()); + long physicalNetworkId = _networkModel.findPhysicalNetworkId(zone.getId(), requiredOfferings.get(0).getTags(), requiredOfferings.get(0) + .getTrafficType()); // Validate physical network PhysicalNetwork physicalNetwork = _physicalNetworkDao.findById(physicalNetworkId); if (physicalNetwork == null) { - throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + - requiredOfferings.get(0).getTags()); + throw new InvalidParameterValueException("Unable to find physical network with id: " + physicalNetworkId + " and tag: " + + requiredOfferings.get(0).getTags()); } - s_logger.debug("Creating network for account " + newAccount + " from the network offering id=" + requiredOfferings.get(0).getId() + - " as a part of deployVM process"); - Network newNetwork = - _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", newAccount.getAccountName() + - "-network", null, null, null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, - null); + s_logger.debug("Creating network for account " + newAccount + " from the network offering id=" + requiredOfferings.get(0).getId() + + " as a part of deployVM process"); + Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", + newAccount.getAccountName() + "-network", null, null, null, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, + null, null, true, null); // if the network offering has persistent set to true, implement the network if (requiredOfferings.get(0).getIsPersistent()) { DeployDestination dest = new DeployDestination(zone, null, null, null); @@ -4580,10 +4550,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } newNetwork = implementedNetwork.second(); } catch (Exception ex) { - s_logger.warn("Failed to implement network " + newNetwork + " elements and" + - " resources as a part of network provision for persistent network due to ", ex); - CloudRuntimeException e = - new CloudRuntimeException("Failed to implement network" + s_logger.warn("Failed to implement network " + newNetwork + " elements and" + + " resources as a part of network provision for persistent network due to ", ex); + CloudRuntimeException e = new CloudRuntimeException("Failed to implement network" + " (with specified id) elements and resources as a part of network provision"); e.addProxyObject(newNetwork.getUuid(), "networkId"); throw e; @@ -4591,14 +4560,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } defaultNetwork = _networkDao.findById(newNetwork.getId()); } else if (virtualNetworks.size() > 1) { - throw new InvalidParameterValueException("More than 1 default Isolated networks are found " + "for account " + newAccount + - "; please specify networkIds"); + throw new InvalidParameterValueException("More than 1 default Isolated networks are found " + "for account " + newAccount + + "; please specify networkIds"); } else { defaultNetwork = _networkDao.findById(virtualNetworks.get(0).getId()); } } else { - throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + - NetworkOffering.State.Enabled); + throw new InvalidParameterValueException("Required network offering id=" + requiredOfferings.get(0).getId() + " is not in " + NetworkOffering.State.Enabled); } applicableNetworks.add(defaultNetwork); diff --git a/server/src/com/cloud/vm/UserVmStateListener.java b/server/src/com/cloud/vm/UserVmStateListener.java index a51b5ef6973..2f6be6cbbcf 100644 --- a/server/src/com/cloud/vm/UserVmStateListener.java +++ b/server/src/com/cloud/vm/UserVmStateListener.java @@ -24,6 +24,7 @@ import java.util.Map; import javax.inject.Inject; +import com.cloud.server.ManagementService; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -32,10 +33,12 @@ import org.apache.cloudstack.framework.events.EventBus; import com.cloud.event.EventCategory; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; +import com.cloud.event.UsageEventVO; import com.cloud.event.dao.UsageEventDao; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; -import com.cloud.server.ManagementService; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.fsm.StateListener; import com.cloud.vm.VirtualMachine.Event; @@ -44,20 +47,19 @@ import com.cloud.vm.dao.NicDao; public class UserVmStateListener implements StateListener { - @Inject - protected UsageEventDao _usageEventDao; - @Inject - protected NetworkDao _networkDao; - @Inject - protected NicDao _nicDao; + @Inject protected UsageEventDao _usageEventDao; + @Inject protected NetworkDao _networkDao; + @Inject protected NicDao _nicDao; + @Inject protected ServiceOfferingDao _offeringDao; private static final Logger s_logger = Logger.getLogger(UserVmStateListener.class); protected static EventBus s_eventBus = null; - public UserVmStateListener(UsageEventDao usageEventDao, NetworkDao networkDao, NicDao nicDao) { - _usageEventDao = usageEventDao; - _networkDao = networkDao; - _nicDao = nicDao; + public UserVmStateListener(UsageEventDao usageEventDao, NetworkDao networkDao, NicDao nicDao, ServiceOfferingDao offeringDao) { + this._usageEventDao = usageEventDao; + this._networkDao = networkDao; + this._nicDao = nicDao; + this._offeringDao = offeringDao; } @Override @@ -79,14 +81,11 @@ public class UserVmStateListener implements StateListener nics = _nicDao.listByVmId(vo.getId()); for (NicVO nic : nics) { NetworkVO network = _networkDao.findById(nic.getNetworkId()); @@ -94,12 +93,29 @@ public class UserVmStateListener implements StateListener customParameters = new HashMap(); + customParameters.put(UsageEventVO.DynamicParameters.cpuNumber.name(), serviceOffering.getCpu().toString()); + customParameters.put(UsageEventVO.DynamicParameters.cpuSpeed.name(), serviceOffering.getSpeed().toString()); + customParameters.put(UsageEventVO.DynamicParameters.memory.name(), serviceOffering.getRamSize().toString()); + UsageEventUtils.publishUsageEvent(eventType, vm.getAccountId(), vm.getDataCenterId(), vm.getId(), + vm.getHostName(), serviceOffering.getId(), vm.getTemplateId(), vm.getHypervisorType().toString(), + VirtualMachine.class.getName(), vm.getUuid(), customParameters); + } + } + private void pubishOnEventBus(String event, String status, VirtualMachine vo, VirtualMachine.State oldState, VirtualMachine.State newState) { try { diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java index ece75be5750..7d6e0ec71c1 100644 --- a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java +++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java @@ -35,7 +35,16 @@ import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.StorageStrategyFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotOptions; import org.apache.cloudstack.engine.subsystem.api.storage.VMSnapshotStrategy; +import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.AsyncJob; +import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.Outcome; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; +import org.apache.cloudstack.framework.jobs.impl.OutcomeImpl; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; +import org.apache.cloudstack.jobs.JobInfo; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; @@ -55,15 +64,22 @@ import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; +import com.cloud.user.User; import com.cloud.user.dao.AccountDao; import com.cloud.uservm.UserVm; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; +import com.cloud.utils.Predicate; import com.cloud.utils.Ternary; import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.EntityManager; import com.cloud.utils.db.Filter; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; @@ -71,40 +87,56 @@ import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VmWork; +import com.cloud.vm.VmWorkConstants; +import com.cloud.vm.VmWorkJobHandler; +import com.cloud.vm.VmWorkJobHandlerProxy; +import com.cloud.vm.VmWorkSerializer; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component -@Local(value = {VMSnapshotManager.class, VMSnapshotService.class}) -public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotManager, VMSnapshotService { +@Local(value = { VMSnapshotManager.class, VMSnapshotService.class }) +public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotManager, VMSnapshotService, VmWorkJobHandler { private static final Logger s_logger = Logger.getLogger(VMSnapshotManagerImpl.class); + + public static final String VM_WORK_JOB_HANDLER = VMSnapshotManagerImpl.class.getSimpleName(); + String _name; @Inject - VMSnapshotDao _vmSnapshotDao; - @Inject - VolumeDao _volumeDao; - @Inject - AccountDao _accountDao; - @Inject - UserVmDao _userVMDao; - @Inject - AccountManager _accountMgr; - @Inject - GuestOSDao _guestOSDao; - @Inject - SnapshotDao _snapshotDao; - @Inject - VirtualMachineManager _itMgr; - @Inject - ConfigurationDao _configDao; - @Inject - HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; + VMInstanceDao _vmInstanceDao; + @Inject VMSnapshotDao _vmSnapshotDao; + @Inject VolumeDao _volumeDao; + @Inject AccountDao _accountDao; + @Inject UserVmDao _userVMDao; + @Inject AccountManager _accountMgr; + @Inject GuestOSDao _guestOSDao; + @Inject SnapshotDao _snapshotDao; + @Inject VirtualMachineManager _itMgr; + @Inject ConfigurationDao _configDao; + @Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; @Inject StorageStrategyFactory storageStrategyFactory; + @Inject + EntityManager _entityMgr; + @Inject + AsyncJobManager _jobMgr; + + VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); + int _vmSnapshotMax; int _wait; + // TODO + static final ConfigKey VmJobEnabled = new ConfigKey("Advanced", + Boolean.class, "vm.job.enabled", "false", + "True to enable new VM sync model. false to use the old way", false); + static final ConfigKey VmJobCheckInterval = new ConfigKey("Advanced", + Long.class, "vm.job.check.interval", "3000", + "Interval in milliseconds to check if the job is complete", false); + @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; @@ -249,7 +281,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana throw new InvalidParameterValueException("Creating vm snapshot failed due to VM:" + vmId + " is not in the running or Stopped state"); } - if (snapshotMemory && userVmVo.getState() == VirtualMachine.State.Stopped) { + if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Stopped){ throw new InvalidParameterValueException("Can not snapshot memory when VM is in stopped state"); } @@ -327,6 +359,46 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana if (vmSnapshot == null) { throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found"); } + + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateCreateVMSnapshot(vmId, vmSnapshotId, quiescevm); + } else { + Outcome outcome = createVMSnapshotThroughJobQueue(vmId, vmSnapshotId, quiescevm); + + VMSnapshot result = null; + try { + result = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + return result; + } + } + + private VMSnapshot orchestrateCreateVMSnapshot(Long vmId, Long vmSnapshotId, Boolean quiescevm) { + UserVmVO userVm = _userVMDao.findById(vmId); + if (userVm == null) { + throw new InvalidParameterValueException("Create vm to snapshot failed due to vm: " + vmId + " is not found"); + } + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId); + if (vmSnapshot == null) { + throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found"); + } + VMSnapshotOptions options = new VMSnapshotOptions(quiescevm); vmSnapshot.setOptions(options); try { @@ -376,6 +448,62 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); } + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateDeleteVMSnapshot(vmSnapshotId); + } else { + Outcome outcome = deleteVMSnapshotThroughJobQueue(vmSnapshot.getVmId(), vmSnapshotId); + + VMSnapshot result = null; + try { + result = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + if (jobResult instanceof Boolean) + return ((Boolean)jobResult).booleanValue(); + + return false; + } + } + + private boolean orchestrateDeleteVMSnapshot(Long vmSnapshotId) { + Account caller = getCaller(); + + VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId); + if (vmSnapshot == null) { + throw new InvalidParameterValueException("unable to find the vm snapshot with id " + vmSnapshotId); + } + + _accountMgr.checkAccess(caller, null, true, vmSnapshot); + + // check VM snapshot states, only allow to delete vm snapshots in created and error state + if (VMSnapshot.State.Ready != vmSnapshot.getState() && VMSnapshot.State.Expunging != vmSnapshot.getState() && VMSnapshot.State.Error != vmSnapshot.getState()) { + throw new InvalidParameterValueException("Can't delete the vm snapshotshot " + vmSnapshotId + " due to it is not in Created or Error, or Expunging State"); + } + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(vmSnapshot.getVmId())) { + List expungingSnapshots = _vmSnapshotDao.listByInstanceId(vmSnapshot.getVmId(), VMSnapshot.State.Expunging); + if (expungingSnapshots.size() > 0 && expungingSnapshots.get(0).getId() == vmSnapshot.getId()) + s_logger.debug("Target VM snapshot already in expunging state, go on deleting it: " + vmSnapshot.getDisplayName()); + else + throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); + } + if (vmSnapshot.getState() == VMSnapshot.State.Allocated) { return _vmSnapshotDao.remove(vmSnapshot.getId()); } else { @@ -413,6 +541,77 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana Account caller = getCaller(); _accountMgr.checkAccess(caller, null, true, vmSnapshotVo); + // VM should be in running or stopped states + if (userVm.getState() != VirtualMachine.State.Running + && userVm.getState() != VirtualMachine.State.Stopped) { + throw new InvalidParameterValueException( + "VM Snapshot reverting failed due to vm is not in the state of Running or Stopped."); + } + + // if snapshot is not created, error out + if (vmSnapshotVo.getState() != VMSnapshot.State.Ready) { + throw new InvalidParameterValueException( + "VM Snapshot reverting failed due to vm snapshot is not in the state of Created."); + } + + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateRevertToVMSnapshot(vmSnapshotId); + } else { + Outcome outcome = revertToVMSnapshotThroughJobQueue(vmSnapshotVo.getVmId(), vmSnapshotId); + + VMSnapshot result = null; + try { + result = outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof InsufficientCapacityException) + throw (InsufficientCapacityException)jobResult; + else if (jobResult instanceof ResourceUnavailableException) + throw (ResourceUnavailableException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + return userVm; + } + } + + private UserVm orchestrateRevertToVMSnapshot(Long vmSnapshotId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException { + + // check if VM snapshot exists in DB + VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId); + if (vmSnapshotVo == null) { + throw new InvalidParameterValueException( + "unable to find the vm snapshot with id " + vmSnapshotId); + } + Long vmId = vmSnapshotVo.getVmId(); + UserVmVO userVm = _userVMDao.findById(vmId); + // check if VM exists + if (userVm == null) { + throw new InvalidParameterValueException("Revert vm to snapshot: " + + vmSnapshotId + " failed due to vm: " + vmId + + " is not found"); + } + + // check if there are other active VM snapshot tasks + if (hasActiveVMSnapshotTasks(vmId)) { + throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later"); + } + + Account caller = getCaller(); + _accountMgr.checkAccess(caller, null, true, vmSnapshotVo); + // VM should be in running or stopped states if (userVm.getState() != VirtualMachine.State.Running && userVm.getState() != VirtualMachine.State.Stopped) { throw new InvalidParameterValueException("VM Snapshot reverting failed due to vm is not in the state of Running or Stopped."); @@ -429,7 +628,7 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana // start or stop VM first, if revert from stopped state to running state, or from running to stopped if (userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory) { try { - _itMgr.advanceStart(userVm.getUuid(), new HashMap()); + _itMgr.advanceStart(userVm.getUuid(), new HashMap(), null); vm = _userVMDao.findById(userVm.getId()); hostId = vm.getHostId(); } catch (Exception e) { @@ -481,6 +680,38 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana @Override public boolean deleteAllVMSnapshots(long vmId, VMSnapshot.Type type) { + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + if (!VmJobEnabled.value() || jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + return orchestrateDeleteAllVMSnapshots(vmId, type); + } else { + Outcome outcome = deleteAllVMSnapshotsThroughJobQueue(vmId, type); + + try { + outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation is interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution excetion", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) + throw (ConcurrentOperationException)jobResult; + else if (jobResult instanceof Throwable) + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } + + if (jobResult instanceof Boolean) + return (Boolean)jobResult; + + return false; + } + } + + private boolean orchestrateDeleteAllVMSnapshots(long vmId, VMSnapshot.Type type) { boolean result = true; List listVmSnapshots = _vmSnapshotDao.findByVm(vmId); if (listVmSnapshots == null || listVmSnapshots.isEmpty()) { @@ -501,14 +732,13 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana @Override public boolean syncVMSnapshot(VMInstanceVO vm, Long hostId) { - try { + try{ UserVmVO userVm = _userVMDao.findById(vm.getId()); if (userVm == null) return false; - List vmSnapshotsInExpungingStates = - _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Reverting, VMSnapshot.State.Creating); + List vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Reverting, VMSnapshot.State.Creating); for (VMSnapshotVO vmSnapshotVO : vmSnapshotsInExpungingStates) { VMSnapshotStrategy strategy = findVMSnapshotStrategy(vmSnapshotVO); if (vmSnapshotVO.getState() == VMSnapshot.State.Expunging) { @@ -529,4 +759,254 @@ public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotMana return false; } + public class VmJobVMSnapshotOutcome extends OutcomeImpl { + private long _vmSnapshotId; + + public VmJobVMSnapshotOutcome(final AsyncJob job, final long vmSnapshotId) { + super(VMSnapshot.class, job, VmJobCheckInterval.value(), new Predicate() { + @Override + public boolean checkCondition() { + AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId()); + assert (jobVo != null); + if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) + return true; + + return false; + } + }, AsyncJob.Topics.JOB_STATE); + _vmSnapshotId = vmSnapshotId; + } + + @Override + protected VMSnapshot retrieve() { + return _vmSnapshotDao.findById(_vmSnapshotId); + } + } + + public class VmJobVirtualMachineOutcome extends OutcomeImpl { + long vmId; + + public VmJobVirtualMachineOutcome(final AsyncJob job, final long vmId) { + super(VirtualMachine.class, job, VmJobCheckInterval.value(), new Predicate() { + @Override + public boolean checkCondition() { + AsyncJobVO jobVo = _entityMgr.findById(AsyncJobVO.class, job.getId()); + assert (jobVo != null); + if (jobVo == null || jobVo.getStatus() != JobInfo.Status.IN_PROGRESS) + return true; + + return false; + } + }, AsyncJob.Topics.JOB_STATE); + } + + @Override + protected VirtualMachine retrieve() { + return _vmInstanceDao.findById(vmId); + } + } + + public Outcome createVMSnapshotThroughJobQueue(final Long vmId, final Long vmSnapshotId, final boolean quiesceVm) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkCreateVMSnapshot.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkCreateVMSnapshot workInfo = new VmWorkCreateVMSnapshot(callingUser.getId(), callingAccount.getId(), vm.getId(), + VMSnapshotManagerImpl.VM_WORK_JOB_HANDLER, vmSnapshotId, quiesceVm); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVMSnapshotOutcome((VmWorkJobVO)result[0], + vmSnapshotId); + } + + public Outcome deleteVMSnapshotThroughJobQueue(final Long vmId, final Long vmSnapshotId) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkDeleteVMSnapshot.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkDeleteVMSnapshot workInfo = new VmWorkDeleteVMSnapshot(callingUser.getId(), callingAccount.getId(), vm.getId(), + VMSnapshotManagerImpl.VM_WORK_JOB_HANDLER, vmSnapshotId); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVMSnapshotOutcome((VmWorkJobVO)result[0], + vmSnapshotId); + } + + public Outcome revertToVMSnapshotThroughJobQueue(final Long vmId, final Long vmSnapshotId) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkRevertToVMSnapshot.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkRevertToVMSnapshot workInfo = new VmWorkRevertToVMSnapshot(callingUser.getId(), callingAccount.getId(), vm.getId(), + VMSnapshotManagerImpl.VM_WORK_JOB_HANDLER, vmSnapshotId); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVMSnapshotOutcome((VmWorkJobVO)result[0], + vmSnapshotId); + } + + public Outcome deleteAllVMSnapshotsThroughJobQueue(final Long vmId, final VMSnapshot.Type type) { + + final CallContext context = CallContext.current(); + final User callingUser = context.getCallingUser(); + final Account callingAccount = context.getCallingAccount(); + + final VMInstanceVO vm = _vmInstanceDao.findById(vmId); + + Object[] result = Transaction.execute(new TransactionCallback() { + @Override + public Object[] doInTransaction(TransactionStatus status) { + VmWorkJobVO workJob = null; + + _vmInstanceDao.lockRow(vm.getId(), true); + workJob = new VmWorkJobVO(context.getContextId()); + + workJob.setDispatcher(VmWorkConstants.VM_WORK_JOB_DISPATCHER); + workJob.setCmd(VmWorkDeleteAllVMSnapshots.class.getName()); + + workJob.setAccountId(callingAccount.getId()); + workJob.setUserId(callingUser.getId()); + workJob.setStep(VmWorkJobVO.Step.Starting); + workJob.setVmType(vm.getType()); + workJob.setVmInstanceId(vm.getId()); + workJob.setRelated(AsyncJobExecutionContext.getOriginJobContextId()); + + // save work context info (there are some duplications) + VmWorkDeleteAllVMSnapshots workInfo = new VmWorkDeleteAllVMSnapshots(callingUser.getId(), callingAccount.getId(), vm.getId(), + VMSnapshotManagerImpl.VM_WORK_JOB_HANDLER, type); + workJob.setCmdInfo(VmWorkSerializer.serialize(workInfo)); + + _jobMgr.submitAsyncJob(workJob, VmWorkConstants.VM_WORK_QUEUE, vm.getId()); + + return new Object[] {workJob, new Long(workJob.getId())}; + } + }); + + final long jobId = (Long)result[1]; + AsyncJobExecutionContext.getCurrentExecutionContext().joinJob(jobId); + + return new VmJobVirtualMachineOutcome((VmWorkJobVO)result[0], + vmId); + } + + public Pair orchestrateCreateVMSnapshot(VmWorkCreateVMSnapshot work) throws Exception { + VMSnapshot snapshot = orchestrateCreateVMSnapshot(work.getVmId(), work.getVmSnapshotId(), work.isQuiesceVm()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Long(snapshot.getId()))); + } + + public Pair orchestrateDeleteVMSnapshot(VmWorkDeleteVMSnapshot work) { + boolean result = orchestrateDeleteVMSnapshot(work.getVmSnapshotId()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Boolean(result))); + } + + public Pair orchestrateRevertToVMSnapshot(VmWorkRevertToVMSnapshot work) throws Exception { + orchestrateRevertToVMSnapshot(work.getVmSnapshotId()); + return new Pair(JobInfo.Status.SUCCEEDED, null); + } + + public Pair orchestrateDeleteAllVMSnapshots(VmWorkDeleteAllVMSnapshots work) { + boolean result = orchestrateDeleteAllVMSnapshots(work.getVmId(), work.getSnapshotType()); + return new Pair(JobInfo.Status.SUCCEEDED, + _jobMgr.marshallResultObject(new Boolean(result))); + } + + @Override + public Pair handleVmWorkJob(VmWork work) throws Exception { + return _jobHandlerProxy.handleVmWorkJob(work); + } } diff --git a/server/src/com/cloud/vm/snapshot/VmWorkCreateVMSnapshot.java b/server/src/com/cloud/vm/snapshot/VmWorkCreateVMSnapshot.java new file mode 100644 index 00000000000..33718029303 --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VmWorkCreateVMSnapshot.java @@ -0,0 +1,41 @@ +// 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.vm.snapshot; + +import com.cloud.vm.VmWork; + +public class VmWorkCreateVMSnapshot extends VmWork { + private static final long serialVersionUID = 124386202146049838L; + + private Long vmSnapshotId; + private boolean quiesceVm; + + public VmWorkCreateVMSnapshot(long userId, long accountId, long vmId, String handlerName, Long vmSnapshotId, boolean quiesceVm) { + super(userId, accountId, vmId, handlerName); + + this.vmSnapshotId = vmSnapshotId; + this.quiesceVm = quiesceVm; + } + + public Long getVmSnapshotId() { + return vmSnapshotId; + } + + public boolean isQuiesceVm() { + return quiesceVm; + } +} diff --git a/server/src/com/cloud/vm/snapshot/VmWorkDeleteAllVMSnapshots.java b/server/src/com/cloud/vm/snapshot/VmWorkDeleteAllVMSnapshots.java new file mode 100644 index 00000000000..ce20dfc984d --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VmWorkDeleteAllVMSnapshots.java @@ -0,0 +1,35 @@ +// 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.vm.snapshot; + +import com.cloud.vm.VmWork; + +public class VmWorkDeleteAllVMSnapshots extends VmWork { + private static final long serialVersionUID = -6010083039865471888L; + + private VMSnapshot.Type type; + + public VmWorkDeleteAllVMSnapshots(long userId, long accountId, long vmId, String handlerName, VMSnapshot.Type type) { + super(userId, accountId, vmId, handlerName); + + this.type = type; + } + + public VMSnapshot.Type getSnapshotType() { + return type; + } +} diff --git a/server/src/com/cloud/vm/snapshot/VmWorkDeleteVMSnapshot.java b/server/src/com/cloud/vm/snapshot/VmWorkDeleteVMSnapshot.java new file mode 100644 index 00000000000..1a80e39406d --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VmWorkDeleteVMSnapshot.java @@ -0,0 +1,35 @@ +// 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.vm.snapshot; + +import com.cloud.vm.VmWork; + +public class VmWorkDeleteVMSnapshot extends VmWork { + private static final long serialVersionUID = 7168101866614517508L; + + private Long vmSnapshotId; + + public VmWorkDeleteVMSnapshot(long userId, long accountId, long vmId, String handlerName, Long vmSnapshotId) { + super(userId, accountId, vmId, handlerName); + + this.vmSnapshotId = vmSnapshotId; + } + + public Long getVmSnapshotId() { + return vmSnapshotId; + } +} diff --git a/server/src/com/cloud/vm/snapshot/VmWorkRevertToVMSnapshot.java b/server/src/com/cloud/vm/snapshot/VmWorkRevertToVMSnapshot.java new file mode 100644 index 00000000000..f7beee5c4e5 --- /dev/null +++ b/server/src/com/cloud/vm/snapshot/VmWorkRevertToVMSnapshot.java @@ -0,0 +1,35 @@ +// 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.vm.snapshot; + +import com.cloud.vm.VmWork; + +public class VmWorkRevertToVMSnapshot extends VmWork { + private static final long serialVersionUID = -3406543280278986843L; + + private Long vmSnapshotId; + + public VmWorkRevertToVMSnapshot(long userId, long accountId, long vmId, String handlerName, Long vmSnapshotId) { + super(userId, accountId, vmId, handlerName); + + this.vmSnapshotId = vmSnapshotId; + } + + public Long getVmSnapshotId() { + return vmSnapshotId; + } +} diff --git a/server/test/com/cloud/network/MockNetworkModelImpl.java b/server/test/com/cloud/network/MockNetworkModelImpl.java index 6ae6ae8beef..6c9e597b9f3 100644 --- a/server/test/com/cloud/network/MockNetworkModelImpl.java +++ b/server/test/com/cloud/network/MockNetworkModelImpl.java @@ -92,6 +92,15 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + /* (non-Javadoc) + * @see com.cloud.network.NetworkModel#listPublicIpsAssignedToGuestNtwk(long, long, java.lang.Boolean) + */ + @Override + public List listPublicIpsAssignedToGuestNtwk(long associatedNetworkId, Boolean sourceNat) { + // TODO Auto-generated method stub + return null; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#getSystemAccountNetworkOfferings(java.lang.String[]) */ diff --git a/server/test/com/cloud/resource/MockResourceManagerImpl.java b/server/test/com/cloud/resource/MockResourceManagerImpl.java index 70f88b3bd50..5599e8c42b9 100644 --- a/server/test/com/cloud/resource/MockResourceManagerImpl.java +++ b/server/test/com/cloud/resource/MockResourceManagerImpl.java @@ -308,6 +308,15 @@ public class MockResourceManagerImpl extends ManagerBase implements ResourceMana return false; } + /* (non-Javadoc) + * @see com.cloud.resource.ResourceManager#maintain(long) + */ + @Override + public boolean checkAndMaintain(long hostId) { + // TODO Auto-generated method stub + return false; + } + /* (non-Javadoc) * @see com.cloud.resource.ResourceManager#deleteHost(long, boolean, boolean) */ diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java index 0d82ff22c15..751a3bdd43f 100644 --- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -168,7 +168,7 @@ public class DeploymentPlanningManagerImplTest { DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); Mockito.when(avoids.shouldAvoid((DataCenterVO)Matchers.anyObject())).thenReturn(true); - DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("DataCenter is in avoid set, destination should be null! ", dest); } @@ -183,7 +183,7 @@ public class DeploymentPlanningManagerImplTest { Mockito.when(avoids.shouldAvoid((DataCenterVO)Matchers.anyObject())).thenReturn(false); Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(false); - DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("Planner cannot handle, destination should be null! ", dest); } @@ -199,7 +199,7 @@ public class DeploymentPlanningManagerImplTest { Mockito.when(_planner.canHandle(vmProfile, plan, avoids)).thenReturn(true); Mockito.when(((DeploymentClusterPlanner)_planner).orderClusters(vmProfile, plan, avoids)).thenReturn(null); - DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids); + DeployDestination dest = _dpm.planDeployment(vmProfile, plan, avoids, null); assertNull("Planner cannot handle, destination should be null! ", dest); } diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 56ba66aebfc..be305dee0f4 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -194,7 +194,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches * @see com.cloud.network.NetworkService#searchForNetworks(com.cloud.api.commands.ListNetworksCmd) */ @Override - public List searchForNetworks(ListNetworksCmd cmd) { + public Pair, Integer> searchForNetworks(ListNetworksCmd cmd) { // TODO Auto-generated method stub return null; } @@ -791,7 +791,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } @Override - public NicSecondaryIp allocateSecondaryGuestIP(Account account, long zoneId, Long nicId, Long networkId, String ipaddress) { + public NicSecondaryIp allocateSecondaryGuestIP(long nicId, String ipaddress) { // TODO Auto-generated method stub return null; } @@ -803,7 +803,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches } @Override - public List listVmNics(Long vmId, Long nicId) { + public List listVmNics(long vmId, Long nicId, Long networkId) { // TODO Auto-generated method stub return null; } @@ -835,6 +835,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches //To change body of implemented methods use File | Settings | File Templates. } + @Override + public boolean resourceCountNeedsUpdate(NetworkOffering ntwkOff, ACLType aclType) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + @Override public void prepareNicForMigration(VirtualMachineProfile vm, DeployDestination dest) { // TODO Auto-generated method stub diff --git a/server/test/com/cloud/vpc/MockNetworkModelImpl.java b/server/test/com/cloud/vpc/MockNetworkModelImpl.java index e0583d8d317..67ab8e875cb 100644 --- a/server/test/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkModelImpl.java @@ -103,6 +103,16 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + /* (non-Javadoc) + * @see com.cloud.network.NetworkModel#listPublicIpsAssignedToGuestNtwk(long, long, java.lang.Boolean) + */ + @Override + public List listPublicIpsAssignedToGuestNtwk(long associatedNetworkId, Boolean sourceNat) { + + // TODO Auto-generated method stub + return null; + } + /* (non-Javadoc) * @see com.cloud.network.NetworkModel#getSystemAccountNetworkOfferings(java.lang.String[]) */ diff --git a/server/test/com/cloud/vpc/NetworkACLServiceTest.java b/server/test/com/cloud/vpc/NetworkACLServiceTest.java index 99fb77189b4..786789fec77 100644 --- a/server/test/com/cloud/vpc/NetworkACLServiceTest.java +++ b/server/test/com/cloud/vpc/NetworkACLServiceTest.java @@ -188,7 +188,9 @@ public class NetworkACLServiceTest extends TestCase { @Test public void testDeleteACLItem() throws Exception { Mockito.when(_networkACLItemDao.findById(Matchers.anyLong())).thenReturn(aclItem); + Mockito.when(_networkAclMgr.getNetworkACL(Matchers.anyLong())).thenReturn(acl); Mockito.when(_networkAclMgr.revokeNetworkACLItem(Matchers.anyLong())).thenReturn(true); + Mockito.when(_entityMgr.findById(Mockito.eq(Vpc.class), Mockito.anyLong())).thenReturn(new VpcVO()); assertTrue(_aclService.revokeNetworkACLItem(1L)); } diff --git a/server/test/resources/SecurityGroupManagerTestContext.xml b/server/test/resources/SecurityGroupManagerTestContext.xml index 7ff2976dd6b..3b9a7525948 100644 --- a/server/test/resources/SecurityGroupManagerTestContext.xml +++ b/server/test/resources/SecurityGroupManagerTestContext.xml @@ -1,19 +1,19 @@ - 3.8.1 test + org.apache.tomcat.embed tomcat-embed-core 7.0.30 - test + + + + org.bouncycastle + bcprov-jdk16 + 1.46 diff --git a/services/console-proxy-rdp/rdpconsole/rdp-config.bat b/services/console-proxy-rdp/rdpconsole/rdp-config.bat old mode 100644 new mode 100755 index e9510144426..f19b00da958 --- a/services/console-proxy-rdp/rdpconsole/rdp-config.bat +++ b/services/console-proxy-rdp/rdpconsole/rdp-config.bat @@ -16,7 +16,7 @@ rem specific language governing permissions and limitations rem under the License. rem -rem Configure and start RDP service. +rem Configure and start RDP service. Run this script on RDP server. rem rem Turn off firewall diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java old mode 100644 new mode 100755 index 3a25c8f1cfb..8d643311699 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java @@ -40,7 +40,8 @@ public class BitmapOrder extends Order { @Override public String toString() { final int maxLen = 10; - return String.format("BitmapUpdateOrder [rectangles=%s]", rectangles != null ? Arrays.asList(rectangles).subList(0, Math.min(rectangles.length, maxLen)) : null); + return String.format("BitmapUpdateOrder [rectangles=%s]", rectangles != null ? Arrays.asList(rectangles).subList(0, Math.min(rectangles.length, maxLen)) + : null); } } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java old mode 100644 new mode 100755 index 08dcbf69da7..1685114b2a0 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.java @@ -65,8 +65,10 @@ public class BitmapRectangle { @Override public String toString() { - return String.format("BitmapUpdateRectangle [x=%s, y=%s, width=%s, height=%s, bitsPerPixel=%s, bitmapDataStream=%s]", x, y, width, height, colorDepth, - bitmapDataStream); + return String + .format( + "BitmapUpdateRectangle [x=%s, y=%s, width=%s, height=%s, bitsPerPixel=%s, bitmapDataStream=%s]", + x, y, width, height, colorDepth, bitmapDataStream); } } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java old mode 100644 new mode 100755 index 2418de3832f..43abb273477 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java @@ -49,7 +49,7 @@ public class BufferedImageCanvas extends Canvas { } public void setCanvasSize(int width, int height) { - this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); graphics = offlineImage.createGraphics(); setSize(offlineImage.getWidth(), offlineImage.getHeight()); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java old mode 100644 new mode 100755 index 28bf640cfbd..804cab71096 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.java @@ -81,23 +81,23 @@ public class BufferedImageCopyRectAdapter extends BaseElement { Element renderer = new BufferedImageCopyRectAdapter("renderer", canvas); int[] pixelsBeforeCopy = new int[] { - // 0 - 1, 2, 3, 4, - // 1 - 5, 6, 7, 8, - // 2 - 9, 10, 11, 12, - // 3 - 13, 14, 15, 16}; + // 0 + 1, 2, 3, 4, + // 1 + 5, 6, 7, 8, + // 2 + 9, 10, 11, 12, + // 3 + 13, 14, 15, 16}; int[] pixelsAfterCopy = new int[] { - // 0 - 11, 12, 3, 4, - // 1 - 15, 16, 7, 8, - // 2 - 9, 10, 11, 12, - // 3 - 13, 14, 15, 16}; + // 0 + 11, 12, 3, 4, + // 1 + 15, 16, 7, 8, + // 2 + 9, 10, 11, 12, + // 3 + 13, 14, 15, 16}; // Initalize image int[] data = ((DataBufferInt)canvas.getOfflineImage().getRaster().getDataBuffer()).getData(); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java old mode 100644 new mode 100755 index e93df3e7add..396bdd47b4c --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.java @@ -82,32 +82,32 @@ public class BufferedImagePixelsAdapter extends BaseElement { switch (dataBuf.getDataType()) { - case DataBuffer.TYPE_INT: { + case DataBuffer.TYPE_INT: { - // Convert array of bytes to array of int's - int[] intArray = buf.toIntLEArray(); + // Convert array of bytes to array of int's + int[] intArray = buf.toIntLEArray(); - // We chose RGB888 model, so Raster will use DataBufferInt type - DataBufferInt dataBuffer = (DataBufferInt)dataBuf; + // We chose RGB888 model, so Raster will use DataBufferInt type + DataBufferInt dataBuffer = (DataBufferInt)dataBuf; - int imageWidth = image.getWidth(); - int imageHeight = image.getHeight(); + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); - // Paint rectangle directly on buffer, line by line - int[] imageBuffer = dataBuffer.getData(); + // Paint rectangle directly on buffer, line by line + int[] imageBuffer = dataBuffer.getData(); - for (int srcLine = 0, dstLine = y; srcLine < rectHeight && dstLine < imageHeight; srcLine++, dstLine++) { - try { - System.arraycopy(intArray, srcLine * rectWidth, imageBuffer, x + dstLine * imageWidth, rectWidth); - } catch (IndexOutOfBoundsException e) { - } + for (int srcLine = 0, dstLine = y; srcLine < rectHeight && dstLine < imageHeight; srcLine++, dstLine++) { + try { + System.arraycopy(intArray, srcLine * rectWidth, imageBuffer, x + dstLine * imageWidth, rectWidth); + } catch (IndexOutOfBoundsException e) { } - break; } + break; + } - default: - throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " + - dataBuf.getClass().getSimpleName()); + default: + throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " + + dataBuf.getClass().getSimpleName()); } // Request update of repainted area @@ -123,13 +123,11 @@ public class BufferedImagePixelsAdapter extends BaseElement { Element renderer = new BufferedImagePixelsAdapter("renderer", canvas); - byte[] pixels = - new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + byte[] pixels = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - int[] pixelsLE = - new int[] {0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, - 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d}; + int[] pixelsLE = new int[] {0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, + 0x0c0b0a09, 0x100f0e0d, 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d}; ByteBuffer buf = new ByteBuffer(pixels); buf.putMetadata(TARGET_X, 0); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java old mode 100644 new mode 100755 index b6dc4a26023..688693bfd10 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java @@ -22,46 +22,216 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.net.InetSocketAddress; +import java.util.Arrays; import rdpclient.RdpClient; import streamer.Element; import streamer.Pipeline; import streamer.PipelineImpl; import streamer.SocketWrapper; +import streamer.SocketWrapperImpl; +import streamer.apr.AprSocketWrapperImpl; +import streamer.bco.BcoSocketWrapperImpl; +import streamer.ssl.SSLState; import vncclient.VncClient; +import common.opt.IntOption; +import common.opt.Option; +import common.opt.OptionParser; +import common.opt.StringEnumerationOption; +import common.opt.StringOption; public class Client { + enum Protocol { + NONE, VNC, RDP, HYPERV + } + + // Common options + private Option help = new Option() { + { + name = "--help"; + alias = "-h"; + description = "Show this help text."; + } + }; + private Option debugLink = new Option() { + { + name = "--debug-link"; + alias = "-DL"; + description = "Print debugging messages when packets are trasnferred via links."; + } + }; + private Option debugElement = new Option() { + { + name = "--debug-element"; + alias = "-DE"; + description = "Print debugging messages when packets are received or sended by elements."; + } + }; + private Option debugPipeline = new Option() { + { + name = "--debug-pipeline"; + alias = "-DP"; + description = "Print debugging messages in pipelines."; + } + }; + + private StringOption hostName = new StringOption() { + { + name = "--host"; + alias = "-n"; + aliases = new String[] {"--host-name"}; + required = true; + description = "Name or IP address of host to connect to."; + } + }; + private IntOption canvasWidth = new IntOption() { + { + name = "--width"; + alias = "-W"; + value = 1024; + description = "Width of canvas."; + } + }; + + private IntOption canvasHeight = new IntOption() { + { + name = "--height"; + alias = "-H"; + value = 768; + description = "Height of canvas."; + } + }; + + // Protocol specific options + + private IntOption vncPort = new IntOption() { + { + name = "--port"; + alias = "-p"; + value = 5901; + description = "Port of VNC display server to connect to. Calculate as 5900 + display number, e.g. 5900 for display #0, 5901 for display #1, and so on."; + } + }; + + private IntOption rdpPort = new IntOption() { + { + name = "--port"; + alias = "-p"; + value = 3389; + description = "Port of RDP server to connect to."; + } + }; + + private IntOption hyperVPort = new IntOption() { + { + name = "--port"; + alias = "-p"; + value = 2179; + description = "Port of HyperV server to connect to."; + } + }; + + private StringOption password = new StringOption() { + { + name = "--password"; + alias = "-P"; + required = true; + description = "Password to use."; + } + }; + + private StringOption rdpPassword = new StringOption() { + { + name = "--password"; + alias = "-P"; + required = false; + description = "Password to use. If omitted, then login screen will be shown."; + } + }; + + private StringOption userName = new StringOption() { + { + name = "--user"; + alias = "-U"; + value = "Administrator"; + description = "User name to use."; + } + }; + + private StringOption domain = new StringOption() { + { + name = "--domain"; + alias = "-D"; + value = "Workgroup"; + description = "NTLM domain to login into."; + } + }; + + private StringOption hyperVInstanceId = new StringOption() { + { + name = "--instance"; + alias = "-i"; + required = true; + description = "HyperV instance ID to use."; + } + }; + private StringEnumerationOption sslImplementation = new StringEnumerationOption() { + { + name = "--ssl-implementation"; + alias = "-j"; + value = "apr"; + choices = new String[] {"jre", "apr", "bco"}; + description = "Select SSL engine to use: JRE standard implementation, Apache Portable Runtime native library, BonuncyCastle.org implementation."; + } + }; + + private final Option[] commonOptions = new Option[] {help, debugLink, debugElement, debugPipeline, hostName, canvasWidth, canvasHeight}; + private final Option[] vncOptions = new Option[] {vncPort, password}; + private final Option[] rdpOptions = new Option[] {sslImplementation, rdpPort, domain, userName, rdpPassword}; + private final Option[] hyperVOptions = new Option[] {sslImplementation, hyperVPort, hyperVInstanceId, domain, userName, password}; + private static Frame frame; private static SocketWrapper socket; private static ScrollPane scroller; private static ScreenDescription screen; private static BufferedImageCanvas canvas; - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - // System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); + private void help() { + System.out.println("Usage: \n java common.Client vnc|rdp|hyperv OPTIONS\n"); + System.out.println(Option.toHelp("Common options", commonOptions)); + System.out.println(Option.toHelp("VNC options", vncOptions)); + System.out.println(Option.toHelp("RDP options", rdpOptions)); + System.out.println(Option.toHelp("HyperV options", hyperVOptions)); + } + + public void runClient(String[] args) { try { - if (args.length < 4) { - System.out.println("Usage: \n java common.Client vnc IP PORT PASSWORD\n java common.Client rdp IP PORT username\n"); - System.exit(0); - } - String connectionType = args[0]; - String hostname = args[1]; - int port = Integer.parseInt(args[2]); - String userNameOrPassword = args[3]; + Protocol protocol = parseOptions(args); + if (protocol == Protocol.NONE) + return; - // Create address from arguments - InetSocketAddress address = new InetSocketAddress(hostname, port); + System.setProperty("streamer.Link.debug", "" + debugLink.used); + System.setProperty("streamer.Element.debug", "" + debugElement.used); + System.setProperty("streamer.Pipeline.debug", "" + debugPipeline.used); + + SSLState sslState = new SSLState(); // Create socket wrapper - socket = new SocketWrapper("socket"); + if ("jre".equals(sslImplementation.value)) { + socket = new SocketWrapperImpl("socket", sslState); + } else if ("apr".equals(sslImplementation.value)) { + socket = new AprSocketWrapperImpl("socket", sslState); + } else if ("bco".equals(sslImplementation.value)) { + socket = new BcoSocketWrapperImpl("socket", sslState); + } else { + throw new RuntimeException("Unexpected option value: \"" + sslImplementation.value + "\". " + sslImplementation.help()); + } screen = new ScreenDescription(); - canvas = new BufferedImageCanvas(1024, 768); + canvas = new BufferedImageCanvas(canvasWidth.value, canvasHeight.value); screen.addSizeChangeListener(new SizeChangeListener() { @Override public void sizeChanged(int width, int height) { @@ -74,13 +244,24 @@ public class Client { }); // Assemble pipeline + InetSocketAddress address; Element main; - if ("vnc".equals(connectionType)) { - main = new VncClient("client", userNameOrPassword, screen, canvas); - } else if ("rdp".equals(connectionType)) { - main = new RdpClient("client", userNameOrPassword, screen, canvas); - } else { - throw new RuntimeException("Unknown connection type. Expected value: \"vnc\" or \"rdp\", actual value: \"" + connectionType + "\"."); + switch (protocol) { + case VNC: + address = new InetSocketAddress(hostName.value, vncPort.value); + main = new VncClient("client", password.value, screen, canvas); + break; + case RDP: + address = new InetSocketAddress(hostName.value, rdpPort.value); + main = new RdpClient("client", hostName.value, domain.value, userName.value, rdpPassword.value, null, screen, canvas, sslState); + break; + case HYPERV: + address = new InetSocketAddress(hostName.value, hyperVPort.value); + main = new RdpClient("client", hostName.value, domain.value, userName.value, password.value, hyperVInstanceId.value, screen, canvas, sslState); + break; + default: + address = null; + main = null; } Pipeline pipeline = new PipelineImpl("Client"); @@ -89,7 +270,7 @@ public class Client { pipeline.validate(); - frame = createVncClientMainWindow(canvas, "VNC", new WindowAdapter() { + frame = createVncClientMainWindow(canvas, protocol.toString() + " " + hostName.value, new WindowAdapter() { @Override public void windowClosing(WindowEvent evt) { shutdown(); @@ -108,6 +289,41 @@ public class Client { } } + private Protocol parseOptions(String[] args) { + String protocolName = (args.length > 0) ? args[0] : ""; + Protocol protocol = Protocol.NONE; + + Option[] options; + if (protocolName.equals("vnc")) { + protocol = Protocol.VNC; + options = join(commonOptions, vncOptions); + } else if (protocolName.equals("rdp")) { + protocol = Protocol.RDP; + options = join(commonOptions, rdpOptions); + } else if (protocolName.equals("hyperv")) { + protocol = Protocol.HYPERV; + options = join(commonOptions, hyperVOptions); + } else { + help(); + return Protocol.NONE; + } + + // Parse all options for given protocol + String[] arguments = OptionParser.parseOptions(args, 1, options); + + if (arguments.length > 0) { + System.err.println("[Client] ERROR: Arguments are not allowed here. Check command syntax. Extra arguments: \"" + Arrays.toString(arguments) + "\"."); + help(); + return Protocol.NONE; + } + + if (help.used) { + help(); + return Protocol.NONE; + } + return protocol; + } + protected static void shutdown() { if (frame != null) { frame.setVisible(false); @@ -135,4 +351,25 @@ public class Client { return frame; } + /** + * Join two arrays with options and return new array. + */ + private Option[] join(Option[] a1, Option[] a2) { + // Extend first array + Option[] result = Arrays.copyOf(a1, a1.length + a2.length); + + // Append second array to first + for (int i = 0, p = a1.length; i < a2.length; i++, p++) + result[p] = a2[i]; + + return result; + } + + public static void main(String args[]) { + // *DEBUG*/System.setProperty("javax.net.debug", "ssl"); + // * DEBUG */System.setProperty("javax.net.debug", "ssl:record:packet"); + + new Client().runClient(args); + } + } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java old mode 100644 new mode 100755 index a9155e8dee0..1c33a63cb61 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java @@ -56,12 +56,12 @@ public class ScreenDescription { * Store information about server pixel format. */ public void setPixelFormat(int bitsPerPixel, int depth, boolean bigEndianFlag, boolean trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, - int greenShift, int blueShift) { + int greenShift, int blueShift) { - this.bytesPerPixel = (bitsPerPixel + 7) / 8; + bytesPerPixel = (bitsPerPixel + 7) / 8; this.bitsPerPixel = bitsPerPixel; - this.colorDepth = depth; + colorDepth = depth; this.bigEndianFlag = bigEndianFlag; this.trueColorFlag = trueColorFlag; @@ -79,23 +79,23 @@ public class ScreenDescription { public void setPixelFormatRGBTrueColor(int bitsPerPixel) { switch (bitsPerPixel) { - case 8: - setPixelFormat(8, 8, false, false, -1, -1, -1, -1, -1, -1); - break; - case 15: - setPixelFormat(16, 15, false, true, 31, 31, 31, 0, 5, 10); - break; - case 16: - setPixelFormat(16, 16, false, true, 31, 63, 31, 0, 5, 11); - break; - case 24: - setPixelFormat(24, 24, false, true, 255, 255, 255, 0, 8, 16); - break; - case 32: - setPixelFormat(32, 24, false, true, 255, 255, 255, 0, 8, 16); - break; - default: - throw new RuntimeException("Unknown color depth."); + case 8: + setPixelFormat(8, 8, false, false, -1, -1, -1, -1, -1, -1); + break; + case 15: + setPixelFormat(16, 15, false, true, 31, 31, 31, 0, 5, 10); + break; + case 16: + setPixelFormat(16, 16, false, true, 31, 63, 31, 0, 5, 11); + break; + case 24: + setPixelFormat(24, 24, false, true, 255, 255, 255, 0, 8, 16); + break; + case 32: + setPixelFormat(32, 24, false, true, 255, 255, 255, 0, 8, 16); + break; + default: + throw new RuntimeException("Unknown color depth."); } } @@ -107,8 +107,8 @@ public class ScreenDescription { if (height <= 0 || width <= 0) throw new RuntimeException("Incorrect framebuffer size: " + width + "x" + height + "."); - this.framebufferWidth = width; - this.framebufferHeight = height; + framebufferWidth = width; + framebufferHeight = height; callSizeChangeListeners(width, height); } @@ -145,16 +145,16 @@ public class ScreenDescription { } public boolean isRGB888_32_LE() { - return (colorDepth == 24 && bitsPerPixel == 32 && redShift == 0 && greenShift == 8 && blueShift == 16 && redMax == 255 && greenMax == 255 && blueMax == 255 && - !bigEndianFlag && trueColorFlag); + return (colorDepth == 24 && bitsPerPixel == 32 && redShift == 0 && greenShift == 8 && blueShift == 16 && redMax == 255 && greenMax == 255 && blueMax == 255 + && !bigEndianFlag && trueColorFlag); } @Override public String toString() { - return "ScreenDescription [framebufferWidth=" + framebufferWidth + ", framebufferHeight=" + framebufferHeight + ", desktopName=" + desktopName + - ", bytesPerPixel=" + bytesPerPixel + ", depth=" + colorDepth + ", bitsPerPixel=" + bitsPerPixel + ", redShift=" + redShift + ", greenShift=" + greenShift + - ", blueShift=" + blueShift + ", redMax=" + redMax + ", greenMax=" + greenMax + ", blueMax=" + blueMax + ", bigEndianFlag=" + bigEndianFlag + - ", trueColorFlag=" + trueColorFlag + "]"; + return "ScreenDescription [framebufferWidth=" + framebufferWidth + ", framebufferHeight=" + framebufferHeight + ", desktopName=" + desktopName + + ", bytesPerPixel=" + bytesPerPixel + ", depth=" + colorDepth + ", bitsPerPixel=" + bitsPerPixel + ", redShift=" + redShift + ", greenShift=" + greenShift + + ", blueShift=" + blueShift + ", redMax=" + redMax + ", greenMax=" + greenMax + ", blueMax=" + blueMax + ", bigEndianFlag=" + bigEndianFlag + + ", trueColorFlag=" + trueColorFlag + "]"; } public void addSizeChangeListener(SizeChangeListener sizeChangeListener) { diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java old mode 100644 new mode 100755 similarity index 92% rename from services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java index 5970abb21a9..c25b07f19c6 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java @@ -14,17 +14,17 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package common; +package common.adapter; import java.awt.Toolkit; import streamer.BaseElement; import streamer.ByteBuffer; import streamer.Element; -import streamer.FakeSource; import streamer.Link; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.FakeSource; public class AwtBellAdapter extends BaseElement { @@ -61,9 +61,9 @@ public class AwtBellAdapter extends BaseElement { Element source = new FakeSource("source") { { - this.incommingBufLength = 0; - this.delay = 1000; - this.numBuffers = 3; + incommingBufLength = 0; + delay = 1000; + numBuffers = 3; } }; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java old mode 100644 new mode 100755 similarity index 63% rename from services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java index 8adee499cb6..55ca8fdbf64 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java @@ -14,13 +14,13 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package common; +package common.adapter; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; -import rdpclient.ServerBitmapUpdate; +import rdpclient.rdp.ServerBitmapUpdate; import streamer.BaseElement; import streamer.ByteBuffer; import streamer.Element; @@ -28,6 +28,12 @@ import streamer.Link; import streamer.Order; import streamer.Pipeline; import streamer.PipelineImpl; +import common.BitmapOrder; +import common.BitmapRectangle; +import common.BufferedImageCanvas; +import common.CopyRectOrder; +import common.OrderType; +import common.ScreenDescription; public class AwtCanvasAdapter extends BaseElement { @@ -54,17 +60,17 @@ public class AwtCanvasAdapter extends BaseElement { Order order = buf.getOrder(); switch ((OrderType)order.type) { - case BITMAP_UPDATE: - handleBitmap((BitmapOrder)order, buf); - break; + case BITMAP_UPDATE: + handleBitmap((BitmapOrder)order, buf); + break; - case COPY_RECT: - handleCopyRect((CopyRectOrder)order, buf); - break; + case COPY_RECT: + handleCopyRect((CopyRectOrder)order, buf); + break; - default: - throw new RuntimeException("Order is not implemented: " + buf + "."); - // break; + default: + throw new RuntimeException("Order is not implemented: " + buf + "."); + // break; } buf.unref(); @@ -98,33 +104,33 @@ public class AwtCanvasAdapter extends BaseElement { BufferedImage rectImage; switch (rectangle.colorDepth) { - case 8: { - rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_BYTE_INDEXED, screen.colorMap); - WritableRaster raster = rectImage.getRaster(); - raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toByteArray()); - break; - } - case 15: { - rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_555_RGB); - WritableRaster raster = rectImage.getRaster(); - raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray()); - break; - } - case 16: { - rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_565_RGB); - WritableRaster raster = rectImage.getRaster(); - raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray()); - break; - } - case 24: - case 32: { - rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_INT_RGB); - WritableRaster raster = rectImage.getRaster(); - raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toIntLEArray()); - break; - } - default: - throw new RuntimeException("Unsupported color depth: " + rectangle.colorDepth + "."); + case 8: { + rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_BYTE_INDEXED, screen.colorMap); + WritableRaster raster = rectImage.getRaster(); + raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toByteArray()); + break; + } + case 15: { + rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_555_RGB); + WritableRaster raster = rectImage.getRaster(); + raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray()); + break; + } + case 16: { + rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_565_RGB); + WritableRaster raster = rectImage.getRaster(); + raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray()); + break; + } + case 24: + case 32: { + rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_INT_RGB); + WritableRaster raster = rectImage.getRaster(); + raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toIntLEArray()); + break; + } + default: + throw new RuntimeException("Unsupported color depth: " + rectangle.colorDepth + "."); } g.setClip(x, y, width, height); @@ -143,9 +149,8 @@ public class AwtCanvasAdapter extends BaseElement { // System.setProperty("streamer.Link.debug", "true"); // System.setProperty("streamer.Element.debug", "true"); // System.setProperty("streamer.Pipeline.debug", "true"); - ByteBuffer packet = - new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x04, 0x0a, - 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + ByteBuffer packet = new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x01, 0x04, 0x0a, 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); Pipeline pipeline = new PipelineImpl("test"); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java old mode 100644 new mode 100755 similarity index 88% rename from services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java index 75dc1b91122..cc7f33071e3 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package common; +package common.adapter; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; @@ -22,10 +22,11 @@ import java.awt.datatransfer.StringSelection; import streamer.BaseElement; import streamer.ByteBuffer; import streamer.Link; -import vncclient.VncMessageHandler; public class AwtClipboardAdapter extends BaseElement { + public static final String CLIPBOARD_CONTENT = "content"; + public AwtClipboardAdapter(String id) { super(id); declarePads(); @@ -43,14 +44,14 @@ public class AwtClipboardAdapter extends BaseElement { if (buf == null) return; - String content = (String)buf.getMetadata(VncMessageHandler.CLIPBOARD_CONTENT); + String content = (String)buf.getMetadata(CLIPBOARD_CONTENT); StringSelection contents = new StringSelection(content); Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); } @Override public String toString() { - return "Clipboard(" + id + ")"; + return "ClipboardAdapter(" + id + ")"; } } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java new file mode 100755 index 00000000000..2ef7c69d457 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java @@ -0,0 +1,78 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +/** + * Any type. Don't forget to set type. + */ +public class Any extends Tag { + + /** + * Raw bytes of any value. + */ + public ByteBuffer value; + + public Any(String name) { + super(name); + } + + @Override + public boolean isValueSet() { + return value != null; + } + + @Override + public long calculateLengthOfValuePayload() { + return value.length; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + buf.writeBytes(value); + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + long length = buf.readBerLength(); + + value = buf.readBytes((int)length); + } + + @Override + public Tag deepCopy(String suffix) { + return new Any(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + tagType = tag.tagType; + value = new ByteBuffer(((Any)tag).value.toByteArray()); + return this; + } + + @Override + public boolean isTypeValid(BerType typeAndFlags, boolean explicit) { + if (explicit) + return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber; + else + return true; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java new file mode 100755 index 00000000000..6dacd60272b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java @@ -0,0 +1,83 @@ +// 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 common.asn1; + +public interface Asn1Constants { + + /** + * Universal class type. + */ + public static final int UNIVERSAL_CLASS = 0x00; + + /** + * Application class type. + */ + public static final int APPLICATION_CLASS = 0x40; + + public static final int CONTEXT_CLASS = 0x80; + + public static final int PRIVATE_CLASS = 0xC0; + + /** + * Constructed type. + */ + public static final int CONSTRUCTED = 0x20; + + /** + * Mask to extract class. + */ + public static final int CLASS_MASK = 0xC0; + + /** + * Mask to extract type. + */ + public static final int TYPE_MASK = 0x1F; + + public static final int EOF = 0x00; + public static final int BOOLEAN = 0x01; + /** + * Integer primitive. + */ + public static final int INTEGER = 0x02; + public static final int BIT_STRING = 0x03; + /** + * Octet string primitive. + */ + public static final int OCTET_STRING = 0x04; + public static final int NULL = 0x05; + public static final int OBJECT_ID = 0x06; + public static final int REAL = 0x09; + public static final int ENUMERATED = 0x0A; + /** + * Sequence primitive. + */ + public static final int SEQUENCE = 0x10; + public static final int SET = 0x11; + public static final int NUMERIC_STRING = 0x12; + public static final int PRINTABLE_STRING = 0x13; + public static final int TELETEX_STRING = 0x14; + public static final int VIDEOTEXT_STRING = 0x15; + public static final int IA5_STRING = 0x16; + public static final int UTCTIME = 0x17; + public static final int GENERAL_TIME = 0x18; + public static final int GRAPHIC_STRING = 0x19; + public static final int VISIBLE_STRING = 0x1A; + public static final int GENERAL_STRING = 0x1B; + + public static final int EXTENDED_TYPE = 0x1F; + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java new file mode 100755 index 00000000000..678e04eb131 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java @@ -0,0 +1,116 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +/** + * Variable length integer. + */ +public class Asn1Integer extends Tag { + + public Long value = null; + + public Asn1Integer(String name) { + super(name); + tagType = INTEGER; + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + // Type is already read by parent parser + + long length = buf.readBerLength(); + if (length > 8) + throw new RuntimeException("[" + this + "] ERROR: Integer value is too long: " + length + " bytes. Cannot handle integers more than 8 bytes long. Data: " + + buf + "."); + + value = buf.readSignedVarInt((int)length); + } + + @Override + public Tag deepCopy(String suffix) { + return new Asn1Integer(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + value = ((Asn1Integer)tag).value; + return this; + } + + @Override + public String toString() { + return super.toString() + "= " + value; + } + + @Override + public long calculateLengthOfValuePayload() { + if (value <= 0xff) + return 1; + if (value <= 0xffFF) + return 2; + if (value <= 0xffFFff) + return 3; + if (value <= 0xffFFffFFL) + return 4; + if (value <= 0xffFFffFFffL) + return 5; + if (value <= 0xffFFffFFffFFL) + return 6; + if (value <= 0xffFFffFFffFFffL) + return 7; + + return 8; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + long value = this.value.longValue(); + + if (value < 0xff) { + buf.writeByte((int)value); + } else if (value <= 0xffFF) { + buf.writeShort((int)value); + } else if (value <= 0xffFFff) { + buf.writeByte((int)(value >> 16)); + buf.writeShort((int)value); + } else if (value <= 0xffFFffFFL) { + buf.writeInt((int)value); + } else if (value <= 0xffFFffFFffL) { + buf.writeByte((int)(value >> 32)); + buf.writeInt((int)value); + } else if (value <= 0xffFFffFFffFFL) { + buf.writeShort((int)(value >> 32)); + buf.writeInt((int)value); + } else if (value <= 0xffFFffFFffFFffL) { + buf.writeByte((int)(value >> (32 + 16))); + buf.writeShort((int)(value >> 32)); + buf.writeInt((int)value); + } else { + buf.writeInt((int)(value >> 32)); + buf.writeInt((int)value); + } + } + + @Override + public boolean isValueSet() { + return value != null; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java new file mode 100755 index 00000000000..8e53f249568 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common.asn1; + +public class BerType { + + public int tagClass; + public boolean constructed; + public int typeOrTagNumber; + + public BerType() { + } + + public BerType(int tagClass, boolean constructed, int typeOrTagNumber) { + this.tagClass = tagClass; + this.constructed = constructed; + this.typeOrTagNumber = typeOrTagNumber; + } + + @Override + public String toString() { + return "BerType [tagClass=" + Tag.tagClassToString(tagClass) + ", constructed=" + constructed + ", type or tag number=" + + Tag.tagTypeOrNumberToString(tagClass, typeOrTagNumber) + "]"; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java new file mode 100755 index 00000000000..3323c727744 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java @@ -0,0 +1,67 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +public class BitString extends Tag { + + /** + * Bit string value. + */ + public ByteBuffer value; + + public BitString(String name) { + super(name); + tagType = BIT_STRING; + } + + @Override + public boolean isValueSet() { + return value != null; + } + + @Override + public long calculateLengthOfValuePayload() { + return value.length; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + buf.writeBytes(value); + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + long length = buf.readBerLength(); + + value = buf.readBytes((int)length); + } + + @Override + public Tag deepCopy(String suffix) { + return new BitString(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + value = new ByteBuffer(((BitString)tag).value.toByteArray()); + return this; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java new file mode 100755 index 00000000000..c112cd0b992 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java @@ -0,0 +1,67 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +public class ObjectID extends Tag { + + /** + * Raw bytes of encoded OID. + */ + public ByteBuffer value; + + public ObjectID(String name) { + super(name); + tagType = OBJECT_ID; + } + + @Override + public boolean isValueSet() { + return value != null; + } + + @Override + public long calculateLengthOfValuePayload() { + return value.length; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + buf.writeBytes(value); + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + long length = buf.readBerLength(); + + value = buf.readBytes((int)length); + } + + @Override + public Tag deepCopy(String suffix) { + return new ObjectID(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + value = new ByteBuffer(((ObjectID)tag).value.toByteArray()); + return this; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java new file mode 100755 index 00000000000..3da9c849166 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java @@ -0,0 +1,80 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +public class OctetString extends Tag { + + public ByteBuffer value = null; + + public OctetString(String name) { + super(name); + tagType = OCTET_STRING; + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + // Type is already read by parent parser + + long length = buf.readBerLength(); + + if (length > buf.length) + throw new RuntimeException("BER octet string is too long: " + length + " bytes. Data: " + buf + "."); + + value = buf.readBytes((int)length); + } + + @Override + public Tag deepCopy(String suffix) { + return new OctetString(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + value = ((OctetString)tag).value; + return this; + } + + @Override + public String toString() { + return super.toString() + "= " + value; + } + + @Override + public long calculateLengthOfValuePayload() { + if (value != null) + return value.length; + else + return 0; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + if (value != null) + buf.writeBytes(value); + else + return; + } + + @Override + public boolean isValueSet() { + return value != null; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java new file mode 100755 index 00000000000..6fa23f8baab --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java @@ -0,0 +1,143 @@ +// 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 common.asn1; + +import java.util.Arrays; + +import streamer.ByteBuffer; + +/** + * One or more elements of different types. + * + * Only prefixed tags are supported. + */ +public class Sequence extends Tag { + + public Tag[] tags; + + public Sequence(String name) { + super(name); + tagType = SEQUENCE; + // Sequence and SequenceOf are always encoded as constructed + constructed = true; + } + + @Override + public long calculateLengthOfValuePayload() { + long sum = 0; + + for (Tag tag : tags) { + long tagLength = tag.calculateFullLength(); + sum += tagLength; + } + + return sum; + } + + @Override + public void writeTagValuePayload(ByteBuffer buf) { + // Write tags + for (Tag tag : tags) { + tag.writeTag(buf); + } + } + + @Override + public void readTagValue(ByteBuffer buf, BerType typeAndFlags) { + // Type is already read by parent parser + + long length = buf.readBerLength(); + if (length > buf.remainderLength()) + throw new RuntimeException("BER sequence is too long: " + length + " bytes, while buffer remainder length is " + buf.remainderLength() + ". Data: " + buf + + "."); + + ByteBuffer value = buf.readBytes((int)length); + parseContent(value); + + value.unref(); + } + + protected void parseContent(ByteBuffer buf) { + for (int i = 0; buf.remainderLength() > 0 && i < tags.length; i++) { + BerType typeAndFlags = readBerType(buf); + + // If current tag does not match data in buffer + if (!tags[i].isTypeValid(typeAndFlags)) { + + // If tag is required, then throw exception + if (!tags[i].optional) { + throw new RuntimeException("[" + this + "] ERROR: Required tag is missed: " + tags[i] + ". Unexected tag type: " + typeAndFlags + ". Data: " + buf + + "."); + } else { + // One or more tags are omitted, so skip them + for (; i < tags.length; i++) { + if (tags[i].isTypeValid(typeAndFlags)) { + break; + } + } + + if (i >= tags.length || !tags[i].isTypeValid(typeAndFlags)) { + throw new RuntimeException("[" + this + "] ERROR: No more tags to read or skip, but some data still left in buffer. Unexected tag type: " + + typeAndFlags + ". Data: " + buf + "."); + } + } + } + + tags[i].readTag(buf, typeAndFlags); + } + + } + + @Override + public boolean isTypeValid(BerType typeAndFlags, boolean explicit) { + if (explicit) + return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber; + else + // Sequences are always encoded as "constructed" in BER. + return typeAndFlags.tagClass == UNIVERSAL_CLASS && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == SEQUENCE; + } + + @Override + public Tag deepCopy(String suffix) { + return new Sequence(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + + if (tags.length != ((Sequence)tag).tags.length) + throw new RuntimeException("Incompatible sequences. This: " + this + ", another: " + tag + "."); + + for (int i = 0; i < tags.length; i++) { + tags[i].copyFrom(((Sequence)tag).tags[i]); + } + + return this; + } + + @Override + public String toString() { + return super.toString() + "{" + Arrays.toString(tags) + " }"; + } + + @Override + public boolean isValueSet() { + return tags != null; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java new file mode 100755 index 00000000000..f288991c259 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java @@ -0,0 +1,82 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common.asn1; + +import java.util.ArrayList; + +import streamer.ByteBuffer; + +/** + * Zero or more elements of same type (array). + */ +public class SequenceOf extends Sequence { + + /** + * Type of this array. + */ + public Tag type; + + /* Values are stored in tags[] variable inherited from Sequence. */ + + public SequenceOf(String name) { + super(name); + } + + @Override + protected void parseContent(ByteBuffer buf) { + ArrayList tagList = new ArrayList(); + + for (int index = 0; buf.remainderLength() > 0; index++) { + // End of array is marked with two zero bytes (0x00 0x00) + if (buf.peekUnsignedByte(0) == 0x00 && buf.peekUnsignedByte(1) == 0x00) { + break; + } + + Tag tag = type.deepCopy(index); + + tag.readTag(buf); + tagList.add(tag); + } + + tags = tagList.toArray(new Tag[tagList.size()]); + } + + @Override + public Tag deepCopy(String suffix) { + return new SequenceOf(name + suffix).copyFrom(this); + } + + @Override + public Tag copyFrom(Tag tag) { + super.copyFrom(tag); + // We can create shallow copy of type, because it will not be modified + type = ((SequenceOf)tag).type; + + tags = new Tag[((Sequence)tag).tags.length]; + for (int i = 0; i < tags.length; i++) { + tags[i] = ((Sequence)tag).tags[i].deepCopy(""); + } + + return this; + } + + @Override + public String toString() { + return super.toString() + ": " + type; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java new file mode 100755 index 00000000000..80ece43db74 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java @@ -0,0 +1,462 @@ +// 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 common.asn1; + +import streamer.ByteBuffer; + +public abstract class Tag implements Asn1Constants { + + /** + * Name of this tag, for debugging purposes. + */ + public String name = ""; + + /** + * Is this tag required or optional, for explicit tags only. + */ + public boolean optional = false; + + /** + * Tag primitive (e.g. implicit boolean), or constructed (e.g. sequence, or + * explicit boolean). + */ + public boolean constructed = false; + + /** + * Class of tag, when it is explicit. + */ + public int tagClass = UNIVERSAL_CLASS; + + /** + * Tag number (e.g. index in sequence), when tag is explicit. + */ + public int tagNumber = -1; + + /** + * Tag type (e.g. INDER), when tag is implicit. + */ + public int tagType = -1; + + /** + * If tag is explicit, then it is prefixed with tag number, so it can be + * optional or used in unordered set. + */ + public boolean explicit = false; + + public Tag(String name) { + this.name = name; + } + + /** + * Write tag value, with or without prefix. + */ + public void writeTag(ByteBuffer buf) { + + if (!isMustBeWritten()) + return; + + // Write prefix, when necessary + if (explicit) { + + // Write tag prefix, always constructed + BerType berTagPrefix = new BerType(tagClass, true, tagNumber); + writeBerType(buf, berTagPrefix); + + // Write tag prefix length + buf.writeBerLength(calculateLength()); + + // Write tag value + writeTagValue(buf); + } else { + // If implicit, just write tag value + writeTagValue(buf); + } + } + + /** + * Must return true when value of this tag is set or tag is required, so it + * can be written, false otherwise. + */ + public boolean isMustBeWritten() { + return !optional || isValueSet(); + } + + /** + * Must return true when value of this tag is set or tag is required, so it + * can be written, false otherwise. + */ + public abstract boolean isValueSet(); + + /** + * Calculate full length of tag, including type (or prefix, when explicit). + */ + public long calculateFullLength() { + if (!isMustBeWritten()) + return 0; + + // Length of value, including type + long length = calculateLength(); + + if (!explicit) { + // Length of tag type and it length + length += calculateLengthOfTagTypeOrTagNumber(tagType) + calculateLengthOfLength(length); + } else { + // Length of tag prefix and it length + length += calculateLengthOfTagTypeOrTagNumber(tagNumber) + calculateLengthOfLength(length); + } + + return length; + } + + /** + * Calculate length of tag, including type when explicit, but without length + * of prefix (or type, when implicit). + */ + public long calculateLength() { + if (!isMustBeWritten()) + return 0; + + // Length of value + long length = calculateLengthOfValuePayload(); + + if (explicit) { + // Length of tag type and it length + length += calculateLengthOfTagTypeOrTagNumber(tagType) + calculateLengthOfLength(length); + } + + return length; + } + + /** + * Calculate length of BER length. + */ + public int calculateLengthOfLength(long length) { + if (length < 0) + throw new RuntimeException("[" + this + "] ERROR: Length of tag cannot be less than zero: " + length + "."); + + if (length <= 0x7f) + return 1; + if (length <= 0xff) + return 2; + if (length <= 0xffFF) + return 3; + if (length <= 0xffFFff) + return 4; + if (length <= 0xffFFffFFL) + return 5; + if (length <= 0xffFFffFFffL) + return 6; + if (length <= 0xffFFffFFffFFL) + return 7; + if (length <= 0xffFFffFFffFFffL) + return 8; + + return 9; + } + + /** + * Calculate length of type to tag number. Values less than 31 are encoded + * using lower 5 bits of first byte of tag. Values larger than 31 are + * indicated by lower 5 bits set to 1 (0x1F, 31), and next bytes are contain + * value in network order, where topmost bit of byte (0x80) indicates is value + * contains more bytes, i.e. last byte of sequence has this bit set to 0. + */ + public int calculateLengthOfTagTypeOrTagNumber(int tagType) { + if (tagType >= EXTENDED_TYPE) + throw new RuntimeException("Multibyte tag types are not supported yet."); + + return 1; + } + + /** + * Calculate length of payload only, without tag prefix, tag type, and + * lengths. + * + * @return + */ + public abstract long calculateLengthOfValuePayload(); + + /** + * Write tag value only, without prefix. + */ + public void writeTagValue(ByteBuffer buf) { + + // Write type + BerType valueType = new BerType(UNIVERSAL_CLASS, constructed, tagType); + writeBerType(buf, valueType); + + // Write length + long lengthOfPayload = calculateLengthOfValuePayload(); + buf.writeBerLength(lengthOfPayload); + + // Store cursor to check is calculated length matches length of actual bytes + // written + int storedCursor = buf.cursor; + + // Write value + writeTagValuePayload(buf); + + // Check is calculated length matches length of actual bytes written, to catch errors early + int actualLength = buf.cursor - storedCursor; + if (actualLength != lengthOfPayload) + throw new RuntimeException("[" + this + "] ERROR: Unexpected length of data in buffer. Expected " + lengthOfPayload + " of bytes of payload, but " + + actualLength + " bytes are written instead. Data: " + buf + "."); + } + + /** + * Write tag value only, without prefix, tag type, and length. + */ + public abstract void writeTagValuePayload(ByteBuffer buf); + + /** + * Read required tag, i.e. we are 100% sure that byte buffer will contain this + * tag, or exception will be thrown otherwise. + * + * @param buf + * buffer with tag data + */ + public void readTag(ByteBuffer buf) { + BerType typeAndFlags = readBerType(buf); + + // * DEBUG */System.out.println("Tag, read " + typeAndFlags); + + if (!isTypeValid(typeAndFlags)) + throw new RuntimeException("[" + this + "] Unexpected type: " + typeAndFlags + "."); + + readTag(buf, typeAndFlags); + } + + /** + * Read tag when it type is already read. + */ + public void readTag(ByteBuffer buf, BerType typeAndFlags) { + + if (explicit) { + long length = buf.readBerLength(); + + if (length > buf.length) + throw new RuntimeException("BER value is too long: " + length + " bytes. Data: " + buf + "."); + + ByteBuffer value = buf.readBytes((int)length); + + readTagValue(value); + + value.unref(); + } else { + + readTagValue(buf, typeAndFlags); + } + } + + /** + * Read tag value only, i.e. it prefix is already read. + */ + public void readTagValue(ByteBuffer value) { + BerType typeAndFlags = readBerType(value); + + // * DEBUG */System.out.println("Tag, read value " + typeAndFlags); + + if (!isTypeValid(typeAndFlags, false)) + throw new RuntimeException("[" + this + "] Unexpected type: " + typeAndFlags + "."); + + readTagValue(value, typeAndFlags); + } + + /** + * Check are tag type and flags valid for this tag. + */ + public final boolean isTypeValid(BerType typeAndFlags) { + return isTypeValid(typeAndFlags, explicit); + } + + /** + * Check are tag type and flags valid for this tag with or without tag prefix. + * + * @param explicit + * if true, then value is wrapped in tag prefix + */ + public boolean isTypeValid(BerType typeAndFlags, boolean explicit) { + if (explicit) + return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber; + else + return typeAndFlags.tagClass == UNIVERSAL_CLASS && !typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagType; + } + + @Override + public String toString() { + return " \nTag [name=" + + name + + + ((constructed) ? ", constructed=" + constructed : "") + + + (", tagType=" + tagTypeOrNumberToString(UNIVERSAL_CLASS, tagType)) + + + ((explicit) ? ", explicit=" + explicit + ", optional=" + optional + ", tagClass=" + tagClassToString(tagClass) + ", tagNumber=" + + tagTypeOrNumberToString(tagClass, tagNumber) : "") + "]"; + } + + public static final String tagTypeOrNumberToString(int tagClass, int tagTypeOrNumber) { + switch (tagClass) { + case UNIVERSAL_CLASS: + switch (tagTypeOrNumber) { + case EOF: + return "EOF"; + case BOOLEAN: + return "BOOLEAN"; + case INTEGER: + return "INTEGER"; + case BIT_STRING: + return "BIT_STRING"; + case OCTET_STRING: + return "OCTET_STRING"; + case NULL: + return "NULL"; + case OBJECT_ID: + return "OBJECT_ID"; + case REAL: + return "REAL"; + case ENUMERATED: + return "ENUMERATED"; + case SEQUENCE: + return "SEQUENCE"; + case SET: + return "SET"; + case NUMERIC_STRING: + return "NUMERIC_STRING"; + case PRINTABLE_STRING: + return "PRINTABLE_STRING"; + case TELETEX_STRING: + return "TELETEX_STRING"; + case VIDEOTEXT_STRING: + return "VIDEOTEXT_STRING"; + case IA5_STRING: + return "IA5_STRING"; + case UTCTIME: + return "UTCTIME"; + case GENERAL_TIME: + return "GENERAL_TIME"; + case GRAPHIC_STRING: + return "GRAPHIC_STRING"; + case VISIBLE_STRING: + return "VISIBLE_STRING"; + case GENERAL_STRING: + return "GENERAL_STRING"; + case EXTENDED_TYPE: + return "EXTENDED_TYPE (multibyte)"; + default: + return "UNKNOWN(" + tagTypeOrNumber + ")"; + + } + + default: + return "[" + tagTypeOrNumber + "]"; + } + } + + public static final String tagClassToString(int tagClass) { + switch (tagClass) { + case UNIVERSAL_CLASS: + return "UNIVERSAL"; + case CONTEXT_CLASS: + return "CONTEXT"; + case APPLICATION_CLASS: + return "APPLICATION"; + case PRIVATE_CLASS: + return "PRIVATE"; + default: + return "UNKNOWN"; + } + } + + /** + * Read BER tag type. + */ + public BerType readBerType(ByteBuffer buf) { + int typeAndFlags = buf.readUnsignedByte(); + + int tagClass = typeAndFlags & CLASS_MASK; + + boolean constructed = (typeAndFlags & CONSTRUCTED) != 0; + + int type = typeAndFlags & TYPE_MASK; + if (type == EXTENDED_TYPE) + throw new RuntimeException("Extended tag types/numbers (31+) are not supported yet."); + + return new BerType(tagClass, constructed, type); + } + + /** + * Write BER tag type. + */ + public void writeBerType(ByteBuffer buf, BerType berType) { + + if (berType.typeOrTagNumber >= EXTENDED_TYPE || berType.typeOrTagNumber < 0) + throw new RuntimeException("Extended tag types/numbers (31+) are not supported yet: " + berType + "."); + + if ((berType.tagClass & CLASS_MASK) != berType.tagClass) + throw new RuntimeException("Value of BER tag class is out of range: " + berType.tagClass + ". Expected values: " + UNIVERSAL_CLASS + ", " + CONTEXT_CLASS + + ", " + APPLICATION_CLASS + ", " + PRIVATE_CLASS + "."); + + int typeAndFlags = berType.tagClass | ((berType.constructed) ? CONSTRUCTED : 0) | berType.typeOrTagNumber; + + buf.writeByte(typeAndFlags); + } + + /** + * Read tag value only, i.e. it prefix is already read, when value type is + * already read. + * + * @param buf + * buffer with tag data + */ + public abstract void readTagValue(ByteBuffer buf, BerType typeAndFlags); + + /** + * Create deep copy of this tag with given suffix appended to name. + * + * @param suffix + * suffix to add to tag name, or empty string + * @return deep copy of this tag + */ + public abstract Tag deepCopy(String suffix); + + /** + * Create deep copy of this tag for array or set. + * + * @param index + * index of element in array or set + * @return deep copy of this tag + */ + public Tag deepCopy(int index) { + return deepCopy("[" + index + "]"); + } + + /** + * Copy tag values from an other tag, except name. + * + * @return this + */ + public Tag copyFrom(Tag tag) { + constructed = tag.constructed; + explicit = tag.explicit; + optional = tag.optional; + tagClass = tag.tagClass; + tagNumber = tag.tagNumber; + return this; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java new file mode 100755 index 00000000000..eb24d11671a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common.opt; + +public class IncrementalOption extends Option { + int value = 0; + + @Override + public int parse(int position, String[] args) { + value++; + return super.parse(position, args); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java new file mode 100755 index 00000000000..87e732a82f4 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java @@ -0,0 +1,41 @@ +// 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 common.opt; + +public class IntOption extends Option { + public int value = 0; + + @Override + public int parse(int position, String[] args) { + if (position + 1 >= args.length) + throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\"."); + + value = Integer.parseInt(args[position + 1]); + return super.parse(position, args) + 1; + } + + @Override + public String help() { + StringBuilder help = new StringBuilder(); + help.append(join("|", name, alias, aliases)).append(" VALUE\t").append(description); + if (required) + help.append(" Required."); + else if (value != 0) + help.append(" Default value is \"").append("" + value).append("\"."); + return help.toString(); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java new file mode 100755 index 00000000000..a074672c489 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.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 common.opt; + +public class NoArgumentForOptionException extends RuntimeException { + public NoArgumentForOptionException(String message) { + super(message); + } + + private static final long serialVersionUID = 1L; + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java new file mode 100755 index 00000000000..e69d3f39304 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java @@ -0,0 +1,102 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common.opt; + +public class Option { + + public String name = ""; + public String alias = null; + public String aliases[] = null; + + public String description = ""; + public boolean used = false; + public boolean required = false; + + /** + * Parse option value, if any. + * + * @param position + * position of this option in list of arguments + * @param args + * command line arguments + * @return how many arguments are consumed, at least 1 + */ + public int parse(int position, String args[]) { + used = true; + return 1; + } + + @Override + public String toString() { + return help(); + } + + /** + * Return help string for this option. Example: + * + *
+     *   --foo|-f    Foo option.
+     * 
+ */ + public String help() { + return join("|", name, alias, aliases) + "\t" + description + ((required) ? " Required." : ""); + } + + /** + * Return string like "--foo|-f|--another-foo-alias". + */ + protected String join(String delim, String name, String alias, String aliases[]) { + + // Option name is mandatory + StringBuilder sb = new StringBuilder(name.length()); + sb.append(name); + + // Alias is optional + if (alias != null && alias.length() > 0) { + sb.append(delim).append(alias); + } + + // Other aliases are optional too + if (aliases != null) { + for (String s : aliases) { + if (s != null && s.length() > 0) { + sb.append(delim).append(s); + } + } + } + + return sb.toString(); + } + + /** + * Return description of options in format suitable for help and usage text. + * + * @param header + * header string to print before list of options + * @param options + * list of options to print + */ + public static String toHelp(String header, Option[] options) { + StringBuffer sb = new StringBuffer(); + sb.append(header).append(":\n"); + for (Option option : options) { + sb.append(" ").append(option.help()).append('\n'); + } + return sb.toString(); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java new file mode 100755 index 00000000000..11c42fec6c6 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java @@ -0,0 +1,147 @@ +// 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 common.opt; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Simple parser of GNU-like options. + */ +public class OptionParser { + + public static Option helpOption() { + return new Option() { + { + name = "--help"; + alias = "-h"; + } + }; + } + + /** + * Parse options, capture values and return rest of arguments. + * + * @param args + * command line arguments to parse + * @param startFrom + * number of first argument to start parsing from + * @param options + * options to fill with values + * @return rest of command line after first non-option or "--" separator + */ + public static String[] parseOptions(String args[], int startFrom, Option options[]) { + // Convert array of options into map, where key is option name or alias + Map optionMap = new HashMap(options.length); + for (Option option : options) { + optionMap.put(option.name, option); + + if (option.alias != null) + optionMap.put(option.alias, option); + + if (option.aliases != null) { + for (String alias : option.aliases) + optionMap.put(alias, option); + } + } + + // Parse arguments + int position = startFrom; + while (position < args.length) { + // Double dash means end of options + String optionName = args[position]; + if (optionName.equals("--")) { + position++; + break; + } + + Option option = optionMap.get(optionName); + + // If option is not found, then this is argument, unless is starts with + // dash + if (option == null) + if (!optionName.startsWith("-")) + break; + else + throw new UnknownOptionException("Option \"" + optionName + + "\" is unknown. If this is not an option, then use \"--\" to separate options and arguments. Known options: " + optionMap.keySet().toString()); + + position += option.parse(position, args); + } + + // Check is required options are used on command line + for (Option option : options) { + if (option.required && !option.used) + throw new OptionRequiredException("Option \"" + option.name + "\" is required."); + } + + // Return rest of arguments, which are left after options + return (position < args.length) ? Arrays.copyOfRange(args, position, args.length) : new String[] {}; + } + + /* Example. */ + public static void main(String args[]) { + if (args.length == 0) + args = new String[] {"--help", "--foo", "fooval", "--bar", "123", "-v", "--verbose", "-v", "-a", "a1", "-aa", "a2", "-aaa", "a3", "rest", "of", + "arguments"}; + + StringOption foo = new StringOption() { + { + name = "--foo"; + alias = "-f"; + value = "fooDefault"; + } + }; + + IntOption bar = new IntOption() { + { + name = "--bar"; + alias = "-b"; + value = 123; + } + }; + + IncrementalOption verbose = new IncrementalOption() { + { + name = "--verbose"; + alias = "-v"; + } + }; + + StringArrayOption array = new StringArrayOption() { + { + name = "--array"; + alias = "-a"; + aliases = new String[] {"-aa", "-aaa"}; + } + }; + + String arguments[] = OptionParser.parseOptions(args, 0, new Option[] {helpOption(), foo, bar, verbose, array}); + + assertTrue(foo.value.equals("fooval")); + assertTrue(bar.value == 123); + assertTrue(verbose.value == 3); + assertTrue(Arrays.equals(array.value, new String[] {"a1", "a2", "a3"})); + assertTrue(Arrays.equals(arguments, new String[] {"rest", "of", "arguments"})); + } + + public static void assertTrue(boolean result) { + if (!result) + throw new AssertionError(); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java new file mode 100755 index 00000000000..b39762793a5 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.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 common.opt; + +public class OptionRequiredException extends RuntimeException { + public OptionRequiredException(String message) { + super(message); + } + + private static final long serialVersionUID = 1L; + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java new file mode 100755 index 00000000000..b2f3c308695 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java @@ -0,0 +1,38 @@ +// 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 common.opt; + +import java.util.Arrays; + +public class StringArrayOption extends Option { + public String value[] = null; + + @Override + public int parse(int position, String[] args) { + if (position + 1 >= args.length) + throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\"."); + + // Append value to end of array of values + if (value == null) { + value = new String[] {args[position + 1]}; + } else { + value = Arrays.copyOf(value, value.length + 1); + value[value.length - 1] = args[position + 1]; + } + return super.parse(position, args) + 1; + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java new file mode 100755 index 00000000000..f59952ed473 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common.opt; + +public class StringEnumerationOption extends Option { + public String value = ""; + public String choices[] = new String[] {}; + + @Override + public int parse(int position, String[] args) { + if (position + 1 >= args.length) + throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\"."); + + value = args[position + 1]; + + for (String s : choices) { + if (value.equals(s)) + return super.parse(position, args) + 1; + } + + throw new NoArgumentForOptionException("Unexpected argument for option \"" + args[position] + "\": \"" + value + "\". Expected argument: " + + join("|", choices) + "."); + } + + @Override + public String help() { + StringBuilder help = new StringBuilder(); + help.append(join("|", name, alias, aliases)).append(" ").append(join("|", choices)).append("\t").append(description); + if (required) + help.append(" Required."); + else if (value != null && value.length() > 0) + help.append(" Default value is \"").append(value).append("\"."); + return help.toString(); + } + + /** + * Join strings in array into one large string. + */ + protected String join(String delim, String values[]) { + StringBuilder sb = new StringBuilder(); + if (values != null) { + boolean first = true; + for (String s : values) { + if (s != null && s.length() > 0) { + if (first) + first = false; + else + sb.append(delim); + + sb.append(s); + } + } + } + + return sb.toString(); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java new file mode 100755 index 00000000000..ec9c82c3da8 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java @@ -0,0 +1,41 @@ +// 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 common.opt; + +public class StringOption extends Option { + public String value = ""; + + @Override + public int parse(int position, String[] args) { + if (position + 1 >= args.length) + throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\"."); + + value = args[position + 1]; + return super.parse(position, args) + 1; + } + + @Override + public String help() { + StringBuilder help = new StringBuilder(); + help.append(join("|", name, alias, aliases)).append(" VALUE\t").append(description); + if (required) + help.append(" Required."); + else if (value != null && value.length() > 0) + help.append(" Default value is \"").append(value).append("\"."); + return help.toString(); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java new file mode 100755 index 00000000000..58cce7327b9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java @@ -0,0 +1,27 @@ +// 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 common.opt; + +public class UnknownOptionException extends RuntimeException { + + public UnknownOptionException(String message) { + super(message); + } + + private static final long serialVersionUID = 1L; + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java deleted file mode 100644 index 06449f6f874..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java +++ /dev/null @@ -1,350 +0,0 @@ -// 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 rdpclient; - -import java.awt.event.KeyEvent; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Link; -import common.KeyOrder; - -public class AwtRdpKeyboardAdapter extends BaseElement { - - /** - * Absence of this flag indicates a key-down event, while its presence - * indicates a key-release event. - */ - public static final int FASTPATH_INPUT_KBDFLAGS_RELEASE = 0x01; - - /** - * Keystroke message contains an extended scancode. For enhanced 101-key and - * 102-key keyboards, extended keys include the right ALT and right CTRL keys - * on the main section of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE - * DOWN and ARROW keys in the clusters to the left of the numeric keypad; and - * the Divide ("/") and ENTER keys in the numeric keypad. - */ - public static final int FASTPATH_INPUT_KBDFLAGS_EXTENDED = 0x02; - - public static final int FASTPATH_INPUT_EVENT_SCANCODE = 0; - - public AwtRdpKeyboardAdapter(String id) { - super(id); - } - - @Override - public void handleData(ByteBuffer buf, Link link) { - if (verbose) - System.out.println("[" + this + "] INFO: Data received: " + buf + "."); - - KeyOrder order = (KeyOrder)buf.getOrder(); - buf.unref(); - - ByteBuffer outBuf = new ByteBuffer(2, true); - - int scanCode = map_en_us(order.event); - - // eventHeader (1 byte): An 8-bit, unsigned integer. The format of this - // field is the same as the eventHeader byte field described in section - // 2.2.8.1.2.2. The eventCode bitfield (3 bits in size) MUST be set to - // FASTPATH_INPUT_EVENT_SCANCODE (0). The eventFlags bitfield (5 bits in - // size) contains flags describing the keyboard event. - outBuf.writeByte((scanCode >> 8) | (FASTPATH_INPUT_EVENT_SCANCODE << 5) | ((order.pressed) ? 0 : FASTPATH_INPUT_KBDFLAGS_RELEASE)); - - // keyCode (1 byte): An 8-bit, unsigned integer. The scancode of the key - // which triggered the event. - outBuf.writeByte(scanCode); - - // Push buffer to one pad only, so it can be modified without copying of - // data - pushDataToPad(STDOUT, outBuf); - } - - /** - * Return key scan code (in lower byte) and extended flags (in second byte). - */ - private int map_en_us(KeyEvent event) { - // Also set extended key flag when necessary. - // For enhanced 101-key and 102-key keyboards, extended keys include the - // right ALT and right CTRL keys on the main section of the keyboard; the - // INS, DEL, HOME, END, PAGE UP, PAGE DOWN and ARROW keys in the clusters to - // the left of the numeric keypad; and the Divide ("/") and ENTER keys in - // the numeric keypad. - - switch (event.getKeyCode()) { - // Functional keys - case KeyEvent.VK_ESCAPE: - return 1; - case KeyEvent.VK_F1: - return 59; - case KeyEvent.VK_F2: - return 60; - case KeyEvent.VK_F3: - return 61; - case KeyEvent.VK_F4: - return 62; - case KeyEvent.VK_F5: - return 63; - case KeyEvent.VK_F6: - return 64; - case KeyEvent.VK_F7: - return 65; - case KeyEvent.VK_F8: - return 66; - case KeyEvent.VK_F9: - return 67; - case KeyEvent.VK_F10: - return 68; - case KeyEvent.VK_F11: - return 87; - case KeyEvent.VK_F12: - return 88; - - // Row #1 - case KeyEvent.VK_BACK_QUOTE: - return 41; - case KeyEvent.VK_1: - return 2; - case KeyEvent.VK_2: - return 3; - case KeyEvent.VK_3: - return 4; - case KeyEvent.VK_4: - return 5; - case KeyEvent.VK_5: - return 6; - case KeyEvent.VK_6: - return 7; - case KeyEvent.VK_7: - return 8; - case KeyEvent.VK_8: - return 9; - case KeyEvent.VK_9: - return 10; - case KeyEvent.VK_0: - return 11; - case KeyEvent.VK_MINUS: - return 12; - case KeyEvent.VK_EQUALS: - return 13; - case KeyEvent.VK_BACK_SPACE: - return 14; - - // Row #2 - case KeyEvent.VK_TAB: - return 15; - case KeyEvent.VK_Q: - return 16; - case KeyEvent.VK_W: - return 17; - case KeyEvent.VK_E: - return 18; - case KeyEvent.VK_R: - return 19; - case KeyEvent.VK_T: - return 20; - case KeyEvent.VK_Y: - return 21; - case KeyEvent.VK_U: - return 22; - case KeyEvent.VK_I: - return 23; - case KeyEvent.VK_O: - return 24; - case KeyEvent.VK_P: - return 25; - case KeyEvent.VK_OPEN_BRACKET: - return 26; - case KeyEvent.VK_CLOSE_BRACKET: - return 27; - case KeyEvent.VK_ENTER: - switch (event.getKeyLocation()) { - default: - case KeyEvent.KEY_LOCATION_STANDARD: - return 28; - case KeyEvent.KEY_LOCATION_NUMPAD: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 28; - } - - // Row #3 - case KeyEvent.VK_CAPS_LOCK: - return 58; - case KeyEvent.VK_A: - return 30; - case KeyEvent.VK_S: - return 31; - case KeyEvent.VK_D: - return 32; - case KeyEvent.VK_F: - return 33; - case KeyEvent.VK_G: - return 34; - case KeyEvent.VK_H: - return 35; - case KeyEvent.VK_J: - return 36; - case KeyEvent.VK_K: - return 37; - case KeyEvent.VK_L: - return 38; - case KeyEvent.VK_SEMICOLON: - return 39; - case KeyEvent.VK_QUOTE: - return 40; - - // Row #4 - case KeyEvent.VK_SHIFT: - switch (event.getKeyLocation()) { - default: - case KeyEvent.KEY_LOCATION_LEFT: - return 42; - case KeyEvent.KEY_LOCATION_RIGHT: - return 54; - } - case KeyEvent.VK_BACK_SLASH: - return 43; - case KeyEvent.VK_Z: - return 44; - case KeyEvent.VK_X: - return 45; - case KeyEvent.VK_C: - return 46; - case KeyEvent.VK_V: - return 47; - case KeyEvent.VK_B: - return 48; - case KeyEvent.VK_N: - return 49; - case KeyEvent.VK_M: - return 50; - case KeyEvent.VK_COMMA: - return 51; - case KeyEvent.VK_PERIOD: - return 52; - case KeyEvent.VK_SLASH: - return 53; - - // - // Bottom row - case KeyEvent.VK_CONTROL: - switch (event.getKeyLocation()) { - default: - case KeyEvent.KEY_LOCATION_LEFT: - return 29; - case KeyEvent.KEY_LOCATION_RIGHT: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 29; - } - case KeyEvent.VK_WINDOWS: - switch (event.getKeyLocation()) { - default: - case KeyEvent.KEY_LOCATION_LEFT: - return 91; - case KeyEvent.KEY_LOCATION_RIGHT: - return 92; - } - case KeyEvent.VK_ALT: - switch (event.getKeyLocation()) { - default: - case KeyEvent.KEY_LOCATION_LEFT: - return 56; - case KeyEvent.KEY_LOCATION_RIGHT: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56; - } - case KeyEvent.VK_ALT_GRAPH: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56; - - case KeyEvent.VK_SPACE: - return 57; - - case KeyEvent.VK_CONTEXT_MENU: - return 93; - - // - // Special keys - case KeyEvent.VK_PRINTSCREEN: - return 55; - case KeyEvent.VK_SCROLL_LOCK: - return 70; - case KeyEvent.VK_PAUSE: - return 29; - - // Text navigation keys - case KeyEvent.VK_INSERT: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 82; - case KeyEvent.VK_HOME: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 71; - case KeyEvent.VK_PAGE_UP: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 73; - case KeyEvent.VK_DELETE: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 83; - case KeyEvent.VK_END: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 79; - case KeyEvent.VK_PAGE_DOWN: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 81; - - // Cursor keys - case KeyEvent.VK_UP: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 72; - case KeyEvent.VK_LEFT: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 75; - case KeyEvent.VK_DOWN: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 80; - case KeyEvent.VK_RIGHT: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 77; - - // Keypad - case KeyEvent.VK_NUM_LOCK: - return 69; - case KeyEvent.VK_DIVIDE: - return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 53; - case KeyEvent.VK_MULTIPLY: - return 55; - case KeyEvent.VK_SUBTRACT: - return 74; - case KeyEvent.VK_ADD: - return 78; - - case KeyEvent.VK_NUMPAD7: - return 71; - case KeyEvent.VK_NUMPAD8: - return 72; - case KeyEvent.VK_NUMPAD9: - return 73; - case KeyEvent.VK_NUMPAD4: - return 75; - case KeyEvent.VK_NUMPAD5: - return 76; - case KeyEvent.VK_NUMPAD6: - return 77; - case KeyEvent.VK_NUMPAD1: - return 79; - case KeyEvent.VK_NUMPAD2: - return 80; - case KeyEvent.VK_NUMPAD3: - return 81; - case KeyEvent.VK_NUMPAD0: - return 82; - case KeyEvent.VK_DECIMAL: - return 83; - - default: - System.err.println("Key is not mapped: " + event + "."); - return 57; // Space - } - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java deleted file mode 100644 index d757e8e7950..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java +++ /dev/null @@ -1,1129 +0,0 @@ -// 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 rdpclient; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Element; -import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; -import streamer.Pipeline; -import streamer.PipelineImpl; -import common.ScreenDescription; - -/** - * @see http://msdn.microsoft.com/en-us/library/cc240488.aspx - */ -public class ClientConfirmActivePDU extends BaseElement { - - public static final String SOURCE_DESC = "MSTSC"; - - public static final int CAPSTYPE_BITMAP = 0x2; - - protected int numberCapabilities; - - protected RdpState state; - protected ScreenDescription screen; - - protected boolean desktopResize = false; - protected int prefferedBitsPerPixel = 16; - - public ClientConfirmActivePDU(String id, ScreenDescription screen, RdpState state) { - super(id); - this.state = state; - this.screen = screen; - } - - @Override - public void handleData(ByteBuffer aBuf, Link link) { - - // Body - ByteBuffer buf = new ByteBuffer(1024, true); - numberCapabilities = 0; - writeCapabilities(buf); - buf.trimAtCursor(); - - // Header - ByteBuffer header = createMCSHeader(buf); - - // Length of source descriptor, including NULL character (LE) - header.writeShortLE(SOURCE_DESC.length() + 1); - - // Length of combined capabilities + 4 bytes (number of capabilities and - // padding) (LE) - header.writeShortLE(buf.length + 4); - - header.writeString(SOURCE_DESC, RdpConstants.CHARSET_8); - header.writeByte(0); - - // Number of capabilities - header.writeShortLE(numberCapabilities); - - // Padding 2 bytes - header.writeShortLE(0); - - header.trimAtCursor(); - - // Prepend header to capabilities - buf.prepend(header); - - // Trim buffer to actual length of data written - buf.length = buf.cursor; - - pushDataToPad(STDOUT, buf); - - sendOtherRequredPackets(); - - } - - private ByteBuffer createMCSHeader(ByteBuffer buf) { - ByteBuffer header = new ByteBuffer(100); - // MCS Send Data Request - header.writeByte(0x64); - - // Initiator: 1004 (1001+3) - header.writeShort(3); - - // Channel ID: 1003 (I/O channel) - header.writeShort(1003); - - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - header.writeByte(0x70); - - int length = buf.length + 26; - - // User data length: (variable length field, LE) - header.writeVariableShort(length); - - // Total length: (LE) - header.writeShortLE(length); - - // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE) - header.writeShortLE(0x13); - - // PDU source: 1004 (LE) - header.writeShortLE(1004); - - // Share ID, e.g. 0x000103ea (LE) - header.writeIntLE((int)state.serverShareId); - - // Originator ID: 1002 (LE) - header.writeShortLE(1002); - return header; - } - - private void sendOtherRequredPackets() { - // Send sequence in bulk - - sendSynchronizePDU(); - sendControlPDUActionCooperate(); - sendControlPDUActionRequestControl(); - // sendBitmapCachePersistentListPDU(); - sendFontListPDU(); - } - - private void sendFontListPDU() { - { - int length = 1024; // Large enough - ByteBuffer buf = new ByteBuffer(length, true); - - /* @formatter:off */ - buf.writeBytes(new byte[] { - // MCS Send Data Request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // User data length: 26 bytes (0x1a, variable length field) - (byte)0x80, (byte)0x1a, - - // Total length: 26 bytes (0x1a, LE) - (byte)0x1a, (byte)0x00, - // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) - (byte)0x17, (byte)0x00, - // PDU source: 1004 (LE) - (byte)0xec, (byte)0x03, - }); - // Share ID, 4 bytes (LE) - buf.writeIntLE((int)state.serverShareId); - - buf.writeBytes(new byte[] { - // Padding 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 12 bytes (LE) - (byte)0x0c, (byte)0x00, - - // pduType2: PDUTYPE2_FONTLIST (39) - (byte)0x27, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE) - (byte)0x00, (byte)0x00, - - // numberEntries (should be set to zero): 0 (LE) - (byte)0x00, (byte)0x00, - // totalNumEntries (should be set to zero): 0 (LE) - (byte)0x00, (byte)0x00, - // listFlags (should be set to 0x3): 0x0003 (LE), FONTLIST_LAST(0x2) | FONTLIST_FIRST(0x1) - (byte)0x03, (byte)0x00, - // entrySize: 50 bytes (0x0032, LE) - (byte)0x32, (byte)0x00, - }); - /* @formatter:on */ - - // Trim buffer to actual length of data written - buf.trimAtCursor(); - - pushDataToPad(STDOUT, buf); - } - } - - private void sendControlPDUActionRequestControl() { - int length = 1024; // Large enough - ByteBuffer buf = new ByteBuffer(length, true); - - /* @formatter:off */ - buf.writeBytes(new byte[] { - // MCS Send Data Request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // User data length: 26 bytes (0x1a, variable length field) - (byte)0x80, (byte)0x1a, - - // Total length: 26 bytes (0x1a, LE) - (byte)0x1a, (byte)0x00, - // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) - (byte)0x17, (byte)0x00, - // PDU source: 1004 (LE) - (byte)0xec, (byte)0x03, - }); - // Share ID, 4 bytes (LE) - buf.writeIntLE((int)state.serverShareId); - - buf.writeBytes(new byte[] { - // Padding 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 12 bytes (LE) - (byte)0x0c, (byte)0x00, - // pduType2: PDUTYPE2_CONTROL (20) - (byte)0x14, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE) - (byte)0x00, (byte)0x00, - - // action: CTRLACTION_REQUEST_CONTROL (1) (LE) - (byte)0x01, (byte)0x00, - // grantId: 0 (LE) - (byte)0x00, (byte)0x00, - // controlId: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - }); - /* @formatter:on */ - - // Trim buffer to actual length of data written - buf.trimAtCursor(); - - pushDataToPad(STDOUT, buf); - } - - private void sendControlPDUActionCooperate() { - int length = 1024; // Large enough - ByteBuffer buf = new ByteBuffer(length, true); - - /* @formatter:off */ - buf.writeBytes(new byte[] { - // MCS Send Data Request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // User data length: 26 bytes (0x1a, variable length field) - (byte)0x80, (byte)0x1a, - - // Total length: 26 bytes (0x1a, LE) - (byte)0x1a,(byte)0x00, - // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) - (byte)0x17, (byte)0x00, - // PDU source: 1004 (LE) - (byte)0xec, (byte)0x03, - }); - // Share ID, 4 bytes (LE) - buf.writeIntLE((int)state.serverShareId); - - buf.writeBytes(new byte[] { - // Padding 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 12 bytes (LE) - (byte)0x0c, (byte)0x00, - // pduType2: PDUTYPE2_CONTROL (20) - (byte)0x14, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE?) - (byte)0x00, (byte)0x00, - // action: CTRLACTION_COOPERATE (4) (LE) - (byte)0x04, (byte)0x00, - // grantId: 0 (LE) - (byte)0x00, (byte)0x00, - // controlId: 0 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - }); - /* @formatter:on */ - - buf.trimAtCursor(); - - pushDataToPad(STDOUT, buf); - } - - private void sendSynchronizePDU() { - - ByteBuffer buf = new ByteBuffer(1024, true); - /* @formatter:off */ - buf.writeBytes(new byte[] { - // MCS send data request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O Channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // Data length: 22 bytes (0x16, variable length field) - (byte)0x80, (byte)0x16, - - // RDP: total length: 22 bytes (LE) - (byte)0x16, (byte)0x00, - - // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) - (byte)0x17, (byte)0x00, - - // PDU source: 1007 (LE) - (byte)0xec, (byte)0x03, - }); - // Share ID, 4 bytes (LE) - buf.writeIntLE((int)state.serverShareId); - - buf.writeBytes(new byte[] { - // Padding: 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 8 bytes (LE) - (byte)0x08, (byte)0x00, - // pduType2 = PDUTYPE2_SYNCHRONIZE (31) - (byte)0x1f, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE?) - (byte)0x00, (byte)0x00, - // messageType: SYNCMSGTYPE_SYNC (1) (LE) - (byte)0x01, (byte)0x00, - // targetUser: 0x03ea - (byte)0xea, (byte)0x03, - }); - /* @formatter:on */ - buf.trimAtCursor(); - pushDataToPad(STDOUT, buf); - } - - private void writeCapabilities(ByteBuffer buf) { - writeGeneralCS(buf); - - writeBitmapCS(buf); - - writeOrderCS(buf); - - writeBitmapCache2CS(buf); - - writeColorTableCacheCS(buf); - - writeWindowActivationCS(buf); - - writeControlCS(buf); - - writePointerCS(buf); - - writeShareCS(buf); - - writeInputCS(buf); - - writeBrushCS(buf); - - writeSoundCS(buf); - - writeFontCS(buf); - - writeOffscreenBitmapCS(buf); - - writeGlyphCacheCS(buf); - } - - private void writeBrushCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Brush Capability Set (8 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240564.aspx - (byte)0x0f, (byte)0x00, // capability set type: CAPSTYPE_BRUSH (15, - // LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // brushSupportLevel: - // BRUSH_DEFAULT - // (0x0, LE) - - }); - } - - private void writeInputCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Input Capability Set (88 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240563.aspx - (byte)0x0d, - (byte)0x00, // capability set type: CAPSTYPE_INPUT (13, LE) - (byte)0x58, - (byte)0x00, // length of capability set: 88 bytes (LE) - (byte)0x35, - (byte)0x00, // inputFlags: 0x0035 (LE), INPUT_FLAG_FASTPATH_INPUT2 - // (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX - // (0x4), INPUT_FLAG_SCANCODES (0x1) - (byte)0x00, - (byte)0x00, // Padding 2 bytes - (byte)0x09, - (byte)0x04, - (byte)0x00, - (byte)0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE) - (byte)0x00, - (byte)0x00, - (byte)0x00, - (byte)0x00, // keyboardType: unknown (LE) - (byte)0x00, - (byte)0x00, - (byte)0x00, - (byte)0x00, // keyboardSubType: unknown (LE) - (byte)0x00, - (byte)0x00, - (byte)0x00, - (byte)0x00, // keyboardFunctionKey: unknown (LE) - // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - - }); - } - - private void writeShareCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Share Capability Set (8 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240570.aspx - (byte)0x09, (byte)0x00, // capability set type: CAPSTYPE_SHARE (9, LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, // nodeID (must be set to 0 by client): 0 (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes (LE) - - }); - } - - private void writePointerCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Pointer Capability Set (10 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240562.aspx - (byte)0x08, (byte)0x00, // capability set type: CAPSTYPE_POINTER (8, - // LE) - (byte)0x0a, (byte)0x00, // length of capability set: 10 bytes (LE) - (byte)0x00, (byte)0x00, // colorPointerFlag: FALSE (LE) - (byte)0x00, (byte)0x00, // colorPointerCacheSize: 0 (LE) - (byte)0x14, (byte)0x00, // pointerCacheSize: 20 (LE) - - }); - } - - private void writeControlCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Control Capability Set (12 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240568.aspx - (byte)0x05, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION - // (7) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, // controlFlags (should be set to 0): 0 (LE) - (byte)0x00, (byte)0x00, // remoteDetachFlag (should be set to 0): 0 - // (LE) - (byte)0x02, (byte)0x00, // controlInterest (should be set to - // CONTROLPRIORITY_NEVER): - // CONTROLPRIORITY_NEVER (2) (LE) - (byte)0x02, (byte)0x00, // detachInterest (should be set to - // CONTROLPRIORITY_NEVER): - // CONTROLPRIORITY_NEVER (2) (LE) - - }); - } - - private void writeWindowActivationCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Window Activation Capability Set (12 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240569.aspx - (byte)0x07, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION - // (7) (LE) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, // helpKeyFlag (should be set to FALSE (0)): - // FALSE (0, LE) - (byte)0x00, (byte)0x00, // helpKeyIndexFlag (should be set to FALSE - // (0)): FALSE (0, LE) - (byte)0x00, (byte)0x00, // helpExtendedKeyFlag (should be set to FALSE - // (0)): FALSE (0, LE) - (byte)0x00, (byte)0x00, // windowManagerKeyFlag (should be set to - // FALSE (0)): FALSE (0, LE) - - }); - } - - private void writeColorTableCacheCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - - // - // Color Table Cache Capability Set (8 bytes), see - // http://msdn.microsoft.com/en-us/library/cc241564.aspx - (byte)0x0a, (byte)0x00, // capability set type: CAPSTYPE_COLORCACHE - // (10) (LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x06, (byte)0x00, // Color table cache size (must be ignored - // during capability exchange and is assumed - // to be 0x0006): 6 (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - }); - } - - private void writeBitmapCache2CS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Bitmap Cache Rev. 2 Capability Set (40 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240560.aspx - (byte)0x13, (byte)0x00, // capability set type: - // CAPSTYPE_BITMAPCACHE_REV2 (19) (LE) - (byte)0x28, (byte)0x00, // length of capability set: 40 bytes (LE) - (byte)0x00, (byte)0x00, // Cache flags: 0 (LE) - (byte)0x00, // Padding 1 byte - (byte)0x00, // Number of cell caches: 0 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache0 - // cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache1 - // cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache2 - // cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache3 - // cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache4 - // cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 12 bytes - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding - }); - } - - private void writeGeneralCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // Capabilities, see - // http://msdn.microsoft.com/en-us/library/cc240486.aspx - - // - // General capability set (24 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240549.aspx - (byte)0x01, (byte)0x00, // capability set type: CAPSTYPE_GENERAL (1) - // (LE) - (byte)0x18, (byte)0x00, // length of capability set: 24 bytes (LE) - (byte)0x01, (byte)0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE) - (byte)0x03, (byte)0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE) - (byte)0x00, (byte)0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x00, (byte)0x00, // generalCompressionTypes: 0 (LE) - - // Extra flags: 0x040d (LE) - // FastPathOutput: (...............1) Advertiser supports fast-path - // output - // ShadowCompression: (..............0.) Advertiser NOT supports shadow - // compression - // LongLengthCredentials: (.............1..) Advertiser supports - // long-length credentials for the user name, password, or domain name - // SessionAutoreconnection: (............1...) Advertiser supports - // session auto-reconnection - // ImprovedEncryptionChecksum: (...........0....) Client and server NOT - // support improved encryption checksum - // Reserved1: (......00000.....) - // CompressedBitMapDataFlag: (.....1..........) No 8-UINT8 header is - // present for compressed bitmap data - // Reserved2: (00000...........) - (byte)0x0d, (byte)0x04, - - (byte)0x00, (byte)0x00, // updateCapabilityFlag: 0 (LE) - (byte)0x00, (byte)0x00, // remoteUnshareFlag: 0 (LE) - (byte)0x00, (byte)0x00, // generalCompressionLevel: 0 (LE) - (byte)0x00, // refreshRectSupport: FALSE (0) - (byte)0x00, // suppressOutputSupport: FALSE (0) - - }); - } - - private void writeBitmapCS(ByteBuffer buf) { - // Bitmap capability set (28 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240554.aspx - - numberCapabilities++; - - // Capability set type: CAPSTYPE_BITMAP (2) (LE) - buf.writeShortLE(CAPSTYPE_BITMAP); - - // Length of capability set: 28 bytes (LE) - buf.writeShortLE(28); - - // preferredBitsPerPixel: 16 bpp (LE) - buf.writeShortLE(prefferedBitsPerPixel); - - // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - buf.writeShortLE(1); - - // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - buf.writeShortLE(1); - - // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - buf.writeShortLE(1); - - // Desktop width and height (LE) - buf.writeShortLE(screen.getFramebufferWidth()); - buf.writeShortLE(screen.getFramebufferHeight()); - - // Padding 2 bytes - buf.writeShortLE(0); - - // desktopResizeFlag (LE) - buf.writeShortLE((desktopResize) ? 1 : 0); - - buf.writeBytes(new byte[] {(byte)0x01, (byte)0x00, // bitmapCompressionFlag (must be set to TRUE - // (0x1)): TRUE (0x1) (LE) - (byte)0x00, // highColorFlags (field is ignored and SHOULD be set to - // zero): 0 - (byte)0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ??? - (byte)0x01, (byte)0x00, // multipleRectangleSupport: TRUE (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - }); - } - - private void writeOrderCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Order Capability Set (88 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240556.aspx - (byte)0x03, - (byte)0x00, // capability set type: CAPSTYPE_ORDER (3) (LE) - (byte)0x58, - (byte)0x00, // length of capability set: 88 bytes (LE) - // terminalDescriptor = "" (16 bytes, UCS2) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // pad4octetsA - (byte)0x01, (byte)0x00, // desktopSaveXGranularity (ignored): 1 (LE) - (byte)0x14, (byte)0x00, // desktopSaveYGranularity (ignored): 20 (LE) - (byte)0x00, (byte)0x00, // pad2octetsA (ignored) - (byte)0x01, (byte)0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1) - (byte)0x00, (byte)0x00, // number of fonts (ignored): 0 - (byte)0x4a, (byte)0x00, // orderFlags = 0x004a (LE), - // SOLIDPATTERNBRUSHONLY (0x40), - // ZEROBOUNDSDELTASSUPPORT (0x8, MUST), - // NEGOTIATEORDERSUPPORT (0x2, MUST) - // Order support: 32 bytes (no primary drawing orders are supported, so - // this array MUST be initialized to all zeros, use 0x01 for TRUE). - (byte)0x00, // TS_NEG_DSTBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_PATBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_SCRBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEMBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEM3BLT_INDEX: FALSE - (byte)0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE - (byte)0x00, // TS_NEG_LINETO_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE - (byte)0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE - (byte)0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE - (byte)0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE - (byte)0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE - (byte)0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYLINE_INDEX: TRUE - (byte)0x00, // Unused: 0 - (byte)0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE - (byte)0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE - (byte)0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE - (byte)0x00, // TS_NEG_INDEX_INDEX: FALSE - (byte)0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // Unused: 0 - (byte)0x00, (byte)0x00, // Text flags (ignored): 0 (LE) - (byte)0x00, (byte)0x00, // Order support extra flags: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 4 bytes - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Desktop save size - // (ignored): 0 - // (assumed to be - // 230400 bytes - // (480*480, - // 0x38400, LE)) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0xe4, (byte)0x04, // Text ANSI Code Page: 1252, ANSI - Latin I - // (0x04e4, LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - }); - } - - private void writeSoundCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Sound Capability Set (8 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240552.aspx - (byte)0x0c, (byte)0x00, // capability set type: CAPSTYPE_SOUND (12, - // LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // soundFlags: - // 0x0000 (LE) // - // SOUND_FLAG_BEEPS - // (0x1) - - }); - } - - private void writeFontCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Font Capability Set (8 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240571.aspx - (byte)0x0e, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, - - }); - } - - private void writeOffscreenBitmapCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Offscreen Bitmap Cache Capability Set (12 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240550.aspx - (byte)0x11, (byte)0x00, // capability set type: - // CAPSTYPE_OFFSCREENCACHE (17, LE) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // offscreenSupportLevel: - // FALSE (LE) - (byte)0x00, (byte)0x00, // offscreenCacheSize: 0 (LE) - (byte)0x00, (byte)0x00, // offscreenCacheEntries: 0 (LE) - - }); - } - - private void writeGlyphCacheCS(ByteBuffer buf) { - numberCapabilities++; - buf.writeBytes(new byte[] { - // - // Glyph Cache Capability Set (52 bytes), see - // http://msdn.microsoft.com/en-us/library/cc240565.aspx - (byte)0x10, (byte)0x00, // capability set type: - // CAPSTYPE_OFFSCREENCACHE (16, LE) - (byte)0x34, (byte)0x00, // length of capability set: 52 bytes (LE) - // Glyph Cache (40 bytes) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x10, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x20, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x40, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x80, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 4 (LE) - (byte)0x40, (byte)0x00, // CacheEntries: 64 (LE) - (byte)0x00, (byte)0x08, // CacheMaximumCellSize: 2048 (LE) - // FragCache - (byte)0x00, (byte)0x01, // CacheEntries: 256 (LE) - (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 256 (LE) - // - (byte)0x00, (byte)0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0, - // LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - }); - } - - /** - * Example. - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - /* @formatter:off */ - byte[] packet = new byte[] { - // MCS Send Data Request - (byte)0x64, - - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - - // Channel ID: 1003 (I/O channel) - (byte)0x03, (byte)0xeb, - - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - - // User data length: 432 bytes (0x1b0, variable length field) - (byte)0x81, (byte)0xb0, - - // Total length: 432 bytes (0x1b0, LE) - (byte)0xb0, (byte)0x01, - - // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE) - (byte)0x13, (byte)0x00, - - // PDU source: 1004 (LE) - (byte)0xec, (byte)0x03, - - // Share ID: 0x000103ea (LE) - (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, - - // Originator ID: 1002 (LE) - (byte)0xea, (byte)0x03, - - // Length of source descriptor: 6 bytes (including NULL character) (LE) - (byte)0x06, (byte)0x00, - - // Length of combined capabilities: 410 bytes (LE) - (byte)0x9a, (byte)0x01, - - // Source descriptor: "MSTSC" ??? - (byte)0x4d, (byte)0x53, (byte)0x54, (byte)0x53, (byte)0x43, (byte)0x00, - - // Number of capabilities: 15 (LE) - (byte)0x0f, (byte)0x00, - - // Padding 2 bytes - (byte)0x00, (byte)0x00, - - // Capabilities, see http://msdn.microsoft.com/en-us/library/cc240486.aspx - - // - // General capability set (24 bytes), see http://msdn.microsoft.com/en-us/library/cc240549.aspx - (byte)0x01, (byte)0x00, // capability set type: CAPSTYPE_GENERAL (1) (LE) - (byte)0x18, (byte)0x00, // length of capability set: 24 bytes (LE) - (byte)0x01, (byte)0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE) - (byte)0x03, (byte)0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE) - (byte)0x00, (byte)0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x00, (byte)0x00, // generalCompressionTypes: 0 (LE) - - // Extra flags: 0x040d (LE) -// FastPathOutput: (...............1) Advertiser supports fast-path output -// ShadowCompression: (..............0.) Advertiser NOT supports shadow compression -// LongLengthCredentials: (.............1..) Advertiser supports long-length credentials for the user name, password, or domain name -// SessionAutoreconnection: (............1...) Advertiser supports session auto-reconnection -// ImprovedEncryptionChecksum: (...........0....) Client and server NOT support improved encryption checksum -// Reserved1: (......00000.....) -// CompressedBitMapDataFlag: (.....1..........) No 8-UINT8 header is present for compressed bitmap data -// Reserved2: (00000...........) - (byte)0x0d, (byte)0x04, - - (byte)0x00, (byte)0x00, // updateCapabilityFlag: 0 (LE) - (byte)0x00, (byte)0x00, // remoteUnshareFlag: 0 (LE) - (byte)0x00, (byte)0x00, // generalCompressionLevel: 0 (LE) - (byte)0x00, // refreshRectSupport: FALSE (0) - (byte)0x00, // suppressOutputSupport: FALSE (0) - - // - // Bitmap capability set (28 bytes), see http://msdn.microsoft.com/en-us/library/cc240554.aspx - (byte)0x02, (byte)0x00, // capability set type: CAPSTYPE_BITMAP (2) (LE) - (byte)0x1c, (byte)0x00, // length of capability set: 28 bytes (LE) - (byte)0x10, (byte)0x00, // preferredBitsPerPixel: 16 bpp (LE) - (byte)0x01, (byte)0x00, // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - (byte)0x01, (byte)0x00, // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - (byte)0x01, (byte)0x00, // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) - (byte)0x00, (byte)0x04, // desktopWidth = 1024 pixels (LE) - (byte)0x00, (byte)0x03, // desktopHeight = 768 pixels (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x00, (byte)0x00, // desktopResizeFlag: FALSE (0x0) (LE) - (byte)0x01, (byte)0x00, // bitmapCompressionFlag (must be set to TRUE (0x1)): TRUE (0x1) (LE) - (byte)0x00, // highColorFlags (field is ignored and SHOULD be set to zero): 0 - (byte)0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ??? - (byte)0x01, (byte)0x00, // multipleRectangleSupport: TRUE (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - // - // Order Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240556.aspx - (byte)0x03, (byte)0x00, // capability set type: CAPSTYPE_ORDER (3) (LE) - (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE) - // terminalDescriptor = "" (16 bytes, UCS2) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // pad4octetsA - (byte)0x01, (byte)0x00, // desktopSaveXGranularity (ignored): 1 (LE) - (byte)0x14, (byte)0x00, // desktopSaveYGranularity (ignored): 20 (LE) - (byte)0x00, (byte)0x00, // pad2octetsA (ignored) - (byte)0x01, (byte)0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1) - (byte)0x00, (byte)0x00, // number of fonts (ignored): 0 - (byte)0x4a, (byte)0x00, // orderFlags = 0x004a (LE), SOLIDPATTERNBRUSHONLY (0x40), ZEROBOUNDSDELTASSUPPORT (0x8, MUST), NEGOTIATEORDERSUPPORT (0x2, MUST) - // Order support: 32 bytes (no primary drawing orders are supported, so this array MUST be initialized to all zeros, use 0x01 for TRUE). - (byte)0x00, // TS_NEG_DSTBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_PATBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_SCRBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEMBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEM3BLT_INDEX: FALSE - (byte)0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE - (byte)0x00, // TS_NEG_LINETO_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE - (byte)0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE - (byte)0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE - (byte)0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE - (byte)0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE - (byte)0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE - (byte)0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE - (byte)0x00, // TS_NEG_POLYLINE_INDEX: TRUE - (byte)0x00, // Unused: 0 - (byte)0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE - (byte)0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE - (byte)0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE - (byte)0x00, // TS_NEG_INDEX_INDEX: FALSE - (byte)0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE - (byte)0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE - (byte)0x00, // Unused: 0 - (byte)0x00, (byte)0x00, // Text flags (ignored): 0 (LE) - (byte)0x00, (byte)0x00, // Order support extra flags: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 4 bytes - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Desktop save size (ignored): 0 (assumed to be 230400 bytes (480*480, 0x38400, LE)) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0xe4, (byte)0x04, // Text ANSI Code Page: 1252, ANSI - Latin I (0x04e4, LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - // - // Bitmap Cache Rev. 2 Capability Set (40 bytes), see http://msdn.microsoft.com/en-us/library/cc240560.aspx - (byte)0x13, (byte)0x00, // capability set type: CAPSTYPE_BITMAPCACHE_REV2 (19) (LE) - (byte)0x28, (byte)0x00, // length of capability set: 40 bytes (LE) - (byte)0x00, (byte)0x00, // Cache flags: 0 (LE) - (byte)0x00, // Padding 1 byte - (byte)0x00, // Number of cell caches: 0 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache0 cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache1 cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache2 cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache3 cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache4 cell info: 0 (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 12 bytes - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding - - // - // Color Table Cache Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc241564.aspx - (byte)0x0a, (byte)0x00, // capability set type: CAPSTYPE_COLORCACHE (10) (LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x06, (byte)0x00, // Color table cache size (must be ignored during capability exchange and is assumed to be 0x0006): 6 (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - - // - // Window Activation Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240569.aspx - (byte)0x07, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7) (LE) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, // helpKeyFlag (should be set to FALSE (0)): FALSE (0, LE) - (byte)0x00, (byte)0x00, // helpKeyIndexFlag (should be set to FALSE (0)): FALSE (0, LE) - (byte)0x00, (byte)0x00, // helpExtendedKeyFlag (should be set to FALSE (0)): FALSE (0, LE) - (byte)0x00, (byte)0x00, // windowManagerKeyFlag (should be set to FALSE (0)): FALSE (0, LE) - - // - // Control Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240568.aspx - (byte)0x05, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, // controlFlags (should be set to 0): 0 (LE) - (byte)0x00, (byte)0x00, // remoteDetachFlag (should be set to 0): 0 (LE) - (byte)0x02, (byte)0x00, // controlInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE) - (byte)0x02, (byte)0x00, // detachInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE) - - // - // Pointer Capability Set (10 bytes), see http://msdn.microsoft.com/en-us/library/cc240562.aspx - (byte)0x08, (byte)0x00, // capability set type: CAPSTYPE_POINTER (8, LE) - (byte)0x0a, (byte)0x00, // length of capability set: 10 bytes (LE) - (byte)0x00, (byte)0x00, // colorPointerFlag: FALSE (LE) - (byte)0x00, (byte)0x00, // colorPointerCacheSize: 0 (LE) - (byte)0x14, (byte)0x00, // pointerCacheSize: 20 (LE) - - // - // Share Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240570.aspx - (byte)0x09, (byte)0x00, // capability set type: CAPSTYPE_SHARE (9, LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, // nodeID (must be set to 0 by client): 0 (LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes (LE) - - // - // Input Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240563.aspx - (byte)0x0d, (byte)0x00, // capability set type: CAPSTYPE_INPUT (13, LE) - (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE) - (byte)0x35, (byte)0x00, // inputFlags: 0x0035 (LE), INPUT_FLAG_FASTPATH_INPUT2 (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX (0x4), INPUT_FLAG_SCANCODES (0x1) - (byte)0x00, (byte)0x00, // Padding 2 bytes - (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardType: unknown (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardSubType: unknown (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardFunctionKey: unknown (LE) - // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - - // - // Brush Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240564.aspx - (byte)0x0f, (byte)0x00, // capability set type: CAPSTYPE_BRUSH (15, LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // brushSupportLevel: BRUSH_DEFAULT (0x0, LE) - - // - // Sound Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240552.aspx - (byte)0x0c, (byte)0x00, // capability set type: CAPSTYPE_SOUND (12, LE) - (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // soundFlags: 0x0000 (LE) // SOUND_FLAG_BEEPS (0x1) - - // - // Font Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240571.aspx - (byte)0x0e, (byte)0x00, - (byte)0x08, (byte)0x00, - (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, - - // - // Offscreen Bitmap Cache Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240550.aspx - (byte)0x11, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (17, LE) - (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // offscreenSupportLevel: FALSE (LE) - (byte)0x00, (byte)0x00, // offscreenCacheSize: 0 (LE) - (byte)0x00, (byte)0x00, // offscreenCacheEntries: 0 (LE) - - // - // Glyph Cache Capability Set (52 bytes), see http://msdn.microsoft.com/en-us/library/cc240565.aspx - (byte)0x10, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (16, LE) - (byte)0x34, (byte)0x00, // length of capability set: 52 bytes (LE) - // Glyph Cache (40 bytes) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x10, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x20, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x40, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x80, (byte)0x00, // CacheMaximumCellSize: 4 (LE) - (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) - (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 4 (LE) - (byte)0x40, (byte)0x00, // CacheEntries: 64 (LE) - (byte)0x00, (byte)0x08, // CacheMaximumCellSize: 2048 (LE) - // FragCache - (byte)0x00, (byte)0x01, // CacheEntries: 256 (LE) - (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 256 (LE) - // - (byte)0x00, (byte)0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0, LE) - (byte)0x00, (byte)0x00, // Padding 2 bytes - }; - /* @formatter:on */ - - RdpState rdpState = new RdpState(); - ScreenDescription screenDescription = new ScreenDescription(); - screenDescription.setFramebufferSize(1024, 768); - - rdpState.serverShareId = 0x000103ea; - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {})); - Element confirm_active = new ClientConfirmActivePDU("confirm_active", screenDescription, rdpState); - Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, confirm_active, sink); - pipeline.link("source", "confirm_active", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java deleted file mode 100644 index 12692840906..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientInfoPDU.java +++ /dev/null @@ -1,455 +0,0 @@ -// 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 rdpclient; - -import streamer.ByteBuffer; -import streamer.Element; -import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; -import streamer.OneTimeSwitch; -import streamer.Pipeline; -import streamer.PipelineImpl; - -/** - * @see http://msdn.microsoft.com/en-us/library/cc240475.aspx - */ -public class ClientInfoPDU extends OneTimeSwitch { - - public static final int INFO_MOUSE = 0x1; - public static final int INFO_DISABLECTRLALTDEL = 0x2; - public static final int INFO_UNICODE = 0x10; - - public static final int INFO_MAXIMIZESHELL = 0x20; - public static final int INFO_LOGONNOTIFY = 0x40; - public static final int INFO_ENABLEWINDOWSKEY = 0x100; - public static final int INFO_MOUSE_HAS_WHEEL = 0x00020000; - public static final int INFO_NOAUDIOPLAYBACK = 0x00080000; - - public static final int PERF_DISABLE_WALLPAPER = 0x1; - public static final int PERF_DISABLE_FULLWINDOWDRAG = 0x2; - public static final int PERF_DISABLE_MENUANIMATIONS = 0x4; - - protected byte[] userName = "".getBytes(RdpConstants.CHARSET_16); - protected byte[] password = "".getBytes(RdpConstants.CHARSET_16); // No effect - protected byte[] alternateShell = "".getBytes(RdpConstants.CHARSET_16); - protected byte[] domain = "".getBytes(RdpConstants.CHARSET_16); - protected byte[] workingDir = "".getBytes(RdpConstants.CHARSET_16); - protected byte[] clientAddress = "192.168.0.100".getBytes(RdpConstants.CHARSET_16); - protected byte[] clientDir = "C:\\Windows\\System32\\mstscax.dll".getBytes(RdpConstants.CHARSET_16); - - protected String standardTimeZoneName = "EET, Standard Time"; - protected String daylightTimeZoneName = "EET, Summer Time"; - protected int standardTimeZoneBias = 0; /* in minutes */ - protected int daylightTimeZoneBias = 60; /* in minutes */ - - public ClientInfoPDU(String id, String userName) { - super(id); - this.userName = userName.getBytes(RdpConstants.CHARSET_16); - } - - @Override - protected void handleOneTimeData(ByteBuffer buf, Link link) { - if (buf == null) - return; - - throw new RuntimeException("Unexpected packet: " + buf + "."); - } - - @Override - protected void onStart() { - super.onStart(); - - // Length of packet - ByteBuffer buf = new ByteBuffer(1024, true); - - // MCS Send Data Request PDU - buf.writeByte(0x64); - - // Initiator: 0x03 + 1001 = 1004 - buf.writeShort(3); - - // Channel ID: 1003 - buf.writeShort(1003); - - // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70) - buf.writeByte(0x70); - - // User data length: (variable length field) - int length = 224 + userName.length + password.length + alternateShell.length + domain.length + workingDir.length + clientAddress.length + clientDir.length; - buf.writeShort(length | 0x8000); - - // Flags: SEC_INFO_PKT (0x4000) - buf.writeShort(0x4000); - - // TS_SECURITY_HEADER::flagsHi - ignored - buf.writeShort(0x0000); - - // Codepage: 0 (UNKNOWN, LE) (use 0x04090409 (1033,1033) for EN_US) - buf.writeIntLE(0x0000); - - // Flags - buf.writeIntLE(INFO_MOUSE | INFO_DISABLECTRLALTDEL | INFO_UNICODE | INFO_MAXIMIZESHELL | INFO_LOGONNOTIFY | INFO_ENABLEWINDOWSKEY | INFO_MOUSE_HAS_WHEEL | - INFO_NOAUDIOPLAYBACK); - - // - // Lengths - // - - // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) - buf.writeShortLE(domain.length); - - // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator) - buf.writeShortLE(userName.length); - - // cbPassword length: (LE) (NOT including size of mandatory NULL terminator) - buf.writeShortLE(password.length); - - // cbAlternateShell: (LE) (NOT including size of mandatory NULL terminator) - buf.writeShortLE(alternateShell.length); - - // cbWorkingDir: (LE) (NOT including size of mandatory NULL terminator) - buf.writeShortLE(workingDir.length); - - // - // Values - // - - // Domain: (UCS2), see cbDomain - buf.writeBytes(domain); - buf.writeShort(0); - - // User name: (UCS2), see cbUserName - buf.writeBytes(userName); - buf.writeShort(0); - - // Password: (UCS2), see cbPassword - buf.writeBytes(password); - buf.writeShort(0); - - // Alternate shell: (UCS2), see cbAlternateShell - buf.writeBytes(alternateShell); - buf.writeShort(0); - - // Working directory: (UCS2), see cbWorkingDir - buf.writeBytes(workingDir); - buf.writeShort(0); - - // Client address family: 2 (AF_INET, LE) - buf.writeShortLE(2); - - // cbClientAddress: ( LE) (including the size of the mandatory NULL terminator) - buf.writeShortLE(clientAddress.length + 2); - - // Client address: (UCS2) - buf.writeBytes(clientAddress); - buf.writeShort(0); - - // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator) - buf.writeShortLE(clientDir.length + 2); - - // Client directory: (UCS2) - buf.writeBytes(clientDir); - buf.writeShort(0); - - // - // Client time zone: - // - - // Bias: 0 minutes (LE) - buf.writeIntLE(0); - - // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2) - buf.writeFixedString(62, standardTimeZoneName, RdpConstants.CHARSET_16); - buf.writeShort(0); - - // Standard date - buf.writeBytes(new byte[] { - // wYear: 0 (LE) - (byte)0x00, (byte)0x00, - // wMonth: unknown (LE) - (byte)0x00, (byte)0x00, - // wDayOfWeek: Sunday (LE) - (byte)0x00, (byte)0x00, - // wDay: unknown (LE) - (byte)0x00, (byte)0x00, - // wHour: 0 (LE) - (byte)0x00, (byte)0x00, - // wMinute: 0 (LE) - (byte)0x00, (byte)0x00, - // wSecond: 0 (LE) - (byte)0x00, (byte)0x00, - // wMilliseconds: 0 - (byte)0x00, (byte)0x00, - - }); - - // StandardBias: 0 minutes (LE) - buf.writeIntLE(standardTimeZoneBias); - - // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2) - buf.writeFixedString(62, daylightTimeZoneName, RdpConstants.CHARSET_16); - buf.writeShort(0); - - // Daylight date - buf.writeBytes(new byte[] { - // wYear: 0 (LE) - (byte)0x00, (byte)0x00, - // wMonth: unknown (LE) - (byte)0x00, (byte)0x00, - // wDayOfWeek: Sunday (LE) - (byte)0x00, (byte)0x00, - // wDay: unknown (LE) - (byte)0x00, (byte)0x00, - // wHour: 0 (LE) - (byte)0x00, (byte)0x00, - // wMinute: 0 (LE) - (byte)0x00, (byte)0x00, - // wSecond: 0 (LE) - (byte)0x00, (byte)0x00, - // wMilliseconds: 0 - (byte)0x00, (byte)0x00, - - }); - - // Daylight bias: 60 minutes (LE) - buf.writeIntLE(daylightTimeZoneBias); - - // Client session ID: 0x00000000 (LE) - buf.writeIntLE(0); - - // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4) - buf.writeIntLE(PERF_DISABLE_WALLPAPER | PERF_DISABLE_FULLWINDOWDRAG | PERF_DISABLE_MENUANIMATIONS); - - // cbAutoReconnectCookie: 0 bytes (LE) - buf.writeShortLE(0); - - // Trim buffer to actual length of data written - buf.length = buf.cursor; - - pushDataToOTOut(buf); - - switchOff(); - } - - /** - * Example. - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - /* @formatter:off */ - byte[] packet = new byte[] { - - // TPKT - (byte) 0x03, (byte) 0x00, - - // TPKT length: 343 bytes - (byte) 0x01, (byte) 0x57, - - // X224 Data PDU - (byte) 0x02, (byte) 0xf0, (byte) 0x80, - - - // MCS Send Data Request PDU - (byte) 0x64, - - // Initiator: 0x03 + 1001 = 1004 - (byte) 0x00, (byte) 0x03, - - // Channel ID: 1003 (IO Channel) - (byte) 0x03, (byte) 0xeb, - - // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70) - (byte) 0x70, - - // User data length: 328 (0x148) bytes, variable length field - (byte) 0x81, (byte) 0x48, - - // Flags: SEC_INFO_PKT (0x4000) - (byte) 0x40, (byte) 0x00, - - // TS_SECURITY_HEADER::flagsHi - ignored - (byte) 0x00, (byte) 0x00, - - // Codepage: 0 (UNKNOWN, LE) (use 0x04090409 (1033,1033) for EN_US) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // Flags: 0xa0173 (LE), INFO_MOUSE (0x1), INFO_DISABLECTRLALTDEL (0x2), INFO_UNICODE (0x10), - // INFO_MAXIMIZESHELL (0x20), INFO_LOGONNOTIFY (0x40), INFO_ENABLEWINDOWSKEY (0x100), - // INFO_MOUSE_HAS_WHEEL (0x00020000), INFO_NOAUDIOPLAYBACK (0x00080000), - (byte) 0x73, (byte) 0x01, (byte) 0x0a, (byte) 0x00, - - // Lengths - - // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) - (byte) 0x00, (byte) 0x00, - - // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator) - (byte) 0x10, (byte) 0x00, - - // cbPassword length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) - (byte) 0x00, (byte) 0x00, - - // cbAlternateShell: 0 bytes (LE) (NOT including size of mandatory NULL terminator) - (byte) 0x00, (byte) 0x00, - - // cbWorkingDir: 0 bytes (LE) (NOT including size of mandatory NULL terminator) - (byte) 0x00, (byte) 0x00, - - // Values - - // Domain: "" (UCS2), see cbDomain - (byte) 0x00, (byte) 0x00, - - // User name: "vlisivka" (UCS2), see cbUserName - (byte) 0x76, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x73, (byte) 0x00, - (byte) 0x69, (byte) 0x00, (byte) 0x76, (byte) 0x00, (byte) 0x6b, (byte) 0x00, (byte) 0x61, (byte) 0x00, - (byte) 0x00, (byte) 0x00, - - // Password: "" (UCS2), see cbPassword - (byte) 0x00, (byte) 0x00, - - // Alternate shell: "" (UCS2), see cbAlternateShell - (byte) 0x00, (byte) 0x00, - - // Working directory: "" (UCS2), see cbWorkingDir - (byte) 0x00, (byte) 0x00, - - // Client address family: 2 (AF_INET, LE) - (byte) 0x02, (byte) 0x00, - - // cbClientAddress = 28 bytes (0x1c, LE) (including the size of the mandatory NULL terminator) - (byte) 0x1c, (byte) 0x00, - - // Client address: "192.168.0.100" (UCS2) - (byte) 0x31, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x2e, (byte) 0x00, - (byte) 0x31, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x38, (byte) 0x00, (byte) 0x2e, (byte) 0x00, - (byte) 0x30, (byte) 0x00, (byte) 0x2e, (byte) 0x00, (byte) 0x31, (byte) 0x00, (byte) 0x30, (byte) 0x00, - (byte) 0x30, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator) - (byte) 0x40, (byte) 0x00, - - // Client directory: "C:\Windows\System32\mstscax.dll" (UCS2) - (byte) 0x43, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x57, (byte) 0x00, - (byte) 0x69, (byte) 0x00, (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x6f, (byte) 0x00, - (byte) 0x77, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x53, (byte) 0x00, - (byte) 0x79, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65, (byte) 0x00, - (byte) 0x6d, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x5c, (byte) 0x00, - (byte) 0x6d, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x73, (byte) 0x00, - (byte) 0x63, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x78, (byte) 0x00, (byte) 0x2e, (byte) 0x00, - (byte) 0x64, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // - // Client time zone: - - // Bias: 0 minutes (LE) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2) - (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61, (byte) 0x00, - (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72, (byte) 0x00, - (byte) 0x64, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, - (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // - // Standard date - // wYear: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMonth: unknown (LE) - (byte) 0x00, (byte) 0x00, - // wDayOfWeek: Sunday (LE) - (byte) 0x00, (byte) 0x00, - // wDay: unknown (LE) - (byte) 0x00, (byte) 0x00, - // wHour: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMinute: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wSecond: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMilliseconds: 0 - (byte) 0x00, (byte) 0x00, - - // StandardBias: 0 minutes (LE) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2) - (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, - (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x75, (byte) 0x00, (byte) 0x6d, (byte) 0x00, - (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x20, (byte) 0x00, - (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // Daylight date - // wYear: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMonth: unknown (LE) - (byte) 0x00, (byte) 0x00, - // wDayOfWeek: Sunday (LE) - (byte) 0x00, (byte) 0x00, - // wDay: unknown (LE) - (byte) 0x00, (byte) 0x00, - // wHour: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMinute: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wSecond: 0 (LE) - (byte) 0x00, (byte) 0x00, - // wMilliseconds: 0 - (byte) 0x00, (byte) 0x00, - - // Daylight bias: 60 minutes (LE) - (byte) 0x3c, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - - // Client session ID: 0x00000000 (LE) - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4) - (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, - - // cbAutoReconnectCookie: 0 bytes (LE) - (byte) 0x00, (byte) 0x00, - }; - /* @formatter:on */ - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); - Element todo = new ClientInfoPDU("client_info", "vlisivka"); - Element x224 = new ClientX224DataPdu("x224"); - Element tpkt = new ClientTpkt("tpkt"); - Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); - Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, todo, x224, tpkt, sink, mainSink); - pipeline.link("source", "client_info", "mainSink"); - pipeline.link("client_info >" + OTOUT, "x224", "tpkt", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java deleted file mode 100644 index e910c10fcea..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java +++ /dev/null @@ -1,669 +0,0 @@ -// 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 rdpclient; - -import streamer.ByteBuffer; -import streamer.Element; -import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; -import streamer.OneTimeSwitch; -import streamer.Pipeline; -import streamer.PipelineImpl; - -public class ClientMCSConnectInitial extends OneTimeSwitch { - - public ClientMCSConnectInitial(String id) { - super(id); - } - - @Override - protected void handleOneTimeData(ByteBuffer buf, Link link) { - if (buf == null) - return; - - throw new RuntimeException("Unexpected packet: " + buf + "."); - } - - @Override - protected void onStart() { - super.onStart(); - - int length = 1024; // Large enough - ByteBuffer buf = new ByteBuffer(length, true); - - /* @formatter:off */ - buf.writeBytes(new byte[] { -// - T125: MCSConnect Initial -// - MCSConnectInitial: Identifier=Generic Conference Control (0.0.20.124.0.1), ConnectPDULength=254 -// - ConnectInitialHeader: - (byte)0x7F, (byte)0x65, -// - AsnId: Application Constructed Tag (101) -// - HighTag: -// Class: (01......) Application (1) -// Type: (..1.....) Constructed -// TagNumber: (...11111) -// TagValueEnd: 101 (0x65) - (byte)0x82, (byte)0x01, (byte)0x6C, -// - AsnLen: Length = 364, LengthOfLength = 2 -// LengthType: LengthOfLength = 2 -// Length: 364 bytes - (byte)0x04, (byte)0x01, (byte)0x01, -// - CallingDomainSelector: 0x1 -// - AsnOctetStringHeader: -// - AsnId: OctetString type (Universal 4) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00100) 4 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// OctetStream: 0x1 - (byte)0x04, (byte)0x01, (byte)0x01, -// - CalledDomainSelector: 0x1 -// - AsnOctetStringHeader: -// - AsnId: OctetString type (Universal 4) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00100) 4 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// OctetStream: 0x1 - (byte)0x01, (byte)0x01, (byte)0xFF, -// - UpwardFlag: True -// - AsnBooleanHeader: -// - AsnId: Boolean type (Universal 1) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00001) 1 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// Tf: 255 (0xFF) - -// -// - TargetParameters: Length = 26, LengthOfLength = 0 - (byte)0x30, (byte)0x1A, -// - DomainParametersHeader: 0x1 -// - AsnId: Sequence and SequenceOf types (Universal 16) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..1.....) Constructed -// TagValue: (...10000) 16 -// - AsnLen: Length = 26, LengthOfLength = 0 -// Length: 26 bytes, LengthOfLength = 0 - (byte)0x02, (byte)0x01, (byte)0x22, -// - ChannelIds: 34 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 34 (0x22) - (byte)0x02, (byte)0x01, (byte)0x02, -// - UserIDs: 2 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 2 (0x2) - (byte)0x02, (byte)0x01, (byte)0x00, -// - TokenIds: 0 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 0 (0x0) - (byte)0x02, (byte)0x01, (byte)0x01, -// - NumPriorities: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x00, -// - MinThroughput: 0 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 0 (0x0) - (byte)0x02, (byte)0x01, (byte)0x01, -// - Height: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, -// - MCSPDUsize: 65535 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 3, LengthOfLength = 0 -// Length: 3 bytes, LengthOfLength = 0 -// AsnInt: 65535 (0xFFFF) - (byte)0x02, (byte)0x01, (byte)0x02, -// - protocolVersion: 2 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 2 (0x2) - -// -// - MinimumParameters: Length = 25, LengthOfLength = 0 - (byte)0x30, (byte)0x19, -// - DomainParametersHeader: 0x1 -// - AsnId: Sequence and SequenceOf types (Universal 16) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..1.....) Constructed -// TagValue: (...10000) 16 -// - AsnLen: Length = 25, LengthOfLength = 0 -// Length: 25 bytes, LengthOfLength = 0 - (byte)0x02, (byte)0x01, (byte)0x01, -// - ChannelIds: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x01, -// - UserIDs: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x01, -// - TokenIds: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x01, -// - NumPriorities: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x00, -// - MinThroughput: 0 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 0 (0x0) - (byte)0x02, (byte)0x01, (byte)0x01, -// - Height: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, -// - MCSPDUsize: 1056 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 2, LengthOfLength = 0 -// Length: 2 bytes, LengthOfLength = 0 -// AsnInt: 1056 (0x420) - (byte)0x02, (byte)0x01, (byte)0x02, -// - protocolVersion: 2 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 2 (0x2) -// - MaximumParameters: Length = 31, LengthOfLength = 0 -// - DomainParametersHeader: 0x1 - (byte)0x30, (byte)0x1F, -// - AsnId: Sequence and SequenceOf types (Universal 16) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..1.....) Constructed -// TagValue: (...10000) 16 -// - AsnLen: Length = 31, LengthOfLength = 0 -// Length: 31 bytes, LengthOfLength = 0 - (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, -// - ChannelIds: 65535 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 3, LengthOfLength = 0 -// Length: 3 bytes, LengthOfLength = 0 -// AsnInt: 65535 (0xFFFF) - (byte)0x02, (byte)0x02, (byte)0xFC, (byte)0x17, -// - UserIDs: 64535 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 2, LengthOfLength = 0 -// Length: 2 bytes, LengthOfLength = 0 -// AsnInt: 64535 (0xFC17) - (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, -// - TokenIds: 65535 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 3, LengthOfLength = 0 -// Length: 3 bytes, LengthOfLength = 0 -// AsnInt: 65535 (0xFFFF) - (byte)0x02, (byte)0x01, (byte)0x01, -// - NumPriorities: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x01, (byte)0x00, -// - MinThroughput: 0 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 0 (0x0) - (byte)0x02, (byte)0x01, (byte)0x01, -// - Height: 1 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 1 (0x1) - (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, -// - MCSPDUsize: 65535 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 3, LengthOfLength = 0 -// Length: 3 bytes, LengthOfLength = 0 -// AsnInt: 65535 (0xFFFF) - (byte)0x02, (byte)0x01, (byte)0x02, -// - protocolVersion: 2 -// - AsnIntegerHeader: -// - AsnId: Integer type (Universal 2) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00010) 2 -// - AsnLen: Length = 1, LengthOfLength = 0 -// Length: 1 bytes, LengthOfLength = 0 -// AsnInt: 2 (0x2) -// - UserData: Identifier=Generic Conference Contro (0.0.20.124.0.1), ConnectPDULength=254 -// - UserDataHeader: - (byte)0x04, (byte)0x82, (byte)0x01, (byte)0x07, -// - AsnId: OctetString type (Universal 4) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00100) 4 -// - AsnLen: Length = 263, LengthOfLength = 2 -// LengthType: LengthOfLength = 2 -// Length: 263 bytes - (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7C, (byte)0x00, (byte)0x01, -// - AsnBerObjectIdentifier: Generic Conference Contro (0.0.20.124.0.1) -// - AsnObjectIdentifierHeader: -// - AsnId: Reserved for use by the encoding rules (Universal 0) -// - LowTag: -// Class: (00......) Universal (0) -// Type: (..0.....) Primitive -// TagValue: (...00000) 0 -// - AsnLen: Length = 5, LengthOfLength = 0 -// Length: 5 bytes, LengthOfLength = 0 -// First: 0 (0x0) -// Final: 20 (0x14) -// Final: 124 (0x7C) -// Final: 0 (0x0) -// Final: 1 (0x1) - (byte)0x80, (byte)0xFE, -// - ConnectPDULength: 254 -// Align: No Padding -// Length: 254 - (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, -// - ConnectGCCPDU: conferenceCreateRequest -// ExtensionBit: 0 (0x0) -// - ChoiceValue: conferenceCreateRequest -// Value: (000.....) 0x0 -// - conferenceCreateRequest: -// ExtensionBit: 0 (0x0) -// convenerPasswordPresent: 0 (0x0) -// passwordPresent: 0 (0x0) -// conductorPrivilegesPresent: 0 (0x0) -// conductedPrivilegesPresent: 0 (0x0) -// nonConductedPrivilegesPresent: 0 (0x0) -// conferenceDescriptionPresent: 0 (0x0) -// callerIdentifierPresent: 0 (0x0) -// userDataPresent: 1 (0x1) -// - conferenceName: -// ExtensionBit: 0 (0x0) -// textPresent: 0 (0x0) -// - numeric: 1 -// - SimpleNumericString: 1 -// - NumericString: 1 -// - Align: No Padding -// Padding1: (0.......) 0x0 -// - Length: 1 -// Value: (00000000) 0x0 -// - Restrictedstr: 1 -// FourBits: (0001....) 0x1 -// - lockedConference: False -// Value: False 0....... -// - listedConference: False -// Value: False 0....... -// - conductibleConference: False -// Value: False 0....... -// - TerminationMethod: automatic -// ExtensionBit: 0 (0x0) -// - RootIndex: 0 -// Value: (0.......) 0x0 -// - userData: - (byte)0x00, (byte)0x01, -// - Size: 1 -// - Align: No Padding -// Padding7: (0000000.) 0x0 -// Length: 1 -// - UserData: 0x44756361 - (byte)0xC0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, -// valuePresent: 1 (0x1) -// - key: h221NonStandard "Duca" -// - ChoiceValue: h221NonStandard -// Value: (1.......) 0x1 -// - h221NonStandard: -// - H221NonStandardIdentifier: length: 4 -// - ConstrainedLength: 4 -// Value: (00000000) 0x0 -// - Align: No Padding -// Padding6: (000000..) 0x0 -// Value: Binary Large Object (4 Bytes) "Duca" -// - ClientMcsConnectInitialPdu: - (byte)0x80, (byte)0xF0, -// - RDPGCCUserDataRequestLength: 240 -// Align: No Padding -// Length: 240 -// - TsUd: CS_CORE - (byte)0x01, (byte)0xC0, (byte)0xD8, (byte)0x00, -// - TsUdHeader: Type = CS_CORE, Length = 216 -// Type: CS_CORE -// Length: 216 (0xD8) -// - TsUdCsCore: - (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, -// Version: RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 - (byte)0x00, (byte)0x04, -// DesktopWidth: 1024 (0x400) - (byte)0x00, (byte)0x03, -// DesktopHeight: 768 (0x300) - (byte)0x01, (byte)0xCA, -// ColorDepth: 8 bpp - (byte)0x03, (byte)0xAA, -// SASSequence: 0xaa03, SHOULD be set to RNS_UD_SAS_DEL(0xAA03) - (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, -// KeyboardLayout: Language: English, Location: United States - (byte)0x28, (byte)0x0A, (byte)0x00, (byte)0x00, -// ClientBuild: 2600 (0xA28) - (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// ClientName: apollo3 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// KeyboardType: Undefined value: 0 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// KeyboardSubType: 0 (0x0) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// KeyboardFunctionKey: 0 (0x0) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// ImeFileName: - (byte)0x01, (byte)0xCA, -// PostBeta2ColorDepth: 8 bpp - (byte)0x01, (byte)0x00, -// ClientProductId: 0x1, SHOULD be set to initialized to 1 - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// SerialNumber: 0x0, SHOULD be set to 0 - (byte)0x10, (byte)0x00, -// HighColorDepth: 16-bit 565 RGB - (byte)0x07, (byte)0x00, -// - SupportedColorDepth: 7 (0x7) -// Support24BPP: (...............1) Support 24BPP -// Support16BPP: (..............1.) Support 16BPP -// Support15BPP: (.............1..) Support 15BPP -// Support32BPP: (............0...) Not Support 32BPP -// Reserved: (000000000000....) - (byte)0x01, (byte)0x00, -// - EarlyCapabilityFlags: 1 (0x1) -// SupportSetErrorPdu: (...............1) Indicates that the client supports the Set Error Info PDU -// Want32BppSession: (..............0.) Client is not requesting 32BPP session -// SupportStatusInfoPdu: (.............0..) Client not supports the Server Status Info PDU -// StrongAsymmetricKeys: (............0...) Not support asymmetric keys larger than 512-bits -// Unused: (...........0....) -// ValidConnection: (..........0.....) Not Indicates ConnectionType field contains valid data -// SupportMonitorLayoutPdu: (.........0......) Not Indicates that the client supports the Monitor Layout PDU -// Unused2: (000000000.......) - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// ClientDigProductId: -(byte)0x00, -// connectionType: invalid connection type -(byte)0x00, -// pad1octet: 0 (0x0) -(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, -// ServerSelectedProtocols: TLS 1.0 -// -// - TsUd: CS_CLUSTER -// - TsUdHeader: Type = CS_CLUSTER, Length = 12 -(byte)0x04, (byte)0xC0, -// Type: CS_CLUSTER -(byte)0x0C, (byte)0x00, -// Length: 12 (0xC) -(byte)0x0D, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// - TsUdCsCluster: -// - Flags: 13 (0xD) -// RedirectedSupported: (...............................1) Support Redirected -// SessionIDFieldValid: (..............................0.) SessionID Field not Valid -// SupportedVersion: (..........................0011..) REDIRECTION_VERSION4 -// RedirectedSmartcard: (.........................0......) Not Logon with Smartcard -// Unused: (0000000000000000000000000.......) -// RedirectedSessionID: 0 (0x0) -// -// - TsUd: CS_SECURITY -// - TsUdHeader: Type = CS_SECURITY, Length = 12 -(byte)0x02, (byte)0xC0, -// Type: CS_SECURITY -(byte)0x0C, (byte)0x00, -// Length: 12 (0xC) -// -// - TsUdCsSec: -(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// - EncryptionMethod: -// Support40Bit: (...............................0) Not Support -// Support128Bit: (..............................0.) Not Support 128-bit -// Reserved1: (.............................0..) -// Support56Bit: (............................0...) Not Support 56-bit -// SupportFIPS: (...........................0....) Not Support FIPS Compliant -// Reserved2: (000000000000000000000000000.....) -(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, -// - ExtEncryptionMethod: -// Support40Bit: (...............................0) Not Support -// Support128Bit: (..............................0.) Not Support 128-bit -// Reserved1: (.............................0..) -// Support56Bit: (............................0...) Not Support 56-bit -// SupportFIPS: (...........................0....) Not Support FIPS Compliant -// Reserved2: (000000000000000000000000000.....) - }); - /* @formatter:on */ - - buf.length = buf.cursor; - - pushDataToOTOut(buf); - - switchOff(); - } - - /** - * Example. - * - * @see http://msdn.microsoft.com/en-us/library/cc240836.aspx - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - /* @formatter:off */ - byte[] packet = new byte[] { - // TPKT: TPKT version = 3 - (byte) 0x03, (byte) 0x00, - // TPKT: Packet length: 378 bytes - (byte) 0x01, (byte) 0x78, - - // X.224: Length indicator = 2 - (byte) 0x02, - // X.224: Type: Data TPDU - (byte) 0xf0, - // X.224: EOT - (byte) 0x80, - - // Captured packet - (byte)0x7f, (byte)0x65, (byte)0x82, (byte)0x01, (byte)0x6c, (byte)0x04, (byte)0x01, (byte)0x01, (byte)0x04, - (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0xff, (byte)0x30, (byte)0x1a, (byte)0x02, (byte)0x01, (byte)0x22, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x01, (byte)0x00, - (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, - (byte)0x02, (byte)0x30, (byte)0x19, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, - (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x30, (byte)0x1f, (byte)0x02, (byte)0x03, - (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x02, (byte)0xfc, (byte)0x17, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, - (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x82, (byte)0x01, - (byte)0x07, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7c, (byte)0x00, (byte)0x01, (byte)0x80, (byte)0xfe, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x01, - (byte)0xc0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, (byte)0x80, (byte)0xf0, (byte)0x01, (byte)0xc0, (byte)0xd8, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, - (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x03, (byte)0x01, (byte)0xca, (byte)0x03, (byte)0xaa, (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x0a, (byte)0x00, (byte)0x00, - (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0xca, (byte)0x01, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x04, (byte)0xc0, (byte)0x0c, (byte)0x00, (byte)0x0d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0xc0, (byte)0x0c, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - }; - /* @formatter:on */ - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); - Element todo = new ClientMCSConnectInitial("ClientMCSConnectInitial"); - Element x224 = new ClientX224DataPdu("x224"); - Element tpkt = new ClientTpkt("tpkt"); - - Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); - - Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, todo, x224, tpkt, sink, mainSink); - pipeline.link("source", "ClientMCSConnectInitial", "mainSink"); - pipeline.link("ClientMCSConnectInitial >" + OTOUT, "x224", "tpkt", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java deleted file mode 100644 index 78f523552da..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.java +++ /dev/null @@ -1,49 +0,0 @@ -// 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 rdpclient; - -/** - * Try to determine packet content by it header fingerprint. - */ -public class ClientPacketSniffer extends PacketSniffer { - - private static final Pair[] clientRegexps = new Pair[] { -// @formatter:off - new Pair("Client FastPath input", "04"), - new Pair("Client X224ConnectionRequest", "03 00 XX XX 27 E0"), - new Pair("Client ConnectionRequest", "03 00 XX XX XX E0"), - new Pair("Client MCConnectInitial", "03 00 XX XX 02 F0 80 7F 65"), - new Pair("Client ErectDomainRequest", "03 00 XX XX 02 F0 80 04"), - new Pair("Client AttachUserRequest", "03 00 XX XX 02 F0 80 28"), - new Pair("Client ChannelJoinRequest", "03 00 XX XX 02 F0 80 38"), - new Pair("Client Info", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 00 00"), - new Pair("Client ConfirmActivePDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 13 00"), - new Pair("Client SynchronizePDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 1F"), - new Pair("Client ControlPDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 14"), - new Pair("Client FontListPDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 27"), - new Pair("Client BitmapCachePersistentList","03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX XX XX XX XX XX 2b"), -// new Pair("Client TPKT Unknown packet", "03"), -// new Pair("Client UNKNOWN PACKET (ERROR)", ".*"), - // @formatter:on - - }; - - public ClientPacketSniffer(String id) { - super(id, clientRegexps); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java deleted file mode 100644 index cd3a5ed8af2..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RLEBitmapDecompression.java +++ /dev/null @@ -1,985 +0,0 @@ -// 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 rdpclient; - -import streamer.AssertingByteBuffer; -import streamer.ByteBuffer; - -/** - * Based on code example from MSDN, @see - * http://msdn.microsoft.com/en-us/library/dd240593.aspx . - */ -public class RLEBitmapDecompression { - - public static final int g_MaskRegularRunLength = 0x1F; - public static final int g_MaskLiteRunLength = 0x0F; - - public static final int g_MaskSpecialFgBg1 = 0x03; - public static final int g_MaskSpecialFgBg2 = 0x05; - - public static final int REGULAR_BG_RUN = 0x00; - public static final int REGULAR_FG_RUN = 0x01; - public static final int REGULAR_FGBG_IMAGE = 0x02; - public static final int REGULAR_COLOR_RUN = 0x03; - public static final int REGULAR_COLOR_IMAGE = 0x04; - - public static final int LITE_SET_FG_FG_RUN = 0x0C; - public static final int LITE_SET_FG_FGBG_IMAGE = 0x0D; - public static final int LITE_DITHERED_RUN = 0x0E; - - public static final int MEGA_MEGA_BG_RUN = 0xF0; - public static final int MEGA_MEGA_FG_RUN = 0xF1; - public static final int MEGA_MEGA_FGBG_IMAGE = 0xF2; - public static final int MEGA_MEGA_COLOR_RUN = 0xF3; - public static final int MEGA_MEGA_COLOR_IMAGE = 0xF4; - public static final int MEGA_MEGA_SET_FG_RUN = 0xF6; - public static final int MEGA_MEGA_SET_FGBG_IMAGE = 0xF7; - public static final int MEGA_MEGA_DITHERED_RUN = 0xF8; - - public static final int SPECIAL_FGBG_1 = 0xF9; - public static final int SPECIAL_FGBG_2 = 0xFA; - - public static final int SPECIAL_WHITE = 0xFD; - public static final int SPECIAL_BLACK = 0xFE; - - /** - * Writes a pixel to the specified buffer and advance cursor by bpp. - * - * @param bpp - * bytes per pixel - */ - private static void writePixel(int bpp, ByteBuffer destBuf, int pixel) { - switch (bpp) { - case 1: - destBuf.writeByte(pixel); - break; - case 2: - destBuf.writeShortLE(pixel); - break; - case 3: - destBuf.writeByte(pixel); - destBuf.writeShortLE(pixel >> 8); - break; - case 4: - destBuf.writeIntLE(pixel); - break; - default: - throw new RuntimeException("Unsupported color depth."); - } - } - - /** - * Reads a pixel from the specified buffer at given offset without changing of - * cursor. - * - * @param bpp - * bytes per pixel - * @param offset - * -rowDelta (i.e. (-width*bpp)) - */ - private static int peekPixel(int bpp, ByteBuffer destBuf, int offset) { - if (offset >= 0 || (-offset) > destBuf.cursor) - throw new RuntimeException("Incorrect value for offset: offset in destination buffer must point to pixel in previous row."); - - // Adjust cursor to point to pixel in previous row - int oldCursor = destBuf.cursor; - destBuf.cursor += offset; - - int pixel; - switch (bpp) { - case 1: - pixel = destBuf.readUnsignedByte(); - break; - case 2: - pixel = destBuf.readUnsignedShortLE(); - break; - case 3: - pixel = destBuf.readUnsignedByte() | (destBuf.readUnsignedShortLE() >> 8); - break; - case 4: - pixel = destBuf.readSignedIntLE(); - break; - default: - throw new RuntimeException("Unsupported color depth."); - } - destBuf.cursor = oldCursor; - - return pixel; - } - - /** - * Reads a pixel from the specified buffer and advance cursor by bpp value. - * - * @param bpp - * bytes per pixel - */ - private static int readPixel(int bpp, ByteBuffer srcBuf) { - int pixel; - switch (bpp) { - case 1: - pixel = srcBuf.readUnsignedByte(); - break; - case 2: - pixel = srcBuf.readUnsignedShortLE(); - break; - case 3: - pixel = srcBuf.readUnsignedByte() | (srcBuf.readUnsignedShortLE() >> 8); - break; - case 4: - pixel = srcBuf.readSignedIntLE(); - break; - default: - throw new RuntimeException("Unsupported color depth."); - } - - return pixel; - } - - /** - * Returns the size of a pixel in bytes. - */ - private static int getPixelSize(int colorDepth) { - switch (colorDepth) { - case 8: - return 1; - case 15: - case 16: - return 2; - case 24: - return 3; - default: - throw new RuntimeException("Unsupported pixel color depth: " + colorDepth + " bpp."); - } - } - - /** - * Reads the supplied order header & extracts the compression order code ID. - */ - private static int extractCodeId(int orderHeader) { - // Taken from FreeRDP code, bitmap.c - switch (orderHeader) { - case MEGA_MEGA_BG_RUN: - case MEGA_MEGA_FG_RUN: - case MEGA_MEGA_SET_FG_RUN: - case MEGA_MEGA_DITHERED_RUN: - case MEGA_MEGA_COLOR_RUN: - case MEGA_MEGA_FGBG_IMAGE: - case MEGA_MEGA_SET_FGBG_IMAGE: - case MEGA_MEGA_COLOR_IMAGE: - case SPECIAL_FGBG_1: - case SPECIAL_FGBG_2: - case SPECIAL_WHITE: - case SPECIAL_BLACK: - return orderHeader; - } - - int code = orderHeader >> 5; - switch (code) { - case REGULAR_BG_RUN: - case REGULAR_FG_RUN: - case REGULAR_COLOR_RUN: - case REGULAR_FGBG_IMAGE: - case REGULAR_COLOR_IMAGE: - return code; - } - - return orderHeader >> 4; - } - - /** - * Returns a black pixel. - */ - private static int getColorBlack() { - return 0x000000; - } - - /** - * Returns a white pixel. - */ - private static int getColorWhite(int colorDepth) { - if (colorDepth == 8) { - // - // Palette entry #255 holds white. - // - return 0xFF; - } else if (colorDepth == 15) { - // - // 5 bits per RGB component: - // 0111 1111 1111 1111 (binary) - // - return 0x7FFF; - } else if (colorDepth == 16) { - // - // 5 bits for red, 6 bits for green, 5 bits for green: - // 1111 1111 1111 1111 (binary) - // - return 0xFFFF; - } else if (colorDepth == 24) { - // - // 8 bits per RGB component: - // 1111 1111 1111 1111 1111 1111 (binary) - // - return 0xFFFFFF; - } else - throw new RuntimeException("Unsupported color depth."); - } - - /** - * Extract the run length of a compression order. - */ - private static int extractRunLength(int code, int orderHeader, ByteBuffer srcBuf) { - switch (code) { - case REGULAR_FGBG_IMAGE: { - int runLength = orderHeader & g_MaskRegularRunLength; - if (runLength == 0) - runLength = srcBuf.readUnsignedByte() + 1; - else - runLength = runLength * 8; - return runLength; - } - case LITE_SET_FG_FGBG_IMAGE: { - int runLength = orderHeader & g_MaskLiteRunLength; - if (runLength == 0) - runLength = srcBuf.readUnsignedByte() + 1; - else - runLength = runLength * 8; - return runLength; - } - case REGULAR_BG_RUN: - case REGULAR_COLOR_IMAGE: - case REGULAR_COLOR_RUN: - case REGULAR_FG_RUN: { - int runLength = orderHeader & g_MaskRegularRunLength; - if (runLength == 0) - // An extended (MEGA) run. - runLength = srcBuf.readUnsignedByte() + 32; - return runLength; - } - case LITE_DITHERED_RUN: - case LITE_SET_FG_FG_RUN: { - int runLength = orderHeader & g_MaskLiteRunLength; - if (runLength == 0) - // An extended (MEGA) run. - runLength = srcBuf.readUnsignedByte() + 16; - return runLength; - } - case MEGA_MEGA_BG_RUN: - case MEGA_MEGA_COLOR_IMAGE: - case MEGA_MEGA_COLOR_RUN: - case MEGA_MEGA_DITHERED_RUN: - case MEGA_MEGA_FG_RUN: - case MEGA_MEGA_FGBG_IMAGE: - case MEGA_MEGA_SET_FG_RUN: - case MEGA_MEGA_SET_FGBG_IMAGE: { - return srcBuf.readUnsignedShortLE(); - } - default: - return 0; - } - - } - - /** - * Write a foreground/background image to a destination buffer. - */ - private static void writeFgBgImage(int bpp, ByteBuffer destBuf, int rowDelta, int bitmask, int fgPel, int cBits) { - for (; cBits > 0; cBits--, bitmask >>= 1) { - int xorPixel = peekPixel(bpp, destBuf, -rowDelta); - writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? xorPixel ^ fgPel : xorPixel); - } - } - - /** - * Write a foreground/background image to a destination buffer for the first - * line of compressed data. - */ - private static void writeFirstLineFgBgImage(int bpp, ByteBuffer destBuf, int bitmask, int fgPel, int cBits) { - for (; cBits > 0; cBits--, bitmask >>= 1) { - writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? fgPel : getColorBlack()); - } - } - - /** - * Decompress a RLE compressed bitmap and flip decompressed image. - * - * @param srcBuf - * source buffer containing compressed bitmap - * @param imageWidth - * width of destination image in pixels - * @param imageHeight - * height of destination image in pixels - * @param colorDepth - * bits per pixel - * @return destination image buffer - */ - public static ByteBuffer rleDecompress(ByteBuffer srcBuf, int imageWidth, int imageHeight, int colorDepth) { - int bpp = getPixelSize(colorDepth); - - // Decompress image - ByteBuffer destBuf = new ByteBuffer(new byte[imageWidth * imageHeight * bpp]); - rleDecompress(srcBuf, destBuf, imageWidth, imageHeight, colorDepth); - - // Flip image - return flipRawImage(destBuf, imageWidth, imageHeight, bpp); - } - - /** - * Decompress a RLE compressed bitmap. - * - * @param srcBuf - * source buffer containing compressed bitmap - * @param destBuf - * destination buffer - * @param imageWidth - * width of destination image in pixels - * @param imageHeight - * height of destination image in pixels - * @param colorDepth - * bits per pixel - */ - protected static void rleDecompress(final ByteBuffer srcBuf, final ByteBuffer destBuf, final int imageWidth, final int imageHeight, final int colorDepth) { - final int bpp = getPixelSize(colorDepth); - final int rowDelta = imageWidth * bpp; - final int whitePixel = getColorWhite(colorDepth); - final int blackPixel = getColorBlack(); - - int fgPel = whitePixel; - boolean insertFgPel = false; - boolean firstLine = true; - - if (destBuf.length != imageWidth * imageHeight * bpp) - throw new RuntimeException("Incorrect size of destination buffer. Expected size (imageWidth*imageHeight*bpp): " + (imageWidth * imageHeight * bpp) + - ", actual size: " + destBuf.length + "."); - - while (srcBuf.cursor < srcBuf.length) { - // Watch out for the end of the first scanline in destination buffer. - if (firstLine) { - if (destBuf.cursor >= rowDelta) { - firstLine = false; - insertFgPel = false; - } - } - - // Extract the compression order code ID from the compression - // order header. - int orderHeader = srcBuf.readUnsignedByte(); - int code = extractCodeId(orderHeader); - - // Handle Background Run Orders. - if (code == REGULAR_BG_RUN | code == MEGA_MEGA_BG_RUN) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - if (firstLine) { - if (insertFgPel) { - writePixel(bpp, destBuf, fgPel); - runLength--; - } - - for (; runLength > 0; runLength--) - writePixel(bpp, destBuf, blackPixel); - - } else { - if (insertFgPel) { - writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel); - runLength--; - } - - // Copy pixels from previous row of destination image - for (; runLength > 0; runLength--) - writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta)); - } - - // - // A follow-on background run order will need a - // foreground pel inserted. - // - insertFgPel = true; - continue; - } - - // - // For any of the other run-types a follow-on background run - // order does not need a foreground pel inserted. - // - insertFgPel = false; - - // - // Handle Foreground Run Orders. - // - if (code == REGULAR_FG_RUN | code == MEGA_MEGA_FG_RUN | code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - if (code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN) - fgPel = readPixel(bpp, srcBuf); - - if (firstLine) { - for (; runLength > 0; runLength--) { - writePixel(bpp, destBuf, fgPel); - } - } else { - for (; runLength > 0; runLength--) { - writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel); - } - } - - continue; - } - - // - // Handle Dithered Run Orders. - // - if (code == LITE_DITHERED_RUN | code == MEGA_MEGA_DITHERED_RUN) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - int pixelA = readPixel(bpp, srcBuf); - int pixelB = readPixel(bpp, srcBuf); - - for (; runLength > 0; runLength--) { - writePixel(bpp, destBuf, pixelA); - writePixel(bpp, destBuf, pixelB); - } - - continue; - } - - // - // Handle Color Run Orders. - // - if (code == REGULAR_COLOR_RUN | code == MEGA_MEGA_COLOR_RUN) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - int pixelA = readPixel(bpp, srcBuf); - - for (; runLength > 0; runLength--) - writePixel(bpp, destBuf, pixelA); - - continue; - } - - // - // Handle Foreground/Background Image Orders. - // - if (code == REGULAR_FGBG_IMAGE | code == MEGA_MEGA_FGBG_IMAGE | code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - if (code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) { - fgPel = readPixel(bpp, srcBuf); - } - - for (; runLength > 8; runLength -= 8) { - int bitmask = srcBuf.readUnsignedByte(); - - if (firstLine) - writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, 8); - else - writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, 8); - } - - if (runLength > 0) { - int bitmask = srcBuf.readUnsignedByte(); - - if (firstLine) - writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, runLength); - else - writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, runLength); - } - - continue; - } - - // - // Handle Color Image Orders. - // - if (code == REGULAR_COLOR_IMAGE | code == MEGA_MEGA_COLOR_IMAGE) { - int runLength = extractRunLength(code, orderHeader, srcBuf); - - /* DEBUG */ - // Copy bytes from source to destination using writeByte(readByte()) - // for (int byteCount = runLength * bpp; byteCount > 0; byteCount--) { - // destBuf.writeByte(srcBuf.readUnsignedByte()); - // } - /* DEBUG */ - - // Copy bytes from source to destination directly using arraycopy() - int lengthInBytes = runLength * bpp; - System.arraycopy(srcBuf.data, srcBuf.offset + srcBuf.cursor, destBuf.data, destBuf.offset + destBuf.cursor, lengthInBytes); - srcBuf.cursor += lengthInBytes; - destBuf.cursor += lengthInBytes; - - continue; - } - - // - // Handle Special Order 1. - // - if (code == SPECIAL_FGBG_1) { - if (firstLine) - writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg1, fgPel, 8); - else - writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg1, fgPel, 8); - - continue; - } - - // - // Handle Special Order 2. - // - if (code == SPECIAL_FGBG_2) { - if (firstLine) - writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg2, fgPel, 8); - else - writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg2, fgPel, 8); - - continue; - } - - // - // Handle White Order. - // - if (code == SPECIAL_WHITE) { - writePixel(bpp, destBuf, whitePixel); - - continue; - } - - // - // Handle Black Order. - // - if (code == SPECIAL_BLACK) { - writePixel(bpp, destBuf, blackPixel); - - continue; - } - - throw new RuntimeException("Unknown code: " + code + "."); - } - } - - /** - * Flip image in vertical direction. - */ - public static ByteBuffer flipRawImage(ByteBuffer src, int width, int height, int bpp) { - if (width * height * bpp != src.length) - throw new RuntimeException("Incorrect size of buffer. Expected size (imageWidth*imageHeight*bpp): " + (width * height * bpp) + ", actual size: " + - src.length + "."); - ByteBuffer dest = new ByteBuffer(new byte[src.length]); - - int scanLine = width * bpp; - - for (int i = 0; i < height; i++) { - // Copy one row - System.arraycopy(src.data, (height - i - 1) * scanLine, dest.data, i * scanLine, scanLine); - } - - return dest; - - } - - /** - * Example. - */ - public static void main(String args[]) { - - if (true) { - // 16x1@8bpp, all black - int width = 16, height = 1, depth = 8, bpp = depth / 8; - ByteBuffer src = new ByteBuffer(new byte[] {0x10}); - ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]); - rleDecompress(src, dest, width, height, depth); - } - - if (true) { - // 16x1@16bpp, all black - int width = 16, height = 1, depth = 16, bpp = depth / 8; - ByteBuffer src = new ByteBuffer(new byte[] {0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); - ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]); - rleDecompress(src, dest, width, height, depth); - } - - if (true) { - // 32x32@8 - int width = 32, height = 32, depth = 8, bpp = depth / 8; - - ByteBuffer src = - new ByteBuffer(new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, - (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xec, (byte)0x6c, (byte)0x0e, (byte)0x0e, (byte)0x44, (byte)0x0e, - (byte)0x0e, (byte)0x0e, (byte)0x13, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, - (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xe4, (byte)0x04, (byte)0x06, (byte)0x8e, (byte)0x60, (byte)0x0e, (byte)0x60, (byte)0x8c, - (byte)0xb4, (byte)0xb5, (byte)0xdc, (byte)0xdc, (byte)0xbb, (byte)0xb4, (byte)0x8c, (byte)0x66, (byte)0x0b, (byte)0x6c, (byte)0xe4, (byte)0x04, - (byte)0x06, (byte)0x02, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xf8, (byte)0x0e, (byte)0x66, - (byte)0xb4, (byte)0xdc, (byte)0x68, (byte)0xe2, (byte)0x97, (byte)0xdd, (byte)0xb4, (byte)0xa7, (byte)0x16, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x0b, (byte)0xae, (byte)0xdc, (byte)0xe9, (byte)0x6a, (byte)0xdc, (byte)0x96, (byte)0xe9, (byte)0xe9, (byte)0xb4, (byte)0x0e, (byte)0x00, - (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, - (byte)0x0e, (byte)0xae, (byte)0xdc, (byte)0xdb, (byte)0xdb, (byte)0xd0, (byte)0x09, (byte)0x07, (byte)0xcf, (byte)0x03, (byte)0x95, (byte)0xdb, - (byte)0xdb, (byte)0xdc, (byte)0xb4, (byte)0x66, (byte)0x6c, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, - (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x0b, (byte)0xae, (byte)0xdb, (byte)0xd4, (byte)0xd5, (byte)0x6c, (byte)0xdb, (byte)0x80, (byte)0xaf, - (byte)0xd5, (byte)0xd4, (byte)0xdb, (byte)0xb4, (byte)0x66, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, - (byte)0x06, (byte)0xed, (byte)0x66, (byte)0xae, (byte)0xd5, (byte)0xad, (byte)0xd4, (byte)0xd4, (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xdb, - (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xd4, (byte)0xd4, (byte)0xad, (byte)0xd5, - (byte)0xb4, (byte)0x0e, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x60, (byte)0xa7, (byte)0xb4, - (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb3, (byte)0xb3, (byte)0xd4, (byte)0xd4, (byte)0xb3, (byte)0x8c, (byte)0xb6, (byte)0x07, (byte)0xb6, - (byte)0x8c, (byte)0xb3, (byte)0xd4, (byte)0xb3, (byte)0xb3, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb4, (byte)0xad, (byte)0x66, (byte)0x00, - (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0x66, (byte)0xae, (byte)0xad, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, - (byte)0xad, (byte)0xad, (byte)0xb3, (byte)0xad, (byte)0xb5, (byte)0x07, (byte)0x07, (byte)0x07, (byte)0xf0, (byte)0x8b, (byte)0xad, (byte)0xad, - (byte)0xad, (byte)0xad, (byte)0xad, (byte)0x8b, (byte)0xa7, (byte)0xae, (byte)0xa7, (byte)0x6c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, - (byte)0x6c, (byte)0xa7, (byte)0xad, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, - (byte)0xb5, (byte)0xbd, (byte)0xbd, (byte)0xbd, (byte)0xbd, (byte)0xf0, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x8b, (byte)0xa7, - (byte)0xa7, (byte)0xc8, (byte)0xc8, (byte)0x60, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x66, (byte)0xc8, (byte)0xa7, (byte)0x66, - (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x92, (byte)0xf1, (byte)0xf1, (byte)0xf1, - (byte)0xf1, (byte)0xf2, (byte)0x07, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0xc8, (byte)0x66, - (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x60, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0xa7, - (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xa7, (byte)0xb6, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0x07, - (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x6c, - (byte)0x04, (byte)0xa7, (byte)0x60, (byte)0x6b, (byte)0x66, (byte)0x99, (byte)0xb6, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, - (byte)0xef, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x60, - (byte)0xa7, (byte)0x66, (byte)0x60, (byte)0x66, (byte)0x66, (byte)0x8c, (byte)0xf1, (byte)0x6e, (byte)0xff, (byte)0x85, (byte)0xbd, (byte)0x66, - (byte)0x66, (byte)0x66, (byte)0x60, (byte)0x05, (byte)0x87, (byte)0x13, (byte)0x04, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xf4, - (byte)0x70, (byte)0xff, (byte)0x84, (byte)0xbd, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x05, (byte)0x85, (byte)0x0b, (byte)0xa7, (byte)0xb5, - (byte)0xae, (byte)0x8c, (byte)0xd0, (byte)0x13, (byte)0xc1, (byte)0x01, (byte)0x00, (byte)0x08, (byte)0x8e, (byte)0x8c, (byte)0xae, (byte)0xb5, - (byte)0xae, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0xae, (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xb5, (byte)0xd0, - (byte)0x0e, (byte)0x0c, (byte)0x01, (byte)0x00, (byte)0x90, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, - (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xae, (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x68, (byte)0xae, (byte)0x82, - (byte)0x8c, (byte)0x0a, (byte)0x05, (byte)0x8c, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, - (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x05, (byte)0x81, (byte)0xd0, (byte)0x06, (byte)0x9a, (byte)0x8c, (byte)0x0a, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x0a, - (byte)0xb5, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x8b, (byte)0x0a, (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x06, - (byte)0x9b, (byte)0xb6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, - (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, (byte)0x8c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x6c, (byte)0xb5, (byte)0x0a, - (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x05, (byte)0x80, (byte)0x7d, (byte)0xbc, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, (byte)0x0a, (byte)0x8b, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x87, (byte)0x0a, (byte)0xbc, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, - (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xae, (byte)0xd0, (byte)0xb5, - (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x1a, (byte)0xb5, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, - (byte)0x6e, (byte)0xb5, (byte)0x0a, (byte)0xbc, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, - (byte)0xf4, (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x0a, - (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x8b, (byte)0xbc, (byte)0x1a, - (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, - (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0xde, (byte)0x0a, (byte)0xa7, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8b, (byte)0xbc, (byte)0xf2, (byte)0x0a, (byte)0xb6, (byte)0xb6, - (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, - (byte)0x0a, (byte)0xf2, (byte)0x1a, (byte)0x8c, (byte)0xec, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0xa7, (byte)0xbc, (byte)0x1a, (byte)0x0a, (byte)0x0a, (byte)0x6a, (byte)0xb6, (byte)0x96, (byte)0x0a, - (byte)0x0a, (byte)0xf2, (byte)0x0a, (byte)0x87, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, - (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8c, (byte)0xb6, (byte)0xf4, (byte)0xf2, (byte)0xd0, (byte)0x09, (byte)0xbc, - (byte)0x87, (byte)0x03, (byte)0x80, (byte)0x2c, (byte)0xde, (byte)0xf4, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x6c, - (byte)0x87, (byte)0x0a, (byte)0xf4, (byte)0xf4, (byte)0xf2, (byte)0xde, (byte)0xbd, (byte)0xbd, (byte)0xde, (byte)0xf2, (byte)0xf4, (byte)0xf4, - (byte)0x0a, (byte)0xd0, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, - (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x6c, (byte)0x8c, (byte)0xb5, - (byte)0xbc, (byte)0x0a, (byte)0xde, (byte)0xf2, (byte)0xbd, (byte)0x0a, (byte)0xb5, (byte)0x8c, (byte)0x6c, (byte)0x06, (byte)0xed, (byte)0x06, - (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xe6, (byte)0x04, (byte)0x06, (byte)0x86, - (byte)0x04, (byte)0x6c, (byte)0x04, (byte)0x8b, (byte)0x04, (byte)0x6c, (byte)0xe6, (byte)0x04, (byte)0x06, (byte)0x82, (byte)0x00, (byte)0x00 - - }); - - ByteBuffer flippedImage = - new ByteBuffer(new byte[] {(byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x6c, (byte)0x04, (byte)0x8b, (byte)0x04, (byte)0x6c, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x6c, (byte)0x8c, (byte)0xb5, (byte)0xbc, (byte)0x0a, - (byte)0xde, (byte)0xf2, (byte)0xbd, (byte)0x0a, (byte)0xb5, (byte)0x8c, (byte)0x6c, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x04, (byte)0x6c, (byte)0x87, (byte)0x0a, (byte)0xf4, (byte)0xf4, (byte)0xf2, (byte)0xde, (byte)0xbd, (byte)0xbd, (byte)0xde, (byte)0xf2, - (byte)0xf4, (byte)0xf4, (byte)0x0a, (byte)0xd0, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8c, (byte)0xb6, (byte)0xf4, (byte)0xf2, - (byte)0x0a, (byte)0x0a, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0x0a, (byte)0x0a, (byte)0xde, (byte)0xf4, - (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0xa7, (byte)0xbc, (byte)0x1a, (byte)0x0a, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, - (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0x0a, (byte)0xf2, (byte)0x0a, (byte)0x87, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8b, (byte)0xbc, - (byte)0xf2, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, - (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0xf2, (byte)0x1a, (byte)0x8c, (byte)0xec, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x8b, (byte)0xbc, (byte)0x1a, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb5, - (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, - (byte)0xb6, (byte)0x0a, (byte)0xde, (byte)0x0a, (byte)0xa7, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, - (byte)0x6e, (byte)0xb5, (byte)0x0a, (byte)0xbc, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, - (byte)0xf4, (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x0a, - (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x87, (byte)0x0a, (byte)0xbc, (byte)0xb6, - (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, - (byte)0xd0, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x1a, (byte)0xb5, (byte)0x04, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x6c, (byte)0xb5, (byte)0x0a, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xae, - (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xbc, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, - (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x8b, - (byte)0x0a, (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, - (byte)0xb6, (byte)0x0a, (byte)0x8c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0xae, (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, - (byte)0xd0, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0x8c, (byte)0x0a, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x0a, (byte)0xb5, (byte)0x6c, - (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xae, (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, - (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0x8c, (byte)0x0a, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, - (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0xae, - (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xb5, (byte)0xf3, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, - (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x0b, (byte)0xa7, (byte)0xb5, (byte)0xae, (byte)0x8c, (byte)0xa7, - (byte)0xf4, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xbd, (byte)0xa7, (byte)0x8c, (byte)0xae, (byte)0xb5, (byte)0xae, (byte)0x66, - (byte)0x00, (byte)0x00, (byte)0x13, (byte)0x04, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xf4, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xbd, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x60, (byte)0xa7, - (byte)0x66, (byte)0x60, (byte)0x66, (byte)0x66, (byte)0x8c, (byte)0xf1, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xbd, (byte)0x66, (byte)0x66, (byte)0x66, - (byte)0x60, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0x04, (byte)0xa7, (byte)0x60, (byte)0x66, (byte)0x66, - (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xb6, (byte)0xf5, (byte)0xf5, - (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xef, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x60, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0xa7, (byte)0xa7, - (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xa7, (byte)0xb6, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0x07, (byte)0x66, - (byte)0xa7, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x66, - (byte)0xc8, (byte)0xa7, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x92, - (byte)0xf1, (byte)0xf1, (byte)0xf1, (byte)0xf1, (byte)0xf2, (byte)0x07, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0x66, - (byte)0x66, (byte)0xc8, (byte)0x66, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x6c, (byte)0xa7, (byte)0xad, (byte)0xa7, (byte)0xa7, - (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb5, (byte)0xbd, (byte)0xbd, (byte)0xbd, (byte)0xbd, - (byte)0xf0, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0xc8, (byte)0xc8, (byte)0x60, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0x66, (byte)0xae, (byte)0xad, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, - (byte)0xad, (byte)0xb3, (byte)0xad, (byte)0xb5, (byte)0x07, (byte)0x07, (byte)0x07, (byte)0xf0, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, - (byte)0xad, (byte)0xad, (byte)0x8b, (byte)0xa7, (byte)0xae, (byte)0xa7, (byte)0x6c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, - (byte)0x60, (byte)0xa7, (byte)0xb4, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb3, (byte)0xb3, (byte)0xd4, (byte)0xd4, (byte)0xb3, (byte)0x8c, - (byte)0xb6, (byte)0x07, (byte)0xb6, (byte)0x8c, (byte)0xb3, (byte)0xd4, (byte)0xb3, (byte)0xb3, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb4, - (byte)0xad, (byte)0x66, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x66, (byte)0xae, (byte)0xd5, - (byte)0xad, (byte)0xd4, (byte)0xd4, (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xdb, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, - (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xd4, (byte)0xd4, (byte)0xad, (byte)0xd5, (byte)0xb4, (byte)0x0e, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x0b, (byte)0xae, (byte)0xdb, (byte)0xd4, (byte)0xd5, (byte)0xdb, - (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xd5, - (byte)0xd4, (byte)0xdb, (byte)0xb4, (byte)0x66, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x0e, (byte)0xae, (byte)0xdc, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, - (byte)0xdc, (byte)0xdc, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdc, (byte)0xb4, (byte)0x66, (byte)0x6c, - (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x0b, (byte)0xae, (byte)0xdc, (byte)0xe9, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, - (byte)0xdc, (byte)0xdc, (byte)0xe9, (byte)0xe9, (byte)0xb4, (byte)0x0e, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xf8, (byte)0x0e, (byte)0x66, (byte)0xb4, - (byte)0xdc, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xdd, (byte)0xb4, (byte)0xa7, - (byte)0x16, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x60, (byte)0x0e, (byte)0x60, (byte)0x8c, (byte)0xb4, (byte)0xb5, - (byte)0xdc, (byte)0xdc, (byte)0xbb, (byte)0xb4, (byte)0x8c, (byte)0x66, (byte)0x0b, (byte)0x6c, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, - (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xec, (byte)0x6c, (byte)0x0e, (byte)0x0e, (byte)0x44, (byte)0x0e, (byte)0x0e, (byte)0x0e, - (byte)0x13, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}); - ByteBuffer dest = new AssertingByteBuffer(flipRawImage(flippedImage, width, height, bpp).data); - - rleDecompress(src, dest, width, height, depth); - - } - - if (true) { - // 32x32@16 - int width = 32, height = 32, depth = 16; - - ByteBuffer src = - new ByteBuffer(new byte[] {(byte)0x85, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x06, (byte)0x8b, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x06, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0x16, - (byte)0x69, (byte)0x99, (byte)0xd6, (byte)0x06, (byte)0x69, (byte)0x99, (byte)0xd6, (byte)0x04, (byte)0xcc, (byte)0x89, (byte)0x52, (byte)0x03, - (byte)0x6e, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x6e, (byte)0x08, (byte)0x42, (byte)0x01, (byte)0x70, (byte)0x08, (byte)0x42, (byte)0x71, - (byte)0xff, (byte)0xff, (byte)0xce, (byte)0x18, (byte)0xc6, (byte)0x01, (byte)0x81, (byte)0x08, (byte)0x42, (byte)0xce, (byte)0x66, (byte)0x29, - (byte)0x02, (byte)0xcd, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x88, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, - (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xd8, (byte)0x99, - (byte)0xd6, (byte)0x03, (byte)0xf8, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xf0, (byte)0x66, (byte)0x99, (byte)0xd6, - (byte)0x05, (byte)0x6a, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0xc4, (byte)0xcc, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x6e, (byte)0xff, - (byte)0xff, (byte)0x02, (byte)0x6e, (byte)0x08, (byte)0x42, (byte)0x01, (byte)0x70, (byte)0x08, (byte)0x42, (byte)0x71, (byte)0xff, (byte)0xff, - (byte)0xce, (byte)0x18, (byte)0xc6, (byte)0x01, (byte)0x81, (byte)0x08, (byte)0x42, (byte)0xce, (byte)0x66, (byte)0x29, (byte)0x02, (byte)0xcd, - (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x00, (byte)0x04, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xc3, (byte)0x80, (byte)0x61, (byte)0x00, - (byte)0xa5, (byte)0x80, (byte)0x40, (byte)0xec, (byte)0x52, (byte)0x00, (byte)0x5a, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x24, (byte)0x00, - (byte)0x12, (byte)0x00, (byte)0x24, (byte)0x00, (byte)0x12, (byte)0x00, (byte)0x5a, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0xa5, (byte)0x80, - (byte)0x52, (byte)0x00, (byte)0xc3, (byte)0x80, (byte)0x61, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xcc, (byte)0x89, - (byte)0x52, (byte)0x03, (byte)0x6e, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0xcb, (byte)0x18, (byte)0xc6, (byte)0x84, (byte)0x08, (byte)0x42, - (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff,}); - - ByteBuffer dest = - new AssertingByteBuffer(new byte[] {(byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, - (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, - (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, - (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, - (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, - (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, - (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, - (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff,}); - - rleDecompress(src, dest, width, height, depth); - - } - } -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java old mode 100644 new mode 100755 index ef05eda327e..afde70698e3 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java @@ -16,13 +16,50 @@ // under the License. package rdpclient; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import rdpclient.adapter.AwtRdpKeyboardAdapter; +import rdpclient.adapter.AwtRdpMouseAdapter; +import rdpclient.hyperv.ClientPreConnectionBlob; +import rdpclient.ntlmssp.ClientNtlmsspNegotiate; +import rdpclient.ntlmssp.ClientNtlmsspPubKeyAuth; +import rdpclient.ntlmssp.ClientNtlmsspUserCredentials; +import rdpclient.ntlmssp.NtlmState; +import rdpclient.ntlmssp.ServerNtlmsspChallenge; +import rdpclient.ntlmssp.ServerNtlmsspPubKeyPlus1; +import rdpclient.rdp.ClientConfirmActivePDU; +import rdpclient.rdp.ClientFastPathPDU; +import rdpclient.rdp.ClientInfoPDU; +import rdpclient.rdp.ClientMCSAttachUserRequest; +import rdpclient.rdp.ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs; +import rdpclient.rdp.ClientMCSConnectInitial; +import rdpclient.rdp.ClientMCSErectDomainRequest; +import rdpclient.rdp.ClientTpkt; +import rdpclient.rdp.ClientX224ConnectionRequestPDU; +import rdpclient.rdp.ClientX224DataPDU; +import rdpclient.rdp.RdpConstants; +import rdpclient.rdp.RdpState; +import rdpclient.rdp.ServerBitmapUpdate; +import rdpclient.rdp.ServerDemandActivePDU; +import rdpclient.rdp.ServerFastPath; +import rdpclient.rdp.ServerIOChannelRouter; +import rdpclient.rdp.ServerLicenseErrorPDUValidClient; +import rdpclient.rdp.ServerMCSAttachUserConfirmPDU; +import rdpclient.rdp.ServerMCSConnectResponse; +import rdpclient.rdp.ServerMCSPDU; +import rdpclient.rdp.ServerPaletteUpdate; +import rdpclient.rdp.ServerX224ConnectionConfirmPDU; +import rdpclient.rdp.ServerX224DataPdu; import streamer.PipelineImpl; import streamer.Queue; -import common.AwtCanvasAdapter; +import streamer.ssl.SSLState; +import streamer.ssl.UpgradeSocketToSSL; import common.AwtKeyEventSource; import common.AwtMouseEventSource; import common.BufferedImageCanvas; import common.ScreenDescription; +import common.adapter.AwtCanvasAdapter; public class RdpClient extends PipelineImpl { @@ -31,12 +68,32 @@ public class RdpClient extends PipelineImpl { */ private static final String HANDSHAKE_END = "server_valid_client"; - public RdpClient(String id, String userName, ScreenDescription screen, BufferedImageCanvas canvas) { + /** + * Create new RDP or HyperV cli + * + * @param id + * id of this element + * @param userName + * user name + * @param password + * password + * @param pcb + * pre-connection blob for HyperV server or null/empty string to + * disable. Usually, HyperV VM ID, e.g. + * "39418F90-6D03-468E-B796-91C60DD6653A". + * @param screen + * screen description to fill + * @param canvas + * canvas to draw on + * @param sslState + */ + public RdpClient(String id, String serverHostName, String domain, String userName, String password, String pcb, ScreenDescription screen, + BufferedImageCanvas canvas, SSLState sslState) { super(id); - assembleRDPPipeline(userName, screen, canvas); + assembleRDPPipeline(serverHostName, domain, userName, password, pcb, screen, canvas, sslState); } -// /* DEBUG */ + // /* DEBUG */ // @Override // protected HashMap initElementMap(String id) { // HashMap map = new HashMap(); @@ -45,72 +102,195 @@ public class RdpClient extends PipelineImpl { // return map; // } - private void assembleRDPPipeline(String userName, ScreenDescription screen, BufferedImageCanvas canvas) { + /** + * Assemble connection sequence and main pipeline. + * + * Connection sequence for RDP w/o NLA: cookie(TPKT) SSL x224(TPKT) + * main(FastPath). + * + * Connection sequence for RDP w NLA: cookie(TPKT) SSL credssp x224(TPKT) + * main(FastPath). + * + * Connection sequence for HyperV w NLA: pcb SSL credssp cookie(TPKT) + * x224(TPKT) main(FastPath). + */ + protected void assembleRDPPipeline(String serverHostName, String domain, String userName, String password, String pcb, ScreenDescription screen, + BufferedImageCanvas canvas, SSLState sslState) { + // If preconnection blob with VM ID is specified, then we are connecting to + // HyperV server + boolean hyperv = (pcb != null && !pcb.isEmpty()); + // HyperV server requires NLA (CredSSP/SPNEGO/NTLMSSP) to connect, because + // it cannot display login screen + boolean credssp = hyperv || (password != null && !password.isEmpty()); + + String workstation; + try { + workstation = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + workstation = "workstation"; + } + // // Handshake chain // RdpState state = new RdpState(); - int[] channelsToJoin = new int[] {RdpConstants.CHANNEL_RDPRDR, RdpConstants.CHANNEL_IO}; + NtlmState ntlmState = new NtlmState(); + + int[] channelsToJoin = new int[] {RdpConstants.CHANNEL_IO, + // RdpConstants.CHANNEL_RDPRDR, // RDPRDR channel is not used in current + // version + + // RdpConstants .CHANNEL_CLIPRDR // Clipboard channel is refused to join :-/ + }; // Add elements - add( + // If pre-connection blob is specified, then add element to send it as + // first packet + if (hyperv) { + add(new ClientPreConnectionBlob("pcb", pcb)); + } - new ClientX224ConnectionRequestPDU("client_connection_req", userName), new ServerX224ConnectionConfirmPDU("server_connection_conf"), + // If password is specified, then use CredSSP/NTLM (NTLMSSP) + int protocol = RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL; + if (credssp) { + protocol = RdpConstants.RDP_NEG_REQ_PROTOCOL_HYBRID; - new UpgradeSocketToSSL("upgrade_to_ssl"), + add( + new ClientNtlmsspNegotiate("client_ntlmssp_nego", ntlmState), - new ClientMCSConnectInitial("client_initial_conference_create"), new ServerMCSConnectResponse("server_initial_conference_create"), + new ServerNtlmsspChallenge("server_ntlmssp_challenge", ntlmState), - new ClientMCSErectDomainRequest("client_erect_domain"), + new ClientNtlmsspPubKeyAuth("client_ntlmssp_auth", ntlmState, sslState, serverHostName, domain, workstation, userName, password), - new ClientMCSAttachUserRequest("client_atach_user"), new ServerMCSAttachUserConfirmPDU("server_atach_user_confirm", state), + new ServerNtlmsspPubKeyPlus1("server_ntlmssp_confirm", ntlmState), - new ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs("client_channel_join_rdprdr", channelsToJoin, state), + new ClientNtlmsspUserCredentials("client_ntlmssp_finish", ntlmState) - new ClientInfoPDU("client_info_req", userName), + ); + } - new ServerLicenseErrorPDUValidClient("server_valid_client"), + add(new ClientX224ConnectionRequestPDU("client_connection_req", userName, protocol), new ServerX224ConnectionConfirmPDU("server_connection_conf"), + new UpgradeSocketToSSL("upgrade_to_ssl"), - new ServerFastPath("server_fastpath"), + new ClientMCSConnectInitial("client_initial_conference_create"), new ServerMCSConnectResponse("server_initial_conference_create"), - new ServerTpkt("server_tpkt"), + new ClientMCSErectDomainRequest("client_erect_domain"), - new ServerX224DataPdu("server_x224_data"), + new ClientMCSAttachUserRequest("client_atach_user"), new ServerMCSAttachUserConfirmPDU("server_atach_user_confirm", state), - // These TPKT and X224 wrappers are connected directly to OUT for handshake - // sequence - new ClientTpkt("client_tpkt_ot"), + new ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs("client_channel_join_rdprdr", channelsToJoin, state), - new ClientX224DataPdu("client_x224_data_ot") + new ClientInfoPDU("client_info_req", userName), - ); + new ServerLicenseErrorPDUValidClient("server_valid_client"), - // Handshake sequence (via SlowPath) - link("IN", + new ServerFastPath("server_fastpath"), - "server_fastpath >tpkt", "server_tpkt", + // new ServerTpkt("server_tpkt"), - "client_connection_req", "server_connection_conf", + new ServerX224DataPdu("server_x224_data"), - "upgrade_to_ssl", + // These TPKT and X224 wrappers are connected directly to OUT for + // handshake sequence + new ClientTpkt("client_tpkt_ot"), - "client_initial_conference_create", "server_initial_conference_create", + new ClientX224DataPDU("client_x224_data_ot") - "client_erect_domain", + ); - "server_x224_data", + // If HyperV VM ID is set, then insert element which will send VM ID as + // first packet of connection, before other packets + if (hyperv) { - "client_atach_user", "server_atach_user_confirm", + // HyperV: pcb SSL credssp cookie x224 main. - "client_channel_join_rdprdr", + link("IN", - "client_info_req", + // Pre Connection Blob + "pcb", - "server_valid_client" + // Main (will be used after connection seq) or tpkt (to X224) + "server_fastpath >tpkt", - ); + // SSL + "upgrade_to_ssl", + + // CredSSP + "client_ntlmssp_nego", "server_ntlmssp_challenge", "client_ntlmssp_auth", "server_ntlmssp_confirm", "client_ntlmssp_finish", + + // Cookie + "client_connection_req", "server_connection_conf", + + // X224 + "client_initial_conference_create"); + + for (String element : new String[] {"pcb", "client_ntlmssp_nego", "server_ntlmssp_challenge", "client_ntlmssp_auth", "server_ntlmssp_confirm", + "client_ntlmssp_finish"}) { + link(element + " >otout", element + "< OUT"); + + } + + } else { + + // RDP: cookie SSL (credssp) x224 main. + + link("IN", + + // Main or tpkt + "server_fastpath >tpkt", + + // Cookie + "client_connection_req", "server_connection_conf", + + // SSL + "upgrade_to_ssl"); + + if (credssp) { + // SSL + link("upgrade_to_ssl", + + // CredSSP + "client_ntlmssp_nego", "server_ntlmssp_challenge", "client_ntlmssp_auth", "server_ntlmssp_confirm", "client_ntlmssp_finish", + + // X224 + "client_initial_conference_create"); + + for (String element : new String[] {"client_ntlmssp_nego", "server_ntlmssp_challenge", "client_ntlmssp_auth", "server_ntlmssp_confirm", + "client_ntlmssp_finish"}) { + link(element + " >otout", element + "< OUT"); + + } + + } else { + + link( + // SSL + "upgrade_to_ssl", + + // X224 + "client_initial_conference_create"); + } + } + + link( + // X224 + "client_initial_conference_create", "server_initial_conference_create", + + "client_erect_domain", + + "server_x224_data", + + "client_atach_user", "server_atach_user_confirm", + + "client_channel_join_rdprdr", + + "client_info_req", + + "server_valid_client" + + ); // Chain for direct handshake responses (without involving of queue) link("client_x224_data_ot", "client_tpkt_ot", "client_tpkt_ot< OUT"); @@ -122,8 +302,7 @@ public class RdpClient extends PipelineImpl { } // Connect one time outputs to client X224 input - String x224_peers[] = - new String[] {"client_initial_conference_create", "server_initial_conference_create", "client_erect_domain", "client_atach_user", + String x224_peers[] = new String[] {"client_initial_conference_create", "server_initial_conference_create", "client_erect_domain", "client_atach_user", "server_atach_user_confirm", "client_channel_join_rdprdr", "client_info_req", "server_valid_client"}; for (String element : x224_peers) { link(element + " >otout", element + "< client_x224_data_ot"); @@ -134,13 +313,13 @@ public class RdpClient extends PipelineImpl { // add( - // To transfer packets between input threads and output thread. - new Queue("queue"), + // To transfer packets between input threads and output thread. + new Queue("queue"), - // Slow path: MultiChannel Support - new ServerMCSPDU("server_mcs") + // Slow path: MultiChannel Support + new ServerMCSPDU("server_mcs") - ); + ); // Last element of handshake sequence will wake up queue and and socket // output pull loop, which will switch links, between socket output and @@ -165,38 +344,38 @@ public class RdpClient extends PipelineImpl { // Add elements add( - new ServerChannel1003Router("server_channel_1003", state), + new ServerIOChannelRouter("server_io_channel", state), - new ServerDemandActivePDU("server_demand_active", screen, state), + new ServerDemandActivePDU("server_demand_active", screen, state), - new ClientConfirmActivePDU("client_confirm_active", screen, state), + new ClientConfirmActivePDU("client_confirm_active", screen, state), - new ServerBitmapUpdate("server_bitmap_update"), + new ServerBitmapUpdate("server_bitmap_update"), - new AwtCanvasAdapter("canvas_adapter", canvas, screen), + new AwtCanvasAdapter("canvas_adapter", canvas, screen), - new ServerPaletteUpdate("server_palette", screen), + new ServerPaletteUpdate("server_palette", screen), - keyEventSource, new AwtRdpKeyboardAdapter("keyboard_adapter"), + keyEventSource, new AwtRdpKeyboardAdapter("keyboard_adapter"), - mouseEventSource, new AwtRdpMouseAdapter("mouse_adapter"), + mouseEventSource, new AwtRdpMouseAdapter("mouse_adapter"), - // These FastPath, TPKT, and X224 wrappers are connected to queue - new ClientTpkt("client_tpkt_queue"), + // These FastPath, TPKT, and X224 wrappers are connected to queue + new ClientTpkt("client_tpkt_queue"), - new ClientX224DataPdu("client_x224_data_queue"), + new ClientX224DataPDU("client_x224_data_queue"), - new ClientFastPathPDU("client_fastpath_queue")); + new ClientFastPathPDU("client_fastpath_queue")); // Server packet handlers - link("server_mcs >channel_1003", "server_channel_1003"); + link("server_mcs >channel_1003", "server_io_channel"); link("server_fastpath >bitmap", "fastpath< server_bitmap_update", "server_bitmap_update< canvas_adapter"); - link("server_channel_1003 >bitmap", "slowpath< server_bitmap_update"); + link("server_io_channel >bitmap", "slowpath< server_bitmap_update"); link("server_fastpath >palette", "fastpath< server_palette"); - link("server_channel_1003 >palette", "slowpath< server_palette"); + link("server_io_channel >palette", "slowpath< server_palette"); - link("server_channel_1003 >demand_active", "slowpath< server_demand_active"); + link("server_io_channel >demand_active", "slowpath< server_demand_active"); // link("server_demand_active >confirm_active", "client_confirm_active", // "confirm_active< client_channel_1003"); link("server_demand_active >confirm_active", "client_confirm_active", "confirm_active< client_x224_data_queue"); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java deleted file mode 100644 index 1526edf3a6b..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java +++ /dev/null @@ -1,533 +0,0 @@ -// 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 rdpclient; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Element; -import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; -import streamer.Pipeline; -import streamer.PipelineImpl; - -public class ServerChannel1003Router extends BaseElement { - - /** - * Demand Active PDU. - */ - public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1; - - /** - * Confirm Active PDU. - */ - public static final int PDUTYPE_CONFIRMACTIVEPDU = 0x3; - - /** - * Deactivate All PDU. - */ - public static final int PDUTYPE_DEACTIVATEALLPDU = 0x6; - - /** - * Data PDU (actual type is revealed by the pduType2 field in the Share Data - * Header). - */ - public static final int PDUTYPE_DATAPDU = 0x7; - - /** - * Enhanced Security Server Redirection PDU. - */ - public static final int PDUTYPE_SERVER_REDIR_PKT = 0xA; - - protected RdpState state; - - public ServerChannel1003Router(String id, RdpState state) { - super(id); - this.state = state; - } - - /** - * @see http://msdn.microsoft.com/en-us/library/cc240576.aspx - */ - @Override - public void handleData(ByteBuffer buf, Link link) { - if (verbose) - System.out.println("[" + this + "] INFO: Data received: " + buf + "."); - - int length = buf.readUnsignedShortLE(); - if (buf.length != length) { - // It is ServerErrorAlert-ValidClient - // Ignore it - //throw new RuntimeException("[" + this + "] ERROR: Incorrect PDU length: " + length + ", data: " + buf + "."); - } - - int type = buf.readUnsignedShortLE() & 0xf; - - // int sourceId = buf.readUnsignedShortLE(); - buf.skipBytes(2); - - switch (type) { - case PDUTYPE_DEMANDACTIVEPDU: - pushDataToPad("demand_active", buf); - break; - case PDUTYPE_CONFIRMACTIVEPDU: - throw new RuntimeException("Unexpected client CONFIRM ACTIVE PDU. Data: " + buf + "."); - case PDUTYPE_DEACTIVATEALLPDU: - // pushDataToPad("deactivate_all", buf); - /* ignore */buf.unref(); - break; - case PDUTYPE_DATAPDU: - handleDataPdu(buf); - break; - case PDUTYPE_SERVER_REDIR_PKT: - // pushDataToPad("server_redir", buf); - /* ignore */buf.unref(); - break; - default: - throw new RuntimeException("[" + this + "] ERROR: Unknown PDU type: " + type + ", data: " + buf + "."); - } - - } - - /** - * Graphics Update PDU. - */ - public static final int PDUTYPE2_UPDATE = 0x02; - - /** - * Control PDU. - */ - public static final int PDUTYPE2_CONTROL = 0x14; - - /** - * Pointer Update PDU. - */ - public static final int PDUTYPE2_POINTER = 0x1B; - - /** - * Input Event PDU. - */ - public static final int PDUTYPE2_INPUT = 0x1C; - - /** - * Synchronize PDU. - */ - public static final int PDUTYPE2_SYNCHRONIZE = 0x1F; - - /** - * Refresh Rect PDU. - */ - public static final int PDUTYPE2_REFRESH_RECT = 0x21; - - /** - * Play Sound PDU. - */ - public static final int PDUTYPE2_PLAY_SOUND = 0x22; - - /** - * Suppress Output PDU. - */ - public static final int PDUTYPE2_SUPPRESS_OUTPUT = 0x23; - - /** - * Shutdown Request PDU. - */ - public static final int PDUTYPE2_SHUTDOWN_REQUEST = 0x24; - - /** - * Shutdown Request Denied PDU. - */ - public static final int PDUTYPE2_SHUTDOWN_DENIED = 0x25; - - /** - * Save Session Info PDU. - */ - public static final int PDUTYPE2_SAVE_SESSION_INFO = 0x26; - - /** - * Font List PDU. - */ - public static final int PDUTYPE2_FONTLIST = 0x27; - - /** - * Font Map PDU. - */ - public static final int PDUTYPE2_FONTMAP = 0x28; - - /** - * Set Keyboard Indicators PDU. - */ - public static final int PDUTYPE2_SET_KEYBOARD_INDICATORS = 0x29; - - /** - * Persistent Key List PDU. - */ - public static final int PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST = 0x2B; - - /** - * Bitmap Cache Error PDU. - */ - public static final int PDUTYPE2_BITMAPCACHE_ERROR_PDU = 0x2C; - - /** - * Set Keyboard IME Status PDU. - */ - public static final int PDUTYPE2_SET_KEYBOARD_IME_STATUS = 0x2D; - - /** - * Offscreen Bitmap Cache Error PDU. - */ - public static final int PDUTYPE2_OFFSCRCACHE_ERROR_PDU = 0x2E; - - /** - * Set Error Info PDU. - */ - public static final int PDUTYPE2_SET_ERROR_INFO_PDU = 0x2F; - - /** - * DrawNineGrid Cache Error PDU. - */ - public static final int PDUTYPE2_DRAWNINEGRID_ERROR_PDU = 0x30; - - /** - * GDI+ Error PDU. - */ - public static final int PDUTYPE2_DRAWGDIPLUS_ERROR_PDU = 0x31; - - /** - * Auto-Reconnect Status PDU. - */ - public static final int PDUTYPE2_ARC_STATUS_PDU = 0x32; - - /** - * Status Info PDU. - */ - public static final int PDUTYPE2_STATUS_INFO_PDU = 0x36; - - /** - * Monitor Layout PDU. - */ - public static final int PDUTYPE2_MONITOR_LAYOUT_PDU = 0x37; - - /** - * Indicates an Orders Update. - */ - public static final int UPDATETYPE_ORDERS = 0x0000; - - /** - * Indicates a Bitmap Graphics Update. - */ - public static final int UPDATETYPE_BITMAP = 0x0001; - - /** - * Indicates a Palette Update. - */ - public static final int UPDATETYPE_PALETTE = 0x0002; - - /** - * Indicates a Synchronize Update. - */ - public static final int UPDATETYPE_SYNCHRONIZE = 0x0003; - - /** - * @see http://msdn.microsoft.com/en-us/library/cc240577.aspx - */ - protected void handleDataPdu(ByteBuffer buf) { - - // (4 bytes): A 32-bit, unsigned integer. Share identifier for the packet. - long shareId = buf.readUnsignedIntLE(); - if (shareId != state.serverShareId) - throw new RuntimeException("Unexpected share ID: " + shareId + "."); -// buf.skipBytes(4); - - // Padding. - buf.skipBytes(1); - - // (1 byte): An 8-bit, unsigned integer. The stream identifier for the - // packet. - // int streamId = buf.readUnsignedByte(); - buf.skipBytes(1); - - // (2 bytes): A 16-bit, unsigned integer. The uncompressed length of the - // packet in bytes. - int uncompressedLength = buf.readUnsignedShortLE(); - - // (1 byte): An 8-bit, unsigned integer. The type of Data PDU. - int type2 = buf.readUnsignedByte(); - - // (1 byte): An 8-bit, unsigned integer. The compression type and flags - // specifying the data following the Share Data Header - int compressedType = buf.readUnsignedByte(); - if (compressedType != 0) - throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + "."); - - // (2 bytes): A 16-bit, unsigned integer. The compressed length of the - // packet in bytes. - int compressedLength = buf.readUnsignedShortLE(); - if (compressedLength != 0) - throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + "."); - - ByteBuffer data = buf.readBytes(uncompressedLength - 18); - buf.unref(); - - switch (type2) { - - case PDUTYPE2_UPDATE: { - - // (2 bytes): A 16-bit, unsigned integer. Type of the graphics update. - int updateType = data.readUnsignedShortLE(); - ByteBuffer data2 = data.readBytes(data.length - data.cursor); - data.unref(); - - switch (updateType) { - case UPDATETYPE_ORDERS: - pushDataToPad("orders", data2); - break; - case UPDATETYPE_BITMAP: - pushDataToPad("bitmap", data2); - break; - case UPDATETYPE_PALETTE: - pushDataToPad("palette", data2); - break; - case UPDATETYPE_SYNCHRONIZE: - // Ignore - data2.unref(); - break; - } - - break; - } - case PDUTYPE2_CONTROL: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_CONTROL ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_POINTER: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_POINTER ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_INPUT: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_INPUT ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SYNCHRONIZE: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SYNCHRONIZE ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_REFRESH_RECT: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_REFRESH_RECT ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_PLAY_SOUND: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_PLAY_SOUND ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SUPPRESS_OUTPUT: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SUPPRESS_OUTPUT ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SHUTDOWN_REQUEST: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_REQUEST ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SHUTDOWN_DENIED: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_DENIED ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SAVE_SESSION_INFO: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SAVE_SESSION_INFO ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_FONTLIST: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTLIST ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_FONTMAP: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTMAP ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SET_KEYBOARD_INDICATORS: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_INDICATORS ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_BITMAPCACHE_ERROR_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_ERROR_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SET_KEYBOARD_IME_STATUS: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_IME_STATUS ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_OFFSCRCACHE_ERROR_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_OFFSCRCACHE_ERROR_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_SET_ERROR_INFO_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_ERROR_INFO_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_DRAWNINEGRID_ERROR_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWNINEGRID_ERROR_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_DRAWGDIPLUS_ERROR_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWGDIPLUS_ERROR_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_ARC_STATUS_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_ARC_STATUS_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_STATUS_INFO_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_STATUS_INFO_PDU ignored."); - // Ignore - data.unref(); - break; - case PDUTYPE2_MONITOR_LAYOUT_PDU: - if (verbose) - System.out.println("[" + this + "] INFO: Packet PDUTYPE2_MONITOR_LAYOUT_PDU ignored."); - // Ignore - data.unref(); - break; - - default: - throw new RuntimeException("Unknow data PDU type: " + type2 + ", data: " + buf + "."); - } - } - - /** - * Example. - * - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - byte[] packet = new byte[] { - // TPKT - (byte)0x03, (byte)0x00, // TPKT Header: TPKT version = 3 - (byte)0x00, (byte)0x1B, // TPKT length: 27 bytes - - // X224 - (byte)0x02, // X224 Length: 2 bytes - (byte)0xF0, // X224 Type: Data - (byte)0x80, // X224 EOT - - // MCS - // Type: send data indication: 26 (0x1a, top 6 bits) - (byte)0x68, // ?? - - (byte)0x00, (byte)0x01, // User ID: 1002 (1001+1) - (byte)0x03, (byte)0xEB, // Channel ID: 1003 - (byte)0x70, // Data priority: high, segmentation: begin|end - (byte)0x0D, // Payload length: 13 bytes - - // Deactivate all PDU - (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) - - // - PDUType: (0x16, LE) - // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU - // ProtocolVersion: (000000000001....) 1 - (byte)0x16, (byte)0x00, - - (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) - (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 - - (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) - (byte)0x00, // Source descriptor (should be set to 0): 0 - }; - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); - RdpState rdpState = new RdpState() { - { - serverShareId = 66538; - } - }; - Element channel1003 = new ServerChannel1003Router("channel_1003", rdpState); - Element mcs = new ServerMCSPDU("mcs"); - Element tpkt = new ServerTpkt("tpkt"); - Element x224 = new ServerX224DataPdu("x224"); - Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { - // Deactivate all PDU - (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) - - // - PDUType: 22 (0x16, LE) - // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU - // ProtocolVersion: (000000000001....) 1 - (byte)0x16, (byte)0x00, - - (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) - (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 - - (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) - (byte)0x00, // Source descriptor (should be set to 0): 0 - })); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, tpkt, x224, mcs, channel1003, sink); - pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "channel_1003 >deactivate_all", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java deleted file mode 100644 index 9605b85311d..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java +++ /dev/null @@ -1,660 +0,0 @@ -// 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 rdpclient; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Element; -import streamer.FakeSink; -import streamer.Link; -import streamer.MockSource; -import streamer.Order; -import streamer.Pipeline; -import streamer.PipelineImpl; -import common.ScreenDescription; - -/** - * @see http://msdn.microsoft.com/en-us/library/cc240669.aspx - * @see http://msdn.microsoft.com/en-us/library/cc240484.aspx - */ -public class ServerDemandActivePDU extends BaseElement { - - /** - * Demand Active PDU. - */ - public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1; - - protected RdpState state; - protected ScreenDescription screen; - - public ServerDemandActivePDU(String id, ScreenDescription screen, RdpState state) { - super(id); - this.state = state; - this.screen = screen; - } - - @Override - public void handleData(ByteBuffer buf, Link link) { - if (verbose) - System.out.println("[" + this + "] INFO: Data received: " + buf + "."); - - // Total length of packet - int length = buf.readSignedShortLE(); // Ignore - if (buf.length != length) - throw new RuntimeException("Incorrect length of packet. Length: " + length + ", data: " + buf + "."); - - int type = buf.readSignedShortLE() & 0xf; - if (type != PDUTYPE_DEMANDACTIVEPDU) - throw new RuntimeException("Unknown PDU type. Expected type: Demand Active PDU (0x1), actual tyoe: " + type + ", data: " + buf + "."); - - // TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002) - int pduSource = buf.readSignedShortLE(); - if (pduSource != 1002) - throw new RuntimeException("Unexepcted source of demand active PDU. Expected source: 1002, actual source: " + pduSource + "."); - - // (4 bytes): A 32-bit, unsigned integer. The share identifier for the - // packet (see [T128] section 8.4.2 for more information regarding share - // IDs). - long shareId = buf.readUnsignedIntLE(); - state.serverShareId = shareId; - - // Ignore rest of server data because it is not used by this client. - // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the - // sourceDescriptor field. - int lengthSourceDescriptor = buf.readUnsignedShortLE(); - - // (2 bytes): A 16-bit, unsigned integer. The combined size in bytes of the - // numberCapabilities, pad2Octets, and capabilitySets fields. - int lengthCombinedCapabilities = buf.readUnsignedShortLE(); - - // (variable): A variable-length array of bytes containing a source - // descriptor, - // ByteBuffer sourceDescriptor = buf.readBytes(lengthSourceDescriptor); - buf.skipBytes(lengthSourceDescriptor); - - // (variable): An array of Capability Set (section 2.2.1.13.1.1.1) - // structures. The number of capability sets is specified by the - // numberCapabilities field. - handleCapabiltySets(buf.readBytes(lengthCombinedCapabilities)); - - // (4 bytes): A 32-bit, unsigned integer. The session identifier. This field - // is ignored by the client. - buf.skipBytes(4); - - /* DEBUG */buf.assertThatBufferIsFullyRead(); - - buf.unref(); - - sendHandshakePackets(); - } - - /** - * General Capability Set - */ - public static final int CAPSTYPE_GENERAL = 0x0001; - /** - * Bitmap Capability Set - */ - public static final int CAPSTYPE_BITMAP = 0x0002; - /** - * Order Capability Set - */ - public static final int CAPSTYPE_ORDER = 0x0003; - /** - * Revision 1 Bitmap Cache Capability Set - */ - public static final int CAPSTYPE_BITMAPCACHE = 0x0004; - /** - * Control Capability Set - */ - public static final int CAPSTYPE_CONTROL = 0x0005; - /** - * Window Activation Capability Set - */ - public static final int CAPSTYPE_ACTIVATION = 0x0007; - /** - * Pointer Capability Set - */ - public static final int CAPSTYPE_POINTER = 0x0008; - /** - * Share Capability Set - */ - public static final int CAPSTYPE_SHARE = 0x0009; - /** - * Color Table Cache Capability Set - */ - public static final int CAPSTYPE_COLORCACHE = 0x000A; - /** - * Sound Capability Set - */ - public static final int CAPSTYPE_SOUND = 0x000C; - /** - * Input Capability Set - */ - public static final int CAPSTYPE_INPUT = 0x000D; - /** - * Font Capability Set - */ - public static final int CAPSTYPE_FONT = 0x000E; - /** - * Brush Capability Set - */ - public static final int CAPSTYPE_BRUSH = 0x000F; - /** - * Glyph Cache Capability Set - */ - public static final int CAPSTYPE_GLYPHCACHE = 0x0010; - /** - * Offscreen Bitmap Cache Capability Set - */ - public static final int CAPSTYPE_OFFSCREENCACHE = 0x0011; - /** - * Bitmap Cache Host Support Capability Set - */ - public static final int CAPSTYPE_BITMAPCACHE_HOSTSUPPORT = 0x0012; - /** - * Revision 2 Bitmap Cache Capability Set - */ - public static final int CAPSTYPE_BITMAPCACHE_REV2 = 0x0013; - /** - * Virtual Channel Capability Set - */ - public static final int CAPSTYPE_VIRTUALCHANNEL = 0x0014; - /** - * DrawNineGrid Cache Capability Set - */ - public static final int CAPSTYPE_DRAWNINEGRIDCACHE = 0x0015; - /** - * Draw GDI+ Cache Capability Set - */ - public static final int CAPSTYPE_DRAWGDIPLUS = 0x0016; - /** - * Remote Programs Capability Set - */ - public static final int CAPSTYPE_RAIL = 0x0017; - /** - * Window List Capability Set - */ - public static final int CAPSTYPE_WINDOW = 0x0018; - /** - * Desktop Composition Extension Capability Set - */ - public static final int CAPSETTYPE_COMPDESK = 0x0019; - /** - * Multifragment Update Capability Set - */ - public static final int CAPSETTYPE_MULTIFRAGMENTUPDATE = 0x001A; - /** - * Large Pointer Capability Set - */ - public static final int CAPSETTYPE_LARGE_POINTER = 0x001B; - /** - * Surface Commands Capability Set - */ - public static final int CAPSETTYPE_SURFACE_COMMANDS = 0x001C; - /** - * Bitmap Codecs Capability Set - */ - public static final int CAPSETTYPE_BITMAP_CODECS = 0x001D; - /** - * Frame Acknowledge Capability Set - */ - public static final int CAPSSETTYPE_FRAME_ACKNOWLEDGE = 0x001E; - - /** - * @see http://msdn.microsoft.com/en-us/library/cc240486.aspx - */ - protected void handleCapabiltySets(ByteBuffer buf) { - // (2 bytes): A 16-bit, unsigned integer. The number of capability sets - // included in the Demand Active PDU. - int numberCapabilities = buf.readSignedShortLE(); - - // (2 bytes): Padding. - buf.skipBytes(2); - - for (int i = 0; i < numberCapabilities; i++) { - // (2 bytes): A 16-bit, unsigned integer. The type identifier of the - // capability set. - int capabilitySetType = buf.readUnsignedShortLE(); - - // (2 bytes): A 16-bit, unsigned integer. The length in bytes of the - // capability data, including the size of the capabilitySetType and - // lengthCapability fields. - int lengthCapability = buf.readUnsignedShortLE(); - - // (variable): Capability set data which conforms to the structure of the - // type given by the capabilitySetType field. - ByteBuffer capabilityData = buf.readBytes(lengthCapability - 4); - - switch (capabilitySetType) { - case CAPSTYPE_GENERAL: - break; - case CAPSTYPE_BITMAP: - handleBitmapCapabilities(capabilityData); - break; - case CAPSTYPE_ORDER: - break; - case CAPSTYPE_BITMAPCACHE: - break; - case CAPSTYPE_CONTROL: - break; - case CAPSTYPE_ACTIVATION: - break; - case CAPSTYPE_POINTER: - break; - case CAPSTYPE_SHARE: - break; - case CAPSTYPE_COLORCACHE: - break; - case CAPSTYPE_SOUND: - break; - case CAPSTYPE_INPUT: - break; - case CAPSTYPE_FONT: - break; - case CAPSTYPE_BRUSH: - break; - case CAPSTYPE_GLYPHCACHE: - break; - case CAPSTYPE_OFFSCREENCACHE: - break; - case CAPSTYPE_BITMAPCACHE_HOSTSUPPORT: - break; - case CAPSTYPE_BITMAPCACHE_REV2: - break; - case CAPSTYPE_VIRTUALCHANNEL: - break; - case CAPSTYPE_DRAWNINEGRIDCACHE: - break; - case CAPSTYPE_DRAWGDIPLUS: - break; - case CAPSTYPE_RAIL: - break; - case CAPSTYPE_WINDOW: - break; - case CAPSETTYPE_COMPDESK: - break; - case CAPSETTYPE_MULTIFRAGMENTUPDATE: - break; - case CAPSETTYPE_LARGE_POINTER: - break; - case CAPSETTYPE_SURFACE_COMMANDS: - break; - case CAPSETTYPE_BITMAP_CODECS: - break; - case CAPSSETTYPE_FRAME_ACKNOWLEDGE: - break; - default: - // Ignore - break; - } - - capabilityData.unref(); - } - - // TODO - - buf.unref(); - } - - /** - * @see http://msdn.microsoft.com/en-us/library/cc240554.aspx - */ - protected void handleBitmapCapabilities(ByteBuffer buf) { - - // (2 bytes): A 16-bit, unsigned integer. The server MUST set this field to - // the color depth of the session, while the client SHOULD set this field to - // the color depth requested in the Client Core Data (section 2.2.1.3.2). - int preferredBitsPerPixel = buf.readUnsignedShortLE(); - screen.setPixelFormatRGBTrueColor(preferredBitsPerPixel); - - // receive1BitPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates - // whether the client can receive 1 bpp. This field is ignored and SHOULD be - // set to TRUE (0x0001). - buf.skipBytes(2); - - // receive4BitsPerPixel(2 bytes): A 16-bit, unsigned integer. Indicates - // whether the client can receive 4 bpp. This field is ignored and SHOULD be - // set to TRUE (0x0001). - buf.skipBytes(2); - - // receive8BitsPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates - // whether the client can receive 8 bpp. This field is ignored and SHOULD be - // set to TRUE (0x0001). - buf.skipBytes(2); - - // (2 bytes): A 16-bit, unsigned integer. The width of the desktop in the - // session. - int desktopWidth = buf.readUnsignedShortLE(); - - // (2 bytes): A 16-bit, unsigned integer. The height of the desktop in the - // session. - int desktopHeight = buf.readUnsignedShortLE(); - - screen.setFramebufferSize(desktopWidth, desktopHeight); - - // pad2octets (2 bytes): A 16-bit, unsigned integer. Padding. Values in this - // field MUST be ignored. - - // desktopResizeFlag (2 bytes): A 16-bit, unsigned integer. Indicates - // whether resizing the desktop by using a Deactivation-Reactivation - // Sequence is supported. - - // bitmapCompressionFlag (2 bytes): A 16-bit, unsigned integer. Indicates - // whether bitmap compression is supported. This field MUST be set to TRUE - // (0x0001) because support for compressed bitmaps is required for a - // connection to proceed. - - // highColorFlags (1 byte): An 8-bit, unsigned integer. Client support for - // 16 bpp color modes. This field is ignored and SHOULD be set to zero. - - // drawingFlags (1 byte): An 8-bit, unsigned integer. Flags describing - // support for 32 bpp bitmaps. - - // multipleRectangleSupport (2 bytes): A 16-bit, unsigned integer. Indicates - // whether the use of multiple bitmap rectangles is supported in the Bitmap - // Update (section 2.2.9.1.1.3.1.2). This field MUST be set to TRUE (0x0001) - // because multiple rectangle support is required for a connection to - // proceed. - - // pad2octetsB (2 bytes): A 16-bit, unsigned integer. Padding. Values in - // this field MUST be ignored. - } - - /** - * Send all client requests in one hop, to simplify logic. - */ - protected void sendHandshakePackets() { - // Send reactivation sequence in bulk - pushDataToPad("confirm_active", new ByteBuffer((Order)null)); - } - - /** - * Example. - * - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - /* @formatter:off */ - byte[] packet = new byte[] { - 0x67, 0x01, // TS_SHARECONTROLHEADER::totalLength = 0x0167 = 359 bytes - 0x11, 0x00, // TS_SHARECONTROLHEADER::pduType = 0x0011 0x0011 = 0x0010 | 0x0001 = TS_PROTOCOL_VERSION | PDUTYPE_DEMANDACTIVEPDU - - (byte) 0xea, 0x03, // TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002) - - (byte) 0xea, 0x03, 0x01, 0x00, // TS_DEMAND_ACTIVE_PDU::shareId - 0x04, 0x00, // TS_DEMAND_ACTIVE_PDU::lengthSourceDescriptor = 4 bytes - 0x51, 0x01, // TS_DEMAND_ACTIVE_PDU::lengthCombinedCapabilities = 0x151 = 337 bytes - - 0x52, 0x44, 0x50, 0x00, // TS_DEMAND_ACTIVE_PDU::sourceDescriptor = "RDP" - - 0x0d, 0x00, // TS_DEMAND_ACTIVE_PDU::numberCapabilities = 13 - 0x00, 0x00, // TS_DEMAND_ACTIVE_PDU::pad2octets - - // Share Capability Set (8 bytes) - // 0x09, 0x00, 0x08, 0x00, (byte) 0xea, 0x03, (byte) 0xdc, (byte) 0xe2, - // - 0x09, 0x00, // TS_SHARE_CAPABILITYSET::capabilitySetType = CAPSTYPE_SHARE (9) - 0x08, 0x00, // TS_SHARE_CAPABILITYSET::lengthCapability = 8 bytes - (byte) 0xea, 0x03, // TS_SHARE_CAPABILITYSET::nodeID = 0x03ea (1002) - (byte) 0xdc, (byte) 0xe2, // TS_SHARE_CAPABILITYSET::pad2octets - - // General Capability Set (24 bytes) - // 0x01, 0x00, 0x18, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x04, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - // - 0x01, 0x00, // TS_GENERAL_CAPABILITYSET::capabilitySetType = CAPSTYPE_GENERAL (1) - 0x18, 0x00, // TS_GENERAL_CAPABILITYSET::lengthCapability = 24 bytes - - 0x01, 0x00, // TS_GENERAL_CAPABILITYSET::osMajorType = TS_OSMAJORTYPE_WINDOWS (1) - 0x03, 0x00, // TS_GENERAL_CAPABILITYSET::osMinorType = TS_OSMINORTYPE_WINDOWS_NT (3) - 0x00, 0x02, // TS_GENERAL_CAPABILITYSET::protocolVersion = TS_CAPS_PROTOCOLVERSION (0x0200) - 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::pad2octetsA - 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::generalCompressionTypes = 0 - 0x1d, 0x04, // TS_GENERAL_CAPABILITYSET::extraFlags = 0x041d = 0x0400 | 0x0010 | 0x0008 | 0x0004 | 0x0001 = NO_BITMAP_COMPRESSION_HDR | ENC_SALTED_CHECKSUM | AUTORECONNECT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED | FASTPATH_OUTPUT_SUPPORTED - - 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::updateCapabilityFlag = 0 - 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::remoteUnshareFlag = 0 - 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::generalCompressionLevel = 0 - 0x01, // TS_GENERAL_CAPABILITYSET::refreshRectSupport = TRUE - 0x01, // TS_GENERAL_CAPABILITYSET::suppressOutputSupport = TRUE - - // Virtual Channel Capability Set (8 bytes) - // 0x14, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, - // - 0x14, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::capabilitySetType = CAPSTYPE_VIRTUALCHANNEL (20) - 0x08, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::lengthCapability = 8 bytes - - 0x02, 0x00, 0x00, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::vccaps1 = 0x00000002 = VCCAPS_COMPR_CS_8K - - // DrawGdiPlus Capability Set (40 bytes) - // 0x16, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, (byte) 0xf6, 0x13, (byte) 0xf3, 0x01, 0x00, 0x00, 0x00, - // 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, (byte) 0x9c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x61, (byte) 0xa6, (byte) 0x82, (byte) 0x80, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, (byte) 0x91, (byte) 0xbf, - // - 0x16, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::capabilitySetType = CAPSTYPE_DRAWGDIPLUS (22) - 0x28, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::lengthCapability = 40 bytes - - 0x00, 0x00, 0x00, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusSupportLevel = TS_DRAW_GDIPLUS_DEFAULT (0) - 0x70, (byte) 0xf6, 0x13, (byte) 0xf3, // TS_DRAW_GDIPLUS_CAPABILITYSET::GdipVersion (not initialized by server) - 0x01, 0x00, 0x00, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusCacheLevel = TS_DRAW_GDIPLUS_CACHE_LEVEL_ONE (1) - - 0x01, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipGraphicsCacheEntries (not initialized by server) - 0x00, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectBrushCacheEntries (not initialized by server) - 0x18, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectPenCacheEntries (not initialized by server) - 0x00, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageCacheEntries (not initialized by server) - (byte) 0x9c, (byte) 0xf6, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageAttributesCacheEntries (not initialized by server) - - 0x13, (byte) 0xf3, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipGraphicsCacheChunkSize (not initialized by server) - 0x61, (byte) 0xa6, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectBrushCacheChunkSize (not initialized by server) - (byte) 0x82, (byte) 0x80, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectPenCacheChunkSize (not initialized by server) - 0x00, 0x00, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectImageAttributesCacheChunkSize (not initialized by server) - - 0x00, 0x00, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheChunkSize (not initialized by server) - 0x00, 0x50, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheTotalSize (not initialized by server) - (byte) 0x91, (byte) 0xbf, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheMaxSize (not initialized by server) - - // Font Capability Set (4 bytes) - // 0x0e, 0x00, 0x04, 0x00, - // - // Due to a bug, the TS_FONT_CAPABILITYSET capability set size is incorrectly set to 4 bytes (it must be 8 bytes). As a result of this bug, the fontSupportFlags and pad2octets fields are missing. - 0x0e, 0x00, // TS_FONT_CAPABILITYSET::capabilitySetType = CAPSTYPE_FONT (14) - 0x04, 0x00, // TS_FONT_CAPABILITYSET::lengthCapability = 4 bytes - - - // Bitmap Capability Set (28 bytes) - // 0x02, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, - // 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - // - 0x02, 0x00, // TS_BITMAP_CAPABILITYSET::capabilitySetType = CAPSTYPE_BITMAP (2) - 0x1c, 0x00, // TS_BITMAP_CAPABILITYSET::lengthCapability = 28 bytes - - 0x18, 0x00, // TS_BITMAP_CAPABILITYSET::preferredBitsPerPixel = 24 bpp - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive1BitPerPixel = TRUE - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive4BitsPerPixel = TRUE - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive8BitsPerPixel = TRUE - 0x00, 0x05, // TS_BITMAP_CAPABILITYSET::desktopWidth = 1280 pixels - 0x00, 0x04, // TS_BITMAP_CAPABILITYSET::desktopHeight = 1024 pixels - 0x00, 0x00, // TS_BITMAP_CAPABILITYSET::pad2octets - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::desktopResizeFlag = TRUE - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::bitmapCompressionFlag = TRUE - 0x00, // TS_BITMAP_CAPABILITYSET::highColorFlags = 0 - 0x00, // TS_BITMAP_CAPABILITYSET::pad1octet - 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::multipleRectangleSupport = TRUE - 0x00, 0x00, // TS_BITMAP_CAPABILITYSET::pad2octetsB - - // Order Capability Set (88 bytes) - // 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, - // 0x00, 0x00, 0x22, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, - // 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, - // 0x00, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x40, 0x42, 0x0f, 0x00, - // 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // - 0x03, 0x00, // TS_ORDER_CAPABILITYSET::capabilitySetType = CAPSTYPE_ORDER (3) - 0x58, 0x00, // TS_ORDER_CAPABILITYSET::lengthCapability = 88 bytes - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TS_ORDER_CAPABILITYSET::terminalDescriptor = "" - 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::pad4octetsA - - 0x01, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveXGranularity = 1 - 0x14, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveYGranularity = 20 - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsA - 0x01, 0x00, // TS_ORDER_CAPABILITYSET::maximumOrderLevel = ORD_LEVEL_1_ORDERS (1) - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::numberFonts = 0 - - 0x22, 0x00, // TS_ORDER_CAPABILITYSET::orderFlags = 0x0022 = 0x0020 | 0x0002 = COLORINDEXSUPPORT | NEGOTIATEORDERSUPPORT - - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DSTBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_PATBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SCRBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_INDEX] = TRUE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ATEXTOUT_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_AEXTTEXTOUT_INDEX] = FALSE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DRAWNINEGRID_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_LINETO_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTI_DRAWNINEGRID_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_OPAQUERECT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SAVEBITMAP_INDEX] = TRUE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WTEXTOUT_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_R2_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_R2_INDEX] = FALSE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIDSTBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIPATBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTISCRBLT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIOPAQUERECT_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_INDEX_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_SC_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_CB_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYLINE_INDEX] = TRUE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[23] = 0 - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_GLYPH_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_SC_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_CB_INDEX] = TRUE - 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_INDEX_INDEX] = TRUE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WEXTTEXTOUT_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGTEXTOUT_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGEXTTEXTOUT_INDEX] = FALSE - 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[24] = 0 - - (byte) 0xa1, 0x06, // TS_ORDER_CAPABILITYSET::textFlags = 0x06a1 - - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsB - 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::pad4octetsB - - 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveSize = 0xf4240 = 1000000 - 0x01, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsC - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsD - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::textANSICodePage - 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsE - - // Color Table Cache Capability Set (8 bytes) - // 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - // - 0x0a, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::capabilitySetType = CAPSTYPE_COLORCACHE (10) - 0x08, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::lengthCapability = 8 bytes - - 0x06, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::colorTableCacheSize = 6 - 0x00, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::pad2octets - - // Bitmap Cache Host Support Capability Set (8 bytes) - // 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, - // - 0x12, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::capabilitySetType = CAPSTYPE_BITMAPCACHE_HOSTSUPPORT (18) - 0x08, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::lengthCapability = 8 bytes - - 0x01, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::CacheVersion = 1 (corresponds to rev. 2 capabilities) - 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad1 - 0x00, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad2 - - // Pointer Capability Set (10 bytes) - // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, 0x19, 0x00, - // - 0x08, 0x00, // TS_POINTER_CAPABILITYSET::capabilitySetType = CAPSTYPE_POINTER (8) - 0x0a, 0x00, // TS_POINTER_CAPABILITYSET::lengthCapability = 10 bytes - - 0x01, 0x00, // TS_POINTER_CAPABILITYSET::colorPointerFlag = TRUE - 0x19, 0x00, // TS_POINTER_CAPABILITYSET::colorPointerCacheSize = 25 - 0x19, 0x00, // TS_POINTER_CAPABILITYSET::pointerCacheSize = 25 - - // Input Capability Set (88 bytes) - // 0x0d, 0x00, 0x58, 0x00, 0x35, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, - // 0x0c, (byte) 0xf6, 0x13, (byte) 0xf3, (byte) 0x93, 0x5a, 0x37, (byte) 0xf3, 0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, - // 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, 0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, - // 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, - // 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, - // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, - // - 0x0d, 0x00, // TS_INPUT_CAPABILITYSET::capabilitySetType = CAPSTYPE_INPUT (13) - 0x58, 0x00, // TS_INPUT_CAPABILITYSET::lengthCapability = 88 bytes - - 0x35, 0x00, // TS_INPUT_CAPABILITYSET::inputFlags = 0x0035 = 0x0020 | 0x0010 | 0x0004 | 0x0001 = INPUT_FLAG_FASTPATH_INPUT2 | INPUT_FLAG_VKPACKET | INPUT_FLAG_MOUSEX | INPUT_FLAG_SCANCODES - - 0x00, 0x00, // TS_INPUT_CAPABILITYSET::pad2octetsA - (byte) 0xa1, 0x06, 0x00, 0x00, // TS_INPUT_CAPABILITYSET::keyboardLayout (not initialized by server) - 0x40, 0x42, 0x0f, 0x00, // TS_INPUT_CAPABILITYSET::keyboardType (not initialized by server) - 0x0c, (byte) 0xf6, 0x13, (byte) 0xf3, // TS_INPUT_CAPABILITYSET::keyboardSubType (not initialized by server) - (byte) 0x93, 0x5a, 0x37, (byte) 0xf3, // TS_INPUT_CAPABILITYSET::keyboardFunctionKey (not initialized by server) - - // TS_INPUT_CAPABILITYSET::imeFileName (not initialized by server) - 0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, - 0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, - 0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, - 0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, - - // RAIL Capability Set (8 bytes) - // 0x17, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - // - 0x17, 0x00, // TS_RAIL_CAPABILITYSET::capabilitySetType = CAPSTYPE_RAIL (23) - 0x08, 0x00, // TS_RAIL_CAPABILITYSET::lengthCapability = 8 bytes - - 0x00, 0x00, 0x00, 0x00, // TS_RAIL_CAPABILITYSET::railSupportLevel = TS_RAIL_LEVEL_DEFAULT (0) - - // Windowing Capability Set (11 bytes) - // 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // - 0x18, 0x00, // TS_WINDOW_CAPABILITYSET::capabilitySetType = CAPSTYPE_WINDOW (24) - 0x0b, 0x00, // TS_WINDOW_CAPABILITYSET::lengthCapability = 11 bytes - - 0x00, 0x00, 0x00, 0x00, // TS_WINDOW_CAPABILITYSET::wndSupportLevel = TS_WINDOW_LEVEL_DEFAULT (0) - 0x00, // TS_WINDOW_CAPABILITYSET::nIconCaches = 0 - 0x00, 0x00, // TS_WINDOW_CAPABILITYSET::nIconCacheEntries = 0 - - // Remainder of Demand Active PDU: - - 0x00, 0x00, 0x00, 0x00, // TS_DEMAND_ACTIVE_PDU::sessionId = 0 - }; - /* @formatter:on */ - - RdpState rdpState = new RdpState(); - ScreenDescription screenDescription = new ScreenDescription(); - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); - Element demandActive = new ServerDemandActivePDU("demand_active", screenDescription, rdpState); - Element sink = new FakeSink("sink"); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, demandActive, sink); - pipeline.link("source", "demand_active", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java deleted file mode 100644 index 1d7ddc29224..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java +++ /dev/null @@ -1,258 +0,0 @@ -// 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 rdpclient; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Link; - -/** - * @see http://msdn.microsoft.com/en-us/library/cc240621.aspx - */ -public class ServerFastPath extends BaseElement { - - /** - * TPKT protocol version (first byte). - */ - public static final int PROTOCOL_TPKT = 3; - - /** - * Fast path protocol version (first two bits of first byte). - */ - public static final int PROTOCOL_FASTPATH = 0; - - /** - * TPKT packets will be pushed to that pad. - */ - public static final String TPKT_PAD = "tpkt"; - - private static final String ORDERS_PAD = "orders"; - private static final String BITMAP_PAD = "bitmap"; - private static final String PALETTE_PAD = "palette"; - - /** - * Indicates that packet contains 8 byte secure checksum at top of packet. Top - * two bits of first byte. - */ - public static final int FASTPATH_OUTPUT_SECURE_CHECKSUM = 1; - - /** - * Indicates that packet contains 8 byte secure checksum at top of packet and - * packet content is encrypted. Top two bits of first byte. - */ - public static final int FASTPATH_OUTPUT_ENCRYPTED = 2; - - public static final int FASTPATH_UPDATETYPE_ORDERS = 0; - public static final int FASTPATH_UPDATETYPE_BITMAP = 1; - public static final int FASTPATH_UPDATETYPE_PALETTE = 2; - public static final int FASTPATH_UPDATETYPE_SYNCHRONIZE = 3; - public static final int FASTPATH_UPDATETYPE_SURFCMDS = 4; - public static final int FASTPATH_UPDATETYPE_PTR_NULL = 5; - public static final int FASTPATH_UPDATETYPE_PTR_DEFAULT = 6; - public static final int FASTPATH_UPDATETYPE_PTR_POSITION = 8; - public static final int FASTPATH_UPDATETYPE_COLOR = 9; - public static final int FASTPATH_UPDATETYPE_CACHED = 0xa; - public static final int FASTPATH_UPDATETYPE_POINTER = 0xb; - - public static final int FASTPATH_FRAGMENT_SINGLE = 0; - public static final int FASTPATH_FRAGMENT_LAST = 1; - public static final int FASTPATH_FRAGMENT_FIRST = 2; - public static final int FASTPATH_FRAGMENT_NEXT = 3; - - public static final int FASTPATH_OUTPUT_COMPRESSION_USED = 2; - - public ServerFastPath(String id) { - super(id); - } - - @Override - public void handleData(ByteBuffer buf, Link link) { - if (buf == null) - return; - - if (verbose) - System.out.println("[" + this + "] INFO: Data received: " + buf + "."); - - //* DEBUG */System.out.println(buf.toHexString(buf.length)); - - // We need at 4 bytes to read packet type (TPKT or FastPath) and packet - // length - if (!cap(buf, 4, UNLIMITED, link, false)) - return; - - int typeAndFlags = buf.readUnsignedByte(); - - if (typeAndFlags == PROTOCOL_TPKT) { - // - // TPKT - // - - // Reserved - buf.skipBytes(1); - - // Read TPKT length - int length = buf.readUnsignedShort(); - - if (!cap(buf, length, length, link, false)) - // Wait for full packet to arrive - return; - - pushDataToPad(TPKT_PAD, buf); - - // TPKT is handled - return; - } - - // - // FastPath - // - // Number of bytes in updateData field (including header (1+1 or 2 - // bytes)) - int length = buf.readVariableUnsignedShort(); - - // Length is the size of payload, so we need to calculate from cursor - if (!cap(buf, length, length, link, false)) - // Wait for full packet to arrive - return; - - int type = typeAndFlags & 0x3; - int securityFlags = (typeAndFlags >> 6) & 0x3; - - // Assertions - { - if (type != PROTOCOL_FASTPATH) - throw new RuntimeException("Unknown protocol. Expected protocol: 0 (FastPath). Actual protocol: " + type + ", data: " + buf + "."); - - switch (securityFlags) { - case FASTPATH_OUTPUT_SECURE_CHECKSUM: - // TODO - throw new RuntimeException("Secure checksum is not supported in FastPath packets."); - case FASTPATH_OUTPUT_ENCRYPTED: - // TODO - throw new RuntimeException("Encryption is not supported in FastPath packets."); - } - } - - // TODO: optional FIPS information, when FIPS is selected - // TODO: optional data signature (checksum), when checksum or FIPS is - // selected - - // Array of FastPath update fields - while (buf.cursor < buf.length) { - - int updateHeader = buf.readUnsignedByte(); - - int size = buf.readUnsignedShortLE(); - - int updateCode = updateHeader & 0xf; - int fragmentation = (updateHeader >> 4) & 0x3; - int compression = (updateHeader >> 6) & 0x3; - - if (verbose) - System.out.println("[" + this + "] INFO: FastPath update received. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: " + - compression + ", size: " + size + "."); - - ByteBuffer data = buf.readBytes(size); - buf.putMetadata("fragmentation", fragmentation); - buf.putMetadata("compression", compression); - - switch (updateCode) { - - case FASTPATH_UPDATETYPE_ORDERS: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_ORDERS."); - pushDataToPad(ORDERS_PAD, data); - break; - - case FASTPATH_UPDATETYPE_BITMAP: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_BITMAP."); - pushDataToPad(BITMAP_PAD, data); - break; - - case FASTPATH_UPDATETYPE_PALETTE: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PALETTE."); - pushDataToPad(PALETTE_PAD, data); - break; - - case FASTPATH_UPDATETYPE_SYNCHRONIZE: - // @see http://msdn.microsoft.com/en-us/library/cc240625.aspx - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SYNCHRONIZE."); - - data.unref(); - - if (size != 0) - throw new RuntimeException("Size of FastPath synchronize packet must be 0. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + - ", compression: " + compression + ", size: " + size + ", data: " + data + "."); - break; - - case FASTPATH_UPDATETYPE_SURFCMDS: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SURFCMDS."); - - break; - - case FASTPATH_UPDATETYPE_PTR_NULL: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_NULL."); - - break; - - case FASTPATH_UPDATETYPE_PTR_DEFAULT: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_DEFAULT."); - - break; - - case FASTPATH_UPDATETYPE_PTR_POSITION: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_POSITION."); - - break; - - case FASTPATH_UPDATETYPE_COLOR: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_COLOR."); - - break; - - case FASTPATH_UPDATETYPE_CACHED: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_CACHED."); - - break; - - case FASTPATH_UPDATETYPE_POINTER: - if (verbose) - System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_POINTER."); - - break; - - default: - throw new RuntimeException("Unknown FastPath update. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: " + - compression + ", size: " + size + ", data: " + data + "."); - - } - - } - - buf.unref(); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java deleted file mode 100644 index d9e4f425901..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSPDU.java +++ /dev/null @@ -1,149 +0,0 @@ -// 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 rdpclient; - -import streamer.BaseElement; -import streamer.ByteBuffer; -import streamer.Element; -import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; -import streamer.Pipeline; -import streamer.PipelineImpl; - -public class ServerMCSPDU extends BaseElement { - - public ServerMCSPDU(String id) { - super(id); - } - - @Override - public void handleData(ByteBuffer buf, Link link) { - if (verbose) - System.out.println("[" + this + "] INFO: Data received: " + buf + "."); - - byte headerByte = buf.readSignedByte(); - int type = headerByte >> 2; - - switch (type) { - // Expected type: send data indication: 26 (0x1a, top 6 bits, or 0x68) - case 0x1a: { - // int userId = buf.readUnsignedShort() + 1001; // User ID: 1002 (1001+1) - buf.skipBytes(2); // Ignore user ID - - int channelId = buf.readUnsignedShort(); // Channel ID: 1003 - - int flags = buf.readSignedByte(); - if ((flags & 0x30) != 0x30) - throw new RuntimeException("Fragmented MCS packets are not supported."); - - int payloadLength = buf.readVariableUnsignedShort(); - - ByteBuffer data = buf.readBytes(payloadLength); - - buf.unref(); - - pushDataToPad("channel_" + channelId, data); - break; - } - - case 0x8: { - // Disconnection sequence. - buf.unref(); - break; - } - - default: - throw new RuntimeException("Unsupported MCS packet type: " + type + "(" + headerByte + "), data: " + buf + "."); - } - - } - - /** - * Example. - * - */ - public static void main(String args[]) { - // System.setProperty("streamer.Link.debug", "true"); - // System.setProperty("streamer.Element.debug", "true"); - // System.setProperty("streamer.Pipeline.debug", "true"); - - byte[] packet = new byte[] { - // TPKT - (byte)0x03, (byte)0x00, // TPKT Header: TPKT version = 3 - (byte)0x00, (byte)0x1B, // TPKT length: 27 bytes - - // X224 - (byte)0x02, // X224 Length: 2 bytes - (byte)0xF0, // X224 Type: Data - (byte)0x80, // X224 EOT - - // MCS - // Type: send data indication: 26 (0x1a, top 6 bits) - (byte)0x68, // ?? - - (byte)0x00, (byte)0x01, // User ID: 1002 (1001+1) - (byte)0x03, (byte)0xEB, // Channel ID: 1003 - (byte)0x70, // Data priority: high, segmentation: begin|end - (byte)0x0D, // Payload length: 13 bytes - - // Deactivate all PDU - (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) - - // - PDUType: 22 (0x16, LE) - // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU - // ProtocolVersion: (000000000001....) 1 - (byte)0x16, (byte)0x00, - - (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) - (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 - - (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) - (byte)0x00, // Source descriptor (should be set to 0): 0 - }; - - MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); - Element mcs = new ServerMCSPDU("mcs") { - { - verbose = true; - } - }; - Element tpkt = new ServerTpkt("tpkt"); - Element x224 = new ServerX224DataPdu("x224"); - Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { - // Deactivate all PDU - (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) - - // - PDUType: 22 (0x16, LE) - // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU - // ProtocolVersion: (000000000001....) 1 - (byte)0x16, (byte)0x00, - - (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) - (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 - - (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) - (byte)0x00, // Source descriptor (should be set to 0): 0 - })); - - Pipeline pipeline = new PipelineImpl("test"); - pipeline.add(source, tpkt, x224, mcs, sink); - pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "sink"); - pipeline.runMainLoop("source", STDOUT, false, false); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java deleted file mode 100644 index 8d50ea4ed02..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java +++ /dev/null @@ -1,50 +0,0 @@ -// 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 rdpclient; - -/** - * Try to determine packet content by it header fingerprint. - */ -public class ServerPacketSniffer extends PacketSniffer { - - private static final Pair[] serverRegexps = new Pair[] { -// @formatter:off - new Pair("Server FastPath update", "04"), - new Pair("Server X224ConnectionRequest", "03 00 XX XX 0E D0"), - new Pair("Server MCSConnectResponse", "03 00 XX XX 02 F0 80 7F 66 5A"), - new Pair("Server AttachUserConfirm", "03 00 XX XX 02 F0 80 2E"), - new Pair("Server ChannelJoinConfirm", "03 00 XX XX 02 F0 80 3E"), - new Pair("Server ErrorAlert", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 14 80 00"), - new Pair("Server DemandActivePDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX XX 11"), - new Pair("Server ControlPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 14"), - new Pair("Server SynchronizePDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 1F"), - new Pair("Server FontMapPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 28"), - new Pair("Server SET_ERROR_INFO_PDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 30 XX XX XX 17 00 00 00 EA 03 XX 00 XX XX XX XX 2F"), - new Pair("Server DeactivateAllPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 16 00"), - new Pair("Server CloseConnection", "03 00 00 09 02 F0 80 21 80"), - -// new Pair("Server TPKT unknown packet", "03"), -// new Pair("Server FastPath update with flags or continuation", ".*"), - // @formatter:on - - }; - - public ServerPacketSniffer(String id) { - super(id, serverRegexps); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java new file mode 100755 index 00000000000..f2b19e1af49 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpKeyboardAdapter.java @@ -0,0 +1,350 @@ +// 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 rdpclient.adapter; + +import java.awt.event.KeyEvent; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; +import common.KeyOrder; + +public class AwtRdpKeyboardAdapter extends BaseElement { + + /** + * Absence of this flag indicates a key-down event, while its presence + * indicates a key-release event. + */ + public static final int FASTPATH_INPUT_KBDFLAGS_RELEASE = 0x01; + + /** + * Keystroke message contains an extended scancode. For enhanced 101-key and + * 102-key keyboards, extended keys include the right ALT and right CTRL keys + * on the main section of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE + * DOWN and ARROW keys in the clusters to the left of the numeric keypad; and + * the Divide ("/") and ENTER keys in the numeric keypad. + */ + public static final int FASTPATH_INPUT_KBDFLAGS_EXTENDED = 0x02; + + public static final int FASTPATH_INPUT_EVENT_SCANCODE = 0; + + public AwtRdpKeyboardAdapter(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + KeyOrder order = (KeyOrder)buf.getOrder(); + buf.unref(); + + ByteBuffer outBuf = new ByteBuffer(2, true); + + int scanCode = map_en_us(order.event); + + // eventHeader (1 byte): An 8-bit, unsigned integer. The format of this + // field is the same as the eventHeader byte field described in section + // 2.2.8.1.2.2. The eventCode bitfield (3 bits in size) MUST be set to + // FASTPATH_INPUT_EVENT_SCANCODE (0). The eventFlags bitfield (5 bits in + // size) contains flags describing the keyboard event. + outBuf.writeByte((scanCode >> 8) | (FASTPATH_INPUT_EVENT_SCANCODE << 5) | ((order.pressed) ? 0 : FASTPATH_INPUT_KBDFLAGS_RELEASE)); + + // keyCode (1 byte): An 8-bit, unsigned integer. The scancode of the key + // which triggered the event. + outBuf.writeByte(scanCode); + + // Push buffer to one pad only, so it can be modified without copying of + // data + pushDataToPad(STDOUT, outBuf); + } + + /** + * Return key scan code (in lower byte) and extended flags (in second byte). + */ + private int map_en_us(KeyEvent event) { + // Also set extended key flag when necessary. + // For enhanced 101-key and 102-key keyboards, extended keys include the + // right ALT and right CTRL keys on the main section of the keyboard; the + // INS, DEL, HOME, END, PAGE UP, PAGE DOWN and ARROW keys in the clusters to + // the left of the numeric keypad; and the Divide ("/") and ENTER keys in + // the numeric keypad. + + switch (event.getKeyCode()) { + // Functional keys + case KeyEvent.VK_ESCAPE: + return 1; + case KeyEvent.VK_F1: + return 59; + case KeyEvent.VK_F2: + return 60; + case KeyEvent.VK_F3: + return 61; + case KeyEvent.VK_F4: + return 62; + case KeyEvent.VK_F5: + return 63; + case KeyEvent.VK_F6: + return 64; + case KeyEvent.VK_F7: + return 65; + case KeyEvent.VK_F8: + return 66; + case KeyEvent.VK_F9: + return 67; + case KeyEvent.VK_F10: + return 68; + case KeyEvent.VK_F11: + return 87; + case KeyEvent.VK_F12: + return 88; + + // Row #1 + case KeyEvent.VK_BACK_QUOTE: + return 41; + case KeyEvent.VK_1: + return 2; + case KeyEvent.VK_2: + return 3; + case KeyEvent.VK_3: + return 4; + case KeyEvent.VK_4: + return 5; + case KeyEvent.VK_5: + return 6; + case KeyEvent.VK_6: + return 7; + case KeyEvent.VK_7: + return 8; + case KeyEvent.VK_8: + return 9; + case KeyEvent.VK_9: + return 10; + case KeyEvent.VK_0: + return 11; + case KeyEvent.VK_MINUS: + return 12; + case KeyEvent.VK_EQUALS: + return 13; + case KeyEvent.VK_BACK_SPACE: + return 14; + + // Row #2 + case KeyEvent.VK_TAB: + return 15; + case KeyEvent.VK_Q: + return 16; + case KeyEvent.VK_W: + return 17; + case KeyEvent.VK_E: + return 18; + case KeyEvent.VK_R: + return 19; + case KeyEvent.VK_T: + return 20; + case KeyEvent.VK_Y: + return 21; + case KeyEvent.VK_U: + return 22; + case KeyEvent.VK_I: + return 23; + case KeyEvent.VK_O: + return 24; + case KeyEvent.VK_P: + return 25; + case KeyEvent.VK_OPEN_BRACKET: + return 26; + case KeyEvent.VK_CLOSE_BRACKET: + return 27; + case KeyEvent.VK_ENTER: + switch (event.getKeyLocation()) { + default: + case KeyEvent.KEY_LOCATION_STANDARD: + return 28; + case KeyEvent.KEY_LOCATION_NUMPAD: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 28; + } + + // Row #3 + case KeyEvent.VK_CAPS_LOCK: + return 58; + case KeyEvent.VK_A: + return 30; + case KeyEvent.VK_S: + return 31; + case KeyEvent.VK_D: + return 32; + case KeyEvent.VK_F: + return 33; + case KeyEvent.VK_G: + return 34; + case KeyEvent.VK_H: + return 35; + case KeyEvent.VK_J: + return 36; + case KeyEvent.VK_K: + return 37; + case KeyEvent.VK_L: + return 38; + case KeyEvent.VK_SEMICOLON: + return 39; + case KeyEvent.VK_QUOTE: + return 40; + + // Row #4 + case KeyEvent.VK_SHIFT: + switch (event.getKeyLocation()) { + default: + case KeyEvent.KEY_LOCATION_LEFT: + return 42; + case KeyEvent.KEY_LOCATION_RIGHT: + return 54; + } + case KeyEvent.VK_BACK_SLASH: + return 43; + case KeyEvent.VK_Z: + return 44; + case KeyEvent.VK_X: + return 45; + case KeyEvent.VK_C: + return 46; + case KeyEvent.VK_V: + return 47; + case KeyEvent.VK_B: + return 48; + case KeyEvent.VK_N: + return 49; + case KeyEvent.VK_M: + return 50; + case KeyEvent.VK_COMMA: + return 51; + case KeyEvent.VK_PERIOD: + return 52; + case KeyEvent.VK_SLASH: + return 53; + + // + // Bottom row + case KeyEvent.VK_CONTROL: + switch (event.getKeyLocation()) { + default: + case KeyEvent.KEY_LOCATION_LEFT: + return 29; + case KeyEvent.KEY_LOCATION_RIGHT: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 29; + } + case KeyEvent.VK_WINDOWS: + switch (event.getKeyLocation()) { + default: + case KeyEvent.KEY_LOCATION_LEFT: + return 91; + case KeyEvent.KEY_LOCATION_RIGHT: + return 92; + } + case KeyEvent.VK_ALT: + switch (event.getKeyLocation()) { + default: + case KeyEvent.KEY_LOCATION_LEFT: + return 56; + case KeyEvent.KEY_LOCATION_RIGHT: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56; + } + case KeyEvent.VK_ALT_GRAPH: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 56; + + case KeyEvent.VK_SPACE: + return 57; + + case KeyEvent.VK_CONTEXT_MENU: + return 93; + + // + // Special keys + case KeyEvent.VK_PRINTSCREEN: + return 55; + case KeyEvent.VK_SCROLL_LOCK: + return 70; + case KeyEvent.VK_PAUSE: + return 29; + + // Text navigation keys + case KeyEvent.VK_INSERT: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 82; + case KeyEvent.VK_HOME: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 71; + case KeyEvent.VK_PAGE_UP: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 73; + case KeyEvent.VK_DELETE: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 83; + case KeyEvent.VK_END: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 79; + case KeyEvent.VK_PAGE_DOWN: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 81; + + // Cursor keys + case KeyEvent.VK_UP: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 72; + case KeyEvent.VK_LEFT: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 75; + case KeyEvent.VK_DOWN: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 80; + case KeyEvent.VK_RIGHT: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 77; + + // Keypad + case KeyEvent.VK_NUM_LOCK: + return 69; + case KeyEvent.VK_DIVIDE: + return (FASTPATH_INPUT_KBDFLAGS_EXTENDED << 8) | 53; + case KeyEvent.VK_MULTIPLY: + return 55; + case KeyEvent.VK_SUBTRACT: + return 74; + case KeyEvent.VK_ADD: + return 78; + + case KeyEvent.VK_NUMPAD7: + return 71; + case KeyEvent.VK_NUMPAD8: + return 72; + case KeyEvent.VK_NUMPAD9: + return 73; + case KeyEvent.VK_NUMPAD4: + return 75; + case KeyEvent.VK_NUMPAD5: + return 76; + case KeyEvent.VK_NUMPAD6: + return 77; + case KeyEvent.VK_NUMPAD1: + return 79; + case KeyEvent.VK_NUMPAD2: + return 80; + case KeyEvent.VK_NUMPAD3: + return 81; + case KeyEvent.VK_NUMPAD0: + return 82; + case KeyEvent.VK_DECIMAL: + return 83; + + default: + System.err.println("Key is not mapped: " + event + "."); + return 57; // Space + } + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java old mode 100644 new mode 100755 similarity index 91% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java index f53390695f9..6b347aac832 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/adapter/AwtRdpMouseAdapter.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.adapter; import java.awt.event.InputEvent; @@ -27,58 +27,58 @@ import common.MouseOrder; * @see http://msdn.microsoft.com/en-us/library/cc240594.aspx */ public class AwtRdpMouseAdapter extends BaseElement { - public static final int FASTPATH_INPUT_EVENT_MOUSE = 0x01; + public final static int FASTPATH_INPUT_EVENT_MOUSE = 0x01; /** * Event is a mouse wheel rotation. The only valid flags in a wheel rotation * event are PTRFLAGS_WHEEL_NEGATIVE and the WheelRotationMask; all other * pointer flags are ignored. */ - public static final int PTRFLAGS_WHEEL = 0x0200; + public final static int PTRFLAGS_WHEEL = 0x0200; /** * Wheel rotation value (contained in the WheelRotationMask bit field) is * negative and MUST be sign-extended before injection at the server. */ - public static final int PTRFLAGS_WHEEL_NEGATIVE = 0x0100; + public final static int PTRFLAGS_WHEEL_NEGATIVE = 0x0100; /** * Bit field describing the number of rotation units the mouse wheel was * rotated. The value is negative if the PTRFLAGS_WHEEL_NEGATIVE flag is set. */ - public static final int WHEEL_ROTATION_MASK = 0x01FF; + public final static int WHEEL_ROTATION_MASK = 0x01FF; /** * Indicates that the mouse position MUST be updated to the location specified * by the xPos and yPos fields. */ - public static final int PTRFLAGS_MOVE = 0x0800; + public final static int PTRFLAGS_MOVE = 0x0800; /** * Indicates that a click event has occurred at the position specified by the * xPos and yPos fields. The button flags indicate which button has been * clicked and at least one of these flags MUST be set. */ - public static final int PTRFLAGS_DOWN = 0x8000; + public final static int PTRFLAGS_DOWN = 0x8000; /** * Mouse button 1 (left button) was clicked or released. If the PTRFLAGS_DOWN * flag is set, then the button was clicked, otherwise it was released. */ - public static final int PTRFLAGS_BUTTON1 = 0x1000; + public final static int PTRFLAGS_BUTTON1 = 0x1000; /** * Mouse button 2 (right button) was clicked or released. If the PTRFLAGS_DOWN * flag is set, then the button was clicked, otherwise it was released. */ - public static final int PTRFLAGS_BUTTON2 = 0x2000; + public final static int PTRFLAGS_BUTTON2 = 0x2000; /** * Mouse button 3 (middle button or wheel) was clicked or released. If the * PTRFLAGS_DOWN flag is set, then the button was clicked, otherwise it was * released. */ - public static final int PTRFLAGS_BUTTON3 = 0x4000; + public final static int PTRFLAGS_BUTTON3 = 0x4000; public AwtRdpMouseAdapter(String id) { super(id); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java new file mode 100755 index 00000000000..3a165360933 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardDataFormat.java @@ -0,0 +1,143 @@ +// 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 rdpclient.clip; + +import java.util.Map; + +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; + +public class ClipboardDataFormat { + + public static final String HTML_FORMAT = "HTML Format"; + public static final String RTF_AS_TEXT = "RTF As Text"; + public static final String RICH_TEXT_FORMAT_WITHOUT_OBJECTS = "Rich Text Format Without Objects"; + public static final String RICH_TEXT_FORMAT = "Rich Text Format"; + + public static final int CB_FORMAT_TEXT = 0x0001; + public static final int CB_FORMAT_UNICODETEXT = 0x000D; + + /** + * Supported clipboard data formats in order of preference. + */ + public static final Object[] supportedTextBasedFormats = new Object[] { + // ID's + CB_FORMAT_UNICODETEXT, CB_FORMAT_TEXT, + + // Names + HTML_FORMAT, + + // RTF_AS_TEXT, + // RICH_TEXT_FORMAT_WITHOUT_OBJECTS, + // RICH_TEXT_FORMAT, + + }; + + public final int id; + public final String name; + + public ClipboardDataFormat(int id, String name) { + super(); + this.id = id; + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ClipboardDataFormat other = (ClipboardDataFormat)obj; + if (id != other.id) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } + + @Override + public String toString() { + return "ClipboardDataFormat [id=" + id + ", name=\"" + name + "\"" + ((id == CB_FORMAT_UNICODETEXT) ? " (Unicode text)" : "") + + ((id == CB_FORMAT_TEXT) ? " (text)" : "") + "]"; + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + /** + * Parse response of supported format and return it as string. + */ + public String parseServerResponseAsString(ByteBuffer buf) { + switch (id) { + case CB_FORMAT_UNICODETEXT: + return buf.readVariableWideString(RdpConstants.CHARSET_16); + case CB_FORMAT_TEXT: + return buf.readVariableString(RdpConstants.CHARSET_8); + } + + if (name == null || name.length() == 0) + return null; + + if (HTML_FORMAT.equals(name)) + return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify + + // if (RTF_AS_TEXT.equals(name)) + // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify + // + // if (RICH_TEXT_FORMAT_WITHOUT_OBJECTS.equals(name)) + // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify + // + // if (RICH_TEXT_FORMAT.equals(name)) + // return buf.readVariableString(RdpConstants.CHARSET_8); // TODO: verify + + return null; + } + + /** + * Find first (richest) text-based data format. + * + * @return text-based data format or null, when not found + */ + public static ClipboardDataFormat findBestTextFormat(Map serverClipboardDataFormats) { + for (Object formatKey : ClipboardDataFormat.supportedTextBasedFormats) + if (serverClipboardDataFormats.containsKey(formatKey)) + return serverClipboardDataFormats.get(formatKey); + + return null; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java new file mode 100755 index 00000000000..9c465b54110 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ClipboardState.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package rdpclient.clip; + +import java.util.HashMap; +import java.util.Map; + +public class ClipboardState { + + /** + * The Long Format Name variant of the Format List PDU is supported for + * exchanging updated format names. If this flag is not set, the Short Format + * Name variant MUST be used. If this flag is set by both protocol endpoints, + * then the Long Format Name variant MUST be used. + */ + public boolean serverUseLongFormatNames = false; + public final boolean clientUseLongFormatNames = false; + + /** + * File copy and paste using stream-based operations are supported using the + * File Contents Request PDU and File Contents Response PDU. + */ + public boolean serverStreamFileClipEnabled = false; + public final boolean clientStreamFileClipEnabled = false; + + /** + * Indicates that any description of files to copy and paste MUST NOT include + * the source path of the files. + */ + public boolean serverFileClipNoFilePaths = false; + public final boolean clientFileClipNoFilePaths = false; + + /** + * Locking and unlocking of File Stream data on the clipboard is supported + * using the Lock Clipboard Data PDU and Unlock Clipboard Data PDU. + */ + public boolean serverCanLockClipdata = false; + public final boolean clientCanLockClipdata = false; + + /** + * The Monitor Ready PDU is sent from the server to the client to indicate + * that the server is initialized and ready. + */ + public boolean serverReady = false; + + /** + * Set of data formats, which are supported by server for paste operation. + */ + public Map serverClipboardDataFormats = new HashMap(0); + + /** + * Server sends clipboard data in requested format. + */ + public ClipboardDataFormat serverRequestedFormat; + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java new file mode 100755 index 00000000000..32381247ff8 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipRdrChannelRouter.java @@ -0,0 +1,193 @@ +// 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 rdpclient.clip; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class ServerClipRdrChannelRouter extends BaseElement { + + /** + * Key for ASCII names message flag in payload metadata. Value is Boolean. + */ + public static final String ASCII_NAMES = "ascii_names"; + + /** + * Key for success/fail message flag in payload metadata. Value is Boolean. + */ + public static final String SUCCESS = "success"; + + /** + * Monitor Ready PDU + */ + public static final int CB_MONITOR_READY = 0x0001; + + /** + * Format List PDU + */ + public static final int CB_FORMAT_LIST = 0x0002; + + /** + * Format List Response PDU + */ + public static final int CB_FORMAT_LIST_RESPONSE = 0x0003; + + /** + * Format Data Request PDU + */ + public static final int CB_FORMAT_DATA_REQUEST = 0x0004; + + /** + * Format Data Response PDU + */ + public static final int CB_FORMAT_DATA_RESPONSE = 0x0005; + + /** + * Temporary Directory PDU + */ + public static final int CB_TEMP_DIRECTORY = 0x0006; + + /** + * Clipboard Capabilities PDU + */ + public static final int CB_CLIP_CAPS = 0x0007; + + /** + * File Contents Request PDU + */ + public static final int CB_FILECONTENTS_REQUEST = 0x0008; + + /** + * File Contents Response PDU + */ + public static final int CB_FILECONTENTS_RESPONSE = 0x0009; + + /** + * Lock Clipboard Data PDU + */ + public static final int CB_LOCK_CLIPDATA = 0x000A; + + /** + * Unlock Clipboard Data PDU + */ + public static final int CB_UNLOCK_CLIPDATA = 0x000B; + + /** + * Used by the Format List Response PDU, Format Data Response PDU, and File + * Contents Response PDU to indicate that the associated request Format List + * PDU, Format Data Request PDU, and File Contents Request PDU were processed + * successfully. + */ + public static final int CB_RESPONSE_OK = 0x0001; + + /** + * Used by the Format List Response PDU, Format Data Response PDU, and File + * Contents Response PDU to indicate that the associated Format List PDU, + * Format Data Request PDU, and File Contents Request PDU were not processed + * successfully. + */ + public static final int CB_RESPONSE_FAIL = 0x0002; + + /** + * Used by the Short Format Name variant of the Format List Response PDU to + * indicate the format names are in ASCII 8. + */ + public static final int CB_ASCII_NAMES = 0x0004; + + public ServerClipRdrChannelRouter(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Parse PDU header + // Example: 07 00 -> CLIPRDR_HEADER::msgType = CB_CLIP_CAPS (7) + int msgType = buf.readUnsignedShortLE(); + + // Example: 00 00 -> CLIPRDR_HEADER::msgFlags = 0 + int msgFlags = buf.readUnsignedShortLE(); + + // Example: 10 00 00 00 -> CLIPRDR_HEADER::dataLen = 0x10 = 16 bytes + long dataLenLong = buf.readSignedIntLE(); + if (dataLenLong > 4 * 1024 * 1024) + throw new RuntimeException("Clipboard packet is too long. Expected length: less than 4MiB. Actual length: " + dataLenLong + "."); + int dataLen = (int)dataLenLong; + + ByteBuffer payload = buf.readBytes(dataLen); + + // Parse message flags and store them in the payload metadata + if ((msgFlags & CB_RESPONSE_OK) == CB_RESPONSE_OK) + payload.putMetadata("success", true); + if ((msgFlags & CB_RESPONSE_FAIL) == CB_RESPONSE_FAIL) + payload.putMetadata(SUCCESS, false); + if ((msgFlags & CB_ASCII_NAMES) == CB_ASCII_NAMES) + payload.putMetadata(ASCII_NAMES, true); + + // Push PDU to appropriate handler + switch (msgType) { + case CB_MONITOR_READY: + pushDataToPad("monitor_ready", payload); + break; + case CB_FORMAT_LIST: + pushDataToPad("format_list", payload); + break; + case CB_FORMAT_LIST_RESPONSE: + pushDataToPad("format_list_response", payload); + break; + case CB_FORMAT_DATA_REQUEST: + pushDataToPad("format_data_request", payload); + break; + case CB_FORMAT_DATA_RESPONSE: + pushDataToPad("format_data_response", payload); + break; + case CB_TEMP_DIRECTORY: + throw new RuntimeException("[" + this + "] ERROR: Unexpected clipboard temporary directory PDU received from server. Data: " + buf + "."); + case CB_CLIP_CAPS: + pushDataToPad("clipboard_capabilities", payload); + break; + case CB_FILECONTENTS_REQUEST: + pushDataToPad("filecontent_request", payload); + break; + case CB_FILECONTENTS_RESPONSE: + pushDataToPad("filecontent_response", payload); + break; + case CB_LOCK_CLIPDATA: + pushDataToPad("lock_clipdata", payload); + break; + case CB_UNLOCK_CLIPDATA: + pushDataToPad("unlock_clipdata", payload); + break; + default: + throw new RuntimeException("[" + this + "] ERROR: Unknown clipboard PDU message type: " + msgType + "."); + } + + buf.unref(); + + } + + /** + * Example. + */ + public static void main(String args[]) { + // TODO + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java new file mode 100755 index 00000000000..68278e87d16 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerClipboardCapabilitiesPDU.java @@ -0,0 +1,180 @@ +// 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 rdpclient.clip; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ServerClipboardCapabilitiesPDU extends BaseElement { + + /** + * General capability set. + */ + public static final int CB_CAPSTYPE_GENERAL = 0x1; + + /** + * The Long Format Name variant of the Format List PDU is supported for + * exchanging updated format names. If this flag is not set, the Short Format + * Name variant MUST be used. If this flag is set by both protocol endpoints, + * then the Long Format Name variant MUST be used. + */ + public static final int CB_USE_LONG_FORMAT_NAMES = 0x00000002; + + /** + * File copy and paste using stream-based operations are supported using the + * File Contents Request PDU and File Contents Response PDU. + */ + public static final int CB_STREAM_FILECLIP_ENABLED = 0x00000004; + + /** + * Indicates that any description of files to copy and paste MUST NOT include + * the source path of the files. + */ + public static final int CB_FILECLIP_NO_FILE_PATHS = 0x00000008; + + /** + * Locking and unlocking of File Stream data on the clipboard is supported + * using the Lock Clipboard Data PDU and Unlock Clipboard Data PDU. + */ + public static final int CB_CAN_LOCK_CLIPDATA = 0x00000010; + + protected ClipboardState state; + + public ServerClipboardCapabilitiesPDU(String id, ClipboardState state) { + super(id); + this.state = state; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // 0x01, 0x00, // CLIPRDR_CAPS::cCapabilitiesSets = 1 + int cCapabilitiesSets = buf.readUnsignedShortLE(); + + // 0x00, 0x00, // CLIPRDR_CAPS::pad1 + buf.skipBytes(2); + + // Parse all capability sets + for (int capabilitySet = 0; capabilitySet < cCapabilitiesSets; capabilitySet++) { + // 0x01, 0x00, // CLIPRDR_CAPS_SET::capabilitySetType = + // CB_CAPSTYPE_GENERAL (1) + int capabilitySetType = buf.readUnsignedShortLE(); + + // 0x0c, 0x00, // CLIPRDR_CAPS_SET::lengthCapability = 0x0c = 12 bytes + int lengthCapability = buf.readUnsignedShortLE(); + + // parse capability set + switch (capabilitySetType) { + case CB_CAPSTYPE_GENERAL: + parseGeneralCapabilitySet(buf.readBytes(lengthCapability - 4)); + break; + default: + // Ignore + // throw new RuntimeException("Unknown capability set type: " + + // capabilitySetType + ". Expected value: CB_CAPSTYPE_GENERAL (1)."); + } + } + + buf.unref(); + } + + protected void parseGeneralCapabilitySet(ByteBuffer buf) { + // 0x02, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::version = + // CB_CAPS_VERSION_2 (2) + // long version = buf.readUnsignedIntLE(); + buf.skipBytes(4); + + // 0x0e, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::capabilityFlags + // = 0x0000000e = 0x02 |0x04 |0x08 = CB_USE_LONG_FORMAT_NAMES | + // CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS + int flags = buf.readSignedIntLE(); + + if ((flags & CB_USE_LONG_FORMAT_NAMES) == CB_USE_LONG_FORMAT_NAMES) { + state.serverUseLongFormatNames = true; + if (verbose) + System.out.println("[" + this + "] INFO: Server can use long format names for clipboard data."); + } + + if ((flags & CB_STREAM_FILECLIP_ENABLED) == CB_STREAM_FILECLIP_ENABLED) { + state.serverStreamFileClipEnabled = true; + if (verbose) + System.out.println("[" + this + "] INFO: Server supports stream based file clipboard operations."); + } + + if ((flags & CB_FILECLIP_NO_FILE_PATHS) == CB_FILECLIP_NO_FILE_PATHS) { + state.serverFileClipNoFilePaths = true; + if (verbose) + System.out.println("[" + this + + "] INFO: Server Indicates that any description of files to copy and paste MUST NOT include the source path of the files."); + } + + if ((flags & CB_CAN_LOCK_CLIPDATA) == CB_CAN_LOCK_CLIPDATA) { + state.serverCanLockClipdata = true; + if (verbose) + System.out.println("[" + this + "] INFO: Server can lock and unlock file streams on the clipboard."); + } + + } + + /** + * Example. + */ + public static void main(String[] args) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x07, 0x00, // CLIPRDR_HEADER::msgType = CB_CLIP_CAPS (7) + 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0 + 0x10, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0x10 = 16 bytes + 0x01, 0x00, // CLIPRDR_CAPS::cCapabilitiesSets = 1 + 0x00, 0x00, // CLIPRDR_CAPS::pad1 + 0x01, 0x00, // CLIPRDR_CAPS_SET::capabilitySetType = CB_CAPSTYPE_GENERAL (1) + 0x0c, 0x00, // CLIPRDR_CAPS_SET::lengthCapability = 0x0c = 12 bytes + 0x02, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::version = CB_CAPS_VERSION_2 (2) + 0x0e, 0x00, 0x00, 0x00, // CLIPRDR_GENERAL_CAPABILITY::capabilityFlags = 0x0000000e = 0x02 |0x04 |0x08 = CB_USE_LONG_FORMAT_NAMES | CB_STREAM_FILECLIP_ENABLED | CB_FILECLIP_NO_FILE_PATHS + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element router = new ServerClipRdrChannelRouter("router"); + ClipboardState state = new ClipboardState(); + Element clip_cap = new ServerClipboardCapabilitiesPDU("clip_cap", state); + Element sink = new MockSink("sink", new ByteBuffer[] {}); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, router, clip_cap, sink); + pipeline.link("source", "router >clipboard_capabilities", "clip_cap", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + // Check state + if (!state.serverUseLongFormatNames || !state.serverStreamFileClipEnabled || !state.serverFileClipNoFilePaths || state.serverCanLockClipdata) + throw new RuntimeException("Server clipboard capabilities packet parsed incorrectly."); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java new file mode 100755 index 00000000000..99f9eda2d2b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatDataResponsePDU.java @@ -0,0 +1,97 @@ +// 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 rdpclient.clip; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; +import common.adapter.AwtClipboardAdapter; + +public class ServerFormatDataResponsePDU extends BaseElement { + + public static final String SERVER_CLIPBOARD_ADAPTER_PAD = "clipboard"; + + protected ClipboardState state; + + public ServerFormatDataResponsePDU(String id, ClipboardState state) { + super(id); + this.state = state; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + parseData(buf); + + buf.unref(); + } + + protected void parseData(ByteBuffer buf) { + + String content = state.serverRequestedFormat.parseServerResponseAsString(buf); + + // Send response to clipboard adapter + ByteBuffer outBuf = new ByteBuffer(0); + outBuf.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, content); + + pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf); + } + + /** + * Example. + */ + public static void main(String[] args) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x05, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_DATA_RESPONSE (5) + 0x01, 0x00, // CLIPRDR_HEADER::msgFlags = 0x0001 = CB_RESPONSE_OK + 0x18, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0x18 = 24 bytes + + 0x68, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x77, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x00, 0x00, // CLIPRDR_FORMAT_DATA_RESPONSE::requestedFormatData: "hello world" + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element router = new ServerClipRdrChannelRouter("router"); + ClipboardState state = new ClipboardState(); + state.serverRequestedFormat = new ClipboardDataFormat(ClipboardDataFormat.CB_FORMAT_UNICODETEXT, ""); + Element format_data_response = new ServerFormatDataResponsePDU("format_data_response", state); + + ByteBuffer clipboardAdapterPacket = new ByteBuffer(0); + clipboardAdapterPacket.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, "hello world"); + Element sink = new MockSink("sink", new ByteBuffer[] {clipboardAdapterPacket}); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, router, format_data_response, sink); + pipeline.link("source", "router >format_data_response", "format_data_response >clipboard", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java new file mode 100755 index 00000000000..776f4516b52 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerFormatListPDU.java @@ -0,0 +1,237 @@ +// 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 rdpclient.clip; + +import java.util.HashMap; +import java.util.Map; + +import rdpclient.rdp.RdpConstants; +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ServerFormatListPDU extends BaseElement { + + protected ClipboardState state; + + public ServerFormatListPDU(String id, ClipboardState state) { + super(id); + this.state = state; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + parseFormatNames(buf); + buf.unref(); + + // Automatically send request for text-based data to insert it into local + // clipboard + ClipboardDataFormat textFormat = ClipboardDataFormat.findBestTextFormat(state.serverClipboardDataFormats); + if (textFormat != null) { + // Send response: OK + sendFormatListParseResponse(true); + // Request data + sendFormatDataRequest(textFormat); + } else { + // Send response: FAIL, we are not interested in this data + sendFormatListParseResponse(false); + } + } + + /** + * The Format Data Request PDU is sent by the recipient of the Format List + * PDU. It is used to request the data for one of the formats that was listed + * in the Format List PDU. + */ + protected void sendFormatDataRequest(ClipboardDataFormat textFormat) { + + if (verbose) + System.out.println("[" + this + "] INFO: Sending request for data in following format: " + textFormat + "."); + + // Store data format to parse server response later + state.serverRequestedFormat = textFormat; + + ByteBuffer buf = new ByteBuffer(12, true); + + // Type + buf.writeShortLE(ServerClipRdrChannelRouter.CB_FORMAT_DATA_REQUEST); + // Message flags + buf.writeShortLE(0); + // Length + buf.writeIntLE(4); + + // ID of chosen format + buf.writeIntLE(textFormat.id); + + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + } + + /** + * The Format List Response PDU is sent as a reply to the Format List PDU. It + * is used to indicate whether processing of the Format List PDU was + * successful. + * + * @param b + */ + protected void sendFormatListParseResponse(boolean ok) { + ByteBuffer buf = new ByteBuffer(8, true); + + // Type + buf.writeShortLE(ServerClipRdrChannelRouter.CB_FORMAT_LIST_RESPONSE); + // Message flags + buf.writeShortLE((ok) ? ServerClipRdrChannelRouter.CB_RESPONSE_OK : ServerClipRdrChannelRouter.CB_RESPONSE_FAIL); + // Length + buf.writeIntLE(0); + + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + } + + protected void parseFormatNames(ByteBuffer buf) { + + // Set will not be modified after creation, so there is no need to make it + // synchronous. + Map formats = new HashMap(); + + while (buf.cursor < buf.length) { + int id = buf.readSignedIntLE(); + + String name; + if (state.serverUseLongFormatNames) { + // Long format names in Unicode + name = buf.readVariableWideString(RdpConstants.CHARSET_16); + } else { + Boolean asciiNames = (Boolean)buf.getMetadata(ServerClipRdrChannelRouter.ASCII_NAMES); + + if (asciiNames != null && asciiNames) { + // Short format names in ASCII + name = buf.readString(32, RdpConstants.CHARSET_8); + } else { + // Short format names in Unicode + name = buf.readString(32, RdpConstants.CHARSET_16); + } + + } + + // Store format in map by both ID and name (if name is not empty) + formats.put(id, new ClipboardDataFormat(id, name)); + if (name.length() > 0) + formats.put(name, new ClipboardDataFormat(id, name)); + } + + if (verbose) + System.out.println("Server supports following formats for clipboard data: " + formats.values().toString() + "."); + + state.serverClipboardDataFormats = formats; + } + + /** + * Example. + */ + public static void main(String[] args) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x02, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_LIST (2) + 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0 + (byte) 0xe0, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0xe0 = 224 bytes + + (byte) 0x8a, (byte) 0xc0, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc08a = 49290 + 0x52, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x54, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "Rich Text Format" + + 0x45, (byte) 0xc1, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc145 = 49477 + 0x52, 0x00, 0x69, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x54, 0x00, 0x65, 0x00, 0x78, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x74, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x6a, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x73, 0x00, + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "Rich Text Format Without Objects" + + 0x43, (byte) 0xc1, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc143 = 49475 + 0x52, 0x00, 0x54, 0x00, 0x46, 0x00, 0x20, 0x00, 0x41, 0x00, 0x73, 0x00, 0x20, 0x00, 0x54, 0x00, + 0x65, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "RTF As Text" + + 0x01, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 1 + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "" + + 0x0d, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0x0d = 13 + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "" + + 0x04, (byte) 0xc0, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc004 = 49156 + 0x4e, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x76, 0x00, 0x65, 0x00, 0x00, 0x00, // "Native" + + 0x0e, (byte) 0xc0, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 0xc00e = 49166 + 0x4f, 0x00, 0x62, 0x00, 0x6a, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, 0x44, 0x00, + 0x65, 0x00, 0x73, 0x00, 0x63, 0x00, 0x72, 0x00, 0x69, 0x00, 0x70, 0x00, 0x74, 0x00, 0x6f, 0x00, + 0x72, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "Object Descriptor" + + 0x03, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 3 + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "" + + 0x10, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 16 + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "" + + 0x07, 0x00, 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatId = 7 + 0x00, 0x00, // CLIPRDR_LONG_FORMAT_NAME::formatName = "" + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element router = new ServerClipRdrChannelRouter("router"); + ClipboardState state = new ClipboardState(); + state.serverUseLongFormatNames = true; + Element format_list = new ServerFormatListPDU("format_list", state); + + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { + // Format List Response PDU + 0x03, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_LIST_RESPONSE (3) + 0x01, 0x00, // CLIPRDR_HEADER::msgFlags = 0x0001 = CB_RESPONSE_OK + 0x00, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0 bytes + }, new byte[] { + // Format Data Request PDU + 0x04, 0x00, // CLIPRDR_HEADER::msgType = CB_FORMAT_DATA_REQUEST (4) + 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0 + 0x04, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 4 bytes + 0x0d, 0x00, 0x00, 0x00, // CLIPRDR_FORMAT_DATA_REQUEST::requestedFormatId + // = 0x0d + })); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, router, format_list, sink); + pipeline.link("source", "router >format_list", "format_list", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + // Check state + if (!(state.serverClipboardDataFormats.containsKey(49475) && state.serverClipboardDataFormats.containsKey("Rich Text Format"))) + throw new RuntimeException("Server format list packet parsed incorrectly."); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java new file mode 100755 index 00000000000..bbc356e1432 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/clip/ServerMonitorReadyPDU.java @@ -0,0 +1,85 @@ +// 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 rdpclient.clip; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ServerMonitorReadyPDU extends BaseElement { + + protected ClipboardState state; + + public ServerMonitorReadyPDU(String id, ClipboardState state) { + super(id); + this.state = state; + } + + // 0x01, 0x00, // CLIPRDR_HEADER::msgType = CB_MONITOR_READY (1) + // 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0 + // 0x00, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0 bytes + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + state.serverReady = true; + + buf.unref(); + + } + + /** + * Example. + */ + public static void main(String[] args) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x01, 0x00, // CLIPRDR_HEADER::msgType = CB_MONITOR_READY (1) + 0x00, 0x00, // CLIPRDR_HEADER::msgFlags = 0 + 0x00, 0x00, 0x00, 0x00, // CLIPRDR_HEADER::dataLen = 0 bytes + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element router = new ServerClipRdrChannelRouter("router"); + ClipboardState state = new ClipboardState(); + Element monitor_ready = new ServerMonitorReadyPDU("monitor_ready", state); + Element sink = new MockSink("sink", new ByteBuffer[] {}); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, router, monitor_ready, sink); + pipeline.link("source", "router >monitor_ready", "monitor_ready", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + // Check state + if (!state.serverReady) + throw new RuntimeException("Server monitor ready packet parsed incorrectly."); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java new file mode 100755 index 00000000000..203fde415c2 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ClientPacketSniffer.java @@ -0,0 +1,51 @@ +// 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 rdpclient.debug; + +/** + * Try to determine packet content by it header fingerprint. + */ +public class ClientPacketSniffer extends PacketSniffer { + + private static final Pair[] clientRegexps = new Pair[] { +// @formatter:off + new Pair("Client FastPath input", "04"), + new Pair("Client X224ConnectionRequest", "03 00 XX XX 27 E0"), + new Pair("Client ConnectionRequest", "03 00 XX XX XX E0"), + new Pair("Client MCConnectInitial", "03 00 XX XX 02 F0 80 7F 65"), + new Pair("Client ErectDomainRequest", "03 00 XX XX 02 F0 80 04"), + new Pair("Client AttachUserRequest", "03 00 XX XX 02 F0 80 28"), + new Pair("Client ChannelJoinRequest", "03 00 XX XX 02 F0 80 38"), + new Pair("Client Info", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 00 00"), + new Pair("Client ConfirmActivePDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 13 00"), + new Pair("Client SynchronizePDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 1F"), + new Pair("Client ControlPDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 14"), + new Pair("Client FontListPDU", "03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX 00 XX XX XX XX 27"), + new Pair("Client BitmapCachePersistentList","03 00 XX XX 02 F0 80 64 00 03 03 EB 70 XX XX XX XX 17 00 EC 03 EA 03 XX XX XX XX XX XX 2b"), + new Pair("Client CredSSP", "30"), + new Pair("Client HyperV PreConnection Blob","5E"), + new Pair("Client a TPKT packet", "03"), + new Pair("Client a Fast Path packet", "00"), + // @formatter:on + + }; + + public ClientPacketSniffer(String id) { + super(id, clientRegexps); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java old mode 100644 new mode 100755 similarity index 98% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java index 12fa8e33720..efbe6890902 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/PacketSniffer.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.debug; import java.util.regex.Pattern; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ServerPacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ServerPacketSniffer.java new file mode 100755 index 00000000000..a4ead0c5d1b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/debug/ServerPacketSniffer.java @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package rdpclient.debug; + +/** + * Try to determine packet content by it header fingerprint. + */ +public class ServerPacketSniffer extends PacketSniffer { + + private static final Pair[] serverRegexps = new Pair[] { +// @formatter:off + new Pair("Server FastPath update", "04"), + new Pair("Server X224ConnectionRequest", "03 00 XX XX 0E D0"), + new Pair("Server MCSConnectResponse", "03 00 XX XX 02 F0 80 7F 66 5A"), + new Pair("Server AttachUserConfirm", "03 00 XX XX 02 F0 80 2E"), + new Pair("Server ChannelJoinConfirm", "03 00 XX XX 02 F0 80 3E"), + new Pair("Server ErrorAlert", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 14 80 00"), + new Pair("Server DemandActivePDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX XX 11"), + new Pair("Server ControlPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 14"), + new Pair("Server SynchronizePDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 1F"), + new Pair("Server FontMapPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 17 00 EA 03 EA 03 XX 00 XX XX XX XX 28"), + new Pair("Server SET_ERROR_INFO_PDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 30 XX XX XX 17 00 00 00 EA 03 XX 00 XX XX XX XX 2F"), + new Pair("Server DeactivateAllPDU", "03 00 XX XX 02 F0 80 68 00 01 03 EB 70 XX XX XX 16 00"), + new Pair("Server CloseConnection", "03 00 00 09 02 F0 80 21 80"), + new Pair("Server CredSSP", "30"), + + new Pair("Server a TPKT packet", "03"), + new Pair("Server a FastPath packet", "00"), + // @formatter:on + }; + + public ServerPacketSniffer(String id) { + super(id, serverRegexps); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/hyperv/ClientPreConnectionBlob.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/hyperv/ClientPreConnectionBlob.java new file mode 100755 index 00000000000..05af4c0937d --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/hyperv/ClientPreConnectionBlob.java @@ -0,0 +1,121 @@ +// 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 rdpclient.hyperv; + +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +/** + * For HyperV server, client must send VM ID before any other packet. + */ +public class ClientPreConnectionBlob extends OneTimeSwitch { + + protected String data; + + public ClientPreConnectionBlob(String id, String data) { + super(id); + this.data = data; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("This element only sends data. This method must not be called. Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + sendPreConnectionBlobData(data); + } + + protected void sendPreConnectionBlobData(String data) { + // Length of packet + ByteBuffer buf = new ByteBuffer(1024, true); + + // Header + buf.writeBytes(new byte[] {(byte)0x5e, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}); + + // Length of string in wide characters + two wide \0 (LE) + buf.writeShortLE(data.length() + 2); + + // Wide string + two wide '\0' characters + buf.writeString(data + "\0\0", RdpConstants.CHARSET_16); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + */ + public static void main(String[] args) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + // Header + (byte) 0x5e, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Length of string in wide characters + two wide \0 (LE) + (byte) 0x26, (byte) 0x00, + + // Wide string + (byte) 0x33, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x34, (byte) 0x00, (byte) 0x31, (byte) 0x00, (byte) 0x38, (byte) 0x00, (byte) 0x46, + (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x30, (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x44, (byte) 0x00, + (byte) 0x30, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0x34, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x38, + (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0x42, (byte) 0x00, (byte) 0x37, (byte) 0x00, (byte) 0x39, (byte) 0x00, + (byte) 0x36, (byte) 0x00, (byte) 0x2d, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x31, (byte) 0x00, (byte) 0x43, (byte) 0x00, (byte) 0x36, + (byte) 0x00, (byte) 0x30, (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x36, (byte) 0x00, + (byte) 0x35, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x41, (byte) 0x00, + + // Two wide \0 + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + Element pcb = new ClientPreConnectionBlob("pcb", "39418F90-6D03-468E-B796-91C60DD6653A"); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, pcb, sink, mainSink); + pipeline.link("source", "pcb", "mainSink"); + pipeline.link("pcb >" + OTOUT, "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspNegotiate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspNegotiate.java new file mode 100755 index 00000000000..8bac4abfe52 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspNegotiate.java @@ -0,0 +1,177 @@ +// 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 rdpclient.ntlmssp; + +import rdpclient.ntlmssp.asn1.NegoItem; +import rdpclient.ntlmssp.asn1.TSRequest; +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; +import common.asn1.Tag; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc236641.aspx + */ +public class ClientNtlmsspNegotiate extends OneTimeSwitch { + + /** + * The set of client configuration flags (section 2.2.2.5) that specify the + * full set of capabilities of the client. + */ + public NegoFlags clientConfigFlags = new NegoFlags().set_NEGOTIATE_56().set_NEGOTIATE_KEY_EXCH().set_NEGOTIATE_128().set_NEGOTIATE_VERSION() + .set_NEGOTIATE_EXTENDED_SESSION_SECURITY().set_NEGOTIATE_ALWAYS_SIGN().set_NEGOTIATE_NTLM().set_NEGOTIATE_LM_KEY().set_NEGOTIATE_SEAL() + .set_NEGOTIATE_SIGN().set_REQUEST_TARGET().set_NEGOTIATE_OEM().set_NEGOTIATE_UNICODE(); + + protected NtlmState ntlmState; + + public ClientNtlmsspNegotiate(String id, NtlmState state) { + super(id); + ntlmState = state; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + + ByteBuffer negoToken = generateNegotiateMessage(); + ntlmState.negotiateMessage = negoToken.toByteArray(); // Store message for MIC calculation in AUTH message + + // Length of packet + ByteBuffer buf = new ByteBuffer(1024, true); + + TSRequest tsRequest = new TSRequest("TSRequest"); + tsRequest.version.value = 2L; + NegoItem negoItem = new NegoItem("NegoItem"); + negoItem.negoToken.value = negoToken; + tsRequest.negoTokens.tags = new Tag[] {negoItem}; + + tsRequest.writeTag(buf); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + private ByteBuffer generateNegotiateMessage() { + ByteBuffer buf = new ByteBuffer(1024); + + // Signature + buf.writeString("NTLMSSP", RdpConstants.CHARSET_8); + buf.writeByte(0); + + // Message type + buf.writeIntLE(NtlmConstants.NEGOTIATE); + + buf.writeIntLE(clientConfigFlags.value); // Flags + + // If the NTLMSSP_NEGOTIATE_VERSION flag is set by the client application, + // the Version field MUST be set to the current version (section 2.2.2.10), + // the DomainName field MUST be set to a zero-length string, and the + // Workstation field MUST be set to a zero-length string. + + // Domain: "" + buf.writeShortLE(0); // Length + buf.writeShortLE(0); // Allocated space + buf.writeIntLE(0); // Offset + + // Workstation: "" + buf.writeShortLE(0); // Length + buf.writeShortLE(0); // Allocated space + buf.writeIntLE(0); // Offset + + // OS Version: 6.1 (Build 7601); NTLM Current Revision 15 + buf.writeBytes(new byte[] {(byte)0x06, (byte)0x01, (byte)0xb1, (byte)0x1d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0f}); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + return buf; + } + + /** + * Example. + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + // CredSSP BER header: + + (byte)0x30, // Sequence + (byte)0x37, // Length, 55 bytes + + (byte)0xa0, (byte)0x03, // TAG: [0] (constructed) LEN: 3 byte + (byte)0x02, (byte)0x01, (byte)0x02, // Version: (int, 1 byte, 0x02) + + // Sequence of sequence + (byte)0xa1, (byte)0x30, // TAG: [1] (constructed) LEN: 48 bytes + (byte)0x30, (byte)0x2e, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 46 bytes + (byte)0x30, (byte)0x2c, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 44 bytes + (byte)0xa0, (byte)0x2a, // TAG: [0] (constructed) LEN: 42 bytes + + + (byte)0x04, (byte)0x28, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 40 bytes + + // NTLM negotiate request + + (byte)0x4e, (byte)0x54, (byte)0x4c, (byte)0x4d, (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00, // "NTLMSSP\0" + + (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, // Message type: NEGOTIATE (0x1, LE) + + (byte)0xb7, (byte)0x82, (byte)0x08, (byte)0xe2, // Flags: 0xe20882b7 (LE) + + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Domain (security buffer, 8bit, 8 bytes): length: 0x0000 (LE), allocated space: 0x0000 (LE), offset: 0x00000000 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Workstation (security buffer, 8bit, 8 bytes): length: 0x0000 (LE), allocated space: 0x0000 (LE), offset: 0x00000000 (LE) + (byte)0x06, (byte)0x01, (byte)0xb1, (byte)0x1d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0f, // OS Version: 6.1 (Build 7601); NTLM Current Revision 15, 8 bytes + + }; + /* @formatter:on */ + + NtlmState state = new NtlmState(); + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + Element ntlmssp_negotiate = new ClientNtlmsspNegotiate("ntlmssp_negotiate", state); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, ntlmssp_negotiate, sink, mainSink); + pipeline.link("source", "ntlmssp_negotiate", "mainSink"); + pipeline.link("ntlmssp_negotiate >" + OTOUT, "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspPubKeyAuth.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspPubKeyAuth.java new file mode 100755 index 00000000000..ffd16304df0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspPubKeyAuth.java @@ -0,0 +1,680 @@ +// 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 rdpclient.ntlmssp; + +import java.nio.charset.Charset; + +import rdpclient.ntlmssp.asn1.NegoItem; +import rdpclient.ntlmssp.asn1.SubjectPublicKeyInfo; +import rdpclient.ntlmssp.asn1.TSRequest; +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.Dumper; +import streamer.debug.MockSink; +import streamer.debug.MockSource; +import streamer.ssl.SSLState; +import common.asn1.Tag; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc236643.aspx + */ +public class ClientNtlmsspPubKeyAuth extends OneTimeSwitch implements NtlmConstants, Dumper { + + /** + * Offset of first byte of allocated block after NTLMSSP header and block + * descriptors. + */ + private static final int BLOCKS_OFFSET = 88; + + protected NtlmState ntlmState; + protected SSLState sslState; + + protected String targetDomain; + protected String user; + protected String password; + protected String workstation; + protected String serverHostName; + + public ClientNtlmsspPubKeyAuth(String id, NtlmState ntlmState, SSLState sslState, String serverHostName, String targetDomain, String workstation, + String user, String password) { + super(id); + this.ntlmState = ntlmState; + this.sslState = sslState; + this.serverHostName = serverHostName; + this.targetDomain = targetDomain; + this.workstation = workstation; + this.user = user; + this.password = password; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + + /* + * @see + * http://blogs.msdn.com/b/openspecification/archive/2010/04/20/ntlm-keys + * -and-sundry-stuff.aspx + */ + ntlmState.domain = targetDomain; + ntlmState.user = user; + ntlmState.password = password; + ntlmState.workstation = workstation; + ntlmState.generateServicePrincipalName(serverHostName); + ntlmState.ntlm_construct_authenticate_target_info(); + ntlmState.ntlm_generate_timestamp(); + ntlmState.ntlm_generate_client_challenge(); + ntlmState.ntlm_compute_lm_v2_response(); + ntlmState.ntlm_compute_ntlm_v2_response(); + ntlmState.ntlm_generate_key_exchange_key(); + ntlmState.ntlm_generate_random_session_key(); + ntlmState.ntlm_generate_exported_session_key(); + ntlmState.ntlm_encrypt_random_session_key(); + ntlmState.ntlm_init_rc4_seal_states(); + + ByteBuffer authenticateMessage = generateAuthenticateMessage(ntlmState); + ByteBuffer messageSignatureAndEncryptedServerPublicKey = generateMessageSignatureAndEncryptedServerPublicKey(ntlmState); + + // Length of packet + ByteBuffer buf = new ByteBuffer(4096, true); + + TSRequest tsRequest = new TSRequest("TSRequest"); + tsRequest.version.value = 2L; + NegoItem negoItem = new NegoItem("NegoItem"); + negoItem.negoToken.value = authenticateMessage; + + tsRequest.negoTokens.tags = new Tag[] {negoItem}; + + tsRequest.pubKeyAuth.value = messageSignatureAndEncryptedServerPublicKey; + + tsRequest.writeTag(buf); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + private byte[] getServerPublicKey() { + // SSL certificate public key with algorithm + ByteBuffer subjectPublicKeyInfo = new ByteBuffer(sslState.serverCertificateSubjectPublicKeyInfo); + + // Parse subjectPublicKeyInfo + SubjectPublicKeyInfo parser = new SubjectPublicKeyInfo("SubjectPublicKeyInfo"); + parser.readTag(subjectPublicKeyInfo); + + // Copy subjectPublicKey subfield to separate byte buffer + ByteBuffer subjectPublicKey = new ByteBuffer(subjectPublicKeyInfo.length); + parser.subjectPublicKey.writeTag(subjectPublicKey); + + subjectPublicKeyInfo.unref(); + subjectPublicKey.trimAtCursor(); + + // Skip tag: + // 03 82 01 0f (tag) 00 (padding byte) + subjectPublicKey.trimHeader(5);// FIXME: parse it properly + // * DEBUG */System.out.println("DEBUG: subjectPublicKey:\n" + + // subjectPublicKey.dump()); + + ntlmState.subjectPublicKey = subjectPublicKey.toByteArray(); + + return ntlmState.subjectPublicKey; + } + + /** + * The client encrypts the public key it received from the server (contained + * in the X.509 certificate) in the TLS handshake from step 1, by using the + * confidentiality support of SPNEGO. + * + * The public key that is encrypted is the ASN.1-encoded SubjectPublicKey + * sub-field of SubjectPublicKeyInfo from the X.509 certificate, as specified + * in [RFC3280] section 4.1. The encrypted key is encapsulated in the + * pubKeyAuth field of the TSRequest structure and is sent over the TLS + * channel to the server. + */ + private ByteBuffer generateMessageSignatureAndEncryptedServerPublicKey(NtlmState ntlmState) { + return new ByteBuffer(ntlmState.ntlm_EncryptMessage(getServerPublicKey())); + } + + public static ByteBuffer generateAuthenticateMessage(NtlmState ntlmState) { + + // Allocate memory for blocks from given fixed offset + int blocksCursor = BLOCKS_OFFSET; + + ByteBuffer buf = new ByteBuffer(4096); + + // Signature: "NTLMSSP\0" + buf.writeString(NTLMSSP, RdpConstants.CHARSET_8); + buf.writeByte(0); + + // NTLM Message Type: NTLMSSP_AUTH (0x00000003) + buf.writeIntLE(NtlmConstants.NTLMSSP_AUTH); + + // Although the protocol allows authentication to succeed if the client + // provides either LmChallengeResponse or NtChallengeResponse, Windows + // implementations provide both. + + // LM V2 response + blocksCursor = writeBlock(buf, ntlmState.lmChallengeResponse, blocksCursor); + + // NT v2 response + blocksCursor = writeBlock(buf, ntlmState.ntChallengeResponse, blocksCursor); + + // DomainName + blocksCursor = writeStringBlock(buf, ntlmState.domain, blocksCursor, RdpConstants.CHARSET_16); + + // UserName + blocksCursor = writeStringBlock(buf, ntlmState.user, blocksCursor, RdpConstants.CHARSET_16); + + // Workstation + blocksCursor = writeStringBlock(buf, ntlmState.workstation, blocksCursor, RdpConstants.CHARSET_16); + + // EncryptedRandomSessionKey, 16 bytes + blocksCursor = writeBlock(buf, ntlmState.encryptedRandomSessionKey, blocksCursor); + + // NegotiateFlags (4 bytes): In connection-oriented mode, a NEGOTIATE + // structure that contains the set of bit flags (section 2.2.2.5) negotiated + // in the previous messages. + buf.writeIntLE(/*ntlmState.negotiatedFlags.value*/0xe288b235); // FIXME: remove hardcoded value + + buf.writeBytes(generateVersion()); + + // If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an + // MsvAvTimestamp present, the client SHOULD provide a MIC(Message Integrity + // Check) + + int savedCursorForMIC = buf.cursor; // Save cursor position to write MIC + // later + buf.writeBytes(new byte[16]); // Write 16 zeroes + + if (BLOCKS_OFFSET != buf.cursor) + throw new RuntimeException("BUG: Actual offset of first byte of allocated blocks is not equal hardcoded offset. Hardcoded offset: " + BLOCKS_OFFSET + + ", actual offset: " + buf.cursor + ". Update hardcoded offset to match actual offset."); + + buf.cursor = blocksCursor; + buf.trimAtCursor(); + + ntlmState.authenticateMessage = buf.toByteArray(); + + // Calculate and write MIC to reserved position + ntlmState.ntlm_compute_message_integrity_check(); + buf.cursor = savedCursorForMIC; + buf.writeBytes(ntlmState.messageIntegrityCheck); + buf.rewindCursor(); + + return buf; + } + + /** + * Write string as security buffer, using given charset, without trailing '\0' + * character. + */ + private static int writeStringBlock(ByteBuffer buf, String string, int blocksCursor, Charset charset) { + return writeBlock(buf, string.getBytes(charset), blocksCursor); + } + + /** + * Write block to blocks buffer and block descriptor to main buffer. + */ + private static int writeBlock(ByteBuffer buf, byte[] block, int blocksCursor) { + + // Write block descriptor + + // Length + buf.writeShortLE(block.length); + // Allocated + buf.writeShortLE(block.length); + // Offset + buf.writeIntLE(blocksCursor); + + // Write block to position pointed by blocksCursor instead of buf.cursor + int savedCursor = buf.cursor; + buf.cursor = blocksCursor; + buf.writeBytes(block); + blocksCursor = buf.cursor; + buf.cursor = savedCursor; + + return blocksCursor; + } + + /** + * Version (8 bytes): A VERSION structure (section 2.2.2.10) that is present + * only when the NTLMSSP_NEGOTIATE_VERSION flag is set in the NegotiateFlags + * field. This structure is used for debugging purposes only. In normal + * protocol messages, it is ignored and does not affect the NTLM message + * processing. + */ + private static byte[] generateVersion() { + // Version (6.1, Build 7601), NTLM current revision: 15 + return new byte[] {0x06, 0x01, (byte)0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f}; + } + + /** + * Example. + */ + public static void main(String args[]) { + System.setProperty("streamer.Element.debug", "true"); + + /* @formatter:off */ + // + // Client NEGOTIATE + // + byte[] clientNegotiatePacket = new byte[] { + (byte)0x30, (byte)0x37, (byte)0xa0, (byte)0x03, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0xa1, (byte)0x30, (byte)0x30, (byte)0x2e, (byte)0x30, (byte)0x2c, (byte)0xa0, (byte)0x2a, (byte)0x04, + (byte)0x28, (byte)0x4e, (byte)0x54, (byte)0x4c, (byte)0x4d, (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xb7, (byte)0x82, (byte)0x08, + (byte)0xe2, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x01, (byte)0xb1, (byte)0x1d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0f, + }; + +// +// Server CHALLENGE +// + byte[] serverChallengePacket = new byte[] { + 0x30, (byte) 0x82, 0x01, 0x02, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 258 bytes + + (byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes + 0x02, 0x01, 0x03, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes, Version: 0x3 + (byte) 0xa1, (byte) 0x81, (byte) 0xfa, // TAG: [1] (constructed) LEN: 250 bytes + 0x30, (byte) 0x81, (byte) 0xf7, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 247 bytes + 0x30, (byte) 0x81, (byte) 0xf4, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 244 bytes + (byte) 0xa0, (byte) 0x81, (byte) 0xf1, // TAG: [0] (constructed) LEN: 241 bytes + 0x04, (byte) 0x81, (byte) 0xee, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 238 bytes + + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, // "NTLMSSP\0" + + 0x02, 0x00, 0x00, 0x00, // MessageType (CHALLENGE) + + 0x1e, 0x00, 0x1e, 0x00, 0x38, 0x00, 0x00, 0x00, // TargetName (length: 30, allocated space: 30, offset: 56) + 0x35, (byte) 0x82, (byte) 0x8a, (byte) 0xe2, // NegotiateFlags: NEGOTIATE_56 NEGOTIATE_KEY_EXCH NEGOTIATE_128 NEGOTIATE_VERSION NEGOTIATE_TARGET_INFO NEGOTIATE_EXTENDED_SESSION_SECURITY TARGET_TYPE_SERVER NEGOTIATE_ALWAYS_SIGN NEGOTIATE_NTLM NEGOTIATE_SEAL NEGOTIATE_SIGN REQUEST_TARGET NEGOTIATE_UNICODE + + (byte)0xc1, (byte)0x4a, (byte)0xc8, (byte)0x98, (byte)0x2f, (byte)0xd1, (byte)0x93, (byte)0xd4, // ServerChallenge + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + (byte) 0x98, 0x00, (byte) 0x98, 0x00, 0x56, 0x00, 0x00, 0x00, // TargetInfo (length: 152, allocated space: 152, offset: 86) + 0x06, 0x03, (byte) 0xd7, 0x24, 0x00, 0x00, 0x00, 0x0f, // Version (6.3, build 9431) , NTLM current revision: 15 + + + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // Target name value: "WIN-LO419B2LSR0" + + // Target Info value: + + // Attribute list + + 0x02, 0x00, // Item Type: NetBIOS domain name (0x0002, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x01, 0x00, // Item Type: NetBIOS computer name (0x0001, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x04, 0x00, // Item Type: DNS domain name (0x0004, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x03, 0x00, // Item Type: DNS computer name (0x0003, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x07, 0x00, // Item Type: Timestamp (0x0007, LE) + 0x08, 0x00, // Item Length: 8 (LE) + (byte)0x1d, (byte)0xea, (byte)0x6b, (byte)0x60, (byte)0xf8, (byte)0xc5, (byte)0xce, (byte)0x01, // Time: Oct 10, 2013 23:36:20.056937300 EEST + + // Attribute: End of list + 0x00, 0x00, + 0x00, 0x00, + + }; + +// +// Client NTLMSSP_AUTH +// + byte[] clientAuthPacket = new byte[] { + 0x30, (byte) 0x82, 0x03, 0x13, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 787 bytes + + // + // TSRequest.version + // + (byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes + 0x02, 0x01, 0x02, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes + + // + // TSRequest.negoData + // + (byte) 0xa1, (byte) 0x82, 0x01, (byte) 0xe4, // TAG: [1] (constructed) LEN: 484 bytes + 0x30, (byte) 0x82, 0x01, (byte) 0xe0, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 480 bytes + 0x30, (byte) 0x82, 0x01, (byte) 0xdc, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 476 bytes + + // + // NegoItem.negoToken + // + (byte) 0xa0, (byte) 0x82, 0x01, (byte) 0xd8, // TAG: [0] (constructed) LEN: 472 bytes + 0x04, (byte) 0x82, 0x01, (byte) 0xd4, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 468 bytes + + // NTLMSSP + + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, // "NTLMSSP\0" + + 0x03, 0x00, 0x00, 0x00, // NTLM Message Type: NTLMSSP_AUTH (0x00000003) + 0x18, 0x00, 0x18, 0x00, (byte) 0x92, 0x00, 0x00, 0x00, // LmChallengeResponse (length 24, allocated: 24, offset 146) + 0x1a, 0x01, 0x1a, 0x01, (byte) 0xaa, 0x00, 0x00, 0x00, // NtChallengeResponse (length 282, allocated: 282, offset 170) + 0x12, 0x00, 0x12, 0x00, 0x58, 0x00, 0x00, 0x00, // DomainName (length 18, allocated: 88, offset 88) + 0x1a, 0x00, 0x1a, 0x00, 0x6a, 0x00, 0x00, 0x00, // UserName (length 26, allocated:26, offset 106) + 0x0e, 0x00, 0x0e, 0x00, (byte) 0x84, 0x00, 0x00, 0x00, // Workstation (length 14, offset 132) + 0x10, 0x00, 0x10, 0x00, (byte) 0xc4, 0x01, 0x00, 0x00, // EncryptedRandomSessionKey (length 16, offset 452) + 0x35, (byte) 0xb2, (byte) 0x88, (byte) 0xe2, // NegotiateFlags + 0x06, 0x01, (byte) 0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f, // Version (6.1, Build 7601), NTLM current revision: 15 + + (byte)0x8c, (byte)0x69, (byte)0x53, (byte)0x1c, (byte)0xbb, (byte)0x6f, (byte)0xfb, (byte)0x9a, (byte)0x5d, (byte)0x2c, (byte)0x63, (byte)0xf2, (byte)0xc9, (byte)0x51, (byte)0xc5, (byte)0x11, // Message integrity check + + 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6b, 0x00, 0x67, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x70, 0x00, // Domain name value: "Workgroup" + + 0x41, 0x00, 0x64, 0x00, 0x6d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x72, 0x00, // User name value: "Administrator" + + 0x61, 0x00, 0x70, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x33, 0x00, // Workstation host name value: "apollo3" + + // Lan manager challenge response value + // Response: HMAC_MD(ResponseKeyLM, concatenate(ServerChallenge, ClientChallenge), where ResponseKeyLM=ntlmv2Hash(target, user, password) + (byte)0x17, (byte)0x9b, (byte)0x7d, (byte)0x7b, (byte)0x2f, (byte)0x79, (byte)0x9f, (byte)0x19, (byte)0xa0, (byte)0x4b, (byte)0x00, (byte)0xed, (byte)0x2b, (byte)0x39, (byte)0xbb, (byte)0x23, + // Client challenge (fixed for debugging) + (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, + + // + // NTLM challenge response value: + // + + (byte)0x49, (byte)0xea, (byte)0x27, (byte)0x4f, (byte)0xcc, (byte)0x05, (byte)0x8b, (byte)0x79, (byte)0x20, (byte)0x0b, (byte)0x08, (byte)0x42, (byte)0xa9, (byte)0xc8, (byte)0x0e, (byte)0xc7, // HMAC + + 0x01, 0x01, 0x00, 0x00, // Header: 0x00000101 (LE) + 0x00, 0x00, 0x00, 0x00, // Reserved: 0x00000000 + (byte)0x1d, (byte)0xea, (byte)0x6b, (byte)0x60, (byte)0xf8, (byte)0xc5, (byte)0xce, (byte)0x01, // Time: Oct 10, 2013 23:36:20.056937300 EEST + (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, // Client challenge (fixed) + 0x00, 0x00, 0x00, 0x00, // Reserved + + // Target Info value: + + // Attribute list + + 0x02, 0x00, // Item Type: NetBIOS domain name (0x0002, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0 " + + 0x01, 0x00, // Item Type: NetBIOS computer name (0x0001, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0 " + + 0x04, 0x00, // Item Type: DNS domain name (0x0004, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0 " + + 0x03, 0x00, // Item Type: DNS computer name (0x0003, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0 " + + 0x07, 0x00, // Item Type: Timestamp (0x0007, LE) + 0x08, 0x00, // Item Length: 8 (LE) + (byte)0x1d, (byte)0xea, (byte)0x6b, (byte)0x60, (byte)0xf8, (byte)0xc5, (byte)0xce, (byte)0x01, // Timestamp: Oct 10, 2013 23:36:20.056937300 EEST + + 0x06, 0x00, // Item Type: Flags (0x0006, LE) + 0x04, 0x00, // Item Length: 4 (LE) + 0x02, 0x00, 0x00, 0x00, // Flags: 0x00000002 + + 0x0a, 0x00, // Item Type: Channel Bindings (0x000a, LE) + 0x10, 0x00, // Item Length: 16 (LE) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Channel Bindings: 00000000000000000000000000000000 + + 0x09, 0x00, // Item Type: Target Name (0x0009, LE) + 0x2a, 0x00, // Item Length: 42 (LE) + 0x54, 0x00, 0x45, 0x00, 0x52, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x52, 0x00, 0x56, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x31, 0x00, 0x30, 0x00, 0x31, 0x00, // Target Name: "TERMSRV/192.168.0.101" (UTF-16) + + // Attribute: End of list + 0x00, 0x00, // + 0x00, 0x00, // + // Attribute: End of list + 0x00, 0x00, // + 0x00, 0x00, // + // Attribute: End of list + 0x00, 0x00, // + 0x00, 0x00, // + // Attribute: End of list + 0x00, 0x00, // + 0x00, 0x00, // + + // Session Key + // RC4 key (Server KeyExchangeKey or SessionBaseKey): + // 6e bd e3 da 83 c2 fd f1 38 a2 78 be 8c e6 75 d6 + // + // RC4 data (Client nonce): + // 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 + // + // RC4 encrypted: + // 2c 24 da 10 17 cf 40 69 35 49 6f 58 e1 29 9e 79 + (byte)0x2c, (byte)0x24, (byte)0xda, (byte)0x10, (byte)0x17, (byte)0xcf, (byte)0x40, (byte)0x69, (byte)0x35, (byte)0x49, (byte)0x6f, (byte)0x58, (byte)0xe1, (byte)0x29, (byte)0x9e, (byte)0x79, + + // + // TSRequest.publicKey + // + (byte) 0xa3, (byte) 0x82, 0x01, 0x22, // TAG: [3] (constructed) LEN: 290 bytes + 0x04, (byte) 0x82, 0x01, 0x1e, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 286 bytes + + // NTLMSSP_MESSAGE_SIGNATURE, @see http://msdn.microsoft.com/en-us/library/cc422952.aspx + + // Version: 0x00000001 + (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, + + // Checksum (8 bytes): An 8-byte array that contains the checksum for the message. + (byte)0x72, (byte)0x76, (byte)0x1e, (byte)0x57, (byte)0x49, (byte)0xb5, (byte)0x0f, (byte)0xad, + + // seqNum of the message: 0 + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + + // Encrypted public key + (byte)0x15, (byte)0xf7, (byte)0xf2, (byte)0x54, (byte)0xda, (byte)0xa9, (byte)0xe5, (byte)0xad, (byte)0x85, (byte)0x04, (byte)0x67, (byte)0x4d, (byte)0x0b, (byte)0xcb, (byte)0xf9, (byte)0xb1, + (byte)0xf8, (byte)0x02, (byte)0x8a, (byte)0x77, (byte)0xc2, (byte)0x63, (byte)0xab, (byte)0xd5, (byte)0x74, (byte)0x23, (byte)0x9f, (byte)0x9d, (byte)0x5d, (byte)0x1f, (byte)0xd3, (byte)0xb3, + (byte)0xa0, (byte)0xac, (byte)0x16, (byte)0x8a, (byte)0x4b, (byte)0x08, (byte)0xf5, (byte)0x47, (byte)0x70, (byte)0x58, (byte)0x10, (byte)0xb4, (byte)0xe7, (byte)0x87, (byte)0xb3, (byte)0x4b, + (byte)0xc9, (byte)0xa2, (byte)0xd5, (byte)0xd1, (byte)0xca, (byte)0x0f, (byte)0xd4, (byte)0xe3, (byte)0x8d, (byte)0x76, (byte)0x5a, (byte)0x60, (byte)0x28, (byte)0xf8, (byte)0x06, (byte)0x5d, + (byte)0xe4, (byte)0x7e, (byte)0x21, (byte)0xc8, (byte)0xbb, (byte)0xac, (byte)0xe5, (byte)0x79, (byte)0x85, (byte)0x30, (byte)0x9b, (byte)0x88, (byte)0x13, (byte)0x2f, (byte)0x8f, (byte)0xfc, + (byte)0x04, (byte)0x52, (byte)0xfe, (byte)0x87, (byte)0x94, (byte)0xcf, (byte)0xcb, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x6f, (byte)0xdd, (byte)0xee, (byte)0x57, (byte)0xa5, (byte)0xe4, + (byte)0x4d, (byte)0x0e, (byte)0x5c, (byte)0x3d, (byte)0x0b, (byte)0x63, (byte)0x1f, (byte)0xf6, (byte)0x3d, (byte)0x1b, (byte)0xae, (byte)0x5a, (byte)0xf6, (byte)0x42, (byte)0x2a, (byte)0x46, + (byte)0xfa, (byte)0x42, (byte)0x71, (byte)0x67, (byte)0x46, (byte)0x02, (byte)0x71, (byte)0xea, (byte)0x51, (byte)0x98, (byte)0xf7, (byte)0xd4, (byte)0x43, (byte)0xbf, (byte)0x8e, (byte)0xe8, + (byte)0x3c, (byte)0xc8, (byte)0xfa, (byte)0x79, (byte)0x9d, (byte)0x8c, (byte)0xfc, (byte)0xc2, (byte)0x42, (byte)0xc9, (byte)0xbb, (byte)0xd0, (byte)0xab, (byte)0x81, (byte)0xc4, (byte)0x53, + (byte)0xfd, (byte)0x41, (byte)0xda, (byte)0xab, (byte)0x0f, (byte)0x25, (byte)0x79, (byte)0x5f, (byte)0xbd, (byte)0xa3, (byte)0x8c, (byte)0xd3, (byte)0xf5, (byte)0x1b, (byte)0xab, (byte)0x20, + (byte)0xd1, (byte)0xf4, (byte)0xd8, (byte)0x81, (byte)0x9c, (byte)0x18, (byte)0x4a, (byte)0xa4, (byte)0x77, (byte)0xee, (byte)0xe1, (byte)0x51, (byte)0xee, (byte)0x2a, (byte)0xc1, (byte)0x94, + (byte)0x37, (byte)0xc5, (byte)0x06, (byte)0x7a, (byte)0x3f, (byte)0x0f, (byte)0x25, (byte)0x5b, (byte)0x4e, (byte)0x6a, (byte)0xdc, (byte)0x0b, (byte)0x62, (byte)0x6f, (byte)0x12, (byte)0x83, + (byte)0x03, (byte)0xae, (byte)0x4e, (byte)0xce, (byte)0x2b, (byte)0x6e, (byte)0xd4, (byte)0xd5, (byte)0x23, (byte)0x27, (byte)0xf6, (byte)0xa6, (byte)0x38, (byte)0x67, (byte)0xec, (byte)0x95, + (byte)0x82, (byte)0xc6, (byte)0xba, (byte)0xd4, (byte)0xf6, (byte)0xe6, (byte)0x22, (byte)0x7d, (byte)0xb9, (byte)0xe4, (byte)0x81, (byte)0x97, (byte)0x24, (byte)0xff, (byte)0x40, (byte)0xb2, + (byte)0x42, (byte)0x3c, (byte)0x11, (byte)0x24, (byte)0xd0, (byte)0x3a, (byte)0x96, (byte)0xd9, (byte)0xc1, (byte)0x13, (byte)0xd6, (byte)0x62, (byte)0x45, (byte)0x21, (byte)0x60, (byte)0x5b, + (byte)0x7b, (byte)0x2b, (byte)0x62, (byte)0x44, (byte)0xf7, (byte)0x40, (byte)0x93, (byte)0x29, (byte)0x5b, (byte)0x44, (byte)0xb7, (byte)0xda, (byte)0x9c, (byte)0xa6, (byte)0xa9, (byte)0x3b, + (byte)0xe1, (byte)0x3b, (byte)0x9d, (byte)0x31, (byte)0xf2, (byte)0x21, (byte)0x53, (byte)0x0f, (byte)0xb3, (byte)0x70, (byte)0x55, (byte)0x84, (byte)0x2c, (byte)0xb4, + }; + + SSLState sslState = new SSLState(); + + sslState.serverCertificateSubjectPublicKeyInfo = new byte[] { + 0x30, (byte) 0x82, 0x01, 0x22, // Sequence, length: 290 bytes + 0x30, 0x0d, // Sequence, length: 13 bytes { + 0x06, 0x09, // Object ID, length: 9 bytes + 0x2a, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // NULL, length: 0 bytes + + (byte)0x03, (byte)0x82, (byte)0x01, (byte)0x0f, // Bit string, length: 271 bytes + + (byte)0x00, // Pading + (byte)0x30, (byte)0x82, (byte)0x01, (byte)0x0a, // Sequence + (byte)0x02, (byte)0x82, (byte)0x01, (byte)0x01, // Integer, length: 257 bytes + + (byte)0x00, (byte)0xa8, (byte)0x56, + (byte)0x65, (byte)0xd3, (byte)0xce, (byte)0x8a, (byte)0x54, (byte)0x4d, (byte)0x9d, (byte)0xb0, + (byte)0x84, (byte)0x31, (byte)0x19, (byte)0x71, (byte)0x7f, (byte)0xdd, (byte)0x42, (byte)0xfb, + (byte)0x2a, (byte)0x7a, (byte)0x72, (byte)0x13, (byte)0xa1, (byte)0xb9, (byte)0x72, (byte)0xbb, + (byte)0xd3, (byte)0x08, (byte)0xad, (byte)0x7d, (byte)0x6c, (byte)0x15, (byte)0x65, (byte)0x03, + (byte)0xd1, (byte)0xc4, (byte)0x54, (byte)0xc5, (byte)0x33, (byte)0x6b, (byte)0x7d, (byte)0x69, + (byte)0x89, (byte)0x5e, (byte)0xfe, (byte)0xe0, (byte)0x01, (byte)0xc0, (byte)0x7e, (byte)0x9b, + (byte)0xcb, (byte)0x5d, (byte)0x65, (byte)0x36, (byte)0xcd, (byte)0x77, (byte)0x5d, (byte)0xf3, + (byte)0x7a, (byte)0x5b, (byte)0x29, (byte)0x44, (byte)0x72, (byte)0xd5, (byte)0x38, (byte)0xe2, + (byte)0xcf, (byte)0xb1, (byte)0xc7, (byte)0x78, (byte)0x9b, (byte)0x58, (byte)0xb9, (byte)0x17, + (byte)0x7c, (byte)0xb7, (byte)0xd6, (byte)0xc7, (byte)0xc7, (byte)0xbf, (byte)0x90, (byte)0x4e, + (byte)0x7c, (byte)0x39, (byte)0x93, (byte)0xcb, (byte)0x2e, (byte)0xe0, (byte)0xc2, (byte)0x33, + (byte)0x2d, (byte)0xa5, (byte)0x7e, (byte)0xe0, (byte)0x7b, (byte)0xb6, (byte)0xf9, (byte)0x91, + (byte)0x32, (byte)0xb7, (byte)0xd4, (byte)0x85, (byte)0xb7, (byte)0x35, (byte)0x2d, (byte)0x2b, + (byte)0x00, (byte)0x6d, (byte)0xf8, (byte)0xea, (byte)0x8c, (byte)0x97, (byte)0x5f, (byte)0x51, + (byte)0x1d, (byte)0x68, (byte)0x04, (byte)0x3c, (byte)0x79, (byte)0x14, (byte)0x71, (byte)0xa7, + (byte)0xc7, (byte)0xd7, (byte)0x70, (byte)0x7a, (byte)0xe0, (byte)0xba, (byte)0x12, (byte)0x69, + (byte)0xc8, (byte)0xd3, (byte)0xd9, (byte)0x4e, (byte)0xab, (byte)0x51, (byte)0x47, (byte)0xa3, + (byte)0xec, (byte)0x99, (byte)0xd4, (byte)0x88, (byte)0xca, (byte)0xda, (byte)0xc2, (byte)0x7f, + (byte)0x79, (byte)0x4b, (byte)0x66, (byte)0xed, (byte)0x87, (byte)0xbe, (byte)0xc2, (byte)0x5f, + (byte)0xea, (byte)0xcf, (byte)0xe1, (byte)0xb5, (byte)0xf0, (byte)0x3d, (byte)0x9b, (byte)0xf2, + (byte)0x19, (byte)0xc3, (byte)0xe0, (byte)0xe1, (byte)0x7a, (byte)0x45, (byte)0x71, (byte)0x12, + (byte)0x3d, (byte)0x72, (byte)0x1d, (byte)0x6f, (byte)0x2b, (byte)0x1c, (byte)0x46, (byte)0x68, + (byte)0xc0, (byte)0x8f, (byte)0x4f, (byte)0xce, (byte)0x3a, (byte)0xc5, (byte)0xcd, (byte)0x22, + (byte)0x65, (byte)0x2d, (byte)0x43, (byte)0xb0, (byte)0x5c, (byte)0xdd, (byte)0x89, (byte)0xae, + (byte)0xbe, (byte)0x70, (byte)0x59, (byte)0x5e, (byte)0x0c, (byte)0xbd, (byte)0xf5, (byte)0x46, + (byte)0x82, (byte)0x1e, (byte)0xe4, (byte)0x86, (byte)0x95, (byte)0x7b, (byte)0x60, (byte)0xae, + (byte)0x45, (byte)0x50, (byte)0xc2, (byte)0x54, (byte)0x08, (byte)0x49, (byte)0x9a, (byte)0x9e, + (byte)0xfb, (byte)0xb2, (byte)0xb6, (byte)0x78, (byte)0xe5, (byte)0x2f, (byte)0x9c, (byte)0x5a, + (byte)0xd0, (byte)0x8a, (byte)0x03, (byte)0x77, (byte)0x68, (byte)0x30, (byte)0x93, (byte)0x78, + (byte)0x6d, (byte)0x90, (byte)0x6d, (byte)0x50, (byte)0xfa, (byte)0xa7, (byte)0x65, (byte)0xfe, + (byte)0x59, (byte)0x33, (byte)0x27, (byte)0x4e, (byte)0x4b, (byte)0xf8, (byte)0x38, (byte)0x44, + (byte)0x3a, (byte)0x12, (byte)0xf4, (byte)0x07, (byte)0xa0, (byte)0x8d, (byte)0x02, (byte)0x03, + (byte)0x01, (byte)0x00, (byte)0x01, + }; + /* @formatter:on */ + + NtlmState ntlmState = new NtlmState(); + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(serverChallengePacket, new byte[] {1, 2, 3})); + Element ntlmssp_negotiate = new ClientNtlmsspNegotiate("ntlmssp_negotiate", ntlmState); + Element ntlmssp_challenge = new ServerNtlmsspChallenge("ntlmssp_challenge", ntlmState); + Element ntlmssp_auth = new ClientNtlmsspPubKeyAuth("ntlmssp_auth", ntlmState, sslState, "192.168.0.101", "workgroup", "apollo3", "Administrator", + "R2Preview!"); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(clientNegotiatePacket, clientAuthPacket), (Dumper)ntlmssp_auth); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, ntlmssp_negotiate, ntlmssp_challenge, ntlmssp_auth, sink, mainSink); + pipeline.link("source", "ntlmssp_negotiate", "ntlmssp_challenge", "ntlmssp_auth", "mainSink"); + pipeline.link("ntlmssp_negotiate >" + OTOUT, "ntlmssp_negotiate< sink"); + pipeline.link("ntlmssp_challenge >" + OTOUT, "ntlmssp_challenge< sink"); + pipeline.link("ntlmssp_auth >" + OTOUT, "ntlmssp_auth< sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + } + + @Override + public void dump(ByteBuffer buf) { + buf.rewindCursor(); + TSRequest request = new TSRequest("TSRequest"); + request.readTag(buf); + System.out.println("TSRequest version: " + request.version.value); + System.out.println("TSRequest pubKey: " + request.pubKeyAuth.value.toPlainHexString()); + + ByteBuffer negoToken = ((NegoItem)request.negoTokens.tags[0]).negoToken.value; + System.out.println("TSRequest negotoken: " + negoToken.toPlainHexString()); + dumpNegoToken(negoToken); + + negoToken.unref(); + } + + private void dumpNegoToken(ByteBuffer buf) { + String signature = buf.readVariableString(RdpConstants.CHARSET_8); + if (!signature.equals(NTLMSSP)) + throw new RuntimeException("Unexpected NTLM message singature: \"" + signature + "\". Expected signature: \"" + NTLMSSP + "\". Data: " + buf + "."); + + // MessageType (CHALLENGE) + int messageType = buf.readSignedIntLE(); + if (messageType != NtlmConstants.NTLMSSP_AUTH) + throw new RuntimeException("Unexpected NTLM message type: " + messageType + ". Expected type: CHALLENGE (" + NtlmConstants.CHALLENGE + "). Data: " + buf + + "."); + + System.out.println("lmChallengeResponseFields: " + ServerNtlmsspChallenge.readBlockByDescription(buf).toPlainHexString()); + ByteBuffer ntChallengeResponseBuf = ServerNtlmsspChallenge.readBlockByDescription(buf); + System.out.println("NtChallengeResponse: " + ntChallengeResponseBuf.toPlainHexString()); + System.out.println("DomainName: " + ServerNtlmsspChallenge.readStringByDescription(buf)); + System.out.println("UserName: " + ServerNtlmsspChallenge.readStringByDescription(buf)); + System.out.println("Workstation: " + ServerNtlmsspChallenge.readStringByDescription(buf)); + System.out.println("EncryptedRandomSessionKey: " + ServerNtlmsspChallenge.readBlockByDescription(buf).toPlainHexString()); + System.out.println("NegotiateFlags: " + new NegoFlags(buf.readSignedIntLE())); + System.out.println("Version: " + buf.readBytes(8).toPlainHexString()); + + dumpNtChallengeResponse(ntChallengeResponseBuf); + } + + private void dumpNtChallengeResponse(ByteBuffer buf) { + System.out.println("HMAC: " + buf.readBytes(16).toPlainHexString()); + System.out.format("Header: 0x%08x\n", buf.readUnsignedIntLE()); + System.out.format("Reserved: 0x%08x\n", buf.readUnsignedIntLE()); + System.out.println("Time: " + buf.readBytes(8).toPlainHexString()); + System.out.println("Client challenge: " + buf.readBytes(8).toPlainHexString()); + System.out.format("Reserved: 0x%08x\n", buf.readUnsignedIntLE()); + + // Parse attribute list + + while (buf.remainderLength() > 0) { + int type = buf.readUnsignedShortLE(); + int length = buf.readUnsignedShortLE(); + + if (type == MSV_AV_EOL) + // End of list + break; + + ByteBuffer data = buf.readBytes(length); + switch (type) { + case MSV_AV_NETBIOS_DOMAIN_NAME: + System.out.println("AV Netbios Domain name: " + data.readString(length, RdpConstants.CHARSET_16)); + break; + case MSV_AV_NETBIOS_COMPUTER_NAME: + System.out.println("AV Netbios Computer name: " + data.readString(length, RdpConstants.CHARSET_16)); + break; + case MSV_AV_DNS_DOMAIN_NAME: + System.out.println("AV DNS Domain name: " + data.readString(length, RdpConstants.CHARSET_16)); + break; + case MSV_AV_DNS_COMPUTER_NAME: + System.out.println("AV DNS Computer name: " + data.readString(length, RdpConstants.CHARSET_16)); + break; + case MSV_AV_CHANNEL_BINDINGS: + System.out.println("AV Channel Bindings: " + data.readBytes(length).toPlainHexString()); + break; + case MSV_AV_TIMESTAMP: + System.out.println("AV Timestamp: " + data.readBytes(length).toPlainHexString()); + break; + case MSV_AV_FLAGS: + System.out.println("AV Flags: " + data.readBytes(length).toPlainHexString()); + break; + case MSV_AV_TARGET_NAME: + System.out.println("AV Target Name: " + data.readString(length, RdpConstants.CHARSET_16)); + break; + default: + System.out.println("Unknown NTLM target info attribute: " + type + ". Data: " + data + "."); + } + data.unref(); + } + + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspUserCredentials.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspUserCredentials.java new file mode 100755 index 00000000000..480f448ece0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ClientNtlmsspUserCredentials.java @@ -0,0 +1,128 @@ +// 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 rdpclient.ntlmssp; + +import rdpclient.ntlmssp.asn1.TSCredentials; +import rdpclient.ntlmssp.asn1.TSPasswordCreds; +import rdpclient.ntlmssp.asn1.TSRequest; +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class ClientNtlmsspUserCredentials extends OneTimeSwitch implements Element { + + protected NtlmState ntlmState; + + public ClientNtlmsspUserCredentials(String id, NtlmState ntlmState) { + super(id); + this.ntlmState = ntlmState; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + + ByteBuffer buf = new ByteBuffer(4096, true); + + TSRequest tsRequest = new TSRequest("TSRequest"); + tsRequest.version.value = 2L; + + ByteBuffer tsCredentialsBuf = generateTSCredentials(); + tsRequest.authInfo.value = encryptTSCredentials(tsCredentialsBuf); + tsCredentialsBuf.unref(); + + tsRequest.writeTag(buf); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + private ByteBuffer encryptTSCredentials(ByteBuffer buf2) { + return new ByteBuffer(ntlmState.ntlm_EncryptMessage(buf2.toByteArray())); + } + + private ByteBuffer generateTSCredentials() { + ByteBuffer buf = new ByteBuffer(4096); + + TSCredentials tsCredentials = new TSCredentials("authInfo"); + // 1 means that credentials field contains a TSPasswordCreds structure + tsCredentials.credType.value = 1L; + + ByteBuffer tsPasswordCredsBuf = new ByteBuffer(4096, true); + TSPasswordCreds tsPasswordCreds = new TSPasswordCreds("credentials"); + tsPasswordCreds.domainName.value = new ByteBuffer(ntlmState.domain.getBytes(RdpConstants.CHARSET_16)); + tsPasswordCreds.userName.value = new ByteBuffer(ntlmState.user.getBytes(RdpConstants.CHARSET_16)); + tsPasswordCreds.password.value = new ByteBuffer(ntlmState.password.getBytes(RdpConstants.CHARSET_16)); + tsPasswordCreds.writeTag(tsPasswordCredsBuf); + tsPasswordCredsBuf.trimAtCursor(); + //* DEBUG */System.out.println("TSPasswordCreds:\n" + tsPasswordCredsBuf.dump()); + + tsCredentials.credentials.value = tsPasswordCredsBuf; + + tsCredentials.writeTag(buf); + tsPasswordCredsBuf.unref(); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + //* DEBUG */System.out.println("TSCredentials:\n" + buf.dump()); + + return buf; + } + + /** + * @param args + */ + public static void main(String[] args) { + // TODO Auto-generated method stub + + /* @formatter:off */ + // TSCredentials +// 30 57 // Sequence +// a0 03 // TAG 0 +// 02 01 01 // Integer: 1 : credentials contains a TSPasswordCreds structure +// a1 50 // TAG 1 +// 04 4e // OCTETSTRING + // TSPasswordCreds +// 30 4c // SEQUENCE +// a0 14 // TAG 0 +// 04 12 // OCTETSTRING +// 77 00 6f 00 72 00 6b 00 67 00 72 00 6f 00 75 00 70 00 // "workgroup" +// a1 1c // TAG 1 +// 04 1a // OCTETSTRING +// 41 00 64 00 6d 00 69 00 6e 00 69 00 73 00 74 00 72 00 61 00 74 00 6f 00 72 00 // "Administrator" +// a2 16 // TAG 2 +// 04 14 // +// 52 00 32 00 50 00 72 00 65 00 76 00 69 00 65 00 77 00 21 00 // "R2Preview!" + /* @formatter:on */ + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/CryptoAlgos.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/CryptoAlgos.java new file mode 100755 index 00000000000..d0e72e961fd --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/CryptoAlgos.java @@ -0,0 +1,361 @@ +// 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 rdpclient.ntlmssp; + +import java.lang.reflect.Method; +import java.security.Key; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import rdpclient.rdp.RdpConstants; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc236717.aspx + */ +public class CryptoAlgos implements NtlmConstants { + + /** + * Indicates the left-to-right concatenation of the string parameters, from + * the first string to the Nnth. Any numbers are converted to strings and all + * numeric conversions to strings retain all digits, even nonsignificant ones. + * The result is a string. For example, ConcatenationOf(0x00122, "XYZ", + * "Client") results in the string "00122XYZClient." + */ + public static String concatenationOf(String... args) { + StringBuffer sb = new StringBuffer(); + for (String arg : args) { + sb.append(arg); + } + return sb.toString(); + } + + /** + * Concatenate byte arrays. + */ + public static byte[] concatenationOf(byte[]... arrays) { + int length = 0; + for (byte[] array : arrays) { + length += array.length; + } + + byte[] result = new byte[length]; + int destPos = 0; + for (byte[] array : arrays) { + System.arraycopy(array, 0, result, destPos, array.length); + destPos += array.length; + } + + return result; + } + + /** Indicates a 32-bit CRC calculated over m. */ + public static byte[] CRC32(byte[] m) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the encryption of an 8-byte data item d with the 7-byte key k + * using the Data Encryption Standard (DES) algorithm in Electronic Codebook + * (ECB) mode. The result is 8 bytes in length ([FIPS46-2]). + */ + public static byte[] DES(byte[] k, byte[] d) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the encryption of an 8-byte data item D with the 16-byte key K + * using the Data Encryption Standard Long (DESL) algorithm. The result is 24 + * bytes in length. DESL(K, D) is computed as follows. + * + *
+     *   ConcatenationOf( DES(K[0..6], D),
+     *     DES(K[7..13], D), DES(
+     *       ConcatenationOf(K[14..15], Z(5)), D));
+     * 
+ * + * Note K[] implies a key represented as a character array. + */ + public static byte[] DESL(byte[] k, byte[] d) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * An auxiliary function that returns an operating system version-specific + * value (section 2.2.2.8). + */ + public static byte[] getVersion() { + // Version (6.1, Build 7601), NTLM current revision: 15 + return new byte[] {0x06, 0x01, (byte)0xb1, 0x1d, 0x00, 0x00, 0x00, 0x0f}; + } + + /** + * Retrieve the user's LM response key from the server database (directory or + * local database). + */ + public static byte[] LMGETKEY(byte[] u, byte[] d) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** Retrieve the user's NT response key from the server database. */ + public static byte[] NTGETKEY(byte[] u, byte[] d) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the encryption of data item m with the key k using the HMAC + * algorithm ([RFC2104]). + */ + public static byte[] HMAC(byte[] k, byte[] m) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the computation of a 16-byte HMAC-keyed MD5 message digest of the + * byte string m using the key k. + */ + public static byte[] HMAC_MD5(byte[] k, byte[] m) { + try { + String algorithm = "HMacMD5"; + Mac hashMac = Mac.getInstance(algorithm); + + Key secretKey = new SecretKeySpec(k, 0, k.length, algorithm); + hashMac.init(secretKey); + return hashMac.doFinal(m); + } catch (Exception e) { + throw new RuntimeException("Cannot calculate HMAC-MD5.", e); + } + } + + /** + * Produces a key exchange key from the session base key K, LM response and + * server challenge SC as defined in the sections KXKEY, SIGNKEY, and SEALKEY. + */ + public static byte[] KXKEY(byte[] sessionBaseKey/*K, byte[] LM, byte[] SC*/) { + // Key eXchange Key is server challenge + /* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */ + return Arrays.copyOf(sessionBaseKey, sessionBaseKey.length); + } + + /** + * Computes a one-way function of the user's password to use as the response + * key. NTLM v1 and NTLM v2 define separate LMOWF() functions in the NTLM v1 + * authentication and NTLM v2 authentication sections, respectively. + */ + public static byte[] LMOWF() { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the computation of an MD4 message digest of the null-terminated + * byte string m ([RFC1320]). + */ + public static byte[] MD4(byte[] m) { + try { + return sun.security.provider.MD4.getInstance().digest(m); + } catch (Exception e) { + throw new RuntimeException("Cannot calculate MD5.", e); + } + } + + /** + * Indicates the computation of an MD5 message digest of the null-terminated + * byte string m ([RFC1321]). + */ + public static byte[] MD5(byte[] m) { + try { + return MessageDigest.getInstance("MD5").digest(m); + } catch (Exception e) { + throw new RuntimeException("Cannot calculate MD5.", e); + } + } + + /** + * Indicates the computation of an MD5 message digest of a binary blob + * ([RFC4121] section 4.1.1.2). + */ + public static byte[] MD5_HASH(byte[] m) { + try { + return MessageDigest.getInstance("MD5").digest(m); + } catch (Exception e) { + throw new RuntimeException("Cannot calculate MD5.", e); + } + } + + /** A zero-length string. */ + public static final String NIL = ""; + + /** + * Indicates the computation of an n-byte cryptographic-strength random + * number. + * + * Note The NTLM Authentication Protocol does not define the statistical + * properties of the random number generator. It is left to the discretion of + * the implementation to define the strength requirements of the NONCE(n) + * operation. + */ + public static byte[] NONCE(int n) { + // Generate random nonce for LMv2 and NTv2 responses + byte[] nonce = new byte[n]; + SecureRandom random = new SecureRandom(); + random.nextBytes(nonce); + + // Fixed nonce for debugging purposes + //* DEBUG */for (int i = 0; i < N; i++) nonce[i] = (byte) (i + 1); + + return nonce; + } + + /** + * Computes a one-way function of the user's password to use as the response + * key. NTLM v1 and NTLM v2 define separate NTOWF() functions in the NTLM v1 + * authentication and NTLM v2 authentication sections, respectively. + */ + public static byte[] NTOWF() { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * The RC4 Encryption Algorithm. To obtain this stream cipher that is licensed + * by RSA Data Security, Inc., contact this company. + * + * Indicates the encryption of data item d with the current session or message + * key state, using the RC4 algorithm. h is the handle to a key state + * structure initialized by RC4INIT. + */ + public static byte[] RC4(Cipher h, byte[] d) { + return h.update(d); + } + + /** + * Indicates the encryption of data item d with the key k using the RC4 + * algorithm. + * + * Note The key sizes for RC4 encryption in NTLM are defined in sections + * KXKEY, SIGNKEY, and SEALKEY, where they are created. + */ + public static byte[] RC4K(byte[] k, byte[] d) { + try { + Cipher cipher = Cipher.getInstance("RC4"); + Key key = new SecretKeySpec(k, "RC4"); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(d); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Initialization of the RC4 key and handle to a key state structure for the + * session. + */ + public static Cipher RC4Init(byte[] k) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Produces an encryption key from the session key as defined in sections + * KXKEY, SIGNKEY, and SEALKEY. + */ + public static byte[] SEALKEY(byte[] f, byte[] k, byte[] string1) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Produces a signing key from the session key as defined in sections KXKEY, + * SIGNKEY, and SEALKEY. + */ + public static byte[] SIGNKEY(int flag, byte[] k, byte[] string1) { + throw new RuntimeException("FATAL: Not implemented."); + } + + /** + * Indicates the retrieval of the current time as a 64-bit value, represented + * as the number of 100-nanosecond ticks elapsed since midnight of January + * 1st, 1601 (UTC). + */ + public static byte[] Currenttime() { + // (current time + milliseconds from 1.01.1601 to 1.01.1970) * + // 100-nanosecond ticks + long time = (System.currentTimeMillis() + 11644473600000L) * 10000; + + // Convert 64bit value to byte array. + byte[] result = new byte[8]; + for (int i = 0; i < 8; i++, time >>>= 8) { + result[i] = (byte)time; + } + + return result; + } + + /** + * Indicates the 2-byte little-endian byte order encoding of the Unicode + * UTF-16 representation of string. The Byte Order Mark (BOM) is not sent over + * the wire. + */ + public static byte[] UNICODE(String string) { + return string.getBytes(RdpConstants.CHARSET_16); + } + + /** Indicates the uppercase representation of string. */ + public static String UpperCase(String string) { + return string.toUpperCase(); + } + + /** + * Indicates the creation of a byte array of length N. Each byte in the array + * is initialized to the value zero. + */ + public static byte[] Z(int n) { + return new byte[n]; + } + + public static Cipher initRC4(byte[] key) { + try { + Cipher cipher = Cipher.getInstance("RC4"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4")); + return cipher; + } catch (Exception e) { + throw new RuntimeException("Cannot initialize RC4 sealing handle with client sealing key.", e); + } + } + + /** + * Helper method for embedded test cases. + */ + public static void callAll(Object obj) { + Method[] methods = obj.getClass().getDeclaredMethods(); + for (Method m : methods) { + if (m.getName().startsWith("test")) { + try { + m.invoke(obj, (Object[])null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public static void main(String args[]) { + callAll(new CryptoAlgos()); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NegoFlags.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NegoFlags.java new file mode 100755 index 00000000000..3208a77c30c --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NegoFlags.java @@ -0,0 +1,492 @@ +// 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 rdpclient.ntlmssp; + +/** + * During NTLM authentication, each of the following flags is a possible value + * of the NegotiateFlags field of the NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE, and + * AUTHENTICATE_MESSAGE, unless otherwise noted. These flags define client or + * server NTLM capabilities supported by the sender. + * + * @see http://msdn.microsoft.com/en-us/library/cc236650.aspx + */ +public class NegoFlags { + + /** + * 56-bit encryption. If the client sends NTLMSSP_NEGOTIATE_SEAL or + * NTLMSSP_NEGOTIATE_SIGN with NTLMSSP_NEGOTIATE_56 to the server in the + * NEGOTIATE_MESSAGE, the server MUST return NTLMSSP_NEGOTIATE_56 to the + * client in the CHALLENGE_MESSAGE. Otherwise it is ignored. If both + * NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are requested and supported + * by the client and server, NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 + * will both be returned to the client. Clients and servers that set + * NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_56 if it is supported. + * An alternate name for this field is + */ + public static final int NTLMSSP_NEGOTIATE_56 = 0x80000000; + + /** + * Explicit key exchange. This capability SHOULD be used because it improves + * security for message integrity or confidentiality. See sections 3.2.5.1.2, + * 3.2.5.2.1, and 3.2.5.2.2 for details. + */ + public static final int NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000; + + /** + * 128-bit session key negotiation. An alternate name for this field is + * NTLMSSP_NEGOTIATE_128. If the client sends NTLMSSP_NEGOTIATE_128 to the + * server in the NEGOTIATE_MESSAGE, the server MUST return + * NTLMSSP_NEGOTIATE_128 to the client in the CHALLENGE_MESSAGE only if the + * client sets NTLMSSP_NEGOTIATE_SEAL or NTLMSSP_NEGOTIATE_SIGN. Otherwise it + * is ignored. If both NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128 are + * requested and supported by the client and server, NTLMSSP_NEGOTIATE_56 and + * NTLMSSP_NEGOTIATE_128 will both be returned to the client. Clients and + * servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD set NTLMSSP_NEGOTIATE_128 if + * it is supported. + */ + public static final int NTLMSSP_NEGOTIATE_128 = 0x20000000; + + /** + * Protocol version number. The data corresponding to this flag is provided in + * the Version field of the NEGOTIATE_MESSAGE, the CHALLENGE_MESSAGE, and the + * AUTHENTICATE_MESSAGE. + */ + public static final int NTLMSSP_NEGOTIATE_VERSION = 0x02000000; + + /** + * TargetInfo fields in the CHALLENGE_MESSAGE (section 2.2.1.2) are populated. + */ + public static final int NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000; + + /** LMOWF (section 3.3). */ + public static final int NTLMSSP_REQUEST_NON_NT_SESSION_KEY = 0x00400000; + + /** An identify level token. */ + public static final int NTLMSSP_NEGOTIATE_IDENTIFY = 0x00100000; + + /** + * NTLM v2 session security. NTLM v2 session security is a misnomer because it + * is not NTLM v2. It is NTLM v1 using the extended session security that is + * also in NTLM v2. NTLMSSP_NEGOTIATE_LM_KEY and + * NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both + * NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and NTLMSSP_NEGOTIATE_LM_KEY are + * requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be + * returned to the client. NTLM v2 authentication session key generation MUST + * be supported by both the client and the DC in order to be used, and + * extended session security signing and sealing requires support from the + * client and the server in order to be used. + */ + public static final int NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY = 0x00080000; + + /** + * TargetName MUST be a server name. The data corresponding to this flag is + * provided by the server in the TargetName field of the CHALLENGE_MESSAGE. If + * this bit is set, then NTLMSSP_TARGET_TYPE_DOMAIN MUST NOT be set. This flag + * MUST be ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. + */ + public static final int NTLMSSP_TARGET_TYPE_SERVER = 0x00020000; + + /** + * TargetName MUST be a domain name. The data corresponding to this flag is + * provided by the server in the TargetName field of the CHALLENGE_MESSAGE. If + * set, then NTLMSSP_TARGET_TYPE_SERVER MUST NOT be set. This flag MUST be + * ignored in the NEGOTIATE_MESSAGE and the AUTHENTICATE_MESSAGE. + */ + public static final int NTLMSSP_TARGET_TYPE_DOMAIN = 0x00010000; + + /** + * Signature block on all messages. NTLMSSP_NEGOTIATE_ALWAYS_SIGN MUST be set + * in the NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to the + * client. NTLMSSP_NEGOTIATE_ALWAYS_SIGN is overridden by + * NTLMSSP_NEGOTIATE_SIGN and NTLMSSP_NEGOTIATE_SEAL, if they are supported. + */ + public static final int NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000; + + /** + * Workstation field is present. If this flag is not set, the Workstation + * field MUST be ignored. If this flag is set, the length field of the + * Workstation field specifies whether the workstation name is nonempty or + * not. + */ + public static final int NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED = 0x00002000; + + /** + * Domain name is provided. + * + * Sent by the client in the Type 1 message to indicate that the name of the + * domain in which the client workstation has membership is included in the + * message. This is used by the server to determine whether the client is + * eligible for local authentication. + */ + public static final int NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED = 0x00001000; + + /** + * Connection SHOULD be anonymous. + * + * Sent by the client in the Type 3 message to indicate that an anonymous + * context has been established. This also affects the response fields (as + * detailed in the "Anonymous Response" section). + */ + public static final int NTLMSSP_NEGOTIATE_ANONYMOUS = 0x00000800; + + /** + * Usage of the NTLM v1 session security protocol. NTLMSSP_NEGOTIATE_NTLM MUST + * be set in the NEGOTIATE_MESSAGE to the server and the CHALLENGE_MESSAGE to + * the client. + */ + public static final int NTLMSSP_NEGOTIATE_NTLM = 0x00000200; + + /** + * LAN Manager (LM) session key computation. NTLMSSP_NEGOTIATE_LM_KEY and + * NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are mutually exclusive. If both + * NTLMSSP_NEGOTIATE_LM_KEY and NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY are + * requested, NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY alone MUST be + * returned to the client. NTLM v2 authentication session key generation MUST + * be supported by both the client and the DC in order to be used, and + * extended session security signing and sealing requires support from the + * client and the server to be used. + */ + public static final int NTLMSSP_NEGOTIATE_LM_KEY = 0x00000080; + + /** + * Connectionless authentication. If NTLMSSP_NEGOTIATE_DATAGRAM is set, then + * NTLMSSP_NEGOTIATE_KEY_EXCH MUST always be set in the AUTHENTICATE_MESSAGE + * to the server and the CHALLENGE_MESSAGE to the client. + */ + public static final int NTLMSSP_NEGOTIATE_DATAGRAM = 0x00000040; + + /** + * Session key negotiation for message confidentiality. If the client sends + * NTLMSSP_NEGOTIATE_SEAL to the server in the NEGOTIATE_MESSAGE, the server + * MUST return NTLMSSP_NEGOTIATE_SEAL to the client in the CHALLENGE_MESSAGE. + * Clients and servers that set NTLMSSP_NEGOTIATE_SEAL SHOULD always set + * NTLMSSP_NEGOTIATE_56 and NTLMSSP_NEGOTIATE_128, if they are supported. + */ + public static final int NTLMSSP_NEGOTIATE_SEAL = 0x00000020; + + /** + * Session key negotiation for message signatures. If the client sends + * NTLMSSP_NEGOTIATE_SIGN to the server in the NEGOTIATE_MESSAGE, the server + * MUST return NTLMSSP_NEGOTIATE_SIGN to the client in the CHALLENGE_MESSAGE. + */ + public static final int NTLMSSP_NEGOTIATE_SIGN = 0x00000010; + + /** + * TargetName field of the CHALLENGE_MESSAGE (section 2.2.1.2) MUST be + * supplied. + */ + public static final int NTLMSSP_REQUEST_TARGET = 0x00000004; + + /** + * OEM character set encoding. + * + * @see NTLMSSP_NEGOTIATE_UNICODE + */ + public static final int NTLMSSP_NEGOTIATE_OEM = 0x00000002; + + /** + * Unicode character set encoding. + * + * The NTLMSSP_NEGOTIATE_UNICODE(A) and NTLM_NEGOTIATE_OEM(B) bits are + * evaluated together as follows: + *
    + *
  • A==1: The choice of character set encoding MUST be Unicode. + * + *
  • A==0 and B==1: The choice of character set encoding MUST be OEM. + * + *
  • A==0 and B==0: The protocol MUST return SEC_E_INVALID_TOKEN. + *
      + * */ + public static final int NTLMSSP_NEGOTIATE_UNICODE = 0x00000001; + + public int value; + + public NegoFlags(int value) { + this.value = value; + } + + public NegoFlags() { + value = 0; + } + + @Override + public String toString() { + return String.format("NegoFlags [value=0x%04x (%s)]", value, flagsToSting()); + } + + public String flagsToSting() { + + String str = ""; + + if (NEGOTIATE_56()) + str += "NEGOTIATE_56 "; + if (NEGOTIATE_KEY_EXCH()) + str += "NEGOTIATE_KEY_EXCH "; + if (NEGOTIATE_128()) + str += "NEGOTIATE_128 "; + if (NEGOTIATE_VERSION()) + str += "NEGOTIATE_VERSION "; + if (NEGOTIATE_TARGET_INFO()) + str += "NEGOTIATE_TARGET_INFO "; + if (REQUEST_NON_NT_SESSION_KEY()) + str += "REQUEST_NON_NT_SESSION_KEY "; + if (NEGOTIATE_IDENTIFY()) + str += "NEGOTIATE_IDENTIFY "; + if (NEGOTIATE_EXTENDED_SESSION_SECURITY()) + str += "NEGOTIATE_EXTENDED_SESSION_SECURITY "; + if (TARGET_TYPE_SERVER()) + str += "TARGET_TYPE_SERVER "; + if (TARGET_TYPE_DOMAIN()) + str += "TARGET_TYPE_DOMAIN "; + if (NEGOTIATE_ALWAYS_SIGN()) + str += "NEGOTIATE_ALWAYS_SIGN "; + if (NEGOTIATE_OEM_WORKSTATION_SUPPLIED()) + str += "NEGOTIATE_OEM_WORKSTATION_SUPPLIED "; + if (NEGOTIATE_OEM_DOMAIN_SUPPLIED()) + str += "NEGOTIATE_OEM_DOMAIN_SUPPLIED "; + if (NEGOTIATE_ANONYMOUS()) + str += "NEGOTIATE_ANONYMOUS "; + if (NEGOTIATE_NTLM()) + str += "NEGOTIATE_NTLM "; + if (NEGOTIATE_LM_KEY()) + str += "NEGOTIATE_LM_KEY "; + if (NEGOTIATE_DATAGRAM()) + str += "NEGOTIATE_DATAGRAM "; + if (NEGOTIATE_SEAL()) + str += "NEGOTIATE_SEAL "; + if (NEGOTIATE_SIGN()) + str += "NEGOTIATE_SIGN "; + if (REQUEST_TARGET()) + str += "REQUEST_TARGET "; + if (NEGOTIATE_OEM()) + str += "NEGOTIATE_OEM "; + if (NEGOTIATE_UNICODE()) + str += "NEGOTIATE_UNICODE "; + + return str; + } + + public boolean NEGOTIATE_56() { + return ((value & NTLMSSP_NEGOTIATE_56) != 0); + } + + public boolean NEGOTIATE_KEY_EXCH() { + return ((value & NTLMSSP_NEGOTIATE_KEY_EXCH) != 0); + } + + public boolean NEGOTIATE_128() { + return ((value & NTLMSSP_NEGOTIATE_128) != 0); + } + + public boolean NEGOTIATE_VERSION() { + return ((value & NTLMSSP_NEGOTIATE_VERSION) != 0); + } + + public boolean NEGOTIATE_TARGET_INFO() { + return ((value & NTLMSSP_NEGOTIATE_TARGET_INFO) != 0); + } + + public boolean REQUEST_NON_NT_SESSION_KEY() { + return ((value & NTLMSSP_REQUEST_NON_NT_SESSION_KEY) != 0); + } + + public boolean NEGOTIATE_IDENTIFY() { + return ((value & NTLMSSP_NEGOTIATE_IDENTIFY) != 0); + } + + public boolean NEGOTIATE_EXTENDED_SESSION_SECURITY() { + return ((value & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY) != 0); + } + + public boolean TARGET_TYPE_SERVER() { + return ((value & NTLMSSP_TARGET_TYPE_SERVER) != 0); + } + + public boolean TARGET_TYPE_DOMAIN() { + return ((value & NTLMSSP_TARGET_TYPE_DOMAIN) != 0); + } + + public boolean NEGOTIATE_ALWAYS_SIGN() { + return ((value & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) != 0); + } + + public boolean NEGOTIATE_OEM_WORKSTATION_SUPPLIED() { + return ((value & NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED) != 0); + } + + public boolean NEGOTIATE_OEM_DOMAIN_SUPPLIED() { + return ((value & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED) != 0); + } + + public boolean NEGOTIATE_ANONYMOUS() { + return ((value & NTLMSSP_NEGOTIATE_ANONYMOUS) != 0); + } + + public boolean NEGOTIATE_NTLM() { + return ((value & NTLMSSP_NEGOTIATE_NTLM) != 0); + } + + public boolean NEGOTIATE_LM_KEY() { + return ((value & NTLMSSP_NEGOTIATE_LM_KEY) != 0); + } + + public boolean NEGOTIATE_DATAGRAM() { + return ((value & NTLMSSP_NEGOTIATE_DATAGRAM) != 0); + } + + public boolean NEGOTIATE_SEAL() { + return ((value & NTLMSSP_NEGOTIATE_SEAL) != 0); + } + + public boolean NEGOTIATE_SIGN() { + return ((value & NTLMSSP_NEGOTIATE_SIGN) != 0); + } + + public boolean REQUEST_TARGET() { + return ((value & NTLMSSP_REQUEST_TARGET) != 0); + } + + public boolean NEGOTIATE_OEM() { + return ((value & NTLMSSP_NEGOTIATE_OEM) != 0); + } + + public boolean NEGOTIATE_UNICODE() { + return ((value & NTLMSSP_NEGOTIATE_UNICODE) != 0); + } + + public NegoFlags set_NEGOTIATE_56() { + value |= NTLMSSP_NEGOTIATE_56; + return this; + } + + public NegoFlags set_NEGOTIATE_KEY_EXCH() { + value |= NTLMSSP_NEGOTIATE_KEY_EXCH; + return this; + } + + public NegoFlags set_NEGOTIATE_128() { + value |= NTLMSSP_NEGOTIATE_128; + return this; + } + + public NegoFlags set_NEGOTIATE_VERSION() { + value |= NTLMSSP_NEGOTIATE_VERSION; + return this; + } + + public NegoFlags set_NEGOTIATE_TARGET_INFO() { + value |= NTLMSSP_NEGOTIATE_TARGET_INFO; + return this; + } + + public NegoFlags set_REQUEST_NON_NT_SESSION_KEY() { + value |= NTLMSSP_REQUEST_NON_NT_SESSION_KEY; + return this; + } + + public NegoFlags set_NEGOTIATE_IDENTIFY() { + value |= NTLMSSP_NEGOTIATE_IDENTIFY; + return this; + } + + public NegoFlags set_NEGOTIATE_EXTENDED_SESSION_SECURITY() { + value |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY; + return this; + } + + public NegoFlags set_TARGET_TYPE_SERVER() { + value |= NTLMSSP_TARGET_TYPE_SERVER; + return this; + } + + public NegoFlags set_TARGET_TYPE_DOMAIN() { + value |= NTLMSSP_TARGET_TYPE_DOMAIN; + return this; + } + + public NegoFlags set_NEGOTIATE_ALWAYS_SIGN() { + value |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; + return this; + } + + public NegoFlags set_NEGOTIATE_OEM_WORKSTATION_SUPPLIED() { + value |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED; + return this; + } + + public NegoFlags set_NEGOTIATE_OEM_DOMAIN_SUPPLIED() { + value |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED; + return this; + } + + public NegoFlags set_NEGOTIATE_ANONYMOUS() { + value |= NTLMSSP_NEGOTIATE_ANONYMOUS; + return this; + } + + public NegoFlags set_NEGOTIATE_NTLM() { + value |= NTLMSSP_NEGOTIATE_NTLM; + return this; + } + + public NegoFlags set_NEGOTIATE_LM_KEY() { + value |= NTLMSSP_NEGOTIATE_LM_KEY; + return this; + } + + public NegoFlags set_NEGOTIATE_DATAGRAM() { + value |= NTLMSSP_NEGOTIATE_DATAGRAM; + return this; + } + + public NegoFlags set_NEGOTIATE_SEAL() { + value |= NTLMSSP_NEGOTIATE_SEAL; + return this; + } + + public NegoFlags set_NEGOTIATE_SIGN() { + value |= NTLMSSP_NEGOTIATE_SIGN; + return this; + } + + public NegoFlags set_REQUEST_TARGET() { + value |= NTLMSSP_REQUEST_TARGET; + return this; + } + + public NegoFlags set_NEGOTIATE_OEM() { + value |= NTLMSSP_NEGOTIATE_OEM; + return this; + } + + public NegoFlags set_NEGOTIATE_UNICODE() { + value |= NTLMSSP_NEGOTIATE_UNICODE; + return this; + } + + /** + * Example. + */ + + public static void main(String args[]) { + + NegoFlags flags = new NegoFlags(0xe20882b7); + System.out.println("Negotiation flags: " + flags); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmCompute.java old mode 100644 new mode 100755 similarity index 84% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmCompute.java index 3a8b4b6d1c2..e75c73c17e3 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmCompute.java @@ -14,14 +14,11 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.ntlmssp; -import streamer.BaseElement; +public class NtlmCompute { -public class HandshakeEnd extends BaseElement { + public void compute_ntlm_v2_hash() { - public HandshakeEnd(String id) { - super(id); } - } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmConstants.java new file mode 100755 index 00000000000..a823aac89ec --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmConstants.java @@ -0,0 +1,157 @@ +// 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 rdpclient.ntlmssp; + +public interface NtlmConstants { + + /** + * Attribute type: Indicates that this is the last AV_PAIR in the list. AvLen + * MUST be 0. This type of information MUST be present in the AV pair list. + */ + public final static int MSV_AV_EOL = 0x0000; + + /** + * Attribute type: The server's NetBIOS computer name. The name MUST be in + * Unicode, and is not null-terminated. This type of information MUST be + * present in the AV_pair list. + */ + public final static int MSV_AV_NETBIOS_COMPUTER_NAME = 0x0001; + + /** + * Attribute type: The server's NetBIOS domain name. The name MUST be in + * Unicode, and is not null-terminated. This type of information MUST be + * present in the AV_pair list. + */ + public final static int MSV_AV_NETBIOS_DOMAIN_NAME = 0x0002; + + /** + * Attribute type: The fully qualified domain name (FQDN (1)) of the computer. + * The name MUST be in Unicode, and is not null-terminated. + */ + public final static int MSV_AV_DNS_COMPUTER_NAME = 0x0003; + + /** + * Attribute type: The FQDN of the domain. The name MUST be in Unicode, and is + * not null-terminated. + */ + public final static int MSV_AV_DNS_DOMAIN_NAME = 0x0004; + + /** + * Attribute type: The FQDN of the forest. The name MUST be in Unicode, and is + * not null-terminated. + */ + public final static int MSV_AV_DNS_TREE_NAME = 0x0005; + + /** + * Attribute type: A 32-bit value indicating server or client configuration. + * + *
    • 0x00000001: indicates to the client that the account authentication is + * constrained. + * + *
    • 0x00000002: indicates that the client is providing message integrity in + * the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE. + * + *
    • 0x00000004: indicates that the client is providing a target SPN + * generated from an untrusted source. + **/ + public final static int MSV_AV_FLAGS = 0x0006; + + public static final int MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK = 0x00000002; + + /** + * Attribute type: A FILETIME structure ([MS-DTYP] section 2.3.3) in + * little-endian byte order that contains the server local time. + */ + public final static int MSV_AV_TIMESTAMP = 0x0007; + + /** + * Attribute type: A Single_Host_Data (section 2.2.2.2) structure. The Value + * field contains a platform-specific blob, as well as a MachineID created at + * computer startup to identify the calling machine.<15> + */ + public final static int MSV_AV_SINGLE_HOST = 0x0008; + + /** + * Attribute type: The SPN of the target server. The name MUST be in Unicode + * and is not null-terminated.<16> + */ + public final static int MSV_AV_TARGET_NAME = 0x0009; + + /** + * Attribute type: A channel bindings hash. The Value field contains an MD5 + * hash ([RFC4121] section 4.1.1.2) of a gss_channel_bindings_struct + * ([RFC2744] section 3.11). An all-zero value of the hash is used to indicate + * absence of channel bindings. + */ + public final static int MSV_AV_CHANNEL_BINDINGS = 0x000A; + + /** + * Signature of NTLMSSP blob. + */ + public static final String NTLMSSP = "NTLMSSP"; + + public static final String GSS_RDP_SERVICE_NAME = "TERMSRV"; + + /** + * NTLM message type: NEGOTIATE. + */ + public static final int NEGOTIATE = 0x00000001; + + /** + * NTLM message type: CHALLENGE. + */ + public static final int CHALLENGE = 0x00000002; + + /** + * NTLM message type: NTLMSSP_AUTH. + */ + public static final int NTLMSSP_AUTH = 0x00000003; + + public static final String OID_SPNEGO = "1.3.6.1.5.5.2"; + + public static final String OID_KERBEROS5 = "1.2.840.113554.1.2.2"; + public static final String OID_MSKERBEROS5 = "1.2.840.48018.1.2.2"; + + public static final String OID_KRB5USERTOUSER = "1.2.840.113554.1.2.2.3"; + + public static final String OID_NTLMSSP = "1.3.6.1.4.1.311.2.2.10"; + + /** + * Magic constant used in calculation of Lan Manager response. + */ + public static final String LM_MAGIC = "KGS!@#$%"; + + /** + * Magic constant used in generation of client signing key. + */ + public static final String CLIENT_SIGN_MAGIC = "session key to client-to-server signing key magic constant"; + + /** + * Magic constant used in generation of client sealing key. + */ + public static final String CLIENT_SEAL_MAGIC = "session key to client-to-server sealing key magic constant"; + + public static final String SERVER_SIGN_MAGIC = "session key to server-to-client signing key magic constant"; + public static final String SERVER_SEAL_MAGIC = "session key to server-to-client sealing key magic constant"; + + /** + * In Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008, + * Windows 7, Windows Server 2008 R2, Windows 8, Windows Server 2012, Windows + * 8.1, and Windows Server 2012 R2, the maximum lifetime of challenge is 36 hours. + */ + public static final int CHALLENGE_MAX_LIFETIME = 36 * 60 * 60; +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmState.java new file mode 100755 index 00000000000..248f16654dd --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/NtlmState.java @@ -0,0 +1,887 @@ +// 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 rdpclient.ntlmssp; + +import java.util.Arrays; + +import javax.crypto.Cipher; + +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; + +public class NtlmState implements NtlmConstants { + + /** + * The set of configuration flags (section 2.2.2.5) that specifies the + * negotiated capabilities of the client and server for the current NTLM + * session. + */ + public NegoFlags negotiatedFlags; + + /** + * Target Information extracted from Type2 server response. + */ + public byte[] serverTargetInfo; + + /** + * Challenge extracted from Type2 server response. + */ + public byte[] serverChallenge; + + public byte[] clientChallenge; + + public byte[] keyExchangeKey; + + /** + * A 128-bit (16-byte) session key used to derive ClientSigningKey, + * ClientSealingKey, ServerSealingKey, and ServerSigningKey. + */ + public byte[] exportedSessionKey; + + /** + * The signing key used by the client to sign messages and used by the server + * to verify signed client messages. It is generated after the client is + * authenticated by the server and is not passed over the wire. + */ + public byte[] clientSigningKey; + + /** + * The sealing key used by the client to seal messages and used by the server + * to unseal client messages. It is generated after the client is + * authenticated by the server and is not passed over the wire. + */ + public byte[] clientSealingKey; + + public byte[] encryptedRandomSessionKey; + + public byte[] sessionBaseKey; + + public byte[] responseKeyNT; + + public byte[] ntProofStr1; + + public String domain; + + public String user; + + public String workstation; + + public String password; + + public String serverNetbiosDomainName; + + public String serverNetbiosComputerName; + + public String serverDnsDomainName; + + public String serverDnsComputerName; + + public String serverDnsTreeName; + + public String serverTargetName; + + public byte[] serverTimestamp; + public byte[] clientChallengeTimestamp; + + public byte[] lmChallengeResponse; + + public byte[] ntChallengeResponse; + + public byte[] ntProofStr2; + + public byte[] randomSessionKey; + + public byte[] serverSigningKey; + + public byte[] serverSealingKey; + + public byte[] sendSigningKey; + + public byte[] recvSigningKey; + + public byte[] sendSealingKey; + + public byte[] recvSealingKey; + + public Cipher sendRc4Seal; + + public Cipher recvRc4Seal; + + public byte[] messageIntegrityCheck; + + public byte[] negotiateMessage; + + public byte[] challengeMessage; + + public byte[] authenticateMessage; + + /** + * A 4-byte sequence number. + * + * In the case of connection-oriented authentication, the SeqNum parameter + * MUST start at 0 and is incremented by one for each message sent. The + * receiver expects the first received message to have SeqNum equal to 0, and + * to be one greater for each subsequent message received. If a received + * message does not contain the expected SeqNum, an error MUST be returned to + * the receiving application, and SeqNum is not incremented. + */ + public int sendSeqNum; + public int recvSeqNum; + + public byte[] authenticateTargetInfo; + + public String servicePrincipalName; + + private byte[] channelBindingsHash = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + public byte[] subjectPublicKey; + + public byte[] ntlm_generate_timestamp() { + clientChallengeTimestamp = serverTimestamp; + return clientChallengeTimestamp; + } + + /** + * MD4(UNICODE(Password)) + */ + public byte[] NTOWFv1W(String password) { + return CryptoAlgos.MD4(password.getBytes(RdpConstants.CHARSET_16)); + } + + public void testNTOWFv1W() { + byte[] expected = new byte[] {(byte)0x25, (byte)0xf3, (byte)0x39, (byte)0xc9, (byte)0x86, (byte)0xb5, (byte)0xc2, (byte)0x6f, (byte)0xdc, + (byte)0xab, (byte)0x91, (byte)0x34, (byte)0x93, (byte)0xa2, (byte)0x18, (byte)0x2a}; + byte[] actual = NTOWFv1W("R2Preview!"); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + /** + * HMAC_MD5(NTOWFv1W(Password), UNICODE(ConcatenationOf(UpperCase(User), + * Domain))) + */ + public byte[] NTOWFv2W(String password, String user, String domain) { + return CryptoAlgos.HMAC_MD5(NTOWFv1W(password), (user.toUpperCase() + domain).getBytes(RdpConstants.CHARSET_16)); + } + + public void testNTOWFv2W() { + byte[] expected = new byte[] {(byte)0x5f, (byte)0xcc, (byte)0x4c, (byte)0x48, (byte)0x74, (byte)0x6b, (byte)0x94, (byte)0xce, (byte)0xb7, + (byte)0xae, (byte)0xf1, (byte)0x0d, (byte)0xc9, (byte)0x11, (byte)0x22, (byte)0x8f,}; + byte[] actual = NTOWFv2W("R2Preview!", "Administrator", "workgroup"); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_compute_ntlm_v2_hash() { + return NTOWFv2W(password, user, domain); + } + + public byte[] ntlm_generate_client_challenge() { + if (clientChallenge == null) { + clientChallenge = CryptoAlgos.NONCE(8); + } + return clientChallenge; + } + + public byte[] ntlm_compute_lm_v2_response() { + if (lmChallengeResponse == null) { + + byte[] ntlm_v2_hash = ntlm_compute_ntlm_v2_hash(); + + ntlm_generate_client_challenge(); + + byte[] challenges = CryptoAlgos.concatenationOf(serverChallenge, clientChallenge); + + lmChallengeResponse = CryptoAlgos.concatenationOf(CryptoAlgos.HMAC_MD5(ntlm_v2_hash, challenges), clientChallenge); + } + + return lmChallengeResponse; + } + + public void testComputeLmV2Response() { + serverChallenge = new byte[] {(byte)0x34, (byte)0xe4, (byte)0x4c, (byte)0xd5, (byte)0x75, (byte)0xe3, (byte)0x43, (byte)0x0f}; + clientChallenge = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}; + password = "R2Preview!"; + user = "Administrator"; + domain = "workgroup"; + byte[] expected = new byte[] {(byte)0xa8, (byte)0xae, (byte)0xd7, (byte)0x46, (byte)0x06, (byte)0x32, (byte)0x02, (byte)0x35, (byte)0x1d, + (byte)0x95, (byte)0x99, (byte)0x36, (byte)0x20, (byte)0x36, (byte)0xac, (byte)0xc3, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, + (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08,}; + byte[] actual = ntlm_compute_lm_v2_response(); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] computeNtProofStr(byte[] ntlmV2Hash, byte[] data) { + return CryptoAlgos.HMAC_MD5(ntlmV2Hash, data); + } + + public void testComputeNtProofStr() { + byte[] ntlm_v2_hash = new byte[] {(byte)0x5f, (byte)0xcc, (byte)0x4c, (byte)0x48, (byte)0x74, (byte)0x6b, (byte)0x94, (byte)0xce, (byte)0xb7, + (byte)0xae, (byte)0xf1, (byte)0x0d, (byte)0xc9, (byte)0x11, (byte)0x22, (byte)0x8f,}; + byte[] data = new byte[] {(byte)0x4a, (byte)0x25, (byte)0x50, (byte)0xa5, (byte)0x11, (byte)0x9b, (byte)0xd6, (byte)0x16, (byte)0x01, + (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, + (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, + (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, + (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, + (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, + (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, + (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, + (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, + (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, + (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, + (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, + (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, + (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, + (byte)0x08, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x06, + (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x10, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x26, (byte)0x00, (byte)0x54, (byte)0x00, + (byte)0x45, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x4d, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x56, + (byte)0x00, (byte)0x2f, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x2e, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x36, (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x2e, + (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}; + + byte[] expected = new byte[] {(byte)0x19, (byte)0x4b, (byte)0xeb, (byte)0xad, (byte)0xda, (byte)0x24, (byte)0xd5, (byte)0x96, (byte)0x85, + (byte)0x2e, (byte)0x24, (byte)0x94, (byte)0xd6, (byte)0x4a, (byte)0xb8, (byte)0x5e,}; + byte[] actual = computeNtProofStr(ntlm_v2_hash, data); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] computeSessionBaseKey(byte[] ntlmV2Hash, byte[] ntProofStr) { + return CryptoAlgos.HMAC_MD5(ntlmV2Hash, ntProofStr); + } + + public void testComputeSessionBaseKey() { + byte[] ntlm_v2_hash = new byte[] {(byte)0x5f, (byte)0xcc, (byte)0x4c, (byte)0x48, (byte)0x74, (byte)0x6b, (byte)0x94, (byte)0xce, (byte)0xb7, + (byte)0xae, (byte)0xf1, (byte)0x0d, (byte)0xc9, (byte)0x11, (byte)0x22, (byte)0x8f,}; + byte[] nt_proof_str = new byte[] {(byte)0x19, (byte)0x4b, (byte)0xeb, (byte)0xad, (byte)0xda, (byte)0x24, (byte)0xd5, (byte)0x96, (byte)0x85, + (byte)0x2e, (byte)0x24, (byte)0x94, (byte)0xd6, (byte)0x4a, (byte)0xb8, (byte)0x5e,}; + + byte[] expected = new byte[] {(byte)0x8e, (byte)0x0f, (byte)0xdd, (byte)0x12, (byte)0x4c, (byte)0x3b, (byte)0x11, (byte)0x7f, (byte)0x22, + (byte)0xb9, (byte)0x4b, (byte)0x59, (byte)0x52, (byte)0xbc, (byte)0xa7, (byte)0x18,}; + byte[] actual = computeSessionBaseKey(ntlm_v2_hash, nt_proof_str); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public String generateServicePrincipalName(String serverHostName) { + servicePrincipalName = GSS_RDP_SERVICE_NAME + "/" + serverHostName; + return servicePrincipalName; + } + + public void writeAVPair(ByteBuffer buf, int avPairType, byte[] value) { + if (value != null) { + buf.writeShortLE(avPairType); + buf.writeShortLE(value.length); + buf.writeBytes(value); + } + } + + public void writeAVPair(ByteBuffer buf, int avPairType, String value) { + if (value != null) { + writeAVPair(buf, avPairType, value.getBytes(RdpConstants.CHARSET_16)); + } + } + + public byte[] ntlm_construct_authenticate_target_info() { + ByteBuffer buf = new ByteBuffer(4096); + + writeAVPair(buf, MSV_AV_NETBIOS_DOMAIN_NAME, serverNetbiosDomainName); + + writeAVPair(buf, MSV_AV_NETBIOS_COMPUTER_NAME, serverNetbiosComputerName); + + writeAVPair(buf, MSV_AV_DNS_DOMAIN_NAME, serverDnsDomainName); + + writeAVPair(buf, MSV_AV_DNS_COMPUTER_NAME, serverDnsComputerName); + + writeAVPair(buf, MSV_AV_DNS_TREE_NAME, serverDnsTreeName); + + writeAVPair(buf, MSV_AV_TIMESTAMP, serverTimestamp); + + byte[] flags = new byte[] {(byte)MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK, 0, 0, 0}; + writeAVPair(buf, MSV_AV_FLAGS, flags); + + writeAVPair(buf, MSV_AV_CHANNEL_BINDINGS, channelBindingsHash); + + writeAVPair(buf, MSV_AV_TARGET_NAME, servicePrincipalName); + + writeAVPair(buf, MSV_AV_EOL, ""); + // DEBUG: put EOL 4 times, for compatibility with FreeRDP output + //*DEBUG*/writeAVPair(buf, MSV_AV_EOL, ""); + //*DEBUG*/writeAVPair(buf, MSV_AV_EOL, ""); + //*DEBUG*/writeAVPair(buf, MSV_AV_EOL, ""); + buf.trimAtCursor(); + + authenticateTargetInfo = buf.toByteArray(); + buf.unref(); + + return authenticateTargetInfo; + } + + public void testConstructAuthenticateTargetInfo() { + serverNetbiosDomainName = "WIN-LO419B2LSR0"; + serverNetbiosComputerName = "WIN-LO419B2LSR0"; + serverDnsDomainName = "WIN-LO419B2LSR0"; + serverDnsComputerName = "WIN-LO419B2LSR0"; + serverTimestamp = new byte[] {(byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01,}; + servicePrincipalName = "TERMSRV/192.168.1.3"; + + byte[] expected = new byte[] {(byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, + (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, + (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, + (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, + (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, + (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, + (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, + (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, + (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, + (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0xa0, + (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x06, (byte)0x00, (byte)0x04, (byte)0x00, + (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x26, (byte)0x00, (byte)0x54, (byte)0x00, (byte)0x45, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x4d, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x56, (byte)0x00, (byte)0x2f, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x36, + (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x33, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}; + byte[] actual = ntlm_construct_authenticate_target_info(); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_compute_ntlm_v2_response() { + ByteBuffer buf = new ByteBuffer(4096); + + byte[] ntlm_v2_hash = ntlm_compute_ntlm_v2_hash(); + + buf.writeByte(0x1); // RespType + buf.writeByte(0x1); // HighRespType + buf.writeShort(0); // reserved + buf.writeInt(0); // reserved + buf.writeBytes(clientChallengeTimestamp); // Timestamp, 8 bytes + buf.writeBytes(clientChallenge); // Client nonce, 8 bytes + buf.writeInt(0); // reserved + buf.writeBytes(authenticateTargetInfo); // Target Info block + buf.trimAtCursor(); + byte[] bufBytes = buf.toByteArray(); + buf.unref(); + + ntProofStr2 = computeNtProofStr(ntlm_v2_hash, CryptoAlgos.concatenationOf(serverChallenge, bufBytes)); + + ntChallengeResponse = CryptoAlgos.concatenationOf(ntProofStr2, bufBytes); + + sessionBaseKey = computeSessionBaseKey(ntlm_v2_hash, ntProofStr2); + + return ntChallengeResponse; + } + + public void testComputeNtlmV2Response() { + serverChallenge = new byte[] {(byte)0x4a, (byte)0x25, (byte)0x50, (byte)0xa5, (byte)0x11, (byte)0x9b, (byte)0xd6, (byte)0x16,}; + clientChallenge = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}; + password = "R2Preview!"; + user = "Administrator"; + domain = "workgroup"; + clientChallengeTimestamp = new byte[] {(byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01,}; + authenticateTargetInfo = new byte[] {(byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, + (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, + (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, + (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, + (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, + (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, + (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, + (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, + (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, + (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0xa0, + (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x06, (byte)0x00, (byte)0x04, (byte)0x00, + (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x26, (byte)0x00, (byte)0x54, (byte)0x00, (byte)0x45, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x4d, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x56, (byte)0x00, (byte)0x2f, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x36, + (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x33, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}; + + byte[] expected = new byte[] {(byte)0x19, (byte)0x4b, (byte)0xeb, (byte)0xad, (byte)0xda, (byte)0x24, (byte)0xd5, (byte)0x96, (byte)0x85, + (byte)0x2e, (byte)0x24, (byte)0x94, (byte)0xd6, (byte)0x4a, (byte)0xb8, (byte)0x5e, (byte)0x01, (byte)0x01, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, + (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, + (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, + (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, + (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, + (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, + (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, + (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, + (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, + (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, + (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0xa0, + (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x06, (byte)0x00, (byte)0x04, (byte)0x00, + (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x26, (byte)0x00, (byte)0x54, (byte)0x00, (byte)0x45, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x4d, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x56, (byte)0x00, (byte)0x2f, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x36, + (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x33, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}; + byte[] actual = ntlm_compute_ntlm_v2_response(); + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_key_exchange_key() { + keyExchangeKey = sessionBaseKey; + return keyExchangeKey; + } + + public byte[] ntlm_generate_random_session_key() { + randomSessionKey = CryptoAlgos.NONCE(16); + return randomSessionKey; + } + + public byte[] ntlm_generate_exported_session_key() { + exportedSessionKey = randomSessionKey; + return exportedSessionKey; + } + + public byte[] ntlm_encrypt_random_session_key() { + encryptedRandomSessionKey = CryptoAlgos.RC4K(keyExchangeKey, randomSessionKey); + return encryptedRandomSessionKey; + } + + public void testComputeEncryptedRandomSessionKey() { + keyExchangeKey = new byte[] {(byte)0x8e, (byte)0x0f, (byte)0xdd, (byte)0x12, (byte)0x4c, (byte)0x3b, (byte)0x11, (byte)0x7f, (byte)0x22, + (byte)0xb9, (byte)0x4b, (byte)0x59, (byte)0x52, (byte)0xbc, (byte)0xa7, (byte)0x18,}; + randomSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + + byte[] expected = new byte[] {(byte)0xe4, (byte)0xe9, (byte)0xc2, (byte)0xad, (byte)0x41, (byte)0x02, (byte)0x2f, (byte)0x3c, (byte)0xf9, + (byte)0x4c, (byte)0x72, (byte)0x84, (byte)0xc5, (byte)0x2a, (byte)0x7c, (byte)0x6f,}; + byte[] actual = ntlm_encrypt_random_session_key(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_signing_key(String signMagic) { + return CryptoAlgos.MD5(CryptoAlgos.concatenationOf(exportedSessionKey, signMagic.getBytes(RdpConstants.CHARSET_8), new byte[] {0})); + } + + public void testGenerateSigningKey() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + byte[] expected = new byte[] {(byte)0xf6, (byte)0xae, (byte)0x96, (byte)0xcb, (byte)0x05, (byte)0xe2, (byte)0xab, (byte)0x54, (byte)0xf6, + (byte)0xdd, (byte)0x59, (byte)0xf3, (byte)0xc9, (byte)0xd9, (byte)0xa0, (byte)0x43,}; + byte[] actual = ntlm_generate_signing_key(CLIENT_SIGN_MAGIC); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_client_signing_key() { + clientSigningKey = ntlm_generate_signing_key(CLIENT_SIGN_MAGIC); + return clientSigningKey; + } + + public void testGenerateClientSigningKey() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + byte[] expected = new byte[] {(byte)0xf6, (byte)0xae, (byte)0x96, (byte)0xcb, (byte)0x05, (byte)0xe2, (byte)0xab, (byte)0x54, (byte)0xf6, + (byte)0xdd, (byte)0x59, (byte)0xf3, (byte)0xc9, (byte)0xd9, (byte)0xa0, (byte)0x43,}; + byte[] actual = ntlm_generate_client_signing_key(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_server_signing_key() { + serverSigningKey = ntlm_generate_signing_key(SERVER_SIGN_MAGIC); + return serverSigningKey; + } + + public void testGenerateServerSigningKey() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + byte[] expected = new byte[] {(byte)0xb6, (byte)0x58, (byte)0xc5, (byte)0x98, (byte)0x7a, (byte)0x25, (byte)0xf8, (byte)0x6e, (byte)0xd8, + (byte)0xe5, (byte)0x6c, (byte)0xe9, (byte)0x3e, (byte)0x3c, (byte)0xc0, (byte)0x88,}; + byte[] actual = ntlm_generate_server_signing_key(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_client_sealing_key() { + clientSealingKey = ntlm_generate_signing_key(CLIENT_SEAL_MAGIC); + return clientSealingKey; + } + + public void testGenerateClientSealingKey() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + byte[] expected = new byte[] {(byte)0x58, (byte)0x19, (byte)0x44, (byte)0xc2, (byte)0x7a, (byte)0xc6, (byte)0x34, (byte)0x45, (byte)0xe4, + (byte)0xb8, (byte)0x2b, (byte)0x55, (byte)0xb9, (byte)0x0b, (byte)0x1f, (byte)0xb5,}; + byte[] actual = ntlm_generate_client_sealing_key(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_generate_server_sealing_key() { + serverSealingKey = ntlm_generate_signing_key(SERVER_SEAL_MAGIC); + return serverSealingKey; + } + + public void testGenerateServerSealingKey() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + byte[] expected = new byte[] {(byte)0x92, (byte)0x3a, (byte)0x73, (byte)0x5c, (byte)0x92, (byte)0xa7, (byte)0x04, (byte)0x34, (byte)0xbe, + (byte)0x9a, (byte)0xa2, (byte)0x9f, (byte)0xed, (byte)0xc1, (byte)0xe6, (byte)0x13,}; + byte[] actual = ntlm_generate_server_sealing_key(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public void ntlm_init_rc4_seal_states() { + ntlm_generate_client_signing_key(); + ntlm_generate_server_signing_key(); + ntlm_generate_client_sealing_key(); + ntlm_generate_server_sealing_key(); + + sendSigningKey = clientSigningKey; + recvSigningKey = serverSigningKey; + sendSealingKey = clientSealingKey; + recvSealingKey = serverSealingKey; + + sendRc4Seal = CryptoAlgos.initRC4(sendSealingKey); + recvRc4Seal = CryptoAlgos.initRC4(recvSealingKey); + } + + public byte[] ntlm_compute_message_integrity_check() { + //* DEBUG */System.out.println("ntlm_compute_message_integrity_check: exportedSessionKey:\n" + new ByteBuffer(exportedSessionKey).dump() + "\n"); + //* DEBUG */System.out.println("ntlm_compute_message_integrity_check: negotiateMessage:\n" + new ByteBuffer(negotiateMessage).dump() + "\n"); + //* DEBUG */System.out.println("ntlm_compute_message_integrity_check: challengeMessage:\n" + new ByteBuffer(challengeMessage).dump() + "\n"); + //* DEBUG */System.out.println("ntlm_compute_message_integrity_check: authenticateMessage:\n" + new ByteBuffer(authenticateMessage).dump() + "\n"); + messageIntegrityCheck = CryptoAlgos.HMAC_MD5(exportedSessionKey, CryptoAlgos.concatenationOf(negotiateMessage, challengeMessage, authenticateMessage)); + //* DEBUG */System.out.println("ntlm_compute_message_integrity_check: messageIntegrityCheck:\n" + new ByteBuffer(messageIntegrityCheck).dump() + "\n"); + return messageIntegrityCheck; + } + + public void testComputeMessageIntegrityCheck() { + exportedSessionKey = new byte[] {(byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, + (byte)0x0a, (byte)0x0b, (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f, (byte)0x10,}; + + negotiateMessage = new byte[] {(byte)0x4e, (byte)0x54, (byte)0x4c, (byte)0x4d, (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00, (byte)0x01, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xb7, (byte)0x82, (byte)0x08, (byte)0xe2, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x01, (byte)0xb1, (byte)0x1d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0f,}; + + challengeMessage = new byte[] {(byte)0x4e, (byte)0x54, (byte)0x4c, (byte)0x4d, (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00, (byte)0x02, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x35, (byte)0x82, (byte)0x8a, (byte)0xe2, (byte)0x4a, (byte)0x25, (byte)0x50, (byte)0xa5, (byte)0x11, (byte)0x9b, (byte)0xd6, + (byte)0x16, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x98, (byte)0x00, + (byte)0x98, (byte)0x00, (byte)0x56, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x03, (byte)0xd7, (byte)0x24, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x0f, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, + (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, + (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, + (byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, + (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, + (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, + (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, + (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, + (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, + (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, + (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, + (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, + (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, + (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, + (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, + (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, + (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,}; + + authenticateMessage = new byte[] {(byte)0x4e, (byte)0x54, (byte)0x4c, (byte)0x4d, (byte)0x53, (byte)0x53, (byte)0x50, (byte)0x00, (byte)0x03, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x18, (byte)0x00, (byte)0x18, (byte)0x00, (byte)0x90, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x16, (byte)0x01, (byte)0x16, (byte)0x01, (byte)0xa8, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x12, (byte)0x00, (byte)0x12, + (byte)0x00, (byte)0x58, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x1a, (byte)0x00, (byte)0x1a, (byte)0x00, (byte)0x6a, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x84, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x10, + (byte)0x00, (byte)0x10, (byte)0x00, (byte)0xbe, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x35, (byte)0xb2, (byte)0x88, (byte)0xe2, + (byte)0x06, (byte)0x01, (byte)0xb1, (byte)0x1d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0f, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x77, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x6b, (byte)0x00, (byte)0x67, + (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x75, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x41, (byte)0x00, + (byte)0x64, (byte)0x00, (byte)0x6d, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6e, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x73, + (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x6f, (byte)0x00, + (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6c, + (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x7c, (byte)0xc0, (byte)0xfd, (byte)0x08, (byte)0xc5, (byte)0x14, (byte)0x05, (byte)0x34, + (byte)0xf3, (byte)0x12, (byte)0x9e, (byte)0x3e, (byte)0xa3, (byte)0x09, (byte)0xbc, (byte)0xc6, (byte)0x01, (byte)0x02, (byte)0x03, + (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x19, (byte)0x4b, (byte)0xeb, (byte)0xad, (byte)0xda, (byte)0x24, + (byte)0xd5, (byte)0x96, (byte)0x85, (byte)0x2e, (byte)0x24, (byte)0x94, (byte)0xd6, (byte)0x4a, (byte)0xb8, (byte)0x5e, (byte)0x01, + (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, + (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, + (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, (byte)0x00, + (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x34, + (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x1e, (byte)0x00, (byte)0x57, + (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, (byte)0x00, + (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x4c, + (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x1e, (byte)0x00, + (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x4f, + (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, (byte)0x00, + (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x1e, + (byte)0x00, (byte)0x57, (byte)0x00, (byte)0x49, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x4c, (byte)0x00, + (byte)0x4f, (byte)0x00, (byte)0x34, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x32, + (byte)0x00, (byte)0x4c, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x07, (byte)0x00, + (byte)0x08, (byte)0x00, (byte)0xa0, (byte)0xe8, (byte)0x85, (byte)0x2c, (byte)0xe4, (byte)0xc9, (byte)0xce, (byte)0x01, (byte)0x06, + (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0a, (byte)0x00, (byte)0x10, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x09, (byte)0x00, (byte)0x26, (byte)0x00, (byte)0x54, (byte)0x00, + (byte)0x45, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x4d, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x52, (byte)0x00, (byte)0x56, + (byte)0x00, (byte)0x2f, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x39, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x2e, (byte)0x00, + (byte)0x31, (byte)0x00, (byte)0x36, (byte)0x00, (byte)0x38, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x2e, + (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xe4, (byte)0xe9, (byte)0xc2, + (byte)0xad, (byte)0x41, (byte)0x02, (byte)0x2f, (byte)0x3c, (byte)0xf9, (byte)0x4c, (byte)0x72, (byte)0x84, (byte)0xc5, (byte)0x2a, + (byte)0x7c, (byte)0x6f,}; + + byte[] expected = new byte[] {(byte)0xd9, (byte)0xe9, (byte)0xbc, (byte)0x9b, (byte)0x6f, (byte)0xa5, (byte)0xf9, (byte)0xc8, (byte)0x70, + (byte)0x16, (byte)0x10, (byte)0x20, (byte)0xf8, (byte)0xf1, (byte)0x61, (byte)0x42,}; + byte[] actual = ntlm_compute_message_integrity_check(); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_EncryptMessage(byte[] message) { + byte[] versionBytes = new byte[] {0x01, 0x00, 0x00, 0x00}; // 0x00000001 + // LE + byte[] seqNumBytes = new byte[] {(byte)(sendSeqNum & 0xff), (byte)((sendSeqNum >> 8) & 0xff), (byte)((sendSeqNum >> 16) & 0xff), + (byte)((sendSeqNum >> 24) & 0xff)}; + + byte[] digest = CryptoAlgos.HMAC_MD5(sendSigningKey, CryptoAlgos.concatenationOf(seqNumBytes, message)); + + byte[] encrypted = CryptoAlgos.RC4(sendRc4Seal, message); + + // Encrypt first 8 bytes of digest only + byte[] checksum = CryptoAlgos.RC4(sendRc4Seal, Arrays.copyOf(digest, 8)); + + byte[] signature = CryptoAlgos.concatenationOf(versionBytes, checksum, seqNumBytes); + + sendSeqNum++; + + return CryptoAlgos.concatenationOf(signature, encrypted); + } + + public void testNtlmEncryptMessage() { + sendSigningKey = new byte[] {(byte)0xf6, (byte)0xae, (byte)0x96, (byte)0xcb, (byte)0x05, (byte)0xe2, (byte)0xab, (byte)0x54, (byte)0xf6, + (byte)0xdd, (byte)0x59, (byte)0xf3, (byte)0xc9, (byte)0xd9, (byte)0xa0, (byte)0x43,}; + sendRc4Seal = CryptoAlgos.initRC4(new byte[] {(byte)0x58, (byte)0x19, (byte)0x44, (byte)0xc2, (byte)0x7a, (byte)0xc6, (byte)0x34, (byte)0x45, + (byte)0xe4, (byte)0xb8, (byte)0x2b, (byte)0x55, (byte)0xb9, (byte)0x0b, (byte)0x1f, (byte)0xb5,}); + sendSeqNum = 0; + byte[] serverPublicKey = new byte[] {(byte)0x30, (byte)0x82, (byte)0x01, (byte)0x0a, (byte)0x02, (byte)0x82, (byte)0x01, (byte)0x01, (byte)0x00, + (byte)0xa8, (byte)0x56, (byte)0x65, (byte)0xd3, (byte)0xce, (byte)0x8a, (byte)0x54, (byte)0x4d, (byte)0x9d, (byte)0xb0, (byte)0x84, + (byte)0x31, (byte)0x19, (byte)0x71, (byte)0x7f, (byte)0xdd, (byte)0x42, (byte)0xfb, (byte)0x2a, (byte)0x7a, (byte)0x72, (byte)0x13, + (byte)0xa1, (byte)0xb9, (byte)0x72, (byte)0xbb, (byte)0xd3, (byte)0x08, (byte)0xad, (byte)0x7d, (byte)0x6c, (byte)0x15, (byte)0x65, + (byte)0x03, (byte)0xd1, (byte)0xc4, (byte)0x54, (byte)0xc5, (byte)0x33, (byte)0x6b, (byte)0x7d, (byte)0x69, (byte)0x89, (byte)0x5e, + (byte)0xfe, (byte)0xe0, (byte)0x01, (byte)0xc0, (byte)0x7e, (byte)0x9b, (byte)0xcb, (byte)0x5d, (byte)0x65, (byte)0x36, (byte)0xcd, + (byte)0x77, (byte)0x5d, (byte)0xf3, (byte)0x7a, (byte)0x5b, (byte)0x29, (byte)0x44, (byte)0x72, (byte)0xd5, (byte)0x38, (byte)0xe2, + (byte)0xcf, (byte)0xb1, (byte)0xc7, (byte)0x78, (byte)0x9b, (byte)0x58, (byte)0xb9, (byte)0x17, (byte)0x7c, (byte)0xb7, (byte)0xd6, + (byte)0xc7, (byte)0xc7, (byte)0xbf, (byte)0x90, (byte)0x4e, (byte)0x7c, (byte)0x39, (byte)0x93, (byte)0xcb, (byte)0x2e, (byte)0xe0, + (byte)0xc2, (byte)0x33, (byte)0x2d, (byte)0xa5, (byte)0x7e, (byte)0xe0, (byte)0x7b, (byte)0xb6, (byte)0xf9, (byte)0x91, (byte)0x32, + (byte)0xb7, (byte)0xd4, (byte)0x85, (byte)0xb7, (byte)0x35, (byte)0x2d, (byte)0x2b, (byte)0x00, (byte)0x6d, (byte)0xf8, (byte)0xea, + (byte)0x8c, (byte)0x97, (byte)0x5f, (byte)0x51, (byte)0x1d, (byte)0x68, (byte)0x04, (byte)0x3c, (byte)0x79, (byte)0x14, (byte)0x71, + (byte)0xa7, (byte)0xc7, (byte)0xd7, (byte)0x70, (byte)0x7a, (byte)0xe0, (byte)0xba, (byte)0x12, (byte)0x69, (byte)0xc8, (byte)0xd3, + (byte)0xd9, (byte)0x4e, (byte)0xab, (byte)0x51, (byte)0x47, (byte)0xa3, (byte)0xec, (byte)0x99, (byte)0xd4, (byte)0x88, (byte)0xca, + (byte)0xda, (byte)0xc2, (byte)0x7f, (byte)0x79, (byte)0x4b, (byte)0x66, (byte)0xed, (byte)0x87, (byte)0xbe, (byte)0xc2, (byte)0x5f, + (byte)0xea, (byte)0xcf, (byte)0xe1, (byte)0xb5, (byte)0xf0, (byte)0x3d, (byte)0x9b, (byte)0xf2, (byte)0x19, (byte)0xc3, (byte)0xe0, + (byte)0xe1, (byte)0x7a, (byte)0x45, (byte)0x71, (byte)0x12, (byte)0x3d, (byte)0x72, (byte)0x1d, (byte)0x6f, (byte)0x2b, (byte)0x1c, + (byte)0x46, (byte)0x68, (byte)0xc0, (byte)0x8f, (byte)0x4f, (byte)0xce, (byte)0x3a, (byte)0xc5, (byte)0xcd, (byte)0x22, (byte)0x65, + (byte)0x2d, (byte)0x43, (byte)0xb0, (byte)0x5c, (byte)0xdd, (byte)0x89, (byte)0xae, (byte)0xbe, (byte)0x70, (byte)0x59, (byte)0x5e, + (byte)0x0c, (byte)0xbd, (byte)0xf5, (byte)0x46, (byte)0x82, (byte)0x1e, (byte)0xe4, (byte)0x86, (byte)0x95, (byte)0x7b, (byte)0x60, + (byte)0xae, (byte)0x45, (byte)0x50, (byte)0xc2, (byte)0x54, (byte)0x08, (byte)0x49, (byte)0x9a, (byte)0x9e, (byte)0xfb, (byte)0xb2, + (byte)0xb6, (byte)0x78, (byte)0xe5, (byte)0x2f, (byte)0x9c, (byte)0x5a, (byte)0xd0, (byte)0x8a, (byte)0x03, (byte)0x77, (byte)0x68, + (byte)0x30, (byte)0x93, (byte)0x78, (byte)0x6d, (byte)0x90, (byte)0x6d, (byte)0x50, (byte)0xfa, (byte)0xa7, (byte)0x65, (byte)0xfe, + (byte)0x59, (byte)0x33, (byte)0x27, (byte)0x4e, (byte)0x4b, (byte)0xf8, (byte)0x38, (byte)0x44, (byte)0x3a, (byte)0x12, (byte)0xf4, + (byte)0x07, (byte)0xa0, (byte)0x8d, (byte)0x02, (byte)0x03, (byte)0x01, (byte)0x00, (byte)0x01,}; + + byte[] expected = new byte[] {(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x72, (byte)0x76, (byte)0x1e, (byte)0x57, (byte)0x49, + (byte)0xb5, (byte)0x0f, (byte)0xad, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x15, (byte)0xf7, (byte)0xf2, (byte)0x54, + (byte)0xda, (byte)0xa9, (byte)0xe5, (byte)0xad, (byte)0x85, (byte)0x04, (byte)0x67, (byte)0x4d, (byte)0x0b, (byte)0xcb, (byte)0xf9, + (byte)0xb1, (byte)0xf8, (byte)0x02, (byte)0x8a, (byte)0x77, (byte)0xc2, (byte)0x63, (byte)0xab, (byte)0xd5, (byte)0x74, (byte)0x23, + (byte)0x9f, (byte)0x9d, (byte)0x5d, (byte)0x1f, (byte)0xd3, (byte)0xb3, (byte)0xa0, (byte)0xac, (byte)0x16, (byte)0x8a, (byte)0x4b, + (byte)0x08, (byte)0xf5, (byte)0x47, (byte)0x70, (byte)0x58, (byte)0x10, (byte)0xb4, (byte)0xe7, (byte)0x87, (byte)0xb3, (byte)0x4b, + (byte)0xc9, (byte)0xa2, (byte)0xd5, (byte)0xd1, (byte)0xca, (byte)0x0f, (byte)0xd4, (byte)0xe3, (byte)0x8d, (byte)0x76, (byte)0x5a, + (byte)0x60, (byte)0x28, (byte)0xf8, (byte)0x06, (byte)0x5d, (byte)0xe4, (byte)0x7e, (byte)0x21, (byte)0xc8, (byte)0xbb, (byte)0xac, + (byte)0xe5, (byte)0x79, (byte)0x85, (byte)0x30, (byte)0x9b, (byte)0x88, (byte)0x13, (byte)0x2f, (byte)0x8f, (byte)0xfc, (byte)0x04, + (byte)0x52, (byte)0xfe, (byte)0x87, (byte)0x94, (byte)0xcf, (byte)0xcb, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x6f, (byte)0xdd, + (byte)0xee, (byte)0x57, (byte)0xa5, (byte)0xe4, (byte)0x4d, (byte)0x0e, (byte)0x5c, (byte)0x3d, (byte)0x0b, (byte)0x63, (byte)0x1f, + (byte)0xf6, (byte)0x3d, (byte)0x1b, (byte)0xae, (byte)0x5a, (byte)0xf6, (byte)0x42, (byte)0x2a, (byte)0x46, (byte)0xfa, (byte)0x42, + (byte)0x71, (byte)0x67, (byte)0x46, (byte)0x02, (byte)0x71, (byte)0xea, (byte)0x51, (byte)0x98, (byte)0xf7, (byte)0xd4, (byte)0x43, + (byte)0xbf, (byte)0x8e, (byte)0xe8, (byte)0x3c, (byte)0xc8, (byte)0xfa, (byte)0x79, (byte)0x9d, (byte)0x8c, (byte)0xfc, (byte)0xc2, + (byte)0x42, (byte)0xc9, (byte)0xbb, (byte)0xd0, (byte)0xab, (byte)0x81, (byte)0xc4, (byte)0x53, (byte)0xfd, (byte)0x41, (byte)0xda, + (byte)0xab, (byte)0x0f, (byte)0x25, (byte)0x79, (byte)0x5f, (byte)0xbd, (byte)0xa3, (byte)0x8c, (byte)0xd3, (byte)0xf5, (byte)0x1b, + (byte)0xab, (byte)0x20, (byte)0xd1, (byte)0xf4, (byte)0xd8, (byte)0x81, (byte)0x9c, (byte)0x18, (byte)0x4a, (byte)0xa4, (byte)0x77, + (byte)0xee, (byte)0xe1, (byte)0x51, (byte)0xee, (byte)0x2a, (byte)0xc1, (byte)0x94, (byte)0x37, (byte)0xc5, (byte)0x06, (byte)0x7a, + (byte)0x3f, (byte)0x0f, (byte)0x25, (byte)0x5b, (byte)0x4e, (byte)0x6a, (byte)0xdc, (byte)0x0b, (byte)0x62, (byte)0x6f, (byte)0x12, + (byte)0x83, (byte)0x03, (byte)0xae, (byte)0x4e, (byte)0xce, (byte)0x2b, (byte)0x6e, (byte)0xd4, (byte)0xd5, (byte)0x23, (byte)0x27, + (byte)0xf6, (byte)0xa6, (byte)0x38, (byte)0x67, (byte)0xec, (byte)0x95, (byte)0x82, (byte)0xc6, (byte)0xba, (byte)0xd4, (byte)0xf6, + (byte)0xe6, (byte)0x22, (byte)0x7d, (byte)0xb9, (byte)0xe4, (byte)0x81, (byte)0x97, (byte)0x24, (byte)0xff, (byte)0x40, (byte)0xb2, + (byte)0x42, (byte)0x3c, (byte)0x11, (byte)0x24, (byte)0xd0, (byte)0x3a, (byte)0x96, (byte)0xd9, (byte)0xc1, (byte)0x13, (byte)0xd6, + (byte)0x62, (byte)0x45, (byte)0x21, (byte)0x60, (byte)0x5b, (byte)0x7b, (byte)0x2b, (byte)0x62, (byte)0x44, (byte)0xf7, (byte)0x40, + (byte)0x93, (byte)0x29, (byte)0x5b, (byte)0x44, (byte)0xb7, (byte)0xda, (byte)0x9c, (byte)0xa6, (byte)0xa9, (byte)0x3b, (byte)0xe1, + (byte)0x3b, (byte)0x9d, (byte)0x31, (byte)0xf2, (byte)0x21, (byte)0x53, (byte)0x0f, (byte)0xb3, (byte)0x70, (byte)0x55, (byte)0x84, + (byte)0x2c, (byte)0xb4,}; + byte[] actual = ntlm_EncryptMessage(serverPublicKey); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public byte[] ntlm_DecryptMessage(byte[] wrappedMssage) { + + byte[] versionBytes = new byte[] {0x01, 0x00, 0x00, 0x00}; // 0x00000001 + // LE + byte[] seqNumBytes = new byte[] {(byte)(recvSeqNum & 0xff), (byte)((recvSeqNum >> 8) & 0xff), (byte)((recvSeqNum >> 16) & 0xff), + (byte)((recvSeqNum >> 24) & 0xff)}; + + // Unwrap message + byte[] actualSignature = Arrays.copyOf(wrappedMssage, 16); + byte[] encryptedMessage = Arrays.copyOfRange(wrappedMssage, 16, wrappedMssage.length); + + // Decrypt message + byte[] decryptedMessage = CryptoAlgos.RC4(recvRc4Seal, encryptedMessage); + + // Compare actual signature with expected signature + byte[] digest = CryptoAlgos.HMAC_MD5(recvSigningKey, CryptoAlgos.concatenationOf(seqNumBytes, decryptedMessage)); + + // Encrypt first 8 bytes of digest only + byte[] checksum = CryptoAlgos.RC4(recvRc4Seal, Arrays.copyOf(digest, 8)); + + byte[] expectedSignature = CryptoAlgos.concatenationOf(versionBytes, checksum, seqNumBytes); + + if (!Arrays.equals(expectedSignature, actualSignature)) + throw new RuntimeException("Unexpected signature of message:\nExpected signature: " + new ByteBuffer(expectedSignature).toPlainHexString() + + "\n Actual signature: " + new ByteBuffer(actualSignature).toPlainHexString()); + + recvSeqNum++; + + return decryptedMessage; + } + + public void testNtlmDecryptMessage() { + recvSigningKey = new byte[] {(byte)0xb6, (byte)0x58, (byte)0xc5, (byte)0x98, (byte)0x7a, (byte)0x25, (byte)0xf8, (byte)0x6e, (byte)0xd8, + (byte)0xe5, (byte)0x6c, (byte)0xe9, (byte)0x3e, (byte)0x3c, (byte)0xc0, (byte)0x88,}; + recvRc4Seal = CryptoAlgos.initRC4(new byte[] {(byte)0x92, (byte)0x3a, (byte)0x73, (byte)0x5c, (byte)0x92, (byte)0xa7, (byte)0x04, (byte)0x34, + (byte)0xbe, (byte)0x9a, (byte)0xa2, (byte)0x9f, (byte)0xed, (byte)0xc1, (byte)0xe6, (byte)0x13,}); + recvSeqNum = 0; + byte[] encryptedMessage = new byte[] {(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x25, (byte)0xf8, (byte)0x2d, (byte)0x1e, (byte)0x4e, + (byte)0x6a, (byte)0xec, (byte)0x4f, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x03, (byte)0x12, (byte)0xdd, (byte)0xea, + (byte)0x47, (byte)0xb3, (byte)0xff, (byte)0xe1, (byte)0x66, (byte)0x08, (byte)0xf6, (byte)0x6b, (byte)0xa0, (byte)0x62, (byte)0x42, + (byte)0x67, (byte)0xbf, (byte)0x3d, (byte)0x59, (byte)0x60, (byte)0xef, (byte)0x52, (byte)0xb0, (byte)0x26, (byte)0x95, (byte)0xed, + (byte)0x84, (byte)0x48, (byte)0x44, (byte)0xbb, (byte)0x8d, (byte)0x65, (byte)0xcf, (byte)0xe4, (byte)0x8e, (byte)0x6f, (byte)0x69, + (byte)0xae, (byte)0xed, (byte)0x44, (byte)0xbb, (byte)0x49, (byte)0x1d, (byte)0x2a, (byte)0x40, (byte)0x29, (byte)0x2b, (byte)0x13, + (byte)0x42, (byte)0x1c, (byte)0xeb, (byte)0xb1, (byte)0x6c, (byte)0x8a, (byte)0x3b, (byte)0x80, (byte)0xd1, (byte)0x70, (byte)0xfd, + (byte)0xdd, (byte)0x79, (byte)0xe4, (byte)0x93, (byte)0x0b, (byte)0x47, (byte)0xbd, (byte)0x3a, (byte)0x7e, (byte)0x31, (byte)0x66, + (byte)0x4b, (byte)0x65, (byte)0x8d, (byte)0x5c, (byte)0x2a, (byte)0xcd, (byte)0xc2, (byte)0x09, (byte)0x7a, (byte)0x3b, (byte)0xb2, + (byte)0xfd, (byte)0x09, (byte)0x52, (byte)0x09, (byte)0x47, (byte)0x05, (byte)0xa4, (byte)0x6f, (byte)0x32, (byte)0xd1, (byte)0x76, + (byte)0xb2, (byte)0xd4, (byte)0x59, (byte)0xe0, (byte)0x85, (byte)0xf1, (byte)0x36, (byte)0x7d, (byte)0x76, (byte)0x50, (byte)0x21, + (byte)0x0e, (byte)0x20, (byte)0x22, (byte)0x83, (byte)0x1a, (byte)0x08, (byte)0xc0, (byte)0x85, (byte)0x5d, (byte)0x4f, (byte)0x5c, + (byte)0x77, (byte)0x68, (byte)0x32, (byte)0x95, (byte)0xa9, (byte)0xa2, (byte)0x59, (byte)0x69, (byte)0xea, (byte)0x19, (byte)0x34, + (byte)0x08, (byte)0xed, (byte)0x76, (byte)0xa3, (byte)0x58, (byte)0x37, (byte)0xf2, (byte)0x0a, (byte)0x0c, (byte)0xba, (byte)0x4d, + (byte)0xbb, (byte)0x6f, (byte)0x82, (byte)0x94, (byte)0xd3, (byte)0x87, (byte)0xde, (byte)0xc9, (byte)0x8f, (byte)0xef, (byte)0x34, + (byte)0x2d, (byte)0x8f, (byte)0xd0, (byte)0x0c, (byte)0x91, (byte)0x59, (byte)0xfd, (byte)0xea, (byte)0x6b, (byte)0xcb, (byte)0xbd, + (byte)0xa2, (byte)0x20, (byte)0xed, (byte)0xb9, (byte)0x76, (byte)0xd3, (byte)0x64, (byte)0x1b, (byte)0xb3, (byte)0x3b, (byte)0xf5, + (byte)0x9b, (byte)0x61, (byte)0xd7, (byte)0xab, (byte)0x26, (byte)0x9b, (byte)0x0d, (byte)0xa0, (byte)0xea, (byte)0xbf, (byte)0xad, + (byte)0x2c, (byte)0xad, (byte)0x63, (byte)0x65, (byte)0xc6, (byte)0x70, (byte)0xc4, (byte)0xe5, (byte)0x8d, (byte)0x40, (byte)0xaa, + (byte)0x08, (byte)0x45, (byte)0x66, (byte)0xe2, (byte)0x4d, (byte)0xc9, (byte)0x46, (byte)0x00, (byte)0x33, (byte)0x43, (byte)0xe0, + (byte)0xba, (byte)0xd6, (byte)0x80, (byte)0x29, (byte)0x21, (byte)0x5e, (byte)0xd1, (byte)0x9a, (byte)0xbc, (byte)0x44, (byte)0xfa, + (byte)0x4d, (byte)0x46, (byte)0xf9, (byte)0x25, (byte)0x80, (byte)0x40, (byte)0xb5, (byte)0x27, (byte)0xdd, (byte)0xc5, (byte)0x02, + (byte)0xf8, (byte)0xa4, (byte)0x9a, (byte)0xcb, (byte)0xcf, (byte)0x3f, (byte)0xef, (byte)0xc7, (byte)0xcd, (byte)0x71, (byte)0x45, + (byte)0xa5, (byte)0x35, (byte)0xb1, (byte)0x21, (byte)0x14, (byte)0x39, (byte)0x57, (byte)0xf8, (byte)0x0a, (byte)0x24, (byte)0x98, + (byte)0xea, (byte)0x15, (byte)0xe1, (byte)0xe3, (byte)0xcb, (byte)0x9d, (byte)0xf2, (byte)0x4e, (byte)0xef, (byte)0x89, (byte)0x97, + (byte)0xc0, (byte)0xb2, (byte)0x96, (byte)0x9a, (byte)0x1e, (byte)0xad, (byte)0xd0, (byte)0x9a, (byte)0x99, (byte)0x62, (byte)0x9f, + (byte)0x13, (byte)0x2e,}; + + byte[] expected = new byte[] { + // First byte is increased by 1 + (byte)0x31, (byte)0x82, (byte)0x01, (byte)0x0a, (byte)0x02, (byte)0x82, (byte)0x01, (byte)0x01, (byte)0x00, (byte)0xa8, (byte)0x56, + (byte)0x65, (byte)0xd3, (byte)0xce, (byte)0x8a, (byte)0x54, (byte)0x4d, (byte)0x9d, (byte)0xb0, (byte)0x84, (byte)0x31, (byte)0x19, + (byte)0x71, (byte)0x7f, (byte)0xdd, (byte)0x42, (byte)0xfb, (byte)0x2a, (byte)0x7a, (byte)0x72, (byte)0x13, (byte)0xa1, (byte)0xb9, + (byte)0x72, (byte)0xbb, (byte)0xd3, (byte)0x08, (byte)0xad, (byte)0x7d, (byte)0x6c, (byte)0x15, (byte)0x65, (byte)0x03, (byte)0xd1, + (byte)0xc4, (byte)0x54, (byte)0xc5, (byte)0x33, (byte)0x6b, (byte)0x7d, (byte)0x69, (byte)0x89, (byte)0x5e, (byte)0xfe, (byte)0xe0, + (byte)0x01, (byte)0xc0, (byte)0x7e, (byte)0x9b, (byte)0xcb, (byte)0x5d, (byte)0x65, (byte)0x36, (byte)0xcd, (byte)0x77, (byte)0x5d, + (byte)0xf3, (byte)0x7a, (byte)0x5b, (byte)0x29, (byte)0x44, (byte)0x72, (byte)0xd5, (byte)0x38, (byte)0xe2, (byte)0xcf, (byte)0xb1, + (byte)0xc7, (byte)0x78, (byte)0x9b, (byte)0x58, (byte)0xb9, (byte)0x17, (byte)0x7c, (byte)0xb7, (byte)0xd6, (byte)0xc7, (byte)0xc7, + (byte)0xbf, (byte)0x90, (byte)0x4e, (byte)0x7c, (byte)0x39, (byte)0x93, (byte)0xcb, (byte)0x2e, (byte)0xe0, (byte)0xc2, (byte)0x33, + (byte)0x2d, (byte)0xa5, (byte)0x7e, (byte)0xe0, (byte)0x7b, (byte)0xb6, (byte)0xf9, (byte)0x91, (byte)0x32, (byte)0xb7, (byte)0xd4, + (byte)0x85, (byte)0xb7, (byte)0x35, (byte)0x2d, (byte)0x2b, (byte)0x00, (byte)0x6d, (byte)0xf8, (byte)0xea, (byte)0x8c, (byte)0x97, + (byte)0x5f, (byte)0x51, (byte)0x1d, (byte)0x68, (byte)0x04, (byte)0x3c, (byte)0x79, (byte)0x14, (byte)0x71, (byte)0xa7, (byte)0xc7, + (byte)0xd7, (byte)0x70, (byte)0x7a, (byte)0xe0, (byte)0xba, (byte)0x12, (byte)0x69, (byte)0xc8, (byte)0xd3, (byte)0xd9, (byte)0x4e, + (byte)0xab, (byte)0x51, (byte)0x47, (byte)0xa3, (byte)0xec, (byte)0x99, (byte)0xd4, (byte)0x88, (byte)0xca, (byte)0xda, (byte)0xc2, + (byte)0x7f, (byte)0x79, (byte)0x4b, (byte)0x66, (byte)0xed, (byte)0x87, (byte)0xbe, (byte)0xc2, (byte)0x5f, (byte)0xea, (byte)0xcf, + (byte)0xe1, (byte)0xb5, (byte)0xf0, (byte)0x3d, (byte)0x9b, (byte)0xf2, (byte)0x19, (byte)0xc3, (byte)0xe0, (byte)0xe1, (byte)0x7a, + (byte)0x45, (byte)0x71, (byte)0x12, (byte)0x3d, (byte)0x72, (byte)0x1d, (byte)0x6f, (byte)0x2b, (byte)0x1c, (byte)0x46, (byte)0x68, + (byte)0xc0, (byte)0x8f, (byte)0x4f, (byte)0xce, (byte)0x3a, (byte)0xc5, (byte)0xcd, (byte)0x22, (byte)0x65, (byte)0x2d, (byte)0x43, + (byte)0xb0, (byte)0x5c, (byte)0xdd, (byte)0x89, (byte)0xae, (byte)0xbe, (byte)0x70, (byte)0x59, (byte)0x5e, (byte)0x0c, (byte)0xbd, + (byte)0xf5, (byte)0x46, (byte)0x82, (byte)0x1e, (byte)0xe4, (byte)0x86, (byte)0x95, (byte)0x7b, (byte)0x60, (byte)0xae, (byte)0x45, + (byte)0x50, (byte)0xc2, (byte)0x54, (byte)0x08, (byte)0x49, (byte)0x9a, (byte)0x9e, (byte)0xfb, (byte)0xb2, (byte)0xb6, (byte)0x78, + (byte)0xe5, (byte)0x2f, (byte)0x9c, (byte)0x5a, (byte)0xd0, (byte)0x8a, (byte)0x03, (byte)0x77, (byte)0x68, (byte)0x30, (byte)0x93, + (byte)0x78, (byte)0x6d, (byte)0x90, (byte)0x6d, (byte)0x50, (byte)0xfa, (byte)0xa7, (byte)0x65, (byte)0xfe, (byte)0x59, (byte)0x33, + (byte)0x27, (byte)0x4e, (byte)0x4b, (byte)0xf8, (byte)0x38, (byte)0x44, (byte)0x3a, (byte)0x12, (byte)0xf4, (byte)0x07, (byte)0xa0, + (byte)0x8d, (byte)0x02, (byte)0x03, (byte)0x01, (byte)0x00, (byte)0x01,}; + byte[] actual = ntlm_DecryptMessage(encryptedMessage); + + if (!Arrays.equals(expected, actual)) + throw new RuntimeException("Incorrect result.\nExpected:\n" + new ByteBuffer(expected).toPlainHexString() + "\n actual:\n" + + new ByteBuffer(actual).toPlainHexString() + "."); + } + + public static void main(String args[]) { + CryptoAlgos.callAll(new NtlmState()); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/SecBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/SecBuffer.java new file mode 100755 index 00000000000..6aeb39b5348 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/SecBuffer.java @@ -0,0 +1,21 @@ +// 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 rdpclient.ntlmssp; + +public class SecBuffer { + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspChallenge.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspChallenge.java new file mode 100755 index 00000000000..eaac62b92d9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspChallenge.java @@ -0,0 +1,293 @@ +// 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 rdpclient.ntlmssp; + +import java.util.Arrays; + +import rdpclient.ntlmssp.asn1.NegoItem; +import rdpclient.ntlmssp.asn1.TSRequest; +import rdpclient.rdp.RdpConstants; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc236642.aspx + */ +public class ServerNtlmsspChallenge extends OneTimeSwitch implements NtlmConstants { + + protected NtlmState ntlmState; + + public ServerNtlmsspChallenge(String id, NtlmState state) { + super(id); + ntlmState = state; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Extract server challenge, extract server flags. + + // Parse TSRequest in BER format + TSRequest request = new TSRequest("TSRequest"); + request.readTag(buf); + + ByteBuffer negoToken = ((NegoItem)request.negoTokens.tags[0]).negoToken.value; + ntlmState.challengeMessage = negoToken.toByteArray(); // Store message for MIC calculation in AUTH message + + parseNtlmChallenge(negoToken); + + negoToken.unref(); + buf.unref(); + switchOff(); + } + + public void parseNtlmChallenge(ByteBuffer buf) { + + // Signature: "NTLMSSP\0" + String signature = buf.readVariableString(RdpConstants.CHARSET_8); + if (!signature.equals(NTLMSSP)) + throw new RuntimeException("Unexpected NTLM message singature: \"" + signature + "\". Expected signature: \"" + NTLMSSP + "\". Data: " + buf + "."); + + // MessageType (CHALLENGE) + int messageType = buf.readSignedIntLE(); + if (messageType != NtlmConstants.CHALLENGE) + throw new RuntimeException("Unexpected NTLM message type: " + messageType + ". Expected type: CHALLENGE (" + NtlmConstants.CHALLENGE + "). Data: " + buf + + "."); + + // TargetName + ntlmState.serverTargetName = readStringByDescription(buf); + + // NegotiateFlags + ntlmState.negotiatedFlags = new NegoFlags(buf.readSignedIntLE()); + if (verbose) + System.out.println("[" + this + "] INFO: Server negotiate flags: " + ntlmState.negotiatedFlags + "."); + + // ServerChallenge + ByteBuffer challenge = buf.readBytes(8); + ntlmState.serverChallenge = challenge.toByteArray(); + if (verbose) + System.out.println("[" + this + "] INFO: Server challenge: " + challenge + "."); + challenge.unref(); + + // Reserved/context + buf.skipBytes(8); + + // TargetInfo + ByteBuffer targetInfo = readBlockByDescription(buf); + + // Store raw target info block for Type3 message + ntlmState.serverTargetInfo = targetInfo.toByteArray(); + + // Parse target info block + parseTargetInfo(targetInfo); + targetInfo.unref(); + + // OS Version, NTLM revision, 8 bytes, Optional. Ignore it. + + // Ignore rest of buffer with allocated blocks + + buf.unref(); + } + + public void parseTargetInfo(ByteBuffer buf) { + // Parse attribute list + + while (buf.remainderLength() > 0) { + int type = buf.readUnsignedShortLE(); + int length = buf.readUnsignedShortLE(); + + if (type == MSV_AV_EOL) + // End of list + break; + + ByteBuffer data = buf.readBytes(length); + parseAttribute(data, type, length); + data.unref(); + } + } + + public void parseAttribute(ByteBuffer buf, int type, int length) { + switch (type) { + case MSV_AV_NETBIOS_DOMAIN_NAME: + ntlmState.serverNetbiosDomainName = buf.readString(length, RdpConstants.CHARSET_16); + break; + case MSV_AV_NETBIOS_COMPUTER_NAME: + ntlmState.serverNetbiosComputerName = buf.readString(length, RdpConstants.CHARSET_16); + break; + case MSV_AV_DNS_DOMAIN_NAME: + ntlmState.serverDnsDomainName = buf.readString(length, RdpConstants.CHARSET_16); + break; + case MSV_AV_DNS_COMPUTER_NAME: + ntlmState.serverDnsComputerName = buf.readString(length, RdpConstants.CHARSET_16); + break; + case MSV_AV_DNS_TREE_NAME: + ntlmState.serverDnsTreeName = buf.readString(length, RdpConstants.CHARSET_16); + break; + + case MSV_AV_TIMESTAMP: + ByteBuffer tmp = buf.readBytes(length); + ntlmState.serverTimestamp = tmp.toByteArray(); + //*DEBUG*/System.out.println("Server timestamp: "+tmp.toPlainHexString()); + tmp.unref(); + break; + + default: + // Ignore + //throw new RuntimeException("[" + this + "] ERROR: Unknown NTLM target info attribute: " + type + ". Data: " + buf + "."); + + } + + } + + /** + * Read NTLM wide string, by it description. Buffer offset must point to + * beginning of NTLM message signature. + * + * @param buf + * buffer with cursor pointing to description + * @return + */ + public static String readStringByDescription(ByteBuffer buf) { + ByteBuffer block = readBlockByDescription(buf); + String value = block.readString(block.length, RdpConstants.CHARSET_16); + block.unref(); + + return value; + } + + public static ByteBuffer readBlockByDescription(ByteBuffer buf) { + int blockLength = buf.readUnsignedShortLE(); // In bytes + int allocatedSpace = buf.readUnsignedShortLE(); + int offset = buf.readSignedIntLE(); + + if (allocatedSpace < blockLength) + blockLength = allocatedSpace; + + if (offset > buf.length || offset < 0 || offset + allocatedSpace > buf.length) + throw new RuntimeException("ERROR: NTLM block is too long. Allocated space: " + allocatedSpace + ", block offset: " + offset + ", data: " + + buf + "."); + + // Move cursor to position of allocated block, starting from beginning of + // this buffer + int storedCursor = buf.cursor; + buf.cursor = offset; + + // Read string + ByteBuffer value = buf.readBytes(blockLength); + + // Restore cursor + buf.cursor = storedCursor; + + return value; + } + + /** + * Example. + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x30, (byte) 0x82, 0x01, 0x02, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 258 bytes + (byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes + 0x02, 0x01, 0x03, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes, Version: 0x3 + (byte) 0xa1, (byte) 0x81, (byte) 0xfa, // TAG: [1] (constructed) LEN: 250 bytes + 0x30, (byte) 0x81, (byte) 0xf7, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 247 bytes + 0x30, (byte) 0x81, (byte) 0xf4, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 244 bytes + (byte) 0xa0, (byte) 0x81, (byte) 0xf1, // TAG: [0] (constructed) LEN: 241 bytes + 0x04, (byte) 0x81, (byte) 0xee, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 238 bytes + + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, // "NTLMSSP\0" + + 0x02, 0x00, 0x00, 0x00, // MessageType (CHALLENGE) + 0x1e, 0x00, 0x1e, 0x00, 0x38, 0x00, 0x00, 0x00, // TargetName (length: 30, allocated space: 30, offset: 56) + 0x35, (byte) 0x82, (byte) 0x8a, (byte) 0xe2, // NegotiateFlags + 0x52, (byte) 0xbe, (byte) 0x83, (byte) 0xd1, (byte) 0xf8, (byte) 0x80, 0x16, 0x6a, // ServerChallenge + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + (byte) 0x98, 0x00, (byte) 0x98, 0x00, 0x56, 0x00, 0x00, 0x00, // TargetInfo (length: 152, allocated space: 152, offset: 86) + 0x06, 0x03, (byte) 0xd7, 0x24, 0x00, 0x00, 0x00, 0x0f, // Version (6.3, build 9431) , NTLM current revision: 15 + + + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // Target name value: "WIN-LO419B2LSR0" + + // Target Info value: + + // Attribute list + + 0x02, 0x00, // Item Type: NetBIOS domain name (0x0002, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x01, 0x00, // Item Type: NetBIOS computer name (0x0001, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x04, 0x00, // Item Type: DNS domain name (0x0004, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x03, 0x00, // Item Type: DNS computer name (0x0003, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x07, 0x00, // Item Type: Timestamp (0x0007, LE) + 0x08, 0x00, // Item Length: 8 (LE) + (byte) 0x99, 0x4f, 0x02, (byte) 0xd8, (byte) 0xf4, (byte) 0xaf, (byte) 0xce, 0x01, // TODO + + // Attribute: End of list + 0x00, 0x00, + 0x00, 0x00, + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet, new byte[] {1, 2, 3})); + NtlmState state = new NtlmState(); + Element ntlmssp_challenge = new ServerNtlmsspChallenge("ntlmssp_challenge", state); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers()); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, ntlmssp_challenge, sink, mainSink); + pipeline.link("source", "ntlmssp_challenge", "mainSink"); + pipeline.link("ntlmssp_challenge >" + OTOUT, "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + // Check state challenge + byte[] challenge = new byte[] {0x52, (byte)0xbe, (byte)0x83, (byte)0xd1, (byte)0xf8, (byte)0x80, 0x16, 0x6a}; + if (state.serverChallenge == null) + throw new RuntimeException("Challenge was not extracted from server NTLMSSP Challenge packet."); + if (!Arrays.equals(challenge, state.serverChallenge)) + throw new RuntimeException("Challenge was extracted from server NTLMSSP Challenge packet is not equal to expected. Actual value: " + + state.serverChallenge + ", expected value: " + challenge + "."); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspPubKeyPlus1.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspPubKeyPlus1.java new file mode 100755 index 00000000000..bbb75fcadbb --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/ServerNtlmsspPubKeyPlus1.java @@ -0,0 +1,125 @@ +// 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 rdpclient.ntlmssp; + +import java.util.Arrays; + +import rdpclient.ntlmssp.asn1.TSRequest; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class ServerNtlmsspPubKeyPlus1 extends OneTimeSwitch implements Element { + + protected NtlmState ntlmState; + + public ServerNtlmsspPubKeyPlus1(String id, NtlmState ntlmState) { + super(id); + this.ntlmState = ntlmState; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + TSRequest tsRequest = new TSRequest("TSRequest"); + tsRequest.readTag(buf); + + ByteBuffer encryptedPubKey = tsRequest.pubKeyAuth.value; + if (encryptedPubKey == null || encryptedPubKey.length == 0) + throw new RuntimeException("[" + this + + "] ERROR: Unexpected message from RDP server. Expected encrypted server public key but got nothing instead. Data: " + buf); + + byte[] decryptedPubKey = ntlmState.ntlm_DecryptMessage(encryptedPubKey.toByteArray()); + //* DEBUG */System.out.println("Decrypted pub key:\n" + new ByteBuffer(decryptedPubKey).dump()); + + // Decrease first byte by 1 + decryptedPubKey[0]--; + + // Compare returned value with expected value + if (!Arrays.equals(decryptedPubKey, ntlmState.subjectPublicKey)) + throw new RuntimeException("[" + this + + "] ERROR: Unexpected message from RDP server. Expected encrypted server public key but an unknown response. Encryted key after decryption: " + + new ByteBuffer(decryptedPubKey).toPlainHexString()); + + buf.unref(); + switchOff(); // Ignore packet + } +} + +/* @formatter:off */ + +// CredSSP header in BER format: + +// 0x30, (byte) 0x82, 0x01, 0x2b, // TAG: [UNIVERSAL 16] (constructed) +// "SEQUENCE" LEN: 299 bytes +// (byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes +// 0x02, 0x01, 0x03, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes, +// Version: 0x3 +// (byte) 0xa3, (byte) 0x82, 0x01, 0x22, // TAG: [3] (constructed) LEN: 290 +// bytes +// 0x04, (byte) 0x82, 0x01, 0x1e, // TAG: [UNIVERSAL 4] (primitive) +// "OCTET STRING" LEN: 286 bytes + +// ??? + +// 0x01, 0x00, 0x00, 0x00, // ??? +// (byte) 0x98, (byte) 0xb0, 0x72, 0x48, 0x42, 0x09, (byte) 0xbd, 0x42, 0x00, +// 0x00, 0x00, // +// 0x00, (byte) 0xf6, 0x76, 0x0a, 0x40, (byte) 0xb4, 0x7b, (byte) 0xee, 0x69, +// (byte) 0xfc, (byte) 0x95, 0x2d, 0x5f, 0x6a, (byte) 0xe8, (byte) 0x87, // +// 0x4e, (byte) 0xeb, (byte) 0xae, 0x29, (byte) 0xf2, (byte) 0xde, 0x5e, 0x0a, +// 0x6e, 0x45, (byte) 0xeb, (byte) 0x95, (byte) 0xd9, 0x48, (byte) 0xfc, 0x44, +// // +// 0x7a, 0x34, (byte) 0xb4, (byte) 0xc4, (byte) 0xee, (byte) 0x93, (byte) 0xd2, +// (byte) 0xb4, (byte) 0xe5, (byte) 0xe5, (byte) 0xc1, 0x0f, (byte) 0x9e, 0x3b, +// (byte) 0xce, (byte) 0xaa, // +// 0x76, (byte) 0x9e, 0x2b, 0x33, 0x44, 0x76, 0x2f, 0x2f, (byte) 0x83, 0x34, +// 0x3c, (byte) 0xe9, (byte) 0xc2, (byte) 0xeb, 0x0e, (byte) 0xce, // +// 0x6c, (byte) 0xcd, 0x1c, (byte) 0xae, 0x74, 0x78, 0x3e, (byte) 0x8c, 0x17, +// (byte) 0xb4, 0x39, (byte) 0x9a, 0x21, (byte) 0x99, (byte) 0xde, (byte) 0xae, +// // +// 0x72, 0x23, (byte) 0x94, (byte) 0xc6, (byte) 0xe9, (byte) 0xcb, 0x48, (byte) +// 0xb1, 0x54, 0x20, 0x70, 0x70, (byte) 0xc0, 0x77, 0x10, 0x4b, // +// (byte) 0x8a, (byte) 0xe0, (byte) 0xa0, 0x6c, (byte) 0xb9, 0x65, (byte) 0xfc, +// 0x67, (byte) 0xe3, 0x3b, (byte) 0xb6, 0x46, 0x5e, (byte) 0xaf, (byte) 0xe7, +// (byte) 0x92, // +// 0x6a, (byte) 0xaf, (byte) 0x86, 0x4d, 0x74, 0x33, 0x49, 0x2a, (byte) 0xf0, +// (byte) 0xdd, 0x66, (byte) 0xce, (byte) 0xec, (byte) 0xcc, 0x6b, 0x62, // +// 0x4f, 0x35, (byte) 0xb5, 0x0f, (byte) 0x95, (byte) 0xd7, (byte) 0xf7, (byte) +// 0xf3, 0x4b, 0x59, 0x5f, 0x29, (byte) 0xc9, (byte) 0xc4, (byte) 0xdc, 0x47, // +// (byte) 0xe9, (byte) 0x8d, 0x47, (byte) 0xd2, 0x1d, 0x35, 0x43, (byte) 0xce, +// (byte) 0xff, (byte) 0xd7, 0x6b, 0x28, (byte) 0xd8, 0x06, (byte) 0xe8, (byte) +// 0xba, // +// (byte) 0xf1, 0x4d, (byte) 0xba, 0x43, (byte) 0x8e, 0x64, (byte) 0xba, (byte) +// 0xcd, (byte) 0xcb, (byte) 0xaf, 0x1a, 0x61, (byte) 0xd8, 0x11, 0x19, (byte) +// 0xf7, // +// (byte) 0xae, (byte) 0xfe, (byte) 0x94, 0x48, (byte) 0x8e, (byte) 0x9f, 0x57, +// 0x17, (byte) 0xd2, (byte) 0xa3, (byte) 0xfd, 0x79, (byte) 0xb5, (byte) 0xa3, +// 0x7d, (byte) 0xca, // +// (byte) 0xff, (byte) 0x94, (byte) 0xb5, (byte) 0xb5, 0x03, (byte) 0xf3, 0x13, +// 0x6a, 0x74, 0x7a, (byte) 0xae, (byte) 0x9d, (byte) 0xe9, 0x5c, 0x32, 0x42, // +// 0x37, (byte) 0xa6, (byte) 0xb3, (byte) 0xf5, 0x4b, (byte) 0xaa, 0x22, 0x61, +// (byte) 0xf5, 0x28, 0x5b, 0x41, 0x26, 0x32, 0x63, 0x5f, // +// 0x43, (byte) 0xfd, 0x2e, 0x44, 0x7d, (byte) 0xfb, (byte) 0xb6, 0x09, (byte) +// 0xc5, (byte) 0xc8, 0x33, (byte) 0xbe, (byte) 0x81, 0x08, (byte) 0xd4, 0x5f, +// // +// (byte) 0xad, (byte) 0xee, 0x49, 0x25, 0x62, 0x52, (byte) 0x83, (byte) 0xc1, +// 0x3e, 0x17, 0x5b, (byte) 0xea, 0x4b, (byte) 0x90, 0x62, (byte) 0xf7, // +// 0x4e, 0x28, (byte) 0xfb, (byte) 0xab, (byte) 0x9a, (byte) 0xbc, 0x5e, (byte) +// 0xd4, (byte) 0xd5, 0x56, (byte) 0xf4, 0x4a, 0x2a, 0x7e, (byte) 0xd7, // + +/* @formatter:on */ diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/AlgorithmIdentifier.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/AlgorithmIdentifier.java new file mode 100755 index 00000000000..94f83852e1b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/AlgorithmIdentifier.java @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package rdpclient.ntlmssp.asn1; + +import common.asn1.Any; +import common.asn1.ObjectID; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + * X509 SubjectPublicKeyInfo field ASN.1 description. + */ +public class AlgorithmIdentifier extends Sequence { + public ObjectID algorithm = new ObjectID("algorithm"); + public Any parameters = new Any("parameters") { + { + optional = true; + } + }; + + public AlgorithmIdentifier(String name) { + super(name); + tags = new Tag[] {algorithm, parameters}; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoData.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoData.java new file mode 100755 index 00000000000..ef0afc78745 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoData.java @@ -0,0 +1,64 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.SequenceOf; +import common.asn1.Tag; + +/** + * The NegoData structure contains the SPNEGO messages, as specified in + * [MS-SPNG] section 2. + * + *
      + * NegoData ::= SEQUENCE OF SEQUENCE {
      + *   negoToken     [0] OCTET STRING
      + * }
      + * 
      + * + * If we write NegoItem as + * + *
      + * NegoItem ::= SEQUENCE {
      + *   negoToken     [0] OCTET STRING
      + * }
      + * 
      + * + * then NegoData can be written as + * + *
      + * NegoData ::= SEQUENCE OF NegoItem
      + * 
      + * + *
        + *
      • negoToken: One or more SPNEGO tokens, as specified in [MS-SPNG]. + *
      + * + * @see http://msdn.microsoft.com/en-us/library/cc226781.aspx + */ +public class NegoData extends SequenceOf { + + public NegoData(String name) { + super(name); + type = new NegoItem("NegoItem"); + } + + @Override + public Tag deepCopy(String suffix) { + return new NegoData(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoItem.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoItem.java new file mode 100755 index 00000000000..2b8a26babd0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/NegoItem.java @@ -0,0 +1,73 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + * The NegoData structure contains the SPNEGO messages, as specified in + * [MS-SPNG] section 2. + * + *
      + * NegoData ::= SEQUENCE OF SEQUENCE {
      + *   negoToken     [0] OCTET STRING
      + * }
      + * 
      + * + * If we write NegoItem as + * + *
      + * NegoItem ::= SEQUENCE {
      + *   negoToken     [0] OCTET STRING
      + * }
      + * 
      + * + * then NegoData can be written as + * + *
      + * NegoData ::= SEQUENCE OF NegoItem
      + * 
      + * + *
        + *
      • negoToken: One or more SPNEGO tokens, as specified in [MS-SPNG]. + *
      + * + * @see http://msdn.microsoft.com/en-us/library/cc226781.aspx + */ +public class NegoItem extends Sequence { + + public OctetString negoToken = new OctetString("negoToken") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + + public NegoItem(String name) { + super(name); + tags = new Tag[] {negoToken}; + } + + @Override + public Tag deepCopy(String suffix) { + return new NegoItem(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/SubjectPublicKeyInfo.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/SubjectPublicKeyInfo.java new file mode 100755 index 00000000000..c3e9137ffc0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/SubjectPublicKeyInfo.java @@ -0,0 +1,35 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.BitString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + * X509 SubjectPublicKeyInfo field ASN.1 description. + */ +public class SubjectPublicKeyInfo extends Sequence { + public AlgorithmIdentifier algorithm = new AlgorithmIdentifier("algorithm"); + public BitString subjectPublicKey = new BitString("subjectPublicKey"); + + public SubjectPublicKeyInfo(String name) { + super(name); + tags = new Tag[] {algorithm, subjectPublicKey}; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCredentials.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCredentials.java new file mode 100755 index 00000000000..8142b04d341 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCredentials.java @@ -0,0 +1,62 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.Asn1Integer; +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + *
      + * TSCredentials ::= SEQUENCE {
      + *   credType      [0] INTEGER,
      + *   credentials   [1] OCTET STRING
      + * }
      + *
      + * credType:
      + *   1 - credentials contains a TSPasswordCreds structure that defines the user's password credentials.
      + *   2 - credentials contains a TSSmartCardCreds structure that defines the user's smart card credentials.
      + * 
      + */ +public class TSCredentials extends Sequence { + public Asn1Integer credType = new Asn1Integer("credType") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + public OctetString credentials = new OctetString("credentials") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 1; + } + }; + + public TSCredentials(String name) { + super(name); + tags = new Tag[] {credType, credentials}; + } + + @Override + public Tag deepCopy(String suffix) { + return new TSCredentials(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCspDataDetail.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCspDataDetail.java new file mode 100755 index 00000000000..86257c71694 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSCspDataDetail.java @@ -0,0 +1,98 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package rdpclient.ntlmssp.asn1; + +import common.asn1.Asn1Integer; +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + *
      + * TSCspDataDetail ::= SEQUENCE {
      + *   keySpec       [0] INTEGER,
      + *   cardName      [1] OCTET STRING OPTIONAL,
      + *   readerName    [2] OCTET STRING OPTIONAL,
      + *   containerName [3] OCTET STRING OPTIONAL,
      + *   cspName       [4] OCTET STRING OPTIONAL
      + * }
      + * 
      + *
        + *
      • keySpec: Defines the specification of the user's smart card. + * + *
      • cardName: Specifies the name of the smart card. + * + *
      • readerName: Specifies the name of the smart card reader. + * + *
      • containerName: Specifies the name of the certificate container. + * + *
      • cspName: Specifies the name of the CSP. + *
      + * @see http://msdn.microsoft.com/en-us/library/cc226785.aspx + */ +public class TSCspDataDetail extends Sequence { + public Asn1Integer keySpec = new Asn1Integer("keySpec") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + public OctetString cardName = new OctetString("cardName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 1; + optional = true; + } + }; + public OctetString readerName = new OctetString("readerName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 2; + optional = true; + } + }; + public OctetString containerName = new OctetString("containerName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 3; + optional = true; + } + }; + public OctetString cspName = new OctetString("cspName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 4; + optional = true; + } + }; + + public TSCspDataDetail(String name) { + super(name); + tags = new Tag[] {keySpec, cardName, readerName, containerName, cspName}; + } + + @Override + public Tag deepCopy(String suffix) { + return new TSCspDataDetail(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSPasswordCreds.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSPasswordCreds.java new file mode 100755 index 00000000000..4922c075974 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSPasswordCreds.java @@ -0,0 +1,76 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + *
      + * TSPasswordCreds ::= SEQUENCE {
      + *   domainName    [0] OCTET STRING,
      + *   userName      [1] OCTET STRING,
      + *   password      [2] OCTET STRING
      + * }
      + * 
      + * + *
        + *
      • domainName: Contains the name of the user's account domain, as defined in + * [MS-GLOS]. + * + *
      • userName: Contains the user's account name. + * + *
      • Password: Contains the user's account password. + *
      + * + * @see http://msdn.microsoft.com/en-us/library/cc226783.aspx + */ +public class TSPasswordCreds extends Sequence { + public OctetString domainName = new OctetString("domainName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + public OctetString userName = new OctetString("userName") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 1; + } + }; + public OctetString password = new OctetString("password") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 2; + } + }; + + public TSPasswordCreds(String name) { + super(name); + tags = new Tag[] {domainName, userName, password}; + } + + @Override + public Tag deepCopy(String suffix) { + return new TSPasswordCreds(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSRequest.java new file mode 100755 index 00000000000..c5ba5d62ba9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSRequest.java @@ -0,0 +1,201 @@ +// 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 rdpclient.ntlmssp.asn1; + +import streamer.ByteBuffer; +import common.asn1.Asn1Integer; +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + * The TSRequest structure is the top-most structure used by the CredSSP client + * and CredSSP server. It contains the SPNEGO messages between the client and + * server, and either the public key authentication messages that are used to + * bind to the TLS session or the client credentials that are delegated to the + * server. The TSRequest message is always sent over the TLS-encrypted channel + * between the client and server in a CredSSP Protocol exchange (see step 1 in + * section 3.1.5). + * + *
      + * TSRequest ::= SEQUENCE {
      + *   version       [0] INTEGER,
      + *   negoTokens    [1] NegoData OPTIONAL,
      + *   authInfo      [2] OCTET STRING OPTIONAL,
      + *   pubKeyAuth    [3] OCTET STRING OPTIONAL
      + * }
      + *
      + * 
      + *
        + * + *
      • version: This field specifies the supported version of the CredSSP + * Protocol. This field MUST be 2. If the version is greater than 2, a version 2 + * client or server treats its peer as one that is compatible with version 2 of + * the CredSSP Protocol. + * + *
      • negoTokens: A NegoData structure, as defined in section 2.2.1.1, that + * contains the SPNEGO messages that are passed between the client and server. + * + *
      • authInfo: A TSCredentials structure, as defined in section 2.2.1.2, that + * contains the user's credentials that are delegated to the server. The + * authinfo field MUST be encrypted under the encryption key that is + * negotiated under the SPNEGO package. + * + *
      • pubKeyAuth: This field is used to assure that the public key that is used + * by the server during the TLS handshake belongs to the target server and not + * to a "man in the middle". The client encrypts the public key it received from + * the server (contained in the X.509 certificate) in the TLS handshake from + * step 1, by using the confidentiality support of SPNEGO. The public key that + * is encrypted is the ASN.1-encoded SubjectPublicKey sub-field of + * SubjectPublicKeyInfo from the X.509 certificate, as specified in [RFC3280] + * section 4.1. The encrypted key is encapsulated in the pubKeyAuth field of the + * TSRequest structure and is sent over the TLS channel to the server. After the + * client completes the SPNEGO phase of the CredSSP Protocol, it uses + * GSS_WrapEx() for the negotiated protocol to encrypt the server's public key. + * The pubKeyAuth field carries the message signature and then the encrypted + * public key to the server. In response, the server uses the pubKeyAuth field + * to transmit to the client a modified version of the public key (as described + * in section 3.1.5) that is encrypted under the encryption key that is + * negotiated under SPNEGO. + *
      + * + * @see http://msdn.microsoft.com/en-us/library/cc226780.aspx + */ +public class TSRequest extends Sequence { + public Asn1Integer version = new Asn1Integer("version") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + public NegoData negoTokens = new NegoData("negoTokens") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 1; + optional = true; + } + }; + public OctetString authInfo = new OctetString("authInfo") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 2; + optional = true; + } + }; + public OctetString pubKeyAuth = new OctetString("pubKeyAuth") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 3; + optional = true; + } + }; + + public TSRequest(String name) { + super(name); + tags = new Tag[] {version, negoTokens, authInfo, pubKeyAuth}; + } + + @Override + public Tag deepCopy(String suffix) { + return new TSRequest(name + suffix).copyFrom(this); + } + + /** + * Example. + */ + public static void main(String[] args) { + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x30, (byte) 0x82, 0x01, 0x02, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 258 bytes + (byte) 0xa0, 0x03, // TAG: [0] (constructed) LEN: 3 bytes + 0x02, 0x01, 0x03, // TAG: [UNIVERSAL 2] (primitive) "INTEGER" LEN: 1 bytes, Version: 0x3 + (byte) 0xa1, (byte) 0x81, (byte) 0xfa, // TAG: [1] (constructed) LEN: 250 bytes + 0x30, (byte) 0x81, (byte) 0xf7, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 247 bytes + 0x30, (byte) 0x81, (byte) 0xf4, // TAG: [UNIVERSAL 16] (constructed) "SEQUENCE" LEN: 244 bytes + (byte) 0xa0, (byte) 0x81, (byte) 0xf1, // TAG: [0] (constructed) LEN: 241 bytes + 0x04, (byte) 0x81, (byte) 0xee, // TAG: [UNIVERSAL 4] (primitive) "OCTET STRING" LEN: 238 bytes + + 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, // "NTLMSSP\0" + + 0x02, 0x00, 0x00, 0x00, // MessageType (CHALLENGE) + 0x1e, 0x00, 0x1e, 0x00, 0x38, 0x00, 0x00, 0x00, // TargetName (length: 30, allocated space: 30, offset: 56) + 0x35, (byte) 0x82, (byte) 0x8a, (byte) 0xe2, // NegotiateFlags TODO + 0x52, (byte) 0xbe, (byte) 0x83, (byte) 0xd1, (byte) 0xf8, (byte) 0x80, 0x16, 0x6a, // ServerChallenge + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved + (byte) 0x98, 0x00, (byte) 0x98, 0x00, 0x56, 0x00, 0x00, 0x00, // TargetInfo (length: 152, allocated space: 152, offset: 86) + 0x06, 0x03, (byte) 0xd7, 0x24, 0x00, 0x00, 0x00, 0x0f, // Version (6.3, build 9431) , NTLM current revision: 15 + + + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // Target name value: "WIN-LO419B2LSR0" + + // Target Info value: + + // Attribute list + + 0x02, 0x00, // Item Type: NetBIOS domain name (0x0002, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x01, 0x00, // Item Type: NetBIOS computer name (0x0001, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x04, 0x00, // Item Type: DNS domain name (0x0004, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x03, 0x00, // Item Type: DNS computer name (0x0003, LE) + 0x1e, 0x00, // Item Length: 30 (LE) + 0x57, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x2d, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x34, 0x00, 0x31, 0x00, 0x39, 0x00, 0x42, 0x00, 0x32, 0x00, 0x4c, 0x00, 0x53, 0x00, 0x52, 0x00, 0x30, 0x00, // "WIN-LO419B2LSR0" + + 0x07, 0x00, // Item Type: Timestamp (0x0007, LE) + 0x08, 0x00, // Item Length: 8 (LE) + (byte) 0x99, 0x4f, 0x02, (byte) 0xd8, (byte) 0xf4, (byte) 0xaf, (byte) 0xce, 0x01, // TODO + + // Attribute: End of list + 0x00, 0x00, + 0x00, 0x00, + }; + /* @formatter:on */ + + TSRequest request = new TSRequest("TSRequest"); + + // Read request from buffer + // System.out.println("Request BER tree before parsing: " + request); + ByteBuffer toReadBuf = new ByteBuffer(packet); + request.readTag(toReadBuf); + // System.out.println("Request BER tree after parsing: " + request); + + // System.out.println("version value: " + request.version.value); + // System.out.println("negoToken value: " + ((NegoItem) + // request.negoTokens.tags[0]).negoToken.value); + + // Write request to buffer and compare with original + ByteBuffer toWriteBuf = new ByteBuffer(packet.length + 100, true); + request.writeTag(toWriteBuf); + toWriteBuf.trimAtCursor(); + + if (!toReadBuf.equals(toWriteBuf)) + throw new RuntimeException("Data written to buffer is not equal to data read from buffer. \nExpected: " + toReadBuf + "\nActual: " + toWriteBuf + "."); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSSmartCardCreds.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSSmartCardCreds.java new file mode 100755 index 00000000000..4e8fb36da47 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/asn1/TSSmartCardCreds.java @@ -0,0 +1,90 @@ +// 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 rdpclient.ntlmssp.asn1; + +import common.asn1.OctetString; +import common.asn1.Sequence; +import common.asn1.Tag; + +/** + *
      + * TSSmartCardCreds ::= SEQUENCE {
      + *   pin           [0] OCTET STRING,
      + *   cspData       [1] TSCspDataDetail,
      + *   userHint      [2] OCTET STRING OPTIONAL,
      + *   domainHint    [3] OCTET STRING OPTIONAL
      + * }
      + * 
      + * + *
        + *
      • pin: Contains the user's smart card PIN. + * + *
      • cspData: A TSCspDataDetail structure that contains information about the + * cryptographic service provider (CSP). + * + *
      • userHint: Contains the user's account hint. + * + *
      • domainHint: Contains the user's domain name to which the user's account + * belongs. This name could be entered by the user when the user is first + * prompted for the PIN. + *
      + * + * @see http://msdn.microsoft.com/en-us/library/cc226784.aspx + */ +public class TSSmartCardCreds extends Sequence { + public OctetString pin = new OctetString("pin") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 0; + } + }; + public TSCspDataDetail cspData = new TSCspDataDetail("cspData") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 1; + } + }; + public OctetString userHint = new OctetString("userHint") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 2; + optional = true; + } + }; + public OctetString domainHint = new OctetString("domainHint") { + { + explicit = true; + tagClass = CONTEXT_CLASS; + tagNumber = 3; + optional = true; + } + }; + + public TSSmartCardCreds(String name) { + super(name); + tags = new Tag[] {pin, cspData, userHint, domainHint}; + } + + @Override + public Tag deepCopy(String suffix) { + return new TSSmartCardCreds(name + suffix).copyFrom(this); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/package-info.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/package-info.java new file mode 100755 index 00000000000..42e7c41d9d2 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ntlmssp/package-info.java @@ -0,0 +1,71 @@ +// 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. +/** + * + * CredSSP/SPNEGO/NTLMSSP implementation. + * + * CredSSP ASN.1 definition: + * +
      +CredSSP DEFINITIONS EXPLICIT TAGS ::=
      +
      +BEGIN
      +
      +TSPasswordCreds ::= SEQUENCE {
      +  domainName    [0] OCTET STRING,
      +  userName      [1] OCTET STRING,
      +  password      [2] OCTET STRING
      +}
      +
      +TSCspDataDetail ::= SEQUENCE {
      +  keySpec       [0] INTEGER,
      +  cardName      [1] OCTET STRING OPTIONAL,
      +  readerName    [2] OCTET STRING OPTIONAL,
      +  containerName [3] OCTET STRING OPTIONAL,
      +  cspName       [4] OCTET STRING OPTIONAL
      +}
      +
      +TSSmartCardCreds ::= SEQUENCE {
      +  pin           [0] OCTET STRING,
      +  cspData       [1] TSCspDataDetail,
      +  userHint      [2] OCTET STRING OPTIONAL,
      +  domainHint    [3] OCTET STRING OPTIONAL
      +}
      +
      +TSCredentials ::= SEQUENCE {
      +  credType      [0] INTEGER,
      +  credentials   [1] OCTET STRING
      +}
      +
      +NegoData ::= SEQUENCE OF SEQUENCE {
      +  negoToken     [0] OCTET STRING
      +}
      +
      +TSRequest ::= SEQUENCE {
      +  version       [0] INTEGER,
      +  negoTokens    [1] NegoData OPTIONAL,
      +  authInfo      [2] OCTET STRING OPTIONAL,
      +  pubKeyAuth    [3] OCTET STRING OPTIONAL
      +}
      +
      +END
      +
      + +For packet flow, @see http://msdn.microsoft.com/en-us/library/cc226794.aspx + */ +package rdpclient.ntlmssp; + diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientConfirmActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientConfirmActivePDU.java new file mode 100755 index 00000000000..b77f201dd77 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientConfirmActivePDU.java @@ -0,0 +1,1131 @@ +// 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 rdpclient.rdp; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; +import common.ScreenDescription; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240488.aspx + */ +public class ClientConfirmActivePDU extends BaseElement { + + public static final String SOURCE_DESC = "MSTSC"; + + public static final int CAPSTYPE_BITMAP = 0x2; + + protected int numberCapabilities; + + protected RdpState state; + protected ScreenDescription screen; + + protected boolean desktopResize = false; + protected int prefferedBitsPerPixel = 16; + + public ClientConfirmActivePDU(String id, ScreenDescription screen, RdpState state) { + super(id); + this.state = state; + this.screen = screen; + } + + @Override + public void handleData(ByteBuffer aBuf, Link link) { + + // Body + ByteBuffer buf = new ByteBuffer(1024, true); + numberCapabilities = 0; + writeCapabilities(buf); + buf.trimAtCursor(); + + // Header + ByteBuffer header = createMCSHeader(buf); + + // Length of source descriptor, including NULL character (LE) + header.writeShortLE(SOURCE_DESC.length() + 1); + + // Length of combined capabilities + 4 bytes (number of capabilities and + // padding) (LE) + header.writeShortLE(buf.length + 4); + + header.writeString(SOURCE_DESC, RdpConstants.CHARSET_8); + header.writeByte(0); + + // Number of capabilities + header.writeShortLE(numberCapabilities); + + // Padding 2 bytes + header.writeShortLE(0); + + header.trimAtCursor(); + + // Prepend header to capabilities + buf.prepend(header); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + + sendOtherRequredPackets(); + + } + + private ByteBuffer createMCSHeader(ByteBuffer buf) { + ByteBuffer header = new ByteBuffer(100); + // MCS Send Data Request + header.writeByte(0x64); + + // Initiator: 1004 (1001+3) + header.writeShort(3); + + // Channel ID: 1003 (I/O channel) + header.writeShort(RdpConstants.CHANNEL_IO); + + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + header.writeByte(0x70); + + int length = buf.length + 26; + + // User data length: (variable length field, LE) + header.writeVariableShort(length); + + // Total length: (LE) + header.writeShortLE(length); + + // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE) + header.writeShortLE(0x13); + + // PDU source: 1004 (LE) + header.writeShortLE(1004); + + // Share ID, e.g. 0x000103ea (LE) + header.writeIntLE((int)state.serverShareId); + + // Originator ID: 1002 (LE) + header.writeShortLE(1002); + return header; + } + + private void sendOtherRequredPackets() { + // Send sequence in bulk + + sendSynchronizePDU(); + sendControlPDUActionCooperate(); + sendControlPDUActionRequestControl(); + // sendBitmapCachePersistentListPDU(); + sendFontListPDU(); + } + + private void sendFontListPDU() { + { + int length = 1024; // Large enough + ByteBuffer buf = new ByteBuffer(length, true); + + /* @formatter:off */ + buf.writeBytes(new byte[] { + // MCS Send Data Request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // User data length: 26 bytes (0x1a, variable length field) + (byte)0x80, (byte)0x1a, + + // Total length: 26 bytes (0x1a, LE) + (byte)0x1a, (byte)0x00, + // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) + (byte)0x17, (byte)0x00, + // PDU source: 1004 (LE) + (byte)0xec, (byte)0x03, + }); + // Share ID, 4 bytes (LE) + buf.writeIntLE((int)state.serverShareId); + + buf.writeBytes(new byte[] { + // Padding 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 12 bytes (LE) + (byte)0x0c, (byte)0x00, + + // pduType2: PDUTYPE2_FONTLIST (39) + (byte)0x27, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE) + (byte)0x00, (byte)0x00, + + // numberEntries (should be set to zero): 0 (LE) + (byte)0x00, (byte)0x00, + // totalNumEntries (should be set to zero): 0 (LE) + (byte)0x00, (byte)0x00, + // listFlags (should be set to 0x3): 0x0003 (LE), FONTLIST_LAST(0x2) | FONTLIST_FIRST(0x1) + (byte)0x03, (byte)0x00, + // entrySize: 50 bytes (0x0032, LE) + (byte)0x32, (byte)0x00, + }); + /* @formatter:on */ + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + } + } + + private void sendControlPDUActionRequestControl() { + int length = 1024; // Large enough + ByteBuffer buf = new ByteBuffer(length, true); + + /* @formatter:off */ + buf.writeBytes(new byte[] { + // MCS Send Data Request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // User data length: 26 bytes (0x1a, variable length field) + (byte)0x80, (byte)0x1a, + + // Total length: 26 bytes (0x1a, LE) + (byte)0x1a, (byte)0x00, + // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) + (byte)0x17, (byte)0x00, + // PDU source: 1004 (LE) + (byte)0xec, (byte)0x03, + }); + // Share ID, 4 bytes (LE) + buf.writeIntLE((int)state.serverShareId); + + buf.writeBytes(new byte[] { + // Padding 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 12 bytes (LE) + (byte)0x0c, (byte)0x00, + // pduType2: PDUTYPE2_CONTROL (20) + (byte)0x14, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE) + (byte)0x00, (byte)0x00, + + // action: CTRLACTION_REQUEST_CONTROL (1) (LE) + (byte)0x01, (byte)0x00, + // grantId: 0 (LE) + (byte)0x00, (byte)0x00, + // controlId: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + }); + /* @formatter:on */ + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + } + + private void sendControlPDUActionCooperate() { + int length = 1024; // Large enough + ByteBuffer buf = new ByteBuffer(length, true); + + /* @formatter:off */ + buf.writeBytes(new byte[] { + // MCS Send Data Request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // User data length: 26 bytes (0x1a, variable length field) + (byte)0x80, (byte)0x1a, + + // Total length: 26 bytes (0x1a, LE) + (byte)0x1a,(byte)0x00, + // PDU type: PDUTYPE_DATAPDU (0x7), PDU version: 1 (0x0010) (LE) + (byte)0x17, (byte)0x00, + // PDU source: 1004 (LE) + (byte)0xec, (byte)0x03, + }); + // Share ID, 4 bytes (LE) + buf.writeIntLE((int)state.serverShareId); + + buf.writeBytes(new byte[] { + // Padding 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 12 bytes (LE) + (byte)0x0c, (byte)0x00, + // pduType2: PDUTYPE2_CONTROL (20) + (byte)0x14, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE?) + (byte)0x00, (byte)0x00, + // action: CTRLACTION_COOPERATE (4) (LE) + (byte)0x04, (byte)0x00, + // grantId: 0 (LE) + (byte)0x00, (byte)0x00, + // controlId: 0 + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + }); + /* @formatter:on */ + + buf.trimAtCursor(); + + pushDataToPad(STDOUT, buf); + } + + private void sendSynchronizePDU() { + + ByteBuffer buf = new ByteBuffer(1024, true); + /* @formatter:off */ + buf.writeBytes(new byte[] { + // MCS send data request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O Channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // Data length: 22 bytes (0x16, variable length field) + (byte)0x80, (byte)0x16, + + // RDP: total length: 22 bytes (LE) + (byte)0x16, (byte)0x00, + + // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) + (byte)0x17, (byte)0x00, + + // PDU source: 1007 (LE) + (byte)0xec, (byte)0x03, + }); + // Share ID, 4 bytes (LE) + buf.writeIntLE((int)state.serverShareId); + + buf.writeBytes(new byte[] { + // Padding: 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 8 bytes (LE) + (byte)0x08, (byte)0x00, + // pduType2 = PDUTYPE2_SYNCHRONIZE (31) + (byte)0x1f, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE?) + (byte)0x00, (byte)0x00, + // messageType: SYNCMSGTYPE_SYNC (1) (LE) + (byte)0x01, (byte)0x00, + // targetUser: 0x03ea + (byte)0xea, (byte)0x03, + }); + /* @formatter:on */ + buf.trimAtCursor(); + pushDataToPad(STDOUT, buf); + } + + private void writeCapabilities(ByteBuffer buf) { + writeGeneralCS(buf); + + writeBitmapCS(buf); + + writeOrderCS(buf); + + writeBitmapCache2CS(buf); + + writeColorTableCacheCS(buf); + + writeWindowActivationCS(buf); + + writeControlCS(buf); + + writePointerCS(buf); + + writeShareCS(buf); + + writeInputCS(buf); + + writeBrushCS(buf); + + writeSoundCS(buf); + + writeFontCS(buf); + + writeOffscreenBitmapCS(buf); + + writeGlyphCacheCS(buf); + } + + private void writeBrushCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Brush Capability Set (8 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240564.aspx + (byte)0x0f, (byte)0x00, // capability set type: CAPSTYPE_BRUSH (15, + // LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // brushSupportLevel: + // BRUSH_DEFAULT + // (0x0, LE) + + }); + } + + private void writeInputCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Input Capability Set (88 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240563.aspx + (byte)0x0d, + (byte)0x00, // capability set type: CAPSTYPE_INPUT (13, LE) + (byte)0x58, + (byte)0x00, // length of capability set: 88 bytes (LE) + (byte)0x35, + (byte)0x00, // inputFlags: 0x0035 (LE), INPUT_FLAG_FASTPATH_INPUT2 + // (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX + // (0x4), INPUT_FLAG_SCANCODES (0x1) + (byte)0x00, + (byte)0x00, // Padding 2 bytes + (byte)0x09, + (byte)0x04, + (byte)0x00, + (byte)0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE) + (byte)0x00, + (byte)0x00, + (byte)0x00, + (byte)0x00, // keyboardType: unknown (LE) + (byte)0x00, + (byte)0x00, + (byte)0x00, + (byte)0x00, // keyboardSubType: unknown (LE) + (byte)0x00, + (byte)0x00, + (byte)0x00, + (byte)0x00, // keyboardFunctionKey: unknown (LE) + // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + + }); + } + + private void writeShareCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Share Capability Set (8 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240570.aspx + (byte)0x09, (byte)0x00, // capability set type: CAPSTYPE_SHARE (9, LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, // nodeID (must be set to 0 by client): 0 (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes (LE) + + }); + } + + private void writePointerCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Pointer Capability Set (10 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240562.aspx + (byte)0x08, (byte)0x00, // capability set type: CAPSTYPE_POINTER (8, + // LE) + (byte)0x0a, (byte)0x00, // length of capability set: 10 bytes (LE) + (byte)0x00, (byte)0x00, // colorPointerFlag: FALSE (LE) + (byte)0x00, (byte)0x00, // colorPointerCacheSize: 0 (LE) + (byte)0x14, (byte)0x00, // pointerCacheSize: 20 (LE) + + }); + } + + private void writeControlCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Control Capability Set (12 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240568.aspx + (byte)0x05, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION + // (7) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, // controlFlags (should be set to 0): 0 (LE) + (byte)0x00, (byte)0x00, // remoteDetachFlag (should be set to 0): 0 + // (LE) + (byte)0x02, (byte)0x00, // controlInterest (should be set to + // CONTROLPRIORITY_NEVER): + // CONTROLPRIORITY_NEVER (2) (LE) + (byte)0x02, (byte)0x00, // detachInterest (should be set to + // CONTROLPRIORITY_NEVER): + // CONTROLPRIORITY_NEVER (2) (LE) + + }); + } + + private void writeWindowActivationCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Window Activation Capability Set (12 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240569.aspx + (byte)0x07, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION + // (7) (LE) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, // helpKeyFlag (should be set to FALSE (0)): + // FALSE (0, LE) + (byte)0x00, (byte)0x00, // helpKeyIndexFlag (should be set to FALSE + // (0)): FALSE (0, LE) + (byte)0x00, (byte)0x00, // helpExtendedKeyFlag (should be set to FALSE + // (0)): FALSE (0, LE) + (byte)0x00, (byte)0x00, // windowManagerKeyFlag (should be set to + // FALSE (0)): FALSE (0, LE) + + }); + } + + private void writeColorTableCacheCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + + // + // Color Table Cache Capability Set (8 bytes), see + // http://msdn.microsoft.com/en-us/library/cc241564.aspx + (byte)0x0a, (byte)0x00, // capability set type: CAPSTYPE_COLORCACHE + // (10) (LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x06, (byte)0x00, // Color table cache size (must be ignored + // during capability exchange and is assumed + // to be 0x0006): 6 (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + }); + } + + private void writeBitmapCache2CS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Bitmap Cache Rev. 2 Capability Set (40 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240560.aspx + (byte)0x13, (byte)0x00, // capability set type: + // CAPSTYPE_BITMAPCACHE_REV2 (19) (LE) + (byte)0x28, (byte)0x00, // length of capability set: 40 bytes (LE) + (byte)0x00, (byte)0x00, // Cache flags: 0 (LE) + (byte)0x00, // Padding 1 byte + (byte)0x00, // Number of cell caches: 0 + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache0 + // cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache1 + // cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache2 + // cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache3 + // cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache4 + // cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 12 bytes + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding + }); + } + + private void writeGeneralCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // Capabilities, see + // http://msdn.microsoft.com/en-us/library/cc240486.aspx + + // + // General capability set (24 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240549.aspx + (byte)0x01, (byte)0x00, // capability set type: CAPSTYPE_GENERAL (1) + // (LE) + (byte)0x18, (byte)0x00, // length of capability set: 24 bytes (LE) + (byte)0x01, (byte)0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE) + (byte)0x03, (byte)0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE) + (byte)0x00, (byte)0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x00, (byte)0x00, // generalCompressionTypes: 0 (LE) + + // Extra flags: 0x040d (LE) + // FastPathOutput: (...............1) Advertiser supports fast-path + // output + // ShadowCompression: (..............0.) Advertiser NOT supports shadow + // compression + // LongLengthCredentials: (.............1..) Advertiser supports + // long-length credentials for the user name, password, or domain name + // SessionAutoreconnection: (............1...) Advertiser supports + // session auto-reconnection + // ImprovedEncryptionChecksum: (...........0....) Client and server NOT + // support improved encryption checksum + // Reserved1: (......00000.....) + // CompressedBitMapDataFlag: (.....1..........) No 8-UINT8 header is + // present for compressed bitmap data + // Reserved2: (00000...........) + (byte)0x0d, (byte)0x04, + + (byte)0x00, (byte)0x00, // updateCapabilityFlag: 0 (LE) + (byte)0x00, (byte)0x00, // remoteUnshareFlag: 0 (LE) + (byte)0x00, (byte)0x00, // generalCompressionLevel: 0 (LE) + (byte)0x00, // refreshRectSupport: FALSE (0) + (byte)0x00, // suppressOutputSupport: FALSE (0) + + }); + } + + private void writeBitmapCS(ByteBuffer buf) { + // Bitmap capability set (28 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240554.aspx + + numberCapabilities++; + + // Capability set type: CAPSTYPE_BITMAP (2) (LE) + buf.writeShortLE(CAPSTYPE_BITMAP); + + // Length of capability set: 28 bytes (LE) + buf.writeShortLE(28); + + // preferredBitsPerPixel: 16 bpp (LE) + buf.writeShortLE(prefferedBitsPerPixel); + + // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + buf.writeShortLE(1); + + // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + buf.writeShortLE(1); + + // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + buf.writeShortLE(1); + + // Desktop width and height (LE) + buf.writeShortLE(screen.getFramebufferWidth()); + buf.writeShortLE(screen.getFramebufferHeight()); + + // Padding 2 bytes + buf.writeShortLE(0); + + // desktopResizeFlag (LE) + buf.writeShortLE((desktopResize) ? 1 : 0); + + buf.writeBytes(new byte[] { + (byte)0x01, (byte)0x00, // bitmapCompressionFlag (must be set to TRUE + // (0x1)): TRUE (0x1) (LE) + (byte)0x00, // highColorFlags (field is ignored and SHOULD be set to + // zero): 0 + (byte)0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ??? + (byte)0x01, (byte)0x00, // multipleRectangleSupport: TRUE (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + }); + } + + private void writeOrderCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Order Capability Set (88 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240556.aspx + (byte)0x03, + (byte)0x00, // capability set type: CAPSTYPE_ORDER (3) (LE) + (byte)0x58, + (byte)0x00, // length of capability set: 88 bytes (LE) + // terminalDescriptor = "" (16 bytes, UCS2) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // pad4octetsA + (byte)0x01, (byte)0x00, // desktopSaveXGranularity (ignored): 1 (LE) + (byte)0x14, (byte)0x00, // desktopSaveYGranularity (ignored): 20 (LE) + (byte)0x00, (byte)0x00, // pad2octetsA (ignored) + (byte)0x01, (byte)0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1) + (byte)0x00, (byte)0x00, // number of fonts (ignored): 0 + (byte)0x4a, (byte)0x00, // orderFlags = 0x004a (LE), + // SOLIDPATTERNBRUSHONLY (0x40), + // ZEROBOUNDSDELTASSUPPORT (0x8, MUST), + // NEGOTIATEORDERSUPPORT (0x2, MUST) + // Order support: 32 bytes (no primary drawing orders are supported, so + // this array MUST be initialized to all zeros, use 0x01 for TRUE). + (byte)0x00, // TS_NEG_DSTBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_PATBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_SCRBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEMBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEM3BLT_INDEX: FALSE + (byte)0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE + (byte)0x00, // TS_NEG_LINETO_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE + (byte)0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE + (byte)0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE + (byte)0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE + (byte)0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE + (byte)0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYLINE_INDEX: TRUE + (byte)0x00, // Unused: 0 + (byte)0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE + (byte)0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE + (byte)0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE + (byte)0x00, // TS_NEG_INDEX_INDEX: FALSE + (byte)0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // Unused: 0 + (byte)0x00, (byte)0x00, // Text flags (ignored): 0 (LE) + (byte)0x00, (byte)0x00, // Order support extra flags: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 4 bytes + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Desktop save size + // (ignored): 0 + // (assumed to be + // 230400 bytes + // (480*480, + // 0x38400, LE)) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0xe4, (byte)0x04, // Text ANSI Code Page: 1252, ANSI - Latin I + // (0x04e4, LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + }); + } + + private void writeSoundCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Sound Capability Set (8 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240552.aspx + (byte)0x0c, (byte)0x00, // capability set type: CAPSTYPE_SOUND (12, + // LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // soundFlags: + // 0x0000 (LE) // + // SOUND_FLAG_BEEPS + // (0x1) + + }); + } + + private void writeFontCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Font Capability Set (8 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240571.aspx + (byte)0x0e, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, + + }); + } + + private void writeOffscreenBitmapCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Offscreen Bitmap Cache Capability Set (12 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240550.aspx + (byte)0x11, (byte)0x00, // capability set type: + // CAPSTYPE_OFFSCREENCACHE (17, LE) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // offscreenSupportLevel: + // FALSE (LE) + (byte)0x00, (byte)0x00, // offscreenCacheSize: 0 (LE) + (byte)0x00, (byte)0x00, // offscreenCacheEntries: 0 (LE) + + }); + } + + private void writeGlyphCacheCS(ByteBuffer buf) { + numberCapabilities++; + buf.writeBytes(new byte[] { + // + // Glyph Cache Capability Set (52 bytes), see + // http://msdn.microsoft.com/en-us/library/cc240565.aspx + (byte)0x10, (byte)0x00, // capability set type: + // CAPSTYPE_OFFSCREENCACHE (16, LE) + (byte)0x34, (byte)0x00, // length of capability set: 52 bytes (LE) + // Glyph Cache (40 bytes) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x10, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x20, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x40, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x80, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 4 (LE) + (byte)0x40, (byte)0x00, // CacheEntries: 64 (LE) + (byte)0x00, (byte)0x08, // CacheMaximumCellSize: 2048 (LE) + // FragCache + (byte)0x00, (byte)0x01, // CacheEntries: 256 (LE) + (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 256 (LE) + // + (byte)0x00, (byte)0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0, + // LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + }); + } + + /** + * Example. + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + // MCS Send Data Request + (byte)0x64, + + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + + // Channel ID: 1003 (I/O channel) + (byte)0x03, (byte)0xeb, + + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + + // User data length: 432 bytes (0x1b0, variable length field) + (byte)0x81, (byte)0xb0, + + // Total length: 432 bytes (0x1b0, LE) + (byte)0xb0, (byte)0x01, + + // PDU type: Confirm Active PDU (0x3), TS_PROTOCOL_VERSION (0x10) (LE) + (byte)0x13, (byte)0x00, + + // PDU source: 1004 (LE) + (byte)0xec, (byte)0x03, + + // Share ID: 0x000103ea (LE) + (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, + + // Originator ID: 1002 (LE) + (byte)0xea, (byte)0x03, + + // Length of source descriptor: 6 bytes (including NULL character) (LE) + (byte)0x06, (byte)0x00, + + // Length of combined capabilities: 410 bytes (LE) + (byte)0x9a, (byte)0x01, + + // Source descriptor: "MSTSC" ??? + (byte)0x4d, (byte)0x53, (byte)0x54, (byte)0x53, (byte)0x43, (byte)0x00, + + // Number of capabilities: 15 (LE) + (byte)0x0f, (byte)0x00, + + // Padding 2 bytes + (byte)0x00, (byte)0x00, + + // Capabilities, see http://msdn.microsoft.com/en-us/library/cc240486.aspx + + // + // General capability set (24 bytes), see http://msdn.microsoft.com/en-us/library/cc240549.aspx + (byte)0x01, (byte)0x00, // capability set type: CAPSTYPE_GENERAL (1) (LE) + (byte)0x18, (byte)0x00, // length of capability set: 24 bytes (LE) + (byte)0x01, (byte)0x00, // TS_OSMAJORTYPE_WINDOWS (1) (LE) + (byte)0x03, (byte)0x00, // TS_OSMINORTYPE_WINDOWS_NT (3) (LE) + (byte)0x00, (byte)0x02, // TS_CAPS_PROTOCOLVERSION (0x0200) (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x00, (byte)0x00, // generalCompressionTypes: 0 (LE) + + // Extra flags: 0x040d (LE) +// FastPathOutput: (...............1) Advertiser supports fast-path output +// ShadowCompression: (..............0.) Advertiser NOT supports shadow compression +// LongLengthCredentials: (.............1..) Advertiser supports long-length credentials for the user name, password, or domain name +// SessionAutoreconnection: (............1...) Advertiser supports session auto-reconnection +// ImprovedEncryptionChecksum: (...........0....) Client and server NOT support improved encryption checksum +// Reserved1: (......00000.....) +// CompressedBitMapDataFlag: (.....1..........) No 8-UINT8 header is present for compressed bitmap data +// Reserved2: (00000...........) + (byte)0x0d, (byte)0x04, + + (byte)0x00, (byte)0x00, // updateCapabilityFlag: 0 (LE) + (byte)0x00, (byte)0x00, // remoteUnshareFlag: 0 (LE) + (byte)0x00, (byte)0x00, // generalCompressionLevel: 0 (LE) + (byte)0x00, // refreshRectSupport: FALSE (0) + (byte)0x00, // suppressOutputSupport: FALSE (0) + + // + // Bitmap capability set (28 bytes), see http://msdn.microsoft.com/en-us/library/cc240554.aspx + (byte)0x02, (byte)0x00, // capability set type: CAPSTYPE_BITMAP (2) (LE) + (byte)0x1c, (byte)0x00, // length of capability set: 28 bytes (LE) + (byte)0x10, (byte)0x00, // preferredBitsPerPixel: 16 bpp (LE) + (byte)0x01, (byte)0x00, // receive1BitPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + (byte)0x01, (byte)0x00, // receive4BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + (byte)0x01, (byte)0x00, // receive8BitsPerPixel (ignored and SHOULD be set to TRUE (0x1)): TRUE (0x1) (LE) + (byte)0x00, (byte)0x04, // desktopWidth = 1024 pixels (LE) + (byte)0x00, (byte)0x03, // desktopHeight = 768 pixels (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x00, (byte)0x00, // desktopResizeFlag: FALSE (0x0) (LE) + (byte)0x01, (byte)0x00, // bitmapCompressionFlag (must be set to TRUE (0x1)): TRUE (0x1) (LE) + (byte)0x00, // highColorFlags (field is ignored and SHOULD be set to zero): 0 + (byte)0x01, // drawingFlags: 0x1 TODO: padding, why 0x1 ??? + (byte)0x01, (byte)0x00, // multipleRectangleSupport: TRUE (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + // + // Order Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240556.aspx + (byte)0x03, (byte)0x00, // capability set type: CAPSTYPE_ORDER (3) (LE) + (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE) + // terminalDescriptor = "" (16 bytes, UCS2) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // pad4octetsA + (byte)0x01, (byte)0x00, // desktopSaveXGranularity (ignored): 1 (LE) + (byte)0x14, (byte)0x00, // desktopSaveYGranularity (ignored): 20 (LE) + (byte)0x00, (byte)0x00, // pad2octetsA (ignored) + (byte)0x01, (byte)0x00, // maximumOrderLevel: ORD_LEVEL_1_ORDERS (1) + (byte)0x00, (byte)0x00, // number of fonts (ignored): 0 + (byte)0x4a, (byte)0x00, // orderFlags = 0x004a (LE), SOLIDPATTERNBRUSHONLY (0x40), ZEROBOUNDSDELTASSUPPORT (0x8, MUST), NEGOTIATEORDERSUPPORT (0x2, MUST) + // Order support: 32 bytes (no primary drawing orders are supported, so this array MUST be initialized to all zeros, use 0x01 for TRUE). + (byte)0x00, // TS_NEG_DSTBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_PATBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_SCRBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEMBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEM3BLT_INDEX: FALSE + (byte)0x00, // TS_NEG_ATEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_AEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_DRAWNINEGRID_INDEX: FALSE + (byte)0x00, // TS_NEG_LINETO_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTI_DRAWNINEGRID_INDEX: FALSE + (byte)0x00, // TS_NEG_OPAQUERECT_INDEX: FALSE + (byte)0x00, // TS_NEG_SAVEBITMAP_INDEX: FALSE + (byte)0x00, // TS_NEG_WTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_MEMBLT_R2_INDEX: FALSE + (byte)0x00, // TS_NEG_MEM3BLT_R2_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIDSTBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIPATBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTISCRBLT_INDEX: FALSE + (byte)0x00, // TS_NEG_MULTIOPAQUERECT_INDEX: FALSE + (byte)0x00, // TS_NEG_FAST_INDEX_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYGON_SC_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYGON_CB_INDEX: FALSE + (byte)0x00, // TS_NEG_POLYLINE_INDEX: TRUE + (byte)0x00, // Unused: 0 + (byte)0x00, // TS_NEG_FAST_GLYPH_INDEX: FALSE + (byte)0x00, // TS_NEG_ELLIPSE_SC_INDEX: FALSE + (byte)0x00, // TS_NEG_ELLIPSE_CB_INDEX: FALSE + (byte)0x00, // TS_NEG_INDEX_INDEX: FALSE + (byte)0x00, // TS_NEG_WEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_WLONGTEXTOUT_INDEX: FALSE + (byte)0x00, // TS_NEG_WLONGEXTTEXTOUT_INDEX: FALSE + (byte)0x00, // Unused: 0 + (byte)0x00, (byte)0x00, // Text flags (ignored): 0 (LE) + (byte)0x00, (byte)0x00, // Order support extra flags: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 4 bytes + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Desktop save size (ignored): 0 (assumed to be 230400 bytes (480*480, 0x38400, LE)) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0xe4, (byte)0x04, // Text ANSI Code Page: 1252, ANSI - Latin I (0x04e4, LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + // + // Bitmap Cache Rev. 2 Capability Set (40 bytes), see http://msdn.microsoft.com/en-us/library/cc240560.aspx + (byte)0x13, (byte)0x00, // capability set type: CAPSTYPE_BITMAPCACHE_REV2 (19) (LE) + (byte)0x28, (byte)0x00, // length of capability set: 40 bytes (LE) + (byte)0x00, (byte)0x00, // Cache flags: 0 (LE) + (byte)0x00, // Padding 1 byte + (byte)0x00, // Number of cell caches: 0 + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache0 cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache1 cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache2 cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache3 cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Bitmap cache4 cell info: 0 (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding 12 bytes + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Padding + + // + // Color Table Cache Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc241564.aspx + (byte)0x0a, (byte)0x00, // capability set type: CAPSTYPE_COLORCACHE (10) (LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x06, (byte)0x00, // Color table cache size (must be ignored during capability exchange and is assumed to be 0x0006): 6 (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + + // + // Window Activation Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240569.aspx + (byte)0x07, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7) (LE) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, // helpKeyFlag (should be set to FALSE (0)): FALSE (0, LE) + (byte)0x00, (byte)0x00, // helpKeyIndexFlag (should be set to FALSE (0)): FALSE (0, LE) + (byte)0x00, (byte)0x00, // helpExtendedKeyFlag (should be set to FALSE (0)): FALSE (0, LE) + (byte)0x00, (byte)0x00, // windowManagerKeyFlag (should be set to FALSE (0)): FALSE (0, LE) + + // + // Control Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240568.aspx + (byte)0x05, (byte)0x00, // capability set type: CAPSTYPE_ACTIVATION (7) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, // controlFlags (should be set to 0): 0 (LE) + (byte)0x00, (byte)0x00, // remoteDetachFlag (should be set to 0): 0 (LE) + (byte)0x02, (byte)0x00, // controlInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE) + (byte)0x02, (byte)0x00, // detachInterest (should be set to CONTROLPRIORITY_NEVER): CONTROLPRIORITY_NEVER (2) (LE) + + // + // Pointer Capability Set (10 bytes), see http://msdn.microsoft.com/en-us/library/cc240562.aspx + (byte)0x08, (byte)0x00, // capability set type: CAPSTYPE_POINTER (8, LE) + (byte)0x0a, (byte)0x00, // length of capability set: 10 bytes (LE) + (byte)0x00, (byte)0x00, // colorPointerFlag: FALSE (LE) + (byte)0x00, (byte)0x00, // colorPointerCacheSize: 0 (LE) + (byte)0x14, (byte)0x00, // pointerCacheSize: 20 (LE) + + // + // Share Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240570.aspx + (byte)0x09, (byte)0x00, // capability set type: CAPSTYPE_SHARE (9, LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, // nodeID (must be set to 0 by client): 0 (LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes (LE) + + // + // Input Capability Set (88 bytes), see http://msdn.microsoft.com/en-us/library/cc240563.aspx + (byte)0x0d, (byte)0x00, // capability set type: CAPSTYPE_INPUT (13, LE) + (byte)0x58, (byte)0x00, // length of capability set: 88 bytes (LE) + (byte)0x35, (byte)0x00, // inputFlags: 0x0035 (LE), INPUT_FLAG_FASTPATH_INPUT2 (0x20), INPUT_FLAG_VKPACKET (0x10), INPUT_FLAG_MOUSEX (0x4), INPUT_FLAG_SCANCODES (0x1) + (byte)0x00, (byte)0x00, // Padding 2 bytes + (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, // keyboardLayout: "US" keyboard layout (0x000409, LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardType: unknown (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardSubType: unknown (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // keyboardFunctionKey: unknown (LE) + // imeFileName: "", (64 bytes, including trailing NULL characters, UCS2) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + + // + // Brush Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240564.aspx + (byte)0x0f, (byte)0x00, // capability set type: CAPSTYPE_BRUSH (15, LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // brushSupportLevel: BRUSH_DEFAULT (0x0, LE) + + // + // Sound Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240552.aspx + (byte)0x0c, (byte)0x00, // capability set type: CAPSTYPE_SOUND (12, LE) + (byte)0x08, (byte)0x00, // length of capability set: 8 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // soundFlags: 0x0000 (LE) // SOUND_FLAG_BEEPS (0x1) + + // + // Font Capability Set (8 bytes), see http://msdn.microsoft.com/en-us/library/cc240571.aspx + (byte)0x0e, (byte)0x00, + (byte)0x08, (byte)0x00, + (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, + + // + // Offscreen Bitmap Cache Capability Set (12 bytes), see http://msdn.microsoft.com/en-us/library/cc240550.aspx + (byte)0x11, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (17, LE) + (byte)0x0c, (byte)0x00, // length of capability set: 12 bytes (LE) + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // offscreenSupportLevel: FALSE (LE) + (byte)0x00, (byte)0x00, // offscreenCacheSize: 0 (LE) + (byte)0x00, (byte)0x00, // offscreenCacheEntries: 0 (LE) + + // + // Glyph Cache Capability Set (52 bytes), see http://msdn.microsoft.com/en-us/library/cc240565.aspx + (byte)0x10, (byte)0x00, // capability set type: CAPSTYPE_OFFSCREENCACHE (16, LE) + (byte)0x34, (byte)0x00, // length of capability set: 52 bytes (LE) + // Glyph Cache (40 bytes) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x04, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x08, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x10, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x20, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x40, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x80, (byte)0x00, // CacheMaximumCellSize: 4 (LE) + (byte)0xfe, (byte)0x00, // CacheEntries: 254 (LE) + (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 4 (LE) + (byte)0x40, (byte)0x00, // CacheEntries: 64 (LE) + (byte)0x00, (byte)0x08, // CacheMaximumCellSize: 2048 (LE) + // FragCache + (byte)0x00, (byte)0x01, // CacheEntries: 256 (LE) + (byte)0x00, (byte)0x01, // CacheMaximumCellSize: 256 (LE) + // + (byte)0x00, (byte)0x00, // GlyphSupportLevel: GLYPH_SUPPORT_NONE (0x0, LE) + (byte)0x00, (byte)0x00, // Padding 2 bytes + }; + /* @formatter:on */ + + RdpState rdpState = new RdpState(); + ScreenDescription screenDescription = new ScreenDescription(); + screenDescription.setFramebufferSize(1024, 768); + + rdpState.serverShareId = 0x000103ea; + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {})); + Element confirm_active = new ClientConfirmActivePDU("confirm_active", screenDescription, rdpState); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, confirm_active, sink); + pipeline.link("source", "confirm_active", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientFastPathPDU.java old mode 100644 new mode 100755 similarity index 98% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientFastPathPDU.java index 39dc81a2f9c..108429c82bd --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientFastPathPDU.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientInfoPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientInfoPDU.java new file mode 100755 index 00000000000..61960049dfd --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientInfoPDU.java @@ -0,0 +1,456 @@ +// 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 rdpclient.rdp; + +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240475.aspx + */ +public class ClientInfoPDU extends OneTimeSwitch { + + public static final int INFO_MOUSE = 0x1; + public static final int INFO_DISABLECTRLALTDEL = 0x2; + public static final int INFO_UNICODE = 0x10; + + public static final int INFO_MAXIMIZESHELL = 0x20; + public static final int INFO_LOGONNOTIFY = 0x40; + public static final int INFO_ENABLEWINDOWSKEY = 0x100; + public static final int INFO_MOUSE_HAS_WHEEL = 0x00020000; + public static final int INFO_NOAUDIOPLAYBACK = 0x00080000; + + public static final int PERF_DISABLE_WALLPAPER = 0x1; + public static final int PERF_DISABLE_FULLWINDOWDRAG = 0x2; + public static final int PERF_DISABLE_MENUANIMATIONS = 0x4; + + protected byte[] userName = "".getBytes(RdpConstants.CHARSET_16); + protected byte[] password = "".getBytes(RdpConstants.CHARSET_16); // No effect + protected byte[] alternateShell = "".getBytes(RdpConstants.CHARSET_16); + protected byte[] domain = "".getBytes(RdpConstants.CHARSET_16); + protected byte[] workingDir = "".getBytes(RdpConstants.CHARSET_16); + protected byte[] clientAddress = "192.168.0.100".getBytes(RdpConstants.CHARSET_16); + protected byte[] clientDir = "C:\\Windows\\System32\\mstscax.dll".getBytes(RdpConstants.CHARSET_16); + + protected String standardTimeZoneName = "EET, Standard Time"; + protected String daylightTimeZoneName = "EET, Summer Time"; + protected int standardTimeZoneBias = 0; /* in minutes */ + protected int daylightTimeZoneBias = 60; /* in minutes */ + + public ClientInfoPDU(String id, String userName) { + super(id); + this.userName = userName.getBytes(RdpConstants.CHARSET_16); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + + // Length of packet + ByteBuffer buf = new ByteBuffer(1024, true); + + // MCS Send Data Request PDU + buf.writeByte(0x64); + + // Initiator: 0x03 + 1001 = 1004 + buf.writeShort(3); + + // Channel ID: 1003 + buf.writeShort(1003); + + // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70) + buf.writeByte(0x70); + + // User data length: (variable length field) + int length = 224 + userName.length + password.length + alternateShell.length + domain.length + workingDir.length + clientAddress.length + clientDir.length; + buf.writeShort(length | 0x8000); + + // Flags: SEC_INFO_PKT (0x4000) + buf.writeShort(0x4000); + + // TS_SECURITY_HEADER::flagsHi - ignored + buf.writeShort(0x0000); + + // Codepage: 0 (UNKNOWN, LE) (use 0x04090409 (1033,1033) for EN_US) + buf.writeIntLE(0x0000); + + // Flags + buf.writeIntLE(INFO_MOUSE | INFO_DISABLECTRLALTDEL | INFO_UNICODE | + INFO_MAXIMIZESHELL | INFO_LOGONNOTIFY | INFO_ENABLEWINDOWSKEY | + INFO_MOUSE_HAS_WHEEL | INFO_NOAUDIOPLAYBACK); + + // + // Lengths + // + + // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) + buf.writeShortLE(domain.length); + + // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator) + buf.writeShortLE(userName.length); + + // cbPassword length: (LE) (NOT including size of mandatory NULL terminator) + buf.writeShortLE(password.length); + + // cbAlternateShell: (LE) (NOT including size of mandatory NULL terminator) + buf.writeShortLE(alternateShell.length); + + // cbWorkingDir: (LE) (NOT including size of mandatory NULL terminator) + buf.writeShortLE(workingDir.length); + + // + // Values + // + + // Domain: (UCS2), see cbDomain + buf.writeBytes(domain); + buf.writeShort(0); + + // User name: (UCS2), see cbUserName + buf.writeBytes(userName); + buf.writeShort(0); + + // Password: (UCS2), see cbPassword + buf.writeBytes(password); + buf.writeShort(0); + + // Alternate shell: (UCS2), see cbAlternateShell + buf.writeBytes(alternateShell); + buf.writeShort(0); + + // Working directory: (UCS2), see cbWorkingDir + buf.writeBytes(workingDir); + buf.writeShort(0); + + // Client address family: 2 (AF_INET, LE) + buf.writeShortLE(2); + + // cbClientAddress: ( LE) (including the size of the mandatory NULL terminator) + buf.writeShortLE(clientAddress.length + 2); + + // Client address: (UCS2) + buf.writeBytes(clientAddress); + buf.writeShort(0); + + // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator) + buf.writeShortLE(clientDir.length + 2); + + // Client directory: (UCS2) + buf.writeBytes(clientDir); + buf.writeShort(0); + + // + // Client time zone: + // + + // Bias: 0 minutes (LE) + buf.writeIntLE(0); + + // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2) + buf.writeFixedString(62, standardTimeZoneName, RdpConstants.CHARSET_16); + buf.writeShort(0); + + // Standard date + buf.writeBytes(new byte[] { + // wYear: 0 (LE) + (byte)0x00, (byte)0x00, + // wMonth: unknown (LE) + (byte)0x00, (byte)0x00, + // wDayOfWeek: Sunday (LE) + (byte)0x00, (byte)0x00, + // wDay: unknown (LE) + (byte)0x00, (byte)0x00, + // wHour: 0 (LE) + (byte)0x00, (byte)0x00, + // wMinute: 0 (LE) + (byte)0x00, (byte)0x00, + // wSecond: 0 (LE) + (byte)0x00, (byte)0x00, + // wMilliseconds: 0 + (byte)0x00, (byte)0x00, + + }); + + // StandardBias: 0 minutes (LE) + buf.writeIntLE(standardTimeZoneBias); + + // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2) + buf.writeFixedString(62, daylightTimeZoneName, RdpConstants.CHARSET_16); + buf.writeShort(0); + + // Daylight date + buf.writeBytes(new byte[] { + // wYear: 0 (LE) + (byte)0x00, (byte)0x00, + // wMonth: unknown (LE) + (byte)0x00, (byte)0x00, + // wDayOfWeek: Sunday (LE) + (byte)0x00, (byte)0x00, + // wDay: unknown (LE) + (byte)0x00, (byte)0x00, + // wHour: 0 (LE) + (byte)0x00, (byte)0x00, + // wMinute: 0 (LE) + (byte)0x00, (byte)0x00, + // wSecond: 0 (LE) + (byte)0x00, (byte)0x00, + // wMilliseconds: 0 + (byte)0x00, (byte)0x00, + + }); + + // Daylight bias: 60 minutes (LE) + buf.writeIntLE(daylightTimeZoneBias); + + // Client session ID: 0x00000000 (LE) + buf.writeIntLE(0); + + // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4) + buf.writeIntLE(PERF_DISABLE_WALLPAPER | PERF_DISABLE_FULLWINDOWDRAG | PERF_DISABLE_MENUANIMATIONS); + + // cbAutoReconnectCookie: 0 bytes (LE) + buf.writeShortLE(0); + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + + // TPKT + (byte) 0x03, (byte) 0x00, + + // TPKT length: 343 bytes + (byte) 0x01, (byte) 0x57, + + // X224 Data PDU + (byte) 0x02, (byte) 0xf0, (byte) 0x80, + + + // MCS Send Data Request PDU + (byte) 0x64, + + // Initiator: 0x03 + 1001 = 1004 + (byte) 0x00, (byte) 0x03, + + // Channel ID: 1003 (IO Channel) + (byte) 0x03, (byte) 0xeb, + + // Data priority: high, segmentation: begin | end (0x40 | 0x20 | 0x10 = 0x70) + (byte) 0x70, + + // User data length: 328 (0x148) bytes, variable length field + (byte) 0x81, (byte) 0x48, + + // Flags: SEC_INFO_PKT (0x4000) + (byte) 0x40, (byte) 0x00, + + // TS_SECURITY_HEADER::flagsHi - ignored + (byte) 0x00, (byte) 0x00, + + // Codepage: 0 (UNKNOWN, LE) (use 0x04090409 (1033,1033) for EN_US) + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Flags: 0xa0173 (LE), INFO_MOUSE (0x1), INFO_DISABLECTRLALTDEL (0x2), INFO_UNICODE (0x10), + // INFO_MAXIMIZESHELL (0x20), INFO_LOGONNOTIFY (0x40), INFO_ENABLEWINDOWSKEY (0x100), + // INFO_MOUSE_HAS_WHEEL (0x00020000), INFO_NOAUDIOPLAYBACK (0x00080000), + (byte) 0x73, (byte) 0x01, (byte) 0x0a, (byte) 0x00, + + // Lengths + + // cbDomain length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) + (byte) 0x00, (byte) 0x00, + + // cbUserName length: 16 bytes (0x10, LE) (NOT including size of mandatory NULL terminator) + (byte) 0x10, (byte) 0x00, + + // cbPassword length: 0 bytes (LE) (NOT including size of mandatory NULL terminator) + (byte) 0x00, (byte) 0x00, + + // cbAlternateShell: 0 bytes (LE) (NOT including size of mandatory NULL terminator) + (byte) 0x00, (byte) 0x00, + + // cbWorkingDir: 0 bytes (LE) (NOT including size of mandatory NULL terminator) + (byte) 0x00, (byte) 0x00, + + // Values + + // Domain: "" (UCS2), see cbDomain + (byte) 0x00, (byte) 0x00, + + // User name: "vlisivka" (UCS2), see cbUserName + (byte) 0x76, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x73, (byte) 0x00, + (byte) 0x69, (byte) 0x00, (byte) 0x76, (byte) 0x00, (byte) 0x6b, (byte) 0x00, (byte) 0x61, (byte) 0x00, + (byte) 0x00, (byte) 0x00, + + // Password: "" (UCS2), see cbPassword + (byte) 0x00, (byte) 0x00, + + // Alternate shell: "" (UCS2), see cbAlternateShell + (byte) 0x00, (byte) 0x00, + + // Working directory: "" (UCS2), see cbWorkingDir + (byte) 0x00, (byte) 0x00, + + // Client address family: 2 (AF_INET, LE) + (byte) 0x02, (byte) 0x00, + + // cbClientAddress = 28 bytes (0x1c, LE) (including the size of the mandatory NULL terminator) + (byte) 0x1c, (byte) 0x00, + + // Client address: "192.168.0.100" (UCS2) + (byte) 0x31, (byte) 0x00, (byte) 0x39, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x2e, (byte) 0x00, + (byte) 0x31, (byte) 0x00, (byte) 0x36, (byte) 0x00, (byte) 0x38, (byte) 0x00, (byte) 0x2e, (byte) 0x00, + (byte) 0x30, (byte) 0x00, (byte) 0x2e, (byte) 0x00, (byte) 0x31, (byte) 0x00, (byte) 0x30, (byte) 0x00, + (byte) 0x30, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // cbClientDir: 64 bytes (0x40, LE) (including the size of the mandatory NULL terminator) + (byte) 0x40, (byte) 0x00, + + // Client directory: "C:\Windows\System32\mstscax.dll" (UCS2) + (byte) 0x43, (byte) 0x00, (byte) 0x3a, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x57, (byte) 0x00, + (byte) 0x69, (byte) 0x00, (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x6f, (byte) 0x00, + (byte) 0x77, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x5c, (byte) 0x00, (byte) 0x53, (byte) 0x00, + (byte) 0x79, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x65, (byte) 0x00, + (byte) 0x6d, (byte) 0x00, (byte) 0x33, (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x5c, (byte) 0x00, + (byte) 0x6d, (byte) 0x00, (byte) 0x73, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x73, (byte) 0x00, + (byte) 0x63, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x78, (byte) 0x00, (byte) 0x2e, (byte) 0x00, + (byte) 0x64, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x6c, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // + // Client time zone: + + // Bias: 0 minutes (LE) + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Standard name: "EET, Standard Time" (fixed string: 64 bytes, UCS2) + (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x74, (byte) 0x00, (byte) 0x61, (byte) 0x00, + (byte) 0x6e, (byte) 0x00, (byte) 0x64, (byte) 0x00, (byte) 0x61, (byte) 0x00, (byte) 0x72, (byte) 0x00, + (byte) 0x64, (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, + (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // + // Standard date + // wYear: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMonth: unknown (LE) + (byte) 0x00, (byte) 0x00, + // wDayOfWeek: Sunday (LE) + (byte) 0x00, (byte) 0x00, + // wDay: unknown (LE) + (byte) 0x00, (byte) 0x00, + // wHour: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMinute: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wSecond: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMilliseconds: 0 + (byte) 0x00, (byte) 0x00, + + // StandardBias: 0 minutes (LE) + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Daylight name: "EET, Summer Time" (fixed string: 64 bytes, UCS2) + (byte) 0x45, (byte) 0x00, (byte) 0x45, (byte) 0x00, (byte) 0x54, (byte) 0x00, (byte) 0x2c, (byte) 0x00, + (byte) 0x20, (byte) 0x00, (byte) 0x53, (byte) 0x00, (byte) 0x75, (byte) 0x00, (byte) 0x6d, (byte) 0x00, + (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, (byte) 0x72, (byte) 0x00, (byte) 0x20, (byte) 0x00, + (byte) 0x54, (byte) 0x00, (byte) 0x69, (byte) 0x00, (byte) 0x6d, (byte) 0x00, (byte) 0x65, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Daylight date + // wYear: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMonth: unknown (LE) + (byte) 0x00, (byte) 0x00, + // wDayOfWeek: Sunday (LE) + (byte) 0x00, (byte) 0x00, + // wDay: unknown (LE) + (byte) 0x00, (byte) 0x00, + // wHour: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMinute: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wSecond: 0 (LE) + (byte) 0x00, (byte) 0x00, + // wMilliseconds: 0 + (byte) 0x00, (byte) 0x00, + + // Daylight bias: 60 minutes (LE) + (byte) 0x3c, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + + // Client session ID: 0x00000000 (LE) + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // Performance flags: 0x7 (LE) = PERF_DISABLE_WALLPAPER (0x1), PERF_DISABLE_FULLWINDOWDRAG (0x2), PERF_DISABLE_MENUANIMATIONS (0x4) + (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, + + // cbAutoReconnectCookie: 0 bytes (LE) + (byte) 0x00, (byte) 0x00, + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + Element client_info = new ClientInfoPDU("client_info", "vlisivka"); + Element x224 = new ClientX224DataPDU("x224"); + Element tpkt = new ClientTpkt("tpkt"); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, client_info, x224, tpkt, sink, mainSink); + pipeline.link("source", "client_info", "mainSink"); + pipeline.link("client_info >" + OTOUT, "x224", "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSAttachUserRequest.java old mode 100644 new mode 100755 similarity index 77% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSAttachUserRequest.java index 55ca08acb53..47a07da73e0 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSAttachUserRequest.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * @see http://msdn.microsoft.com/en-us/library/cc240684.aspx @@ -65,30 +65,30 @@ public class ClientMCSAttachUserRequest extends OneTimeSwitch { // System.setProperty("streamer.Pipeline.debug", "true"); /* @formatter:off */ - byte[] packet = new byte[] { + byte[] packet = new byte[] { - 0x03, 0x00, 0x00, 0x08, // TPKT Header (length = 8 bytes) - 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU + 0x03, 0x00, 0x00, 0x08, // TPKT Header (length = 8 bytes) + 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU - // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: - 0x28, + // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: + 0x28, - // 0x28: - // 0 - --\ - // 0 - | - // 1 - | CHOICE: From DomainMCSPDU select attachUserRequest (10) - // 0 - | of type AttachUserRequest - // 1 - | - // 0 - --/ - // 0 - padding - // 0 - padding + // 0x28: + // 0 - --\ + // 0 - | + // 1 - | CHOICE: From DomainMCSPDU select attachUserRequest (10) + // 0 - | of type AttachUserRequest + // 1 - | + // 0 - --/ + // 0 - padding + // 0 - padding - }; - /* @formatter:on */ + }; + /* @formatter:on */ MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); Element todo = new ClientMCSAttachUserRequest("TODO"); - Element x224 = new ClientX224DataPdu("x224"); + Element x224 = new ClientX224DataPDU("x224"); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java old mode 100644 new mode 100755 similarity index 65% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java index 6e06af48cb5..12d94381310 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * The MCS Channel Join Request PDUs are sent sequentially. The first PDU is @@ -60,13 +60,13 @@ public class ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs extends OneT // int flags = typeAndFlags & 0x3; if (type != MCS_CHANNEL_CONFIRM_PDU) - throw new RuntimeException("[" + this + "] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 15, actual value: " + type + ", data: " + buf + - "."); + throw new RuntimeException("[" + this + "] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 15, actual value: " + type + ", data: " + buf + "."); int rtSuccess = buf.readUnsignedByte() >> 4; if (rtSuccess != 0) - throw new RuntimeException("[" + this + "] ERROR: Cannot connect to channel: request failed. Error code: " + rtSuccess + ", channel ID: " + - channels[channelRequestsSent - 1] + ", data: " + buf + "."); + throw new RuntimeException("[" + this + "] ERROR: Cannot connect to channel: request failed. Error code: " + rtSuccess + ", channel ID: " + + channels[channelRequestsSent - 1] + + ", data: " + buf + "."); // Initiator and requested fields MAY be ignored, however, the channelId // field MUST be examined. If the value of the channelId field does not @@ -84,8 +84,8 @@ public class ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs extends OneT // Actual channel int actualChannel = buf.readUnsignedShort(); if (actualChannel != channels[channelRequestsSent - 1]) - throw new RuntimeException("Unexpeceted channeld ID returned. Expected channeld ID: " + channels[channelRequestsSent - 1] + ", actual channel ID: " + - actualChannel + ", data: " + buf + "."); + throw new RuntimeException("Unexpeceted channeld ID returned. Expected channeld ID: " + channels[channelRequestsSent - 1] + ", actual channel ID: " + + actualChannel + ", data: " + buf + "."); state.channelJoined(actualChannel); @@ -111,7 +111,7 @@ public class ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs extends OneT buf.writeByte(0x38); // Channel Join request - buf.writeShort(0x03); // ChannelJoinRequest::initiator: 1004 + buf.writeShort(state.serverUserChannelId - 1001); // ChannelJoinRequest::initiator: 1004 buf.writeShort(channel); pushDataToOTOut(buf); @@ -128,86 +128,87 @@ public class ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs extends OneT // System.setProperty("streamer.Pipeline.debug", "true"); /* @formatter:off */ - byte[] clientRequestPacket = new byte[] { - 0x03, 0x00, 0x00, 0x0c, // TPKT Header (length = 12 bytes) - 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU + byte[] clientRequestPacket = new byte[] { + 0x03, 0x00, 0x00, 0x0c, // TPKT Header (length = 12 bytes) + 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU - // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: - 0x38, 0x00, 0x03, 0x03, (byte) 0xef, + // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: + 0x38, 0x00, 0x03, 0x03, (byte) 0xef, - // 0x38: - // 0 - --\ - // 0 - | - // 1 - | CHOICE: From DomainMCSPDU select channelJoinRequest (14) - // 1 - | of type ChannelJoinRequest - // 1 - | - // 0 - --/ - // 0 - padding - // 0 - padding + // 0x38: + // 0 - --\ + // 0 - | + // 1 - | CHOICE: From DomainMCSPDU select channelJoinRequest (14) + // 1 - | of type ChannelJoinRequest + // 1 - | + // 0 - --/ + // 0 - padding + // 0 - padding - // 0x00: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // | ChannelJoinRequest::initiator = 0x03 + 1001 = 1004 - // 0x03: | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 1 - | - // 1 - | - // 0 - --/ + // 0x00: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // | ChannelJoinRequest::initiator = 0x03 + 1001 = 1004 + // 0x03: | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 1 - | + // 1 - | + // 0 - --/ - // 0x03: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 0 - | - // 1 - | - // 1 - | - // | ChannelJoinRequest::channelId = 0x03ef = 1007 - // 0xef: | - // 1 - | - // 1 - | - // 1 - | - // 0 - | - // 1 - | - // 1 - | - // 1 - | - // 1 - --/ - }; + // 0x03: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 0 - | + // 1 - | + // 1 - | + // | ChannelJoinRequest::channelId = 0x03ef = 1007 + // 0xef: | + // 1 - | + // 1 - | + // 1 - | + // 0 - | + // 1 - | + // 1 - | + // 1 - | + // 1 - --/ + }; - byte[] serverResponsePacket = new byte[] { - // MCS Channel Confirm - (byte)0x3e, + byte[] serverResponsePacket = new byte[] { + // MCS Channel Confirm + (byte)0x3e, - // result: rt-successful (0) - (byte)0x00, + // result: rt-successful (0) + (byte)0x00, - // Initiator: 1007 (6+1001) - (byte)0x00, (byte)0x06, + // Initiator: 1007 (6+1001) + (byte)0x00, (byte)0x06, - // Requested channel - (byte)0x03, (byte)0xef, + // Requested channel + (byte)0x03, (byte)0xef, - // Actual channel - (byte)0x03, (byte)0xef, - }; - /* @formatter:on */ + // Actual channel + (byte)0x03, (byte)0xef, + }; + /* @formatter:on */ RdpState rdpState = new RdpState(); + rdpState.serverUserChannelId = 1004; MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(serverResponsePacket, new byte[] {1, 2, 3})); Element todo = new ClientMCSChannelJoinRequestServerMCSChannelConfirmPDUs("channels", new int[] {1007}, rdpState); - Element x224 = new ClientX224DataPdu("x224"); + Element x224 = new ClientX224DataPDU("x224"); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(clientRequestPacket)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSConnectInitial.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSConnectInitial.java new file mode 100755 index 00000000000..37730aa403a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSConnectInitial.java @@ -0,0 +1,696 @@ +// 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 rdpclient.rdp; + +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ClientMCSConnectInitial extends OneTimeSwitch { + + public ClientMCSConnectInitial(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + throw new RuntimeException("Unexpected packet: " + buf + "."); + } + + @Override + protected void onStart() { + super.onStart(); + + int length = 1024; // Large enough + ByteBuffer buf = new ByteBuffer(length, true); + + /* @formatter:off */ + buf.writeBytes(new byte[] { + (byte)0x7f, (byte)0x65, (byte)0x82, (byte)0x01, (byte)0x6d, (byte)0x04, (byte)0x01, (byte)0x01, (byte)0x04, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0xff, (byte)0x30, (byte)0x1a, + (byte)0x02, (byte)0x01, (byte)0x22, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, + (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x30, (byte)0x19, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, + (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x02, + (byte)0x04, (byte)0x20, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x30, (byte)0x20, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xfc, + (byte)0x17, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, + (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x82, (byte)0x01, (byte)0x07, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7c, + (byte)0x00, (byte)0x01, (byte)0x80, (byte)0xfe, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x01, (byte)0xc0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, + (byte)0x80, (byte)0xf0, (byte)0x01, (byte)0xc0, (byte)0xd8, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x03, (byte)0x01, (byte)0xca, + (byte)0x03, (byte)0xaa, (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x0a, (byte)0x00, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6f, (byte)0x00, + (byte)0x6c, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x2e, (byte)0x00, (byte)0x76, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x73, (byte)0x00, + (byte)0x69, (byte)0x00, (byte)0x76, (byte)0x00, (byte)0x6b, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0xca, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, + (byte)0x07, (byte)0x00, (byte)0x21, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xc0, (byte)0x0c, (byte)0x00, (byte)0x0d, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0xc0, (byte)0x0c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, + }); +// +// buf.writeBytes(new byte[] { +//// - T125: MCSConnect Initial +//// - MCSConnectInitial: Identifier=Generic Conference Control (0.0.20.124.0.1), ConnectPDULength=254 +//// - ConnectInitialHeader: +// (byte)0x7F, (byte)0x65, +//// - AsnId: Application Constructed Tag (101) +//// - HighTag: +//// Class: (01......) Application (1) +//// Type: (..1.....) Constructed +//// TagNumber: (...11111) +//// TagValueEnd: 101 (0x65) +// (byte)0x82, (byte)0x01, (byte)0x6C, +//// - AsnLen: Length = 364, LengthOfLength = 2 +//// LengthType: LengthOfLength = 2 +//// Length: 364 bytes +// (byte)0x04, (byte)0x01, (byte)0x01, +//// - CallingDomainSelector: 0x1 +//// - AsnOctetStringHeader: +//// - AsnId: OctetString type (Universal 4) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00100) 4 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// OctetStream: 0x1 +// (byte)0x04, (byte)0x01, (byte)0x01, +//// - CalledDomainSelector: 0x1 +//// - AsnOctetStringHeader: +//// - AsnId: OctetString type (Universal 4) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00100) 4 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// OctetStream: 0x1 +// (byte)0x01, (byte)0x01, (byte)0xFF, +//// - UpwardFlag: True +//// - AsnBooleanHeader: +//// - AsnId: Boolean type (Universal 1) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00001) 1 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// Tf: 255 (0xFF) +// +//// +//// - TargetParameters: Length = 26, LengthOfLength = 0 +// (byte)0x30, (byte)0x1A, +//// - DomainParametersHeader: 0x1 +//// - AsnId: Sequence and SequenceOf types (Universal 16) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..1.....) Constructed +//// TagValue: (...10000) 16 +//// - AsnLen: Length = 26, LengthOfLength = 0 +//// Length: 26 bytes, LengthOfLength = 0 +// (byte)0x02, (byte)0x01, (byte)0x22, +//// - ChannelIds: 34 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 34 (0x22) +// (byte)0x02, (byte)0x01, (byte)0x02, +//// - UserIDs: 2 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 2 (0x2) +// (byte)0x02, (byte)0x01, (byte)0x00, +//// - TokenIds: 0 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 0 (0x0) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - NumPriorities: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x00, +//// - MinThroughput: 0 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 0 (0x0) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - Height: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, +//// - MCSPDUsize: 65535 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 3, LengthOfLength = 0 +//// Length: 3 bytes, LengthOfLength = 0 +//// AsnInt: 65535 (0xFFFF) +// (byte)0x02, (byte)0x01, (byte)0x02, +//// - protocolVersion: 2 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 2 (0x2) +// +//// +//// - MinimumParameters: Length = 25, LengthOfLength = 0 +// (byte)0x30, (byte)0x19, +//// - DomainParametersHeader: 0x1 +//// - AsnId: Sequence and SequenceOf types (Universal 16) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..1.....) Constructed +//// TagValue: (...10000) 16 +//// - AsnLen: Length = 25, LengthOfLength = 0 +//// Length: 25 bytes, LengthOfLength = 0 +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - ChannelIds: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - UserIDs: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - TokenIds: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - NumPriorities: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x00, +//// - MinThroughput: 0 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 0 (0x0) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - Height: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, +//// - MCSPDUsize: 1056 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 2, LengthOfLength = 0 +//// Length: 2 bytes, LengthOfLength = 0 +//// AsnInt: 1056 (0x420) +// (byte)0x02, (byte)0x01, (byte)0x02, +//// - protocolVersion: 2 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 2 (0x2) +//// - MaximumParameters: Length = 31, LengthOfLength = 0 +//// - DomainParametersHeader: 0x1 +// (byte)0x30, (byte)0x1F, +//// - AsnId: Sequence and SequenceOf types (Universal 16) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..1.....) Constructed +//// TagValue: (...10000) 16 +//// - AsnLen: Length = 31, LengthOfLength = 0 +//// Length: 31 bytes, LengthOfLength = 0 +// (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, +//// - ChannelIds: 65535 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 3, LengthOfLength = 0 +//// Length: 3 bytes, LengthOfLength = 0 +//// AsnInt: 65535 (0xFFFF) +// (byte)0x02, (byte)0x02, (byte)0xFC, (byte)0x17, +//// - UserIDs: 64535 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 2, LengthOfLength = 0 +//// Length: 2 bytes, LengthOfLength = 0 +//// AsnInt: 64535 (0xFC17) +// (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, +//// - TokenIds: 65535 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 3, LengthOfLength = 0 +//// Length: 3 bytes, LengthOfLength = 0 +//// AsnInt: 65535 (0xFFFF) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - NumPriorities: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x01, (byte)0x00, +//// - MinThroughput: 0 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 0 (0x0) +// (byte)0x02, (byte)0x01, (byte)0x01, +//// - Height: 1 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 1 (0x1) +// (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xFF, (byte)0xFF, +//// - MCSPDUsize: 65535 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 3, LengthOfLength = 0 +//// Length: 3 bytes, LengthOfLength = 0 +//// AsnInt: 65535 (0xFFFF) +// (byte)0x02, (byte)0x01, (byte)0x02, +//// - protocolVersion: 2 +//// - AsnIntegerHeader: +//// - AsnId: Integer type (Universal 2) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00010) 2 +//// - AsnLen: Length = 1, LengthOfLength = 0 +//// Length: 1 bytes, LengthOfLength = 0 +//// AsnInt: 2 (0x2) +//// - UserData: Identifier=Generic Conference Control (0.0.20.124.0.1), ConnectPDULength=254 +//// - UserDataHeader: +// (byte)0x04, (byte)0x82, (byte)0x01, (byte)0x07, +//// - AsnId: OctetString type (Universal 4) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00100) 4 +//// - AsnLen: Length = 263, LengthOfLength = 2 +//// LengthType: LengthOfLength = 2 +//// Length: 263 bytes +// (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7C, (byte)0x00, (byte)0x01, +//// - AsnBerObjectIdentifier: Generic Conference Contro (0.0.20.124.0.1) +//// - AsnObjectIdentifierHeader: +//// - AsnId: Reserved for use by the encoding rules (Universal 0) +//// - LowTag: +//// Class: (00......) Universal (0) +//// Type: (..0.....) Primitive +//// TagValue: (...00000) 0 +//// - AsnLen: Length = 5, LengthOfLength = 0 +//// Length: 5 bytes, LengthOfLength = 0 +//// First: 0 (0x0) +//// Final: 20 (0x14) +//// Final: 124 (0x7C) +//// Final: 0 (0x0) +//// Final: 1 (0x1) +// (byte)0x80, (byte)0xFE, +//// - ConnectPDULength: 254 +//// Align: No Padding +//// Length: 254 +// (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, +//// - ConnectGCCPDU: conferenceCreateRequest +//// ExtensionBit: 0 (0x0) +//// - ChoiceValue: conferenceCreateRequest +//// Value: (000.....) 0x0 +//// - conferenceCreateRequest: +//// ExtensionBit: 0 (0x0) +//// convenerPasswordPresent: 0 (0x0) +//// passwordPresent: 0 (0x0) +//// conductorPrivilegesPresent: 0 (0x0) +//// conductedPrivilegesPresent: 0 (0x0) +//// nonConductedPrivilegesPresent: 0 (0x0) +//// conferenceDescriptionPresent: 0 (0x0) +//// callerIdentifierPresent: 0 (0x0) +//// userDataPresent: 1 (0x1) +//// - conferenceName: +//// ExtensionBit: 0 (0x0) +//// textPresent: 0 (0x0) +//// - numeric: 1 +//// - SimpleNumericString: 1 +//// - NumericString: 1 +//// - Align: No Padding +//// Padding1: (0.......) 0x0 +//// - Length: 1 +//// Value: (00000000) 0x0 +//// - Restrictedstr: 1 +//// FourBits: (0001....) 0x1 +//// - lockedConference: False +//// Value: False 0....... +//// - listedConference: False +//// Value: False 0....... +//// - conductibleConference: False +//// Value: False 0....... +//// - TerminationMethod: automatic +//// ExtensionBit: 0 (0x0) +//// - RootIndex: 0 +//// Value: (0.......) 0x0 +//// - userData: +// (byte)0x00, (byte)0x01, +//// - Size: 1 +//// - Align: No Padding +//// Padding7: (0000000.) 0x0 +//// Length: 1 +//// - UserData: 0x44756361 +// (byte)0xC0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, +//// valuePresent: 1 (0x1) +//// - key: h221NonStandard "Duca" +//// - ChoiceValue: h221NonStandard +//// Value: (1.......) 0x1 +//// - h221NonStandard: +//// - H221NonStandardIdentifier: length: 4 +//// - ConstrainedLength: 4 +//// Value: (00000000) 0x0 +//// - Align: No Padding +//// Padding6: (000000..) 0x0 +//// Value: Binary Large Object (4 Bytes) "Duca" +//// - ClientMcsConnectInitialPdu: +// (byte)0x80, (byte)0xF0, +//// - RDPGCCUserDataRequestLength: 240 +//// Align: No Padding +//// Length: 240 +//// - TsUd: CS_CORE +// (byte)0x01, (byte)0xC0, (byte)0xD8, (byte)0x00, +//// - TsUdHeader: Type = CS_CORE, Length = 216 +//// Type: CS_CORE +//// Length: 216 (0xD8) +//// - TsUdCsCore: +// (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, +//// Version: RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 +// (byte)0x00, (byte)0x04, +//// DesktopWidth: 1024 (0x400) +// (byte)0x00, (byte)0x03, +//// DesktopHeight: 768 (0x300) +// (byte)0x01, (byte)0xCA, +//// ColorDepth: 8 bpp +// (byte)0x03, (byte)0xAA, +//// SASSequence: 0xaa03, SHOULD be set to RNS_UD_SAS_DEL(0xAA03) +// (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, +//// KeyboardLayout: Language: English, Location: United States +// (byte)0x28, (byte)0x0A, (byte)0x00, (byte)0x00, +//// ClientBuild: 2600 (0xA28) +// (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6C, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// ClientName: apollo3 +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// KeyboardType: Undefined value: 0 +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// KeyboardSubType: 0 (0x0) +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// KeyboardFunctionKey: 0 (0x0) +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// ImeFileName: +// (byte)0x01, (byte)0xCA, +//// PostBeta2ColorDepth: 8 bpp +// (byte)0x01, (byte)0x00, +//// ClientProductId: 0x1, SHOULD be set to initialized to 1 +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// SerialNumber: 0x0, SHOULD be set to 0 +// (byte)0x10, (byte)0x00, +//// HighColorDepth: 16-bit 565 RGB +// (byte)0x07, (byte)0x00, +//// - SupportedColorDepth: 7 (0x7) +//// Support24BPP: (...............1) Support 24BPP +//// Support16BPP: (..............1.) Support 16BPP +//// Support15BPP: (.............1..) Support 15BPP +//// Support32BPP: (............0...) Not Support 32BPP +//// Reserved: (000000000000....) +// (byte)0x01, (byte)0x00, +//// - EarlyCapabilityFlags: 1 (0x1) +//// SupportSetErrorPdu: (...............1) Indicates that the client supports the Set Error Info PDU +//// Want32BppSession: (..............0.) Client is not requesting 32BPP session +//// SupportStatusInfoPdu: (.............0..) Client not supports the Server Status Info PDU +//// StrongAsymmetricKeys: (............0...) Not support asymmetric keys larger than 512-bits +//// Unused: (...........0....) +//// ValidConnection: (..........0.....) Not Indicates ConnectionType field contains valid data +//// SupportMonitorLayoutPdu: (.........0......) Not Indicates that the client supports the Monitor Layout PDU +//// Unused2: (000000000.......) +// (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// ClientDigProductId: +//(byte)0x00, +//// connectionType: invalid connection type +//(byte)0x00, +//// pad1octet: 0 (0x0) +//(byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, +//// ServerSelectedProtocols: TLS 1.0 +//// +//// - TsUd: CS_CLUSTER +//// - TsUdHeader: Type = CS_CLUSTER, Length = 12 +//(byte)0x04, (byte)0xC0, +//// Type: CS_CLUSTER +//(byte)0x0C, (byte)0x00, +//// Length: 12 (0xC) +//(byte)0x0D, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// - TsUdCsCluster: +//// - Flags: 13 (0xD) +//// RedirectedSupported: (...............................1) Support Redirected +//// SessionIDFieldValid: (..............................0.) SessionID Field not Valid +//// SupportedVersion: (..........................0011..) REDIRECTION_VERSION4 +//// RedirectedSmartcard: (.........................0......) Not Logon with Smartcard +//// Unused: (0000000000000000000000000.......) +//// RedirectedSessionID: 0 (0x0) +//// +//// - TsUd: CS_SECURITY +//// - TsUdHeader: Type = CS_SECURITY, Length = 12 +//(byte)0x02, (byte)0xC0, +//// Type: CS_SECURITY +//(byte)0x0C, (byte)0x00, +//// Length: 12 (0xC) +//// +//// - TsUdCsSec: +//(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// - EncryptionMethod: +//// Support40Bit: (...............................0) Not Support +//// Support128Bit: (..............................0.) Not Support 128-bit +//// Reserved1: (.............................0..) +//// Support56Bit: (............................0...) Not Support 56-bit +//// SupportFIPS: (...........................0....) Not Support FIPS Compliant +//// Reserved2: (000000000000000000000000000.....) +//(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, +//// - ExtEncryptionMethod: +//// Support40Bit: (...............................0) Not Support +//// Support128Bit: (..............................0.) Not Support 128-bit +//// Reserved1: (.............................0..) +//// Support56Bit: (............................0...) Not Support 56-bit +//// SupportFIPS: (...........................0....) Not Support FIPS Compliant +//// Reserved2: (000000000000000000000000000.....) +// }); + /* @formatter:on */ + + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + * + * @see http://msdn.microsoft.com/en-us/library/cc240836.aspx + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + // TPKT: TPKT version = 3 + (byte) 0x03, (byte) 0x00, + // TPKT: Packet length: 378 bytes + (byte) 0x01, (byte) 0x78, + + // X.224: Length indicator = 2 + (byte) 0x02, + // X.224: Type: Data TPDU + (byte) 0xf0, + // X.224: EOT + (byte) 0x80, + + // Captured packet + (byte)0x7f, (byte)0x65, (byte)0x82, (byte)0x01, (byte)0x6c, (byte)0x04, (byte)0x01, (byte)0x01, (byte)0x04, + (byte)0x01, (byte)0x01, (byte)0x01, (byte)0x01, (byte)0xff, (byte)0x30, (byte)0x1a, (byte)0x02, (byte)0x01, (byte)0x22, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x01, (byte)0x00, + (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, + (byte)0x02, (byte)0x30, (byte)0x19, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, + (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x02, (byte)0x04, (byte)0x20, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x30, (byte)0x1f, (byte)0x02, (byte)0x03, + (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x02, (byte)0xfc, (byte)0x17, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, + (byte)0x01, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x00, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x82, (byte)0x01, + (byte)0x07, (byte)0x00, (byte)0x05, (byte)0x00, (byte)0x14, (byte)0x7c, (byte)0x00, (byte)0x01, (byte)0x80, (byte)0xfe, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x01, + (byte)0xc0, (byte)0x00, (byte)0x44, (byte)0x75, (byte)0x63, (byte)0x61, (byte)0x80, (byte)0xf0, (byte)0x01, (byte)0xc0, (byte)0xd8, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x08, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x03, (byte)0x01, (byte)0xca, (byte)0x03, (byte)0xaa, (byte)0x09, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x0a, (byte)0x00, (byte)0x00, + (byte)0x61, (byte)0x00, (byte)0x70, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6c, (byte)0x00, (byte)0x6f, (byte)0x00, (byte)0x33, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0xca, (byte)0x01, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x10, (byte)0x00, (byte)0x07, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x04, (byte)0xc0, (byte)0x0c, (byte)0x00, (byte)0x0d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02, (byte)0xc0, (byte)0x0c, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + }; + /* @formatter:on */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + Element todo = new ClientMCSConnectInitial("ClientMCSConnectInitial"); + Element x224 = new ClientX224DataPDU("x224"); + Element tpkt = new ClientTpkt("tpkt"); + + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); + + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, todo, x224, tpkt, sink, mainSink); + pipeline.link("source", "ClientMCSConnectInitial", "mainSink"); + pipeline.link("ClientMCSConnectInitial >" + OTOUT, "x224", "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSErectDomainRequest.java old mode 100644 new mode 100755 similarity index 67% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSErectDomainRequest.java index 4cb00aad2ae..a6ea406d8c1 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientMCSErectDomainRequest.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * @see http://msdn.microsoft.com/en-us/library/cc240683.aspx @@ -74,71 +74,71 @@ public class ClientMCSErectDomainRequest extends OneTimeSwitch { // System.setProperty("streamer.Pipeline.debug", "true"); /* @formatter:off */ - byte[] packet = new byte[] { + byte[] packet = new byte[] { - 0x03, 0x00, 0x00, 0x0c, // TPKT Header (length = 12 bytes) - 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU + 0x03, 0x00, 0x00, 0x0c, // TPKT Header (length = 12 bytes) + 0x02, (byte) 0xf0, (byte) 0x80, // X.224 Data TPDU - // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: - 0x04, 0x01, 0x00, 0x01, 0x00, + // PER encoded (ALIGNED variant of BASIC-PER) PDU contents: + 0x04, 0x01, 0x00, 0x01, 0x00, - // 0x04: - // 0 - --\ - // 0 - | - // 0 - | CHOICE: From DomainMCSPDU select erectDomainRequest (1) - // 0 - | of type ErectDomainRequest - // 0 - | - // 1 - --/ - // 0 - padding - // 0 - padding + // 0x04: + // 0 - --\ + // 0 - | + // 0 - | CHOICE: From DomainMCSPDU select erectDomainRequest (1) + // 0 - | of type ErectDomainRequest + // 0 - | + // 1 - --/ + // 0 - padding + // 0 - padding - // 0x01: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | ErectDomainRequest::subHeight length = 1 byte - // 0 - | - // 0 - | - // 0 - | - // 1 - --/ + // 0x01: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | ErectDomainRequest::subHeight length = 1 byte + // 0 - | + // 0 - | + // 0 - | + // 1 - --/ - // 0x00: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | ErectDomainRequest::subHeight = 0 - // 0 - | - // 0 - | - // 0 - | - // 0 - --/ + // 0x00: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | ErectDomainRequest::subHeight = 0 + // 0 - | + // 0 - | + // 0 - | + // 0 - --/ - // 0x01: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | ErectDomainRequest::subInterval length = 1 byte - // 0 - | - // 0 - | - // 0 - | - // 1 - --/ + // 0x01: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | ErectDomainRequest::subInterval length = 1 byte + // 0 - | + // 0 - | + // 0 - | + // 1 - --/ - // 0x00: - // 0 - --\ - // 0 - | - // 0 - | - // 0 - | ErectDomainRequest::subInterval = 0 - // 0 - | - // 0 - | - // 0 - | - // 0 - --/ + // 0x00: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | ErectDomainRequest::subInterval = 0 + // 0 - | + // 0 - | + // 0 - | + // 0 - --/ - }; - /* @formatter:on */ + }; + /* @formatter:on */ MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); Element todo = new ClientMCSErectDomainRequest("TODO"); - Element x224 = new ClientX224DataPdu("x224"); + Element x224 = new ClientX224DataPDU("x224"); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientSynchronizePDU.java old mode 100644 new mode 100755 similarity index 58% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientSynchronizePDU.java index 5b370b5cd21..c9d8d0ce316 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientSynchronizePDU.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * @see http://msdn.microsoft.com/en-us/library/cc240489.aspx @@ -50,46 +50,46 @@ public class ClientSynchronizePDU extends OneTimeSwitch { ByteBuffer buf = new ByteBuffer(length, true); /* @formatter:off */ - buf.writeBytes(new byte[] { - // MCS send data request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O Channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // Data length: 22 bytes (0x16, variable length field) - (byte)0x80, (byte)0x16, + buf.writeBytes(new byte[] { + // MCS send data request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O Channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // Data length: 22 bytes (0x16, variable length field) + (byte)0x80, (byte)0x16, - // RDP: total length: 22 bytes (LE) - (byte)0x16, (byte)0x00, + // RDP: total length: 22 bytes (LE) + (byte)0x16, (byte)0x00, - // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) - (byte)0x17, (byte)0x00, + // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) + (byte)0x17, (byte)0x00, - // PDU source: 1007 (LE) - (byte)0xec, (byte)0x03, - // Share ID: 0x000103ea (LE) - (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, - // Padding: 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 8 bytes (LE) - (byte)0x08, (byte)0x00, - // pduType2 = PDUTYPE2_SYNCHRONIZE (31) - (byte)0x1f, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE?) - (byte)0x00, (byte)0x00, - // messageType: SYNCMSGTYPE_SYNC (1) (LE) - (byte)0x01, (byte)0x00, - // targetUser: 0x03ea - (byte)0xea, (byte)0x03, - }); - /* @formatter:on */ + // PDU source: 1007 (LE) + (byte)0xec, (byte)0x03, + // Share ID: 0x000103ea (LE) + (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, + // Padding: 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 8 bytes (LE) + (byte)0x08, (byte)0x00, + // pduType2 = PDUTYPE2_SYNCHRONIZE (31) + (byte)0x1f, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE?) + (byte)0x00, (byte)0x00, + // messageType: SYNCMSGTYPE_SYNC (1) (LE) + (byte)0x01, (byte)0x00, + // targetUser: 0x03ea + (byte)0xea, (byte)0x03, + }); + /* @formatter:on */ // Trim buffer to actual length of data written buf.trimAtCursor(); @@ -110,56 +110,56 @@ public class ClientSynchronizePDU extends OneTimeSwitch { // System.setProperty("streamer.Pipeline.debug", "true"); /* @formatter:off */ - byte[] packet = new byte[] { - // TPKT - (byte)0x03, (byte)0x00, - // TPKT length: 37 bytes - (byte)0x00, (byte)0x25, - // X224 Data PDU - (byte)0x02, (byte)0xf0, (byte)0x80, + byte[] packet = new byte[] { + // TPKT + (byte)0x03, (byte)0x00, + // TPKT length: 37 bytes + (byte)0x00, (byte)0x25, + // X224 Data PDU + (byte)0x02, (byte)0xf0, (byte)0x80, - // MCS send data request - (byte)0x64, - // Initiator: 1004 (1001+3) - (byte)0x00, (byte)0x03, - // Channel ID: 1003 (I/O Channel) - (byte)0x03, (byte)0xeb, - // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) - (byte)0x70, - // Data length: 22 bytes (0x16, variable length field) - (byte)0x80, (byte)0x16, + // MCS send data request + (byte)0x64, + // Initiator: 1004 (1001+3) + (byte)0x00, (byte)0x03, + // Channel ID: 1003 (I/O Channel) + (byte)0x03, (byte)0xeb, + // Data priority: high (0x40), segmentation: begin (0x20) | end (0x10) + (byte)0x70, + // Data length: 22 bytes (0x16, variable length field) + (byte)0x80, (byte)0x16, - // RDP: total length: 22 bytes (LE) - (byte)0x16, (byte)0x00, - // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) - (byte)0x17, (byte)0x00, - // PDU source: 1007 (LE) - (byte)0xec, (byte)0x03, - // Share ID: 0x000103ea (LE) - (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, - // Padding: 1 byte - (byte)0x00, - // Stream ID: STREAM_LOW (1) - (byte)0x01, - // uncompressedLength : 8 bytes (LE) - (byte)0x08, (byte)0x00, - // pduType2 = PDUTYPE2_SYNCHRONIZE (31) - (byte)0x1f, - // generalCompressedType: 0 - (byte)0x00, - // generalCompressedLength: 0 (LE?) - (byte)0x00, (byte)0x00, - // messageType: SYNCMSGTYPE_SYNC (1) (LE) - (byte)0x01, (byte)0x00, - // targetUser: 0x03ea - (byte)0xea, (byte)0x03, + // RDP: total length: 22 bytes (LE) + (byte)0x16, (byte)0x00, + // PDU type: PDUTYPE_DATAPDU (0x7), TS_PROTOCOL_VERSION (0x10) (LE) + (byte)0x17, (byte)0x00, + // PDU source: 1007 (LE) + (byte)0xec, (byte)0x03, + // Share ID: 0x000103ea (LE) + (byte)0xea, (byte)0x03, (byte)0x01, (byte)0x00, + // Padding: 1 byte + (byte)0x00, + // Stream ID: STREAM_LOW (1) + (byte)0x01, + // uncompressedLength : 8 bytes (LE) + (byte)0x08, (byte)0x00, + // pduType2 = PDUTYPE2_SYNCHRONIZE (31) + (byte)0x1f, + // generalCompressedType: 0 + (byte)0x00, + // generalCompressedLength: 0 (LE?) + (byte)0x00, (byte)0x00, + // messageType: SYNCMSGTYPE_SYNC (1) (LE) + (byte)0x01, (byte)0x00, + // targetUser: 0x03ea + (byte)0xea, (byte)0x03, - }; - /* @formatter:on */ + }; + /* @formatter:on */ MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); Element todo = new ClientSynchronizePDU("TODO"); - Element x224 = new ClientX224DataPdu("x224"); + Element x224 = new ClientX224DataPDU("x224"); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientTpkt.java old mode 100644 new mode 100755 similarity index 98% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientTpkt.java index 036c9591e6f..926c80705aa --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientTpkt.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224ConnectionRequestPDU.java old mode 100644 new mode 100755 similarity index 76% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224ConnectionRequestPDU.java index 66d0fd57d11..6413432b711 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224ConnectionRequestPDU.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * @see http://msdn.microsoft.com/en-us/library/cc240470.aspx @@ -47,9 +47,15 @@ public class ClientX224ConnectionRequestPDU extends OneTimeSwitch { */ protected String userName; - public ClientX224ConnectionRequestPDU(String id, String userName) { + /** + * Protocol to use: RDP_NEG_REQ_PROTOCOL_SSL or RDP_NEG_REQ_PROTOCOL_HYBRID. + */ + protected int protocol; + + public ClientX224ConnectionRequestPDU(String id, String userName, int protocol) { super(id); this.userName = userName; + this.protocol = protocol; } @Override @@ -85,7 +91,7 @@ public class ClientX224ConnectionRequestPDU extends OneTimeSwitch { buf.writeByte(0x00); // RDP_NEG_REQ: Requested protocols: PROTOCOL_SSL - buf.writeIntLE(RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL); + buf.writeIntLE(protocol); // Calculate length of packet and prepend it to buffer ByteBuffer data = new ByteBuffer(5); @@ -119,29 +125,29 @@ public class ClientX224ConnectionRequestPDU extends OneTimeSwitch { byte[] packet = new byte[] { - 0x03, // TPKT Header: version = 3 - 0x00, // TPKT Header: Reserved = 0 - 0x00, // TPKT Header: Packet length - high part - 0x2c, // TPKT Header: Packet length - low part (total = 44 bytes) - 0x27, // X.224: Length indicator (39 bytes) - (byte)0xe0, // X.224: Type (high nibble) = 0xe = CR TPDU; - // credit (low nibble) = 0 - 0x00, 0x00, // X.224: Destination reference = 0 - 0x00, 0x00, // X.224: Source reference = 0 - 0x00, // X.224: Class and options = 0 + 0x03, // TPKT Header: version = 3 + 0x00, // TPKT Header: Reserved = 0 + 0x00, // TPKT Header: Packet length - high part + 0x2c, // TPKT Header: Packet length - low part (total = 44 bytes) + 0x27, // X.224: Length indicator (39 bytes) + (byte)0xe0, // X.224: Type (high nibble) = 0xe = CR TPDU; + // credit (low nibble) = 0 + 0x00, 0x00, // X.224: Destination reference = 0 + 0x00, 0x00, // X.224: Source reference = 0 + 0x00, // X.224: Class and options = 0 - 'C', 'o', 'o', 'k', 'i', 'e', ':', ' ', 'm', 's', 't', 's', 'h', 'a', 's', 'h', '=', 'e', 'l', 't', 'o', 'n', 's', // "Cookie: mstshash=eltons" - '\r', '\n', // -Cookie terminator sequence + 'C', 'o', 'o', 'k', 'i', 'e', ':', ' ', 'm', 's', 't', 's', 'h', 'a', 's', 'h', '=', 'e', 'l', 't', 'o', 'n', 's', // "Cookie: mstshash=eltons" + '\r', '\n', // -Cookie terminator sequence - 0x01, // RDP_NEG_REQ::type (TYPE_RDP_NEG_REQ) - 0x00, // RDP_NEG_REQ::flags (0) - 0x08, 0x00, // RDP_NEG_REQ::length (8 bytes) - 0x01, 0x00, 0x00, 0x00 // RDP_NEG_REQ: Requested protocols - // (PROTOCOL_SSL in little endian format) + 0x01, // RDP_NEG_REQ::type (TYPE_RDP_NEG_REQ) + 0x00, // RDP_NEG_REQ::flags (0) + 0x08, 0x00, // RDP_NEG_REQ::length (8 bytes) + 0x01, 0x00, 0x00, 0x00 // RDP_NEG_REQ: Requested protocols + // (PROTOCOL_SSL in little endian format) }; MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); - Element cr = new ClientX224ConnectionRequestPDU("cr", cookie); + Element cr = new ClientX224ConnectionRequestPDU("cr", cookie, RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL); Element tpkt = new ClientTpkt("tpkt"); Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(packet)); Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3})); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224DataPDU.java old mode 100644 new mode 100755 similarity index 92% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224DataPDU.java index fc22c430758..b0373ad0340 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ClientX224DataPDU.java @@ -14,18 +14,18 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; import streamer.Link; -public class ClientX224DataPdu extends BaseElement { +public class ClientX224DataPDU extends BaseElement { public static final int X224_TPDU_DATA = 0xF0; public static final int X224_TPDU_LAST_DATA_UNIT = 0x80; - public ClientX224DataPdu(String id) { + public ClientX224DataPDU(String id) { super(id); } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RLEBitmapDecompression.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RLEBitmapDecompression.java new file mode 100755 index 00000000000..559091172d0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RLEBitmapDecompression.java @@ -0,0 +1,1014 @@ +// 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 rdpclient.rdp; + +import streamer.ByteBuffer; +import streamer.debug.AssertingByteBuffer; + +/** + * Based on code example from MSDN, @see + * http://msdn.microsoft.com/en-us/library/dd240593.aspx . + */ +public class RLEBitmapDecompression { + + public static final int g_MaskRegularRunLength = 0x1F; + public static final int g_MaskLiteRunLength = 0x0F; + + public static final int g_MaskSpecialFgBg1 = 0x03; + public static final int g_MaskSpecialFgBg2 = 0x05; + + public static final int REGULAR_BG_RUN = 0x00; + public static final int REGULAR_FG_RUN = 0x01; + public static final int REGULAR_FGBG_IMAGE = 0x02; + public static final int REGULAR_COLOR_RUN = 0x03; + public static final int REGULAR_COLOR_IMAGE = 0x04; + + public static final int LITE_SET_FG_FG_RUN = 0x0C; + public static final int LITE_SET_FG_FGBG_IMAGE = 0x0D; + public static final int LITE_DITHERED_RUN = 0x0E; + + public static final int MEGA_MEGA_BG_RUN = 0xF0; + public static final int MEGA_MEGA_FG_RUN = 0xF1; + public static final int MEGA_MEGA_FGBG_IMAGE = 0xF2; + public static final int MEGA_MEGA_COLOR_RUN = 0xF3; + public static final int MEGA_MEGA_COLOR_IMAGE = 0xF4; + public static final int MEGA_MEGA_SET_FG_RUN = 0xF6; + public static final int MEGA_MEGA_SET_FGBG_IMAGE = 0xF7; + public static final int MEGA_MEGA_DITHERED_RUN = 0xF8; + + public static final int SPECIAL_FGBG_1 = 0xF9; + public static final int SPECIAL_FGBG_2 = 0xFA; + + public static final int SPECIAL_WHITE = 0xFD; + public static final int SPECIAL_BLACK = 0xFE; + + /** + * Writes a pixel to the specified buffer and advance cursor by bpp. + * + * @param bpp + * bytes per pixel + */ + private static void writePixel(int bpp, ByteBuffer destBuf, int pixel) { + switch (bpp) { + case 1: + destBuf.writeByte(pixel); + break; + case 2: + destBuf.writeShortLE(pixel); + break; + case 3: + destBuf.writeByte(pixel); + destBuf.writeShortLE(pixel >> 8); + break; + case 4: + destBuf.writeIntLE(pixel); + break; + default: + throw new RuntimeException("Unsupported color depth."); + } + } + + /** + * Reads a pixel from the specified buffer at given offset without changing of + * cursor. + * + * @param bpp + * bytes per pixel + * @param offset + * -rowDelta (i.e. (-width*bpp)) + */ + private static int peekPixel(int bpp, ByteBuffer destBuf, int offset) { + if (offset >= 0 || (-offset) > destBuf.cursor) + throw new RuntimeException("Incorrect value for offset: offset in destination buffer must point to pixel in previous row."); + + // Adjust cursor to point to pixel in previous row + int oldCursor = destBuf.cursor; + destBuf.cursor += offset; + + int pixel; + switch (bpp) { + case 1: + pixel = destBuf.readUnsignedByte(); + break; + case 2: + pixel = destBuf.readUnsignedShortLE(); + break; + case 3: + pixel = destBuf.readUnsignedByte() | (destBuf.readUnsignedShortLE() >> 8); + break; + case 4: + pixel = destBuf.readSignedIntLE(); + break; + default: + throw new RuntimeException("Unsupported color depth."); + } + destBuf.cursor = oldCursor; + + return pixel; + } + + /** + * Reads a pixel from the specified buffer and advance cursor by bpp value. + * + * @param bpp + * bytes per pixel + */ + private static int readPixel(int bpp, ByteBuffer srcBuf) { + int pixel; + switch (bpp) { + case 1: + pixel = srcBuf.readUnsignedByte(); + break; + case 2: + pixel = srcBuf.readUnsignedShortLE(); + break; + case 3: + pixel = srcBuf.readUnsignedByte() | (srcBuf.readUnsignedShortLE() >> 8); + break; + case 4: + pixel = srcBuf.readSignedIntLE(); + break; + default: + throw new RuntimeException("Unsupported color depth."); + } + + return pixel; + } + + /** + * Returns the size of a pixel in bytes. + */ + private static int getPixelSize(int colorDepth) { + switch (colorDepth) { + case 8: + return 1; + case 15: + case 16: + return 2; + case 24: + return 3; + default: + throw new RuntimeException("Unsupported pixel color depth: " + colorDepth + " bpp."); + } + } + + /** + * Reads the supplied order header & extracts the compression order code ID. + */ + private static int extractCodeId(int orderHeader) { + // Taken from FreeRDP code, bitmap.c + switch (orderHeader) { + case MEGA_MEGA_BG_RUN: + case MEGA_MEGA_FG_RUN: + case MEGA_MEGA_SET_FG_RUN: + case MEGA_MEGA_DITHERED_RUN: + case MEGA_MEGA_COLOR_RUN: + case MEGA_MEGA_FGBG_IMAGE: + case MEGA_MEGA_SET_FGBG_IMAGE: + case MEGA_MEGA_COLOR_IMAGE: + case SPECIAL_FGBG_1: + case SPECIAL_FGBG_2: + case SPECIAL_WHITE: + case SPECIAL_BLACK: + return orderHeader; + } + + int code = orderHeader >> 5; + switch (code) { + case REGULAR_BG_RUN: + case REGULAR_FG_RUN: + case REGULAR_COLOR_RUN: + case REGULAR_FGBG_IMAGE: + case REGULAR_COLOR_IMAGE: + return code; + } + + return orderHeader >> 4; + } + + /** + * Returns a black pixel. + */ + private static int getColorBlack() { + return 0x000000; + } + + /** + * Returns a white pixel. + */ + private static int getColorWhite(int colorDepth) { + if (colorDepth == 8) { + // + // Palette entry #255 holds white. + // + return 0xFF; + } else if (colorDepth == 15) { + // + // 5 bits per RGB component: + // 0111 1111 1111 1111 (binary) + // + return 0x7FFF; + } else if (colorDepth == 16) { + // + // 5 bits for red, 6 bits for green, 5 bits for green: + // 1111 1111 1111 1111 (binary) + // + return 0xFFFF; + } else if (colorDepth == 24) { + // + // 8 bits per RGB component: + // 1111 1111 1111 1111 1111 1111 (binary) + // + return 0xFFFFFF; + } else + throw new RuntimeException("Unsupported color depth."); + } + + /** + * Extract the run length of a compression order. + */ + private static int extractRunLength(int code, int orderHeader, ByteBuffer srcBuf) { + switch (code) { + case REGULAR_FGBG_IMAGE: { + int runLength = orderHeader & g_MaskRegularRunLength; + if (runLength == 0) + runLength = srcBuf.readUnsignedByte() + 1; + else + runLength = runLength * 8; + return runLength; + } + case LITE_SET_FG_FGBG_IMAGE: { + int runLength = orderHeader & g_MaskLiteRunLength; + if (runLength == 0) + runLength = srcBuf.readUnsignedByte() + 1; + else + runLength = runLength * 8; + return runLength; + } + case REGULAR_BG_RUN: + case REGULAR_COLOR_IMAGE: + case REGULAR_COLOR_RUN: + case REGULAR_FG_RUN: { + int runLength = orderHeader & g_MaskRegularRunLength; + if (runLength == 0) + // An extended (MEGA) run. + runLength = srcBuf.readUnsignedByte() + 32; + return runLength; + } + case LITE_DITHERED_RUN: + case LITE_SET_FG_FG_RUN: { + int runLength = orderHeader & g_MaskLiteRunLength; + if (runLength == 0) + // An extended (MEGA) run. + runLength = srcBuf.readUnsignedByte() + 16; + return runLength; + } + case MEGA_MEGA_BG_RUN: + case MEGA_MEGA_COLOR_IMAGE: + case MEGA_MEGA_COLOR_RUN: + case MEGA_MEGA_DITHERED_RUN: + case MEGA_MEGA_FG_RUN: + case MEGA_MEGA_FGBG_IMAGE: + case MEGA_MEGA_SET_FG_RUN: + case MEGA_MEGA_SET_FGBG_IMAGE: { + return srcBuf.readUnsignedShortLE(); + } + default: + return 0; + } + + } + + /** + * Write a foreground/background image to a destination buffer. + */ + private static void writeFgBgImage(int bpp, ByteBuffer destBuf, int rowDelta, int bitmask, int fgPel, int cBits) { + for (; cBits > 0; cBits--, bitmask >>= 1) { + int xorPixel = peekPixel(bpp, destBuf, -rowDelta); + writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? xorPixel ^ fgPel : xorPixel); + } + } + + /** + * Write a foreground/background image to a destination buffer for the first + * line of compressed data. + */ + private static void writeFirstLineFgBgImage(int bpp, ByteBuffer destBuf, int bitmask, int fgPel, int cBits) { + for (; cBits > 0; cBits--, bitmask >>= 1) { + writePixel(bpp, destBuf, ((bitmask & 0x1) > 0) ? fgPel : getColorBlack()); + } + } + + /** + * Decompress a RLE compressed bitmap and flip decompressed image. + * + * @param srcBuf + * source buffer containing compressed bitmap + * @param imageWidth + * width of destination image in pixels + * @param imageHeight + * height of destination image in pixels + * @param colorDepth + * bits per pixel + * @return destination image buffer + */ + public static ByteBuffer rleDecompress(ByteBuffer srcBuf, int imageWidth, int imageHeight, int colorDepth) { + int bpp = getPixelSize(colorDepth); + + // Decompress image + ByteBuffer destBuf = new ByteBuffer(new byte[imageWidth * imageHeight * bpp]); + rleDecompress(srcBuf, destBuf, imageWidth, imageHeight, colorDepth); + + // Flip image + return flipRawImage(destBuf, imageWidth, imageHeight, bpp); + } + + /** + * Decompress a RLE compressed bitmap. + * + * @param srcBuf + * source buffer containing compressed bitmap + * @param destBuf + * destination buffer + * @param imageWidth + * width of destination image in pixels + * @param imageHeight + * height of destination image in pixels + * @param colorDepth + * bits per pixel + */ + protected static void rleDecompress(final ByteBuffer srcBuf, final ByteBuffer destBuf, final int imageWidth, final int imageHeight, final int colorDepth) { + final int bpp = getPixelSize(colorDepth); + final int rowDelta = imageWidth * bpp; + final int whitePixel = getColorWhite(colorDepth); + final int blackPixel = getColorBlack(); + + int fgPel = whitePixel; + boolean insertFgPel = false; + boolean firstLine = true; + + if (destBuf.length != imageWidth * imageHeight * bpp) + throw new RuntimeException("Incorrect size of destination buffer. Expected size (imageWidth*imageHeight*bpp): " + (imageWidth * imageHeight * bpp) + + ", actual size: " + destBuf.length + "."); + + while (srcBuf.cursor < srcBuf.length) { + // Watch out for the end of the first scanline in destination buffer. + if (firstLine) { + if (destBuf.cursor >= rowDelta) { + firstLine = false; + insertFgPel = false; + } + } + + // Extract the compression order code ID from the compression + // order header. + int orderHeader = srcBuf.readUnsignedByte(); + int code = extractCodeId(orderHeader); + + // Handle Background Run Orders. + if (code == REGULAR_BG_RUN | code == MEGA_MEGA_BG_RUN) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + if (firstLine) { + if (insertFgPel) { + writePixel(bpp, destBuf, fgPel); + runLength--; + } + + for (; runLength > 0; runLength--) + writePixel(bpp, destBuf, blackPixel); + + } else { + if (insertFgPel) { + writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel); + runLength--; + } + + // Copy pixels from previous row of destination image + for (; runLength > 0; runLength--) + writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta)); + } + + // + // A follow-on background run order will need a + // foreground pel inserted. + // + insertFgPel = true; + continue; + } + + // + // For any of the other run-types a follow-on background run + // order does not need a foreground pel inserted. + // + insertFgPel = false; + + // + // Handle Foreground Run Orders. + // + if (code == REGULAR_FG_RUN | code == MEGA_MEGA_FG_RUN | code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + if (code == LITE_SET_FG_FG_RUN | code == MEGA_MEGA_SET_FG_RUN) + fgPel = readPixel(bpp, srcBuf); + + if (firstLine) { + for (; runLength > 0; runLength--) { + writePixel(bpp, destBuf, fgPel); + } + } else { + for (; runLength > 0; runLength--) { + writePixel(bpp, destBuf, peekPixel(bpp, destBuf, -rowDelta) ^ fgPel); + } + } + + continue; + } + + // + // Handle Dithered Run Orders. + // + if (code == LITE_DITHERED_RUN | code == MEGA_MEGA_DITHERED_RUN) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + int pixelA = readPixel(bpp, srcBuf); + int pixelB = readPixel(bpp, srcBuf); + + for (; runLength > 0; runLength--) { + writePixel(bpp, destBuf, pixelA); + writePixel(bpp, destBuf, pixelB); + } + + continue; + } + + // + // Handle Color Run Orders. + // + if (code == REGULAR_COLOR_RUN | code == MEGA_MEGA_COLOR_RUN) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + int pixelA = readPixel(bpp, srcBuf); + + for (; runLength > 0; runLength--) + writePixel(bpp, destBuf, pixelA); + + continue; + } + + // + // Handle Foreground/Background Image Orders. + // + if (code == REGULAR_FGBG_IMAGE | code == MEGA_MEGA_FGBG_IMAGE | code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + if (code == LITE_SET_FG_FGBG_IMAGE | code == MEGA_MEGA_SET_FGBG_IMAGE) { + fgPel = readPixel(bpp, srcBuf); + } + + for (; runLength > 8; runLength -= 8) { + int bitmask = srcBuf.readUnsignedByte(); + + if (firstLine) + writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, 8); + else + writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, 8); + } + + if (runLength > 0) { + int bitmask = srcBuf.readUnsignedByte(); + + if (firstLine) + writeFirstLineFgBgImage(bpp, destBuf, bitmask, fgPel, runLength); + else + writeFgBgImage(bpp, destBuf, rowDelta, bitmask, fgPel, runLength); + } + + continue; + } + + // + // Handle Color Image Orders. + // + if (code == REGULAR_COLOR_IMAGE | code == MEGA_MEGA_COLOR_IMAGE) { + int runLength = extractRunLength(code, orderHeader, srcBuf); + + //* DEBUG */ + // Copy bytes from source to destination using writeByte(readByte()) + // for (int byteCount = runLength * bpp; byteCount > 0; byteCount--) { + // destBuf.writeByte(srcBuf.readUnsignedByte()); + // } + //* DEBUG */ + + // Copy bytes from source to destination directly using arraycopy() + int lengthInBytes = runLength * bpp; + System.arraycopy(srcBuf.data, srcBuf.offset + srcBuf.cursor, destBuf.data, destBuf.offset + destBuf.cursor, lengthInBytes); + srcBuf.cursor += lengthInBytes; + destBuf.cursor += lengthInBytes; + + continue; + } + + // + // Handle Special Order 1. + // + if (code == SPECIAL_FGBG_1) { + if (firstLine) + writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg1, fgPel, 8); + else + writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg1, fgPel, 8); + + continue; + } + + // + // Handle Special Order 2. + // + if (code == SPECIAL_FGBG_2) { + if (firstLine) + writeFirstLineFgBgImage(bpp, destBuf, g_MaskSpecialFgBg2, fgPel, 8); + else + writeFgBgImage(bpp, destBuf, rowDelta, g_MaskSpecialFgBg2, fgPel, 8); + + continue; + } + + // + // Handle White Order. + // + if (code == SPECIAL_WHITE) { + writePixel(bpp, destBuf, whitePixel); + + continue; + } + + // + // Handle Black Order. + // + if (code == SPECIAL_BLACK) { + writePixel(bpp, destBuf, blackPixel); + + continue; + } + + throw new RuntimeException("Unknown code: " + code + "."); + } + } + + /** + * Flip image in vertical direction. + */ + public static ByteBuffer flipRawImage(ByteBuffer src, int width, int height, int bpp) { + if (width * height * bpp != src.length) + throw new RuntimeException("Incorrect size of buffer. Expected size (imageWidth*imageHeight*bpp): " + (width * height * bpp) + ", actual size: " + + src.length + "."); + ByteBuffer dest = new ByteBuffer(new byte[src.length]); + + int scanLine = width * bpp; + + for (int i = 0; i < height; i++) { + // Copy one row + System.arraycopy(src.data, (height - i - 1) * scanLine, dest.data, i * scanLine, scanLine); + } + + return dest; + + } + + /** + * Example. + */ + public static void main(String args[]) { + + if (true) { + // 16x1@8bpp, all black + int width = 16, height = 1, depth = 8, bpp = depth / 8; + ByteBuffer src = new ByteBuffer(new byte[] {0x10}); + ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]); + rleDecompress(src, dest, width, height, depth); + } + + if (true) { + // 16x1@16bpp, all black + int width = 16, height = 1, depth = 16, bpp = depth / 8; + ByteBuffer src = new ByteBuffer(new byte[] {0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + ByteBuffer dest = new AssertingByteBuffer(new byte[width * height * bpp]); + rleDecompress(src, dest, width, height, depth); + } + + if (true) { + // 32x32@8 + int width = 32, height = 32, depth = 8, bpp = depth / 8; + + ByteBuffer src = new ByteBuffer(new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x00, (byte)0x06, (byte)0x06, + (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xec, (byte)0x6c, (byte)0x0e, + (byte)0x0e, (byte)0x44, (byte)0x0e, (byte)0x0e, (byte)0x0e, (byte)0x13, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, + (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xe4, (byte)0x04, (byte)0x06, + (byte)0x8e, (byte)0x60, (byte)0x0e, (byte)0x60, (byte)0x8c, (byte)0xb4, (byte)0xb5, (byte)0xdc, (byte)0xdc, (byte)0xbb, (byte)0xb4, + (byte)0x8c, (byte)0x66, (byte)0x0b, (byte)0x6c, (byte)0xe4, (byte)0x04, (byte)0x06, (byte)0x02, (byte)0x8b, (byte)0x06, (byte)0x06, + (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xf8, (byte)0x0e, (byte)0x66, (byte)0xb4, (byte)0xdc, (byte)0x68, (byte)0xe2, + (byte)0x97, (byte)0xdd, (byte)0xb4, (byte)0xa7, (byte)0x16, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, + (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x0b, (byte)0xae, + (byte)0xdc, (byte)0xe9, (byte)0x6a, (byte)0xdc, (byte)0x96, (byte)0xe9, (byte)0xe9, (byte)0xb4, (byte)0x0e, (byte)0x00, (byte)0x06, + (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, + (byte)0x0e, (byte)0xae, (byte)0xdc, (byte)0xdb, (byte)0xdb, (byte)0xd0, (byte)0x09, (byte)0x07, (byte)0xcf, (byte)0x03, (byte)0x95, + (byte)0xdb, (byte)0xdb, (byte)0xdc, (byte)0xb4, (byte)0x66, (byte)0x6c, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, + (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x0b, (byte)0xae, (byte)0xdb, (byte)0xd4, (byte)0xd5, (byte)0x6c, + (byte)0xdb, (byte)0x80, (byte)0xaf, (byte)0xd5, (byte)0xd4, (byte)0xdb, (byte)0xb4, (byte)0x66, (byte)0x04, (byte)0x06, (byte)0x04, + (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x66, (byte)0xae, (byte)0xd5, (byte)0xad, (byte)0xd4, + (byte)0xd4, (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xdb, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xd5, + (byte)0xd5, (byte)0xd5, (byte)0xd4, (byte)0xd4, (byte)0xad, (byte)0xd5, (byte)0xb4, (byte)0x0e, (byte)0x06, (byte)0x06, (byte)0x06, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x60, (byte)0xa7, (byte)0xb4, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb3, + (byte)0xb3, (byte)0xd4, (byte)0xd4, (byte)0xb3, (byte)0x8c, (byte)0xb6, (byte)0x07, (byte)0xb6, (byte)0x8c, (byte)0xb3, (byte)0xd4, + (byte)0xb3, (byte)0xb3, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb4, (byte)0xad, (byte)0x66, (byte)0x00, (byte)0x06, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x06, (byte)0x66, (byte)0xae, (byte)0xad, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, + (byte)0xad, (byte)0xb3, (byte)0xad, (byte)0xb5, (byte)0x07, (byte)0x07, (byte)0x07, (byte)0xf0, (byte)0x8b, (byte)0xad, (byte)0xad, + (byte)0xad, (byte)0xad, (byte)0xad, (byte)0x8b, (byte)0xa7, (byte)0xae, (byte)0xa7, (byte)0x6c, (byte)0x06, (byte)0x00, (byte)0x00, + (byte)0x04, (byte)0x6c, (byte)0xa7, (byte)0xad, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, + (byte)0xad, (byte)0xad, (byte)0xb5, (byte)0xbd, (byte)0xbd, (byte)0xbd, (byte)0xbd, (byte)0xf0, (byte)0x8b, (byte)0x8b, (byte)0xad, + (byte)0x8b, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0xc8, (byte)0xc8, (byte)0x60, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, + (byte)0x66, (byte)0xc8, (byte)0xa7, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xad, + (byte)0x8b, (byte)0x92, (byte)0xf1, (byte)0xf1, (byte)0xf1, (byte)0xf1, (byte)0xf2, (byte)0x07, (byte)0xa7, (byte)0xa7, (byte)0x8b, + (byte)0xa7, (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0xc8, (byte)0x66, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x60, + (byte)0xa7, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, + (byte)0xa7, (byte)0xb6, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0x07, (byte)0x66, (byte)0xa7, (byte)0xa7, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0x04, (byte)0xa7, + (byte)0x60, (byte)0x6b, (byte)0x66, (byte)0x99, (byte)0xb6, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xef, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x60, + (byte)0xa7, (byte)0x66, (byte)0x60, (byte)0x66, (byte)0x66, (byte)0x8c, (byte)0xf1, (byte)0x6e, (byte)0xff, (byte)0x85, (byte)0xbd, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x60, (byte)0x05, (byte)0x87, (byte)0x13, (byte)0x04, (byte)0x66, (byte)0x66, (byte)0x66, + (byte)0x66, (byte)0xf4, (byte)0x70, (byte)0xff, (byte)0x84, (byte)0xbd, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x05, (byte)0x85, + (byte)0x0b, (byte)0xa7, (byte)0xb5, (byte)0xae, (byte)0x8c, (byte)0xd0, (byte)0x13, (byte)0xc1, (byte)0x01, (byte)0x00, (byte)0x08, + (byte)0x8e, (byte)0x8c, (byte)0xae, (byte)0xb5, (byte)0xae, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0xae, (byte)0xbc, + (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xb5, (byte)0xd0, (byte)0x0e, (byte)0x0c, (byte)0x01, (byte)0x00, (byte)0x90, (byte)0xf2, + (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xae, + (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x68, (byte)0xae, (byte)0x82, (byte)0x8c, (byte)0x0a, (byte)0x05, (byte)0x8c, + (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x6c, (byte)0x00, (byte)0x00, + (byte)0x06, (byte)0x05, (byte)0x81, (byte)0xd0, (byte)0x06, (byte)0x9a, (byte)0x8c, (byte)0x0a, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x0a, (byte)0xb5, + (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x8b, (byte)0x0a, (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x06, + (byte)0x9b, (byte)0xb6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, + (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, (byte)0x8c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x6c, + (byte)0xb5, (byte)0x0a, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x05, (byte)0x80, (byte)0x7d, (byte)0xbc, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, + (byte)0x0a, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x87, (byte)0x0a, (byte)0xbc, + (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x1a, + (byte)0xb5, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, (byte)0x6e, (byte)0xb5, (byte)0x0a, (byte)0xbc, + (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, (byte)0xf4, (byte)0xff, (byte)0xf2, + (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, (byte)0x0a, (byte)0x0a, (byte)0x8b, + (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x8b, (byte)0xbc, (byte)0x1a, (byte)0x0a, + (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, + (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0xde, (byte)0x0a, (byte)0xa7, (byte)0x06, (byte)0x00, + (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8b, (byte)0xbc, (byte)0xf2, (byte)0x0a, + (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, + (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0xf2, (byte)0x1a, (byte)0x8c, (byte)0xec, (byte)0x06, (byte)0x06, (byte)0x06, + (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0xa7, (byte)0xbc, (byte)0x1a, (byte)0x0a, + (byte)0x0a, (byte)0x6a, (byte)0xb6, (byte)0x96, (byte)0x0a, (byte)0x0a, (byte)0xf2, (byte)0x0a, (byte)0x87, (byte)0x06, (byte)0x04, + (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, + (byte)0x8c, (byte)0xb6, (byte)0xf4, (byte)0xf2, (byte)0xd0, (byte)0x09, (byte)0xbc, (byte)0x87, (byte)0x03, (byte)0x80, (byte)0x2c, + (byte)0xde, (byte)0xf4, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x6c, (byte)0x87, (byte)0x0a, + (byte)0xf4, (byte)0xf4, (byte)0xf2, (byte)0xde, (byte)0xbd, (byte)0xbd, (byte)0xde, (byte)0xf2, (byte)0xf4, (byte)0xf4, (byte)0x0a, + (byte)0xd0, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, + (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x6c, (byte)0x8c, + (byte)0xb5, (byte)0xbc, (byte)0x0a, (byte)0xde, (byte)0xf2, (byte)0xbd, (byte)0x0a, (byte)0xb5, (byte)0x8c, (byte)0x6c, (byte)0x06, + (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xe6, + (byte)0x04, (byte)0x06, (byte)0x86, (byte)0x04, (byte)0x6c, (byte)0x04, (byte)0x8b, (byte)0x04, (byte)0x6c, (byte)0xe6, (byte)0x04, + (byte)0x06, (byte)0x82, (byte)0x00, (byte)0x00 + + }); + + ByteBuffer flippedImage = new ByteBuffer(new byte[] {(byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, + (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x6c, (byte)0x04, (byte)0x8b, (byte)0x04, (byte)0x6c, + (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, + (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, + (byte)0x06, (byte)0x6c, (byte)0x8c, (byte)0xb5, (byte)0xbc, (byte)0x0a, (byte)0xde, (byte)0xf2, (byte)0xbd, (byte)0x0a, (byte)0xb5, + (byte)0x8c, (byte)0x6c, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x6c, (byte)0x87, + (byte)0x0a, (byte)0xf4, (byte)0xf4, (byte)0xf2, (byte)0xde, (byte)0xbd, (byte)0xbd, (byte)0xde, (byte)0xf2, (byte)0xf4, (byte)0xf4, + (byte)0x0a, (byte)0xd0, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8c, (byte)0xb6, (byte)0xf4, (byte)0xf2, + (byte)0x0a, (byte)0x0a, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0x0a, (byte)0x0a, (byte)0xde, + (byte)0xf4, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, + (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0xa7, (byte)0xbc, (byte)0x1a, (byte)0x0a, (byte)0x0a, (byte)0xb6, + (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, (byte)0x0a, + (byte)0xf2, (byte)0x0a, (byte)0x87, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, + (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x8b, (byte)0xbc, (byte)0xf2, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, + (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0xb6, (byte)0x0a, + (byte)0xf2, (byte)0x1a, (byte)0x8c, (byte)0xec, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, + (byte)0x04, (byte)0x8b, (byte)0xbc, (byte)0x1a, (byte)0x0a, (byte)0xb6, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, + (byte)0xb5, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xb6, (byte)0x0a, + (byte)0xde, (byte)0x0a, (byte)0xa7, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, (byte)0x6e, + (byte)0xb5, (byte)0x0a, (byte)0xbc, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, + (byte)0xf4, (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xd0, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, + (byte)0x0a, (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x87, (byte)0x0a, + (byte)0xbc, (byte)0xb6, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xd0, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0xbc, + (byte)0x1a, (byte)0xb5, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x6c, (byte)0xb5, (byte)0x0a, (byte)0xb6, + (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xbc, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, + (byte)0x0a, (byte)0x8b, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x8b, (byte)0x0a, (byte)0xbc, (byte)0xb5, (byte)0xb5, + (byte)0xb5, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb6, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xb6, (byte)0x0a, + (byte)0x8c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0xae, (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xd0, + (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0x8c, (byte)0x0a, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xd0, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0x0a, (byte)0xb5, + (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0xae, (byte)0x0a, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xae, + (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0xae, (byte)0x8c, (byte)0x0a, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x6c, + (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0xae, (byte)0xbc, (byte)0xb5, (byte)0xb5, (byte)0xae, (byte)0xb5, (byte)0xf3, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xf2, (byte)0xae, (byte)0xae, (byte)0xb5, (byte)0xb5, (byte)0xbc, (byte)0xb5, (byte)0x66, (byte)0x00, + (byte)0x00, (byte)0x0b, (byte)0xa7, (byte)0xb5, (byte)0xae, (byte)0x8c, (byte)0xa7, (byte)0xf4, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xbd, (byte)0xa7, (byte)0x8c, (byte)0xae, (byte)0xb5, (byte)0xae, (byte)0x66, (byte)0x00, (byte)0x00, + (byte)0x13, (byte)0x04, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xf4, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xbd, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x60, + (byte)0xa7, (byte)0x66, (byte)0x60, (byte)0x66, (byte)0x66, (byte)0x8c, (byte)0xf1, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xbd, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x60, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x6c, (byte)0x04, + (byte)0xa7, (byte)0x60, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, + (byte)0x66, (byte)0x66, (byte)0xb6, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xf5, (byte)0xef, (byte)0x66, (byte)0x66, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0x66, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x60, (byte)0xa7, + (byte)0x66, (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xa7, + (byte)0xb6, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0xf3, (byte)0x07, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x66, + (byte)0x66, (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x6c, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x66, (byte)0xc8, (byte)0xa7, + (byte)0x66, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x92, (byte)0xf1, + (byte)0xf1, (byte)0xf1, (byte)0xf1, (byte)0xf2, (byte)0x07, (byte)0xa7, (byte)0xa7, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0x66, + (byte)0x66, (byte)0xc8, (byte)0x66, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x6c, (byte)0xa7, (byte)0xad, (byte)0xa7, + (byte)0xa7, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb5, (byte)0xbd, (byte)0xbd, + (byte)0xbd, (byte)0xbd, (byte)0xf0, (byte)0x8b, (byte)0x8b, (byte)0xad, (byte)0x8b, (byte)0x8b, (byte)0xa7, (byte)0xa7, (byte)0xc8, + (byte)0xc8, (byte)0x60, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0x66, (byte)0xae, (byte)0xad, (byte)0x8b, + (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb3, (byte)0xad, (byte)0xb5, (byte)0x07, (byte)0x07, (byte)0x07, + (byte)0xf0, (byte)0x8b, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0x8b, (byte)0xa7, (byte)0xae, (byte)0xa7, + (byte)0x6c, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x60, (byte)0xa7, (byte)0xb4, (byte)0xad, (byte)0xad, + (byte)0xad, (byte)0xb3, (byte)0xb3, (byte)0xd4, (byte)0xd4, (byte)0xb3, (byte)0x8c, (byte)0xb6, (byte)0x07, (byte)0xb6, (byte)0x8c, + (byte)0xb3, (byte)0xd4, (byte)0xb3, (byte)0xb3, (byte)0xad, (byte)0xad, (byte)0xad, (byte)0xb4, (byte)0xad, (byte)0x66, (byte)0x00, + (byte)0x06, (byte)0x00, (byte)0x00, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x66, (byte)0xae, (byte)0xd5, (byte)0xad, (byte)0xd4, + (byte)0xd4, (byte)0xd5, (byte)0xd5, (byte)0xd5, (byte)0xdb, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xb4, (byte)0xd5, + (byte)0xd5, (byte)0xd5, (byte)0xd4, (byte)0xd4, (byte)0xad, (byte)0xd5, (byte)0xb4, (byte)0x0e, (byte)0x06, (byte)0x06, (byte)0x06, + (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x0b, (byte)0xae, (byte)0xdb, (byte)0xd4, (byte)0xd5, + (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, + (byte)0xdb, (byte)0xd5, (byte)0xd4, (byte)0xdb, (byte)0xb4, (byte)0x66, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x0e, (byte)0xae, (byte)0xdc, (byte)0xdb, (byte)0xdb, + (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdc, (byte)0xdc, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, (byte)0xdb, + (byte)0xdb, (byte)0xdc, (byte)0xb4, (byte)0x66, (byte)0x6c, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x0b, (byte)0xae, (byte)0xdc, (byte)0xe9, (byte)0xdc, + (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xdc, (byte)0xe9, (byte)0xe9, + (byte)0xb4, (byte)0x0e, (byte)0x00, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, + (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xf8, (byte)0x0e, (byte)0x66, (byte)0xb4, (byte)0xdc, (byte)0xe2, + (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xe2, (byte)0xdd, (byte)0xb4, (byte)0xa7, (byte)0x16, + (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x06, + (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x60, (byte)0x0e, (byte)0x60, (byte)0x8c, (byte)0xb4, + (byte)0xb5, (byte)0xdc, (byte)0xdc, (byte)0xbb, (byte)0xb4, (byte)0x8c, (byte)0x66, (byte)0x0b, (byte)0x6c, (byte)0x04, (byte)0x06, + (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x04, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x06, (byte)0x06, (byte)0xed, + (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xec, (byte)0x6c, (byte)0x0e, (byte)0x0e, + (byte)0x44, (byte)0x0e, (byte)0x0e, (byte)0x0e, (byte)0x13, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, + (byte)0x06, (byte)0xed, (byte)0x06, (byte)0x06, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00}); + ByteBuffer dest = new AssertingByteBuffer(flipRawImage(flippedImage, width, height, bpp).data); + + rleDecompress(src, dest, width, height, depth); + + } + + if (true) { + // 32x32@16 + int width = 32, height = 32, depth = 16; + + ByteBuffer src = new ByteBuffer(new byte[] {(byte)0x85, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x06, (byte)0x8b, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x06, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0x16, (byte)0x69, (byte)0x99, (byte)0xd6, (byte)0x06, (byte)0x69, (byte)0x99, + (byte)0xd6, (byte)0x04, (byte)0xcc, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x6e, (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x6e, + (byte)0x08, (byte)0x42, (byte)0x01, (byte)0x70, (byte)0x08, (byte)0x42, (byte)0x71, (byte)0xff, (byte)0xff, (byte)0xce, (byte)0x18, + (byte)0xc6, (byte)0x01, (byte)0x81, (byte)0x08, (byte)0x42, (byte)0xce, (byte)0x66, (byte)0x29, (byte)0x02, (byte)0xcd, (byte)0x89, + (byte)0x52, (byte)0x03, (byte)0x88, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xd8, (byte)0x99, (byte)0xd6, + (byte)0x03, (byte)0xf8, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xf0, (byte)0x66, (byte)0x99, (byte)0xd6, + (byte)0x05, (byte)0x6a, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0xc4, (byte)0xcc, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x6e, + (byte)0xff, (byte)0xff, (byte)0x02, (byte)0x6e, (byte)0x08, (byte)0x42, (byte)0x01, (byte)0x70, (byte)0x08, (byte)0x42, (byte)0x71, + (byte)0xff, (byte)0xff, (byte)0xce, (byte)0x18, (byte)0xc6, (byte)0x01, (byte)0x81, (byte)0x08, (byte)0x42, (byte)0xce, (byte)0x66, + (byte)0x29, (byte)0x02, (byte)0xcd, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x00, (byte)0x04, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0xc3, (byte)0x80, (byte)0x61, (byte)0x00, (byte)0xa5, (byte)0x80, (byte)0x40, (byte)0xec, (byte)0x52, (byte)0x00, (byte)0x5a, + (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0x24, (byte)0x00, (byte)0x12, (byte)0x00, (byte)0x24, (byte)0x00, (byte)0x12, (byte)0x00, + (byte)0x5a, (byte)0x00, (byte)0x2d, (byte)0x00, (byte)0xa5, (byte)0x80, (byte)0x52, (byte)0x00, (byte)0xc3, (byte)0x80, (byte)0x61, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xcc, (byte)0x89, (byte)0x52, (byte)0x03, (byte)0x6e, (byte)0xff, + (byte)0xff, (byte)0x02, (byte)0xcb, (byte)0x18, (byte)0xc6, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0xff, (byte)0xff,}); + + ByteBuffer dest = new AssertingByteBuffer(new byte[] {(byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, + (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, + (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, + (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, + (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, + (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, + (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, + (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, + (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, + (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, + (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, + (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, + (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, + (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x99, (byte)0xd6, (byte)0x10, (byte)0x84, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, + (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, + (byte)0x10, (byte)0x84, (byte)0x10, (byte)0x84, (byte)0x99, (byte)0xd6, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, + (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, + (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, + (byte)0x08, (byte)0x42, (byte)0x08, (byte)0x42, (byte)0xff, (byte)0xff,}); + + rleDecompress(src, dest, width, height, depth); + + } + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpConstants.java old mode 100644 new mode 100755 similarity index 86% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpConstants.java index 3da132815c5..147d0a72bad --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpConstants.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import java.nio.charset.Charset; @@ -63,8 +63,29 @@ public interface RdpConstants { */ public static final int RDP_NEG_REQ_TYPE_NEG_FAILURE = 3; + /** + * I/O Channel. + */ public static final int CHANNEL_IO = 1003; + /** + * RDP channel. + */ public static final int CHANNEL_RDPRDR = 1004; + /** + * Clipboard channel. + */ + public static final int CHANNEL_CLIPRDR = 1005; + + /** + * RDP sound channel. + */ + public static final int CHANNEL_RDPSND = 1006; + + /** + * User channel. + */ + public static final int CHANNEL_USER = 1007; + } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpState.java old mode 100644 new mode 100755 similarity index 97% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpState.java index 951f0be8e98..1113d149452 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/RdpState.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import java.util.HashSet; import java.util.Set; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerBitmapUpdate.java old mode 100644 new mode 100755 similarity index 94% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerBitmapUpdate.java index 5c30b69dbac..6accc162f0b --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerBitmapUpdate.java @@ -14,15 +14,15 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; import streamer.Element; -import streamer.FakeSink; import streamer.Link; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.FakeSink; import common.BitmapOrder; import common.BitmapRectangle; @@ -62,7 +62,8 @@ public class ServerBitmapUpdate extends BaseElement { // be set to UPDATETYPE_BITMAP (0x0001). int updateType = buf.readSignedShortLE(); if (updateType != UPDATETYPE_BITMAP) - throw new RuntimeException("Unknown update type. Expected update type: UPDATETYPE_BITMAP (0x1). Actual update type: " + updateType + ", buf: " + buf + "."); + throw new RuntimeException("Unknown update type. Expected update type: UPDATETYPE_BITMAP (0x1). Actual update type: " + updateType + ", buf: " + buf + + "."); // (2 bytes): A 16-bit, unsigned integer. The number of screen rectangles // present in the rectangles field. @@ -80,8 +81,7 @@ public class ServerBitmapUpdate extends BaseElement { buf.assertThatBufferIsFullyRead(); - ByteBuffer data = new ByteBuffer(0); - data.setOrder(order); + ByteBuffer data = new ByteBuffer(order); pushDataToAllOuts(data); buf.unref(); @@ -172,9 +172,8 @@ public class ServerBitmapUpdate extends BaseElement { * Example. */ public static void main(String args[]) { - ByteBuffer packet = - new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, 0x04, 0x0a, - 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); + ByteBuffer packet = new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00, + 0x01, 0x04, 0x0a, 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); Element bitmap = new ServerBitmapUpdate("bitmap") { { diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUCooperate.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUCooperate.java index 7bbe0c346cb..2a9d49db551 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUCooperate.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUGrantedControl.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUGrantedControl.java index 62cbd6cedc8..974d622a57e --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerControlPDUGrantedControl.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerDemandActivePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerDemandActivePDU.java new file mode 100755 index 00000000000..88ede17a19a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerDemandActivePDU.java @@ -0,0 +1,660 @@ +// 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 rdpclient.rdp; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Order; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.FakeSink; +import streamer.debug.MockSource; +import common.ScreenDescription; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240669.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240484.aspx + */ +public class ServerDemandActivePDU extends BaseElement { + + /** + * Demand Active PDU. + */ + public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1; + + protected RdpState state; + protected ScreenDescription screen; + + public ServerDemandActivePDU(String id, ScreenDescription screen, RdpState state) { + super(id); + this.state = state; + this.screen = screen; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Total length of packet + int length = buf.readSignedShortLE(); // Ignore + if (buf.length != length) + throw new RuntimeException("Incorrect length of packet. Length: " + length + ", data: " + buf + "."); + + int type = buf.readSignedShortLE() & 0xf; + if (type != PDUTYPE_DEMANDACTIVEPDU) + throw new RuntimeException("Unknown PDU type. Expected type: Demand Active PDU (0x1), actual tyoe: " + type + ", data: " + buf + "."); + + // TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002) + int pduSource = buf.readSignedShortLE(); + if (pduSource != 1002) + throw new RuntimeException("Unexepcted source of demand active PDU. Expected source: 1002, actual source: " + pduSource + "."); + + // (4 bytes): A 32-bit, unsigned integer. The share identifier for the + // packet (see [T128] section 8.4.2 for more information regarding share + // IDs). + long shareId = buf.readUnsignedIntLE(); + state.serverShareId = shareId; + + // Ignore rest of server data because it is not used by this client. + // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the + // sourceDescriptor field. + int lengthSourceDescriptor = buf.readUnsignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The combined size in bytes of the + // numberCapabilities, pad2Octets, and capabilitySets fields. + int lengthCombinedCapabilities = buf.readUnsignedShortLE(); + + // (variable): A variable-length array of bytes containing a source + // descriptor, + // ByteBuffer sourceDescriptor = buf.readBytes(lengthSourceDescriptor); + buf.skipBytes(lengthSourceDescriptor); + + // (variable): An array of Capability Set (section 2.2.1.13.1.1.1) + // structures. The number of capability sets is specified by the + // numberCapabilities field. + handleCapabiltySets(buf.readBytes(lengthCombinedCapabilities)); + + // (4 bytes): A 32-bit, unsigned integer. The session identifier. This field + // is ignored by the client. + buf.skipBytes(4); + + /* DEBUG */buf.assertThatBufferIsFullyRead(); + + buf.unref(); + + sendHandshakePackets(); + } + + /** + * General Capability Set + */ + public static final int CAPSTYPE_GENERAL = 0x0001; + /** + * Bitmap Capability Set + */ + public static final int CAPSTYPE_BITMAP = 0x0002; + /** + * Order Capability Set + */ + public static final int CAPSTYPE_ORDER = 0x0003; + /** + * Revision 1 Bitmap Cache Capability Set + */ + public static final int CAPSTYPE_BITMAPCACHE = 0x0004; + /** + * Control Capability Set + */ + public static final int CAPSTYPE_CONTROL = 0x0005; + /** + * Window Activation Capability Set + */ + public static final int CAPSTYPE_ACTIVATION = 0x0007; + /** + * Pointer Capability Set + */ + public static final int CAPSTYPE_POINTER = 0x0008; + /** + * Share Capability Set + */ + public static final int CAPSTYPE_SHARE = 0x0009; + /** + * Color Table Cache Capability Set + */ + public static final int CAPSTYPE_COLORCACHE = 0x000A; + /** + * Sound Capability Set + */ + public static final int CAPSTYPE_SOUND = 0x000C; + /** + * Input Capability Set + */ + public static final int CAPSTYPE_INPUT = 0x000D; + /** + * Font Capability Set + */ + public static final int CAPSTYPE_FONT = 0x000E; + /** + * Brush Capability Set + */ + public static final int CAPSTYPE_BRUSH = 0x000F; + /** + * Glyph Cache Capability Set + */ + public static final int CAPSTYPE_GLYPHCACHE = 0x0010; + /** + * Offscreen Bitmap Cache Capability Set + */ + public static final int CAPSTYPE_OFFSCREENCACHE = 0x0011; + /** + * Bitmap Cache Host Support Capability Set + */ + public static final int CAPSTYPE_BITMAPCACHE_HOSTSUPPORT = 0x0012; + /** + * Revision 2 Bitmap Cache Capability Set + */ + public static final int CAPSTYPE_BITMAPCACHE_REV2 = 0x0013; + /** + * Virtual Channel Capability Set + */ + public static final int CAPSTYPE_VIRTUALCHANNEL = 0x0014; + /** + * DrawNineGrid Cache Capability Set + */ + public static final int CAPSTYPE_DRAWNINEGRIDCACHE = 0x0015; + /** + * Draw GDI+ Cache Capability Set + */ + public static final int CAPSTYPE_DRAWGDIPLUS = 0x0016; + /** + * Remote Programs Capability Set + */ + public static final int CAPSTYPE_RAIL = 0x0017; + /** + * Window List Capability Set + */ + public static final int CAPSTYPE_WINDOW = 0x0018; + /** + * Desktop Composition Extension Capability Set + */ + public static final int CAPSETTYPE_COMPDESK = 0x0019; + /** + * Multifragment Update Capability Set + */ + public static final int CAPSETTYPE_MULTIFRAGMENTUPDATE = 0x001A; + /** + * Large Pointer Capability Set + */ + public static final int CAPSETTYPE_LARGE_POINTER = 0x001B; + /** + * Surface Commands Capability Set + */ + public static final int CAPSETTYPE_SURFACE_COMMANDS = 0x001C; + /** + * Bitmap Codecs Capability Set + */ + public static final int CAPSETTYPE_BITMAP_CODECS = 0x001D; + /** + * Frame Acknowledge Capability Set + */ + public static final int CAPSSETTYPE_FRAME_ACKNOWLEDGE = 0x001E; + + /** + * @see http://msdn.microsoft.com/en-us/library/cc240486.aspx + */ + protected void handleCapabiltySets(ByteBuffer buf) { + // (2 bytes): A 16-bit, unsigned integer. The number of capability sets + // included in the Demand Active PDU. + int numberCapabilities = buf.readSignedShortLE(); + + // (2 bytes): Padding. + buf.skipBytes(2); + + for (int i = 0; i < numberCapabilities; i++) { + // (2 bytes): A 16-bit, unsigned integer. The type identifier of the + // capability set. + int capabilitySetType = buf.readUnsignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The length in bytes of the + // capability data, including the size of the capabilitySetType and + // lengthCapability fields. + int lengthCapability = buf.readUnsignedShortLE(); + + // (variable): Capability set data which conforms to the structure of the + // type given by the capabilitySetType field. + ByteBuffer capabilityData = buf.readBytes(lengthCapability - 4); + + switch (capabilitySetType) { + case CAPSTYPE_GENERAL: + break; + case CAPSTYPE_BITMAP: + handleBitmapCapabilities(capabilityData); + break; + case CAPSTYPE_ORDER: + break; + case CAPSTYPE_BITMAPCACHE: + break; + case CAPSTYPE_CONTROL: + break; + case CAPSTYPE_ACTIVATION: + break; + case CAPSTYPE_POINTER: + break; + case CAPSTYPE_SHARE: + break; + case CAPSTYPE_COLORCACHE: + break; + case CAPSTYPE_SOUND: + break; + case CAPSTYPE_INPUT: + break; + case CAPSTYPE_FONT: + break; + case CAPSTYPE_BRUSH: + break; + case CAPSTYPE_GLYPHCACHE: + break; + case CAPSTYPE_OFFSCREENCACHE: + break; + case CAPSTYPE_BITMAPCACHE_HOSTSUPPORT: + break; + case CAPSTYPE_BITMAPCACHE_REV2: + break; + case CAPSTYPE_VIRTUALCHANNEL: + break; + case CAPSTYPE_DRAWNINEGRIDCACHE: + break; + case CAPSTYPE_DRAWGDIPLUS: + break; + case CAPSTYPE_RAIL: + break; + case CAPSTYPE_WINDOW: + break; + case CAPSETTYPE_COMPDESK: + break; + case CAPSETTYPE_MULTIFRAGMENTUPDATE: + break; + case CAPSETTYPE_LARGE_POINTER: + break; + case CAPSETTYPE_SURFACE_COMMANDS: + break; + case CAPSETTYPE_BITMAP_CODECS: + break; + case CAPSSETTYPE_FRAME_ACKNOWLEDGE: + break; + default: + // Ignore + break; + } + + capabilityData.unref(); + } + + // TODO + + buf.unref(); + } + + /** + * @see http://msdn.microsoft.com/en-us/library/cc240554.aspx + */ + protected void handleBitmapCapabilities(ByteBuffer buf) { + + // (2 bytes): A 16-bit, unsigned integer. The server MUST set this field to + // the color depth of the session, while the client SHOULD set this field to + // the color depth requested in the Client Core Data (section 2.2.1.3.2). + int preferredBitsPerPixel = buf.readUnsignedShortLE(); + screen.setPixelFormatRGBTrueColor(preferredBitsPerPixel); + + // receive1BitPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates + // whether the client can receive 1 bpp. This field is ignored and SHOULD be + // set to TRUE (0x0001). + buf.skipBytes(2); + + // receive4BitsPerPixel(2 bytes): A 16-bit, unsigned integer. Indicates + // whether the client can receive 4 bpp. This field is ignored and SHOULD be + // set to TRUE (0x0001). + buf.skipBytes(2); + + // receive8BitsPerPixel (2 bytes): A 16-bit, unsigned integer. Indicates + // whether the client can receive 8 bpp. This field is ignored and SHOULD be + // set to TRUE (0x0001). + buf.skipBytes(2); + + // (2 bytes): A 16-bit, unsigned integer. The width of the desktop in the + // session. + int desktopWidth = buf.readUnsignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The height of the desktop in the + // session. + int desktopHeight = buf.readUnsignedShortLE(); + + screen.setFramebufferSize(desktopWidth, desktopHeight); + + // pad2octets (2 bytes): A 16-bit, unsigned integer. Padding. Values in this + // field MUST be ignored. + + // desktopResizeFlag (2 bytes): A 16-bit, unsigned integer. Indicates + // whether resizing the desktop by using a Deactivation-Reactivation + // Sequence is supported. + + // bitmapCompressionFlag (2 bytes): A 16-bit, unsigned integer. Indicates + // whether bitmap compression is supported. This field MUST be set to TRUE + // (0x0001) because support for compressed bitmaps is required for a + // connection to proceed. + + // highColorFlags (1 byte): An 8-bit, unsigned integer. Client support for + // 16 bpp color modes. This field is ignored and SHOULD be set to zero. + + // drawingFlags (1 byte): An 8-bit, unsigned integer. Flags describing + // support for 32 bpp bitmaps. + + // multipleRectangleSupport (2 bytes): A 16-bit, unsigned integer. Indicates + // whether the use of multiple bitmap rectangles is supported in the Bitmap + // Update (section 2.2.9.1.1.3.1.2). This field MUST be set to TRUE (0x0001) + // because multiple rectangle support is required for a connection to + // proceed. + + // pad2octetsB (2 bytes): A 16-bit, unsigned integer. Padding. Values in + // this field MUST be ignored. + } + + /** + * Send all client requests in one hop, to simplify logic. + */ + protected void sendHandshakePackets() { + // Send reactivation sequence in bulk + pushDataToPad("confirm_active", new ByteBuffer((Order)null)); + } + + /** + * Example. + * + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + /* @formatter:off */ + byte[] packet = new byte[] { + 0x67, 0x01, // TS_SHARECONTROLHEADER::totalLength = 0x0167 = 359 bytes + 0x11, 0x00, // TS_SHARECONTROLHEADER::pduType = 0x0011 0x0011 = 0x0010 | 0x0001 = TS_PROTOCOL_VERSION | PDUTYPE_DEMANDACTIVEPDU + + (byte) 0xea, 0x03, // TS_SHARECONTROLHEADER::pduSource = 0x03ea (1002) + + (byte) 0xea, 0x03, 0x01, 0x00, // TS_DEMAND_ACTIVE_PDU::shareId + 0x04, 0x00, // TS_DEMAND_ACTIVE_PDU::lengthSourceDescriptor = 4 bytes + 0x51, 0x01, // TS_DEMAND_ACTIVE_PDU::lengthCombinedCapabilities = 0x151 = 337 bytes + + 0x52, 0x44, 0x50, 0x00, // TS_DEMAND_ACTIVE_PDU::sourceDescriptor = "RDP" + + 0x0d, 0x00, // TS_DEMAND_ACTIVE_PDU::numberCapabilities = 13 + 0x00, 0x00, // TS_DEMAND_ACTIVE_PDU::pad2octets + + // Share Capability Set (8 bytes) + // 0x09, 0x00, 0x08, 0x00, (byte) 0xea, 0x03, (byte) 0xdc, (byte) 0xe2, + // + 0x09, 0x00, // TS_SHARE_CAPABILITYSET::capabilitySetType = CAPSTYPE_SHARE (9) + 0x08, 0x00, // TS_SHARE_CAPABILITYSET::lengthCapability = 8 bytes + (byte) 0xea, 0x03, // TS_SHARE_CAPABILITYSET::nodeID = 0x03ea (1002) + (byte) 0xdc, (byte) 0xe2, // TS_SHARE_CAPABILITYSET::pad2octets + + // General Capability Set (24 bytes) + // 0x01, 0x00, 0x18, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x04, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + // + 0x01, 0x00, // TS_GENERAL_CAPABILITYSET::capabilitySetType = CAPSTYPE_GENERAL (1) + 0x18, 0x00, // TS_GENERAL_CAPABILITYSET::lengthCapability = 24 bytes + + 0x01, 0x00, // TS_GENERAL_CAPABILITYSET::osMajorType = TS_OSMAJORTYPE_WINDOWS (1) + 0x03, 0x00, // TS_GENERAL_CAPABILITYSET::osMinorType = TS_OSMINORTYPE_WINDOWS_NT (3) + 0x00, 0x02, // TS_GENERAL_CAPABILITYSET::protocolVersion = TS_CAPS_PROTOCOLVERSION (0x0200) + 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::pad2octetsA + 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::generalCompressionTypes = 0 + 0x1d, 0x04, // TS_GENERAL_CAPABILITYSET::extraFlags = 0x041d = 0x0400 | 0x0010 | 0x0008 | 0x0004 | 0x0001 = NO_BITMAP_COMPRESSION_HDR | ENC_SALTED_CHECKSUM | AUTORECONNECT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED | FASTPATH_OUTPUT_SUPPORTED + + 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::updateCapabilityFlag = 0 + 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::remoteUnshareFlag = 0 + 0x00, 0x00, // TS_GENERAL_CAPABILITYSET::generalCompressionLevel = 0 + 0x01, // TS_GENERAL_CAPABILITYSET::refreshRectSupport = TRUE + 0x01, // TS_GENERAL_CAPABILITYSET::suppressOutputSupport = TRUE + + // Virtual Channel Capability Set (8 bytes) + // 0x14, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, + // + 0x14, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::capabilitySetType = CAPSTYPE_VIRTUALCHANNEL (20) + 0x08, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::lengthCapability = 8 bytes + + 0x02, 0x00, 0x00, 0x00, // TS_VIRTUALCHANNEL_CAPABILITYSET::vccaps1 = 0x00000002 = VCCAPS_COMPR_CS_8K + + // DrawGdiPlus Capability Set (40 bytes) + // 0x16, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, (byte) 0xf6, 0x13, (byte) 0xf3, 0x01, 0x00, 0x00, 0x00, + // 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, (byte) 0x9c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x61, (byte) 0xa6, (byte) 0x82, (byte) 0x80, + // 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, (byte) 0x91, (byte) 0xbf, + // + 0x16, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::capabilitySetType = CAPSTYPE_DRAWGDIPLUS (22) + 0x28, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::lengthCapability = 40 bytes + + 0x00, 0x00, 0x00, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusSupportLevel = TS_DRAW_GDIPLUS_DEFAULT (0) + 0x70, (byte) 0xf6, 0x13, (byte) 0xf3, // TS_DRAW_GDIPLUS_CAPABILITYSET::GdipVersion (not initialized by server) + 0x01, 0x00, 0x00, 0x00, // TS_DRAW_GDIPLUS_CAPABILITYSET::drawGdiplusCacheLevel = TS_DRAW_GDIPLUS_CACHE_LEVEL_ONE (1) + + 0x01, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipGraphicsCacheEntries (not initialized by server) + 0x00, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectBrushCacheEntries (not initialized by server) + 0x18, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectPenCacheEntries (not initialized by server) + 0x00, 0x00, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageCacheEntries (not initialized by server) + (byte) 0x9c, (byte) 0xf6, // TS_GDIPLUS_CACHE_ENTRIES::GdipObjectImageAttributesCacheEntries (not initialized by server) + + 0x13, (byte) 0xf3, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipGraphicsCacheChunkSize (not initialized by server) + 0x61, (byte) 0xa6, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectBrushCacheChunkSize (not initialized by server) + (byte) 0x82, (byte) 0x80, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectPenCacheChunkSize (not initialized by server) + 0x00, 0x00, // TS_GDIPLUS_CACHE_CHUNK_SIZE::GdipObjectImageAttributesCacheChunkSize (not initialized by server) + + 0x00, 0x00, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheChunkSize (not initialized by server) + 0x00, 0x50, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheTotalSize (not initialized by server) + (byte) 0x91, (byte) 0xbf, // TS_GDIPLUS_IMAGE_CACHE_PROPERTIES::GdipObjectImageCacheMaxSize (not initialized by server) + + // Font Capability Set (4 bytes) + // 0x0e, 0x00, 0x04, 0x00, + // + // Due to a bug, the TS_FONT_CAPABILITYSET capability set size is incorrectly set to 4 bytes (it must be 8 bytes). As a result of this bug, the fontSupportFlags and pad2octets fields are missing. + 0x0e, 0x00, // TS_FONT_CAPABILITYSET::capabilitySetType = CAPSTYPE_FONT (14) + 0x04, 0x00, // TS_FONT_CAPABILITYSET::lengthCapability = 4 bytes + + + // Bitmap Capability Set (28 bytes) + // 0x02, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, + // 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // + 0x02, 0x00, // TS_BITMAP_CAPABILITYSET::capabilitySetType = CAPSTYPE_BITMAP (2) + 0x1c, 0x00, // TS_BITMAP_CAPABILITYSET::lengthCapability = 28 bytes + + 0x18, 0x00, // TS_BITMAP_CAPABILITYSET::preferredBitsPerPixel = 24 bpp + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive1BitPerPixel = TRUE + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive4BitsPerPixel = TRUE + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::receive8BitsPerPixel = TRUE + 0x00, 0x05, // TS_BITMAP_CAPABILITYSET::desktopWidth = 1280 pixels + 0x00, 0x04, // TS_BITMAP_CAPABILITYSET::desktopHeight = 1024 pixels + 0x00, 0x00, // TS_BITMAP_CAPABILITYSET::pad2octets + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::desktopResizeFlag = TRUE + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::bitmapCompressionFlag = TRUE + 0x00, // TS_BITMAP_CAPABILITYSET::highColorFlags = 0 + 0x00, // TS_BITMAP_CAPABILITYSET::pad1octet + 0x01, 0x00, // TS_BITMAP_CAPABILITYSET::multipleRectangleSupport = TRUE + 0x00, 0x00, // TS_BITMAP_CAPABILITYSET::pad2octetsB + + // Order Capability Set (88 bytes) + // 0x03, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, + // 0x00, 0x00, 0x22, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + // 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + // 0x00, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x40, 0x42, 0x0f, 0x00, + // 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // + 0x03, 0x00, // TS_ORDER_CAPABILITYSET::capabilitySetType = CAPSTYPE_ORDER (3) + 0x58, 0x00, // TS_ORDER_CAPABILITYSET::lengthCapability = 88 bytes + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TS_ORDER_CAPABILITYSET::terminalDescriptor = "" + 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::pad4octetsA + + 0x01, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveXGranularity = 1 + 0x14, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveYGranularity = 20 + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsA + 0x01, 0x00, // TS_ORDER_CAPABILITYSET::maximumOrderLevel = ORD_LEVEL_1_ORDERS (1) + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::numberFonts = 0 + + 0x22, 0x00, // TS_ORDER_CAPABILITYSET::orderFlags = 0x0022 = 0x0020 | 0x0002 = COLORINDEXSUPPORT | NEGOTIATEORDERSUPPORT + + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DSTBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_PATBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SCRBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_INDEX] = TRUE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ATEXTOUT_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_AEXTTEXTOUT_INDEX] = FALSE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_DRAWNINEGRID_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_LINETO_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTI_DRAWNINEGRID_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_OPAQUERECT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_SAVEBITMAP_INDEX] = TRUE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WTEXTOUT_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEMBLT_R2_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MEM3BLT_R2_INDEX] = FALSE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIDSTBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIPATBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTISCRBLT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_MULTIOPAQUERECT_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_INDEX_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_SC_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYGON_CB_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_POLYLINE_INDEX] = TRUE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[23] = 0 + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_FAST_GLYPH_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_SC_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_ELLIPSE_CB_INDEX] = TRUE + 0x01, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_INDEX_INDEX] = TRUE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WEXTTEXTOUT_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGTEXTOUT_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[TS_NEG_WLONGEXTTEXTOUT_INDEX] = FALSE + 0x00, // TS_ORDER_CAPABILITYSET::orderSupport[24] = 0 + + (byte) 0xa1, 0x06, // TS_ORDER_CAPABILITYSET::textFlags = 0x06a1 + + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsB + 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::pad4octetsB + + 0x40, 0x42, 0x0f, 0x00, // TS_ORDER_CAPABILITYSET::desktopSaveSize = 0xf4240 = 1000000 + 0x01, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsC + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsD + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::textANSICodePage + 0x00, 0x00, // TS_ORDER_CAPABILITYSET::pad2octetsE + + // Color Table Cache Capability Set (8 bytes) + // 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, + // + 0x0a, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::capabilitySetType = CAPSTYPE_COLORCACHE (10) + 0x08, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::lengthCapability = 8 bytes + + 0x06, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::colorTableCacheSize = 6 + 0x00, 0x00, // TS_COLORTABLECACHE_CAPABILITYSET::pad2octets + + // Bitmap Cache Host Support Capability Set (8 bytes) + // 0x12, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, + // + 0x12, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::capabilitySetType = CAPSTYPE_BITMAPCACHE_HOSTSUPPORT (18) + 0x08, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::lengthCapability = 8 bytes + + 0x01, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::CacheVersion = 1 (corresponds to rev. 2 capabilities) + 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad1 + 0x00, 0x00, // TS_BITMAPCACHE_CAPABILITYSET_HOSTSUPPORT::Pad2 + + // Pointer Capability Set (10 bytes) + // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, 0x19, 0x00, + // + 0x08, 0x00, // TS_POINTER_CAPABILITYSET::capabilitySetType = CAPSTYPE_POINTER (8) + 0x0a, 0x00, // TS_POINTER_CAPABILITYSET::lengthCapability = 10 bytes + + 0x01, 0x00, // TS_POINTER_CAPABILITYSET::colorPointerFlag = TRUE + 0x19, 0x00, // TS_POINTER_CAPABILITYSET::colorPointerCacheSize = 25 + 0x19, 0x00, // TS_POINTER_CAPABILITYSET::pointerCacheSize = 25 + + // Input Capability Set (88 bytes) + // 0x0d, 0x00, 0x58, 0x00, 0x35, 0x00, 0x00, 0x00, (byte) 0xa1, 0x06, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, + // 0x0c, (byte) 0xf6, 0x13, (byte) 0xf3, (byte) 0x93, 0x5a, 0x37, (byte) 0xf3, 0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, + // 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, 0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, + // 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, + // 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, + // 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, + // + 0x0d, 0x00, // TS_INPUT_CAPABILITYSET::capabilitySetType = CAPSTYPE_INPUT (13) + 0x58, 0x00, // TS_INPUT_CAPABILITYSET::lengthCapability = 88 bytes + + 0x35, 0x00, // TS_INPUT_CAPABILITYSET::inputFlags = 0x0035 = 0x0020 | 0x0010 | 0x0004 | 0x0001 = INPUT_FLAG_FASTPATH_INPUT2 | INPUT_FLAG_VKPACKET | INPUT_FLAG_MOUSEX | INPUT_FLAG_SCANCODES + + 0x00, 0x00, // TS_INPUT_CAPABILITYSET::pad2octetsA + (byte) 0xa1, 0x06, 0x00, 0x00, // TS_INPUT_CAPABILITYSET::keyboardLayout (not initialized by server) + 0x40, 0x42, 0x0f, 0x00, // TS_INPUT_CAPABILITYSET::keyboardType (not initialized by server) + 0x0c, (byte) 0xf6, 0x13, (byte) 0xf3, // TS_INPUT_CAPABILITYSET::keyboardSubType (not initialized by server) + (byte) 0x93, 0x5a, 0x37, (byte) 0xf3, // TS_INPUT_CAPABILITYSET::keyboardFunctionKey (not initialized by server) + + // TS_INPUT_CAPABILITYSET::imeFileName (not initialized by server) + 0x00, (byte) 0x90, 0x30, (byte) 0xe1, 0x34, 0x1c, 0x38, (byte) 0xf3, 0x40, (byte) 0xf6, 0x13, (byte) 0xf3, 0x04, 0x00, 0x00, 0x00, + 0x4c, 0x54, (byte) 0xdc, (byte) 0xe2, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, 0x01, 0x00, 0x00, 0x00, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, + 0x00, 0x00, 0x00, 0x00, 0x38, (byte) 0xf6, 0x13, (byte) 0xf3, 0x2e, 0x05, 0x38, (byte) 0xf3, 0x08, 0x50, (byte) 0xdc, (byte) 0xe2, + 0x2c, (byte) 0xf6, 0x13, (byte) 0xf3, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x19, 0x00, + + // RAIL Capability Set (8 bytes) + // 0x17, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + // + 0x17, 0x00, // TS_RAIL_CAPABILITYSET::capabilitySetType = CAPSTYPE_RAIL (23) + 0x08, 0x00, // TS_RAIL_CAPABILITYSET::lengthCapability = 8 bytes + + 0x00, 0x00, 0x00, 0x00, // TS_RAIL_CAPABILITYSET::railSupportLevel = TS_RAIL_LEVEL_DEFAULT (0) + + // Windowing Capability Set (11 bytes) + // 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // + 0x18, 0x00, // TS_WINDOW_CAPABILITYSET::capabilitySetType = CAPSTYPE_WINDOW (24) + 0x0b, 0x00, // TS_WINDOW_CAPABILITYSET::lengthCapability = 11 bytes + + 0x00, 0x00, 0x00, 0x00, // TS_WINDOW_CAPABILITYSET::wndSupportLevel = TS_WINDOW_LEVEL_DEFAULT (0) + 0x00, // TS_WINDOW_CAPABILITYSET::nIconCaches = 0 + 0x00, 0x00, // TS_WINDOW_CAPABILITYSET::nIconCacheEntries = 0 + + // Remainder of Demand Active PDU: + + 0x00, 0x00, 0x00, 0x00, // TS_DEMAND_ACTIVE_PDU::sessionId = 0 + }; + /* @formatter:on */ + + RdpState rdpState = new RdpState(); + ScreenDescription screenDescription = new ScreenDescription(); + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element demandActive = new ServerDemandActivePDU("demand_active", screenDescription, rdpState); + Element sink = new FakeSink("sink"); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, demandActive, sink); + pipeline.link("source", "demand_active", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerFastPath.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerFastPath.java new file mode 100755 index 00000000000..f88f492b047 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerFastPath.java @@ -0,0 +1,315 @@ +// 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 rdpclient.rdp; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240621.aspx + */ +public class ServerFastPath extends BaseElement { + + /** + * TPKT protocol version (first byte). + */ + public static final int PROTOCOL_TPKT = 0x03; + + /** + * Fast path protocol version (first two bits of first byte). + */ + public static final int PROTOCOL_FASTPATH = 0x00; + + /** + * CredSSP packets. + */ + public static final int PROTOCOL_CREDSSP = 0x30; + + /** + * TPKT packets will be pushed to that pad. + */ + public static final String TPKT_PAD = "tpkt"; + + /** + * CredSSP packets will be pushed to same pad as TPKT, because they are part + * of slow-path initialization sequence. + */ + public static final String CREDSSP_PAD = "tpkt"; + + private static final String ORDERS_PAD = "orders"; + private static final String BITMAP_PAD = "bitmap"; + private static final String PALETTE_PAD = "palette"; + + /** + * Indicates that packet contains 8 byte secure checksum at top of packet. Top + * two bits of first byte. + */ + public static final int FASTPATH_OUTPUT_SECURE_CHECKSUM = 1; + + /** + * Indicates that packet contains 8 byte secure checksum at top of packet and + * packet content is encrypted. Top two bits of first byte. + */ + public static final int FASTPATH_OUTPUT_ENCRYPTED = 2; + + public static final int FASTPATH_UPDATETYPE_ORDERS = 0; + public static final int FASTPATH_UPDATETYPE_BITMAP = 1; + public static final int FASTPATH_UPDATETYPE_PALETTE = 2; + public static final int FASTPATH_UPDATETYPE_SYNCHRONIZE = 3; + public static final int FASTPATH_UPDATETYPE_SURFCMDS = 4; + public static final int FASTPATH_UPDATETYPE_PTR_NULL = 5; + public static final int FASTPATH_UPDATETYPE_PTR_DEFAULT = 6; + public static final int FASTPATH_UPDATETYPE_PTR_POSITION = 8; + public static final int FASTPATH_UPDATETYPE_COLOR = 9; + public static final int FASTPATH_UPDATETYPE_CACHED = 0xa; + public static final int FASTPATH_UPDATETYPE_POINTER = 0xb; + + public static final int FASTPATH_FRAGMENT_SINGLE = 0; + public static final int FASTPATH_FRAGMENT_LAST = 1; + public static final int FASTPATH_FRAGMENT_FIRST = 2; + public static final int FASTPATH_FRAGMENT_NEXT = 3; + + public static final int FASTPATH_OUTPUT_COMPRESSION_USED = 2; + + public ServerFastPath(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // * DEBUG */System.out.println(buf.toHexString(buf.length)); + + // We need at 4 bytes to read packet type (TPKT or FastPath) and packet + // length + if (!cap(buf, 4, UNLIMITED, link, false)) + return; + + int typeAndFlags = buf.readUnsignedByte(); + + switch (typeAndFlags) { + case PROTOCOL_TPKT: // 0x03 + handleTpkt(buf, link); + break; + + case PROTOCOL_CREDSSP: // 0x30, potential clash with FastPath + handleCredSSP(buf, link); + break; + + default: // (value & 0x03) == 0x00 + case PROTOCOL_FASTPATH: + handleFastPath(buf, link, typeAndFlags); + break; + } + + } + + private void handleTpkt(ByteBuffer buf, Link link) { + // Reserved + buf.skipBytes(1); + + // Read TPKT length + int length = buf.readUnsignedShort(); + + if (!cap(buf, length, length, link, false)) + // Wait for full packet to arrive + return; + + int payloadLength = length - buf.cursor; + + // Extract payload + ByteBuffer outBuf = buf.slice(buf.cursor, payloadLength, true); + buf.unref(); + + if (verbose) { + outBuf.putMetadata("source", this); + } + + pushDataToPad(TPKT_PAD, outBuf); + } + + private void handleCredSSP(ByteBuffer buf, Link link) { + + if (verbose) + System.out.println("[" + this + "] INFO: CredSSP data received: " + buf + "."); + + // Store header position: will parse whole header later in BER format parser + int headerPosition = buf.cursor - 1; + + long payloadLength = buf.readBerLength(); + if (payloadLength > 10 * 1024) + throw new RuntimeException("[" + this + "] ERROR: CredSSP packets seems to be too long: " + payloadLength + "bytes. Data: " + buf + "."); + + // Length is the size of payload, so we need to append size of header + int headerLength = buf.cursor - headerPosition; + int packetLength = (int)payloadLength + headerLength; + if (!cap(buf, packetLength, packetLength, link, false)) + // Wait for full packet to arrive + return; + + // Extract payload (with header) + ByteBuffer outBuf = buf.slice(headerPosition, packetLength, true); + buf.unref(); + + if (verbose) { + outBuf.putMetadata("source", this); + } + + pushDataToPad(CREDSSP_PAD, outBuf); + } + + private void handleFastPath(ByteBuffer buf, Link link, int typeAndFlags) { + // Number of bytes in updateData field (including header (1+1 or 2 + // bytes)) + int length = buf.readVariableUnsignedShort(); + + if (!cap(buf, length, length, link, false)) + // Wait for full packet to arrive + return; + + int type = typeAndFlags & 0x3; + int securityFlags = (typeAndFlags >> 6) & 0x3; + + // Assertions + { + if (type != PROTOCOL_FASTPATH) + throw new RuntimeException("Unknown protocol. Expected protocol: 0 (FastPath). Actual protocol: " + type + ", data: " + buf + "."); + + switch (securityFlags) { + case FASTPATH_OUTPUT_SECURE_CHECKSUM: + // TODO + throw new RuntimeException("Secure checksum is not supported in FastPath packets."); + case FASTPATH_OUTPUT_ENCRYPTED: + // TODO + throw new RuntimeException("Encryption is not supported in FastPath packets."); + } + } + + // TODO: optional FIPS information, when FIPS is selected + // TODO: optional data signature (checksum), when checksum or FIPS is + // selected + + // Array of FastPath update fields + while (buf.cursor < buf.length) { + + int updateHeader = buf.readUnsignedByte(); + + int size = buf.readUnsignedShortLE(); + + int updateCode = updateHeader & 0xf; + int fragmentation = (updateHeader >> 4) & 0x3; + int compression = (updateHeader >> 6) & 0x3; + + if (verbose) + System.out.println("[" + this + "] INFO: FastPath update received. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: " + + compression + ", size: " + size + "."); + + ByteBuffer data = buf.readBytes(size); + buf.putMetadata("fragmentation", fragmentation); + buf.putMetadata("compression", compression); + + switch (updateCode) { + + case FASTPATH_UPDATETYPE_ORDERS: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_ORDERS."); + pushDataToPad(ORDERS_PAD, data); + break; + + case FASTPATH_UPDATETYPE_BITMAP: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_BITMAP."); + pushDataToPad(BITMAP_PAD, data); + break; + + case FASTPATH_UPDATETYPE_PALETTE: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PALETTE."); + pushDataToPad(PALETTE_PAD, data); + break; + + case FASTPATH_UPDATETYPE_SYNCHRONIZE: + // @see http://msdn.microsoft.com/en-us/library/cc240625.aspx + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SYNCHRONIZE."); + + data.unref(); + + if (size != 0) + throw new RuntimeException("Size of FastPath synchronize packet must be 0. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + + ", compression: " + compression + ", size: " + size + ", data: " + data + "."); + break; + + case FASTPATH_UPDATETYPE_SURFCMDS: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_SURFCMDS."); + + break; + + case FASTPATH_UPDATETYPE_PTR_NULL: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_NULL."); + + break; + + case FASTPATH_UPDATETYPE_PTR_DEFAULT: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_DEFAULT."); + + break; + + case FASTPATH_UPDATETYPE_PTR_POSITION: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_PTR_POSITION."); + + break; + + case FASTPATH_UPDATETYPE_COLOR: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_COLOR."); + + break; + + case FASTPATH_UPDATETYPE_CACHED: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_CACHED."); + + break; + + case FASTPATH_UPDATETYPE_POINTER: + if (verbose) + System.out.println("[" + this + "] INFO: FASTPATH_UPDATETYPE_POINTER."); + + break; + + default: + throw new RuntimeException("Unknown FastPath update. UpdateCode: " + updateCode + ", fragmentation: " + fragmentation + ", compression: " + compression + + ", size: " + size + ", data: " + data + "."); + + } + buf.unref(); + + } + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerIOChannelRouter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerIOChannelRouter.java new file mode 100755 index 00000000000..dad3548d593 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerIOChannelRouter.java @@ -0,0 +1,534 @@ +// 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 rdpclient.rdp; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ServerIOChannelRouter extends BaseElement { + + /** + * Demand Active PDU. + */ + public static final int PDUTYPE_DEMANDACTIVEPDU = 0x1; + + /** + * Confirm Active PDU. + */ + public static final int PDUTYPE_CONFIRMACTIVEPDU = 0x3; + + /** + * Deactivate All PDU. + */ + public static final int PDUTYPE_DEACTIVATEALLPDU = 0x6; + + /** + * Data PDU (actual type is revealed by the pduType2 field in the Share Data + * Header). + */ + public static final int PDUTYPE_DATAPDU = 0x7; + + /** + * Enhanced Security Server Redirection PDU. + */ + public static final int PDUTYPE_SERVER_REDIR_PKT = 0xA; + + protected RdpState state; + + public ServerIOChannelRouter(String id, RdpState state) { + super(id); + this.state = state; + } + + /** + * @see http://msdn.microsoft.com/en-us/library/cc240576.aspx + */ + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + int length = buf.readUnsignedShortLE(); + if (buf.length != length) + { + // It is ServerErrorAlert-ValidClient + // Ignore it + //throw new RuntimeException("[" + this + "] ERROR: Incorrect PDU length: " + length + ", data: " + buf + "."); + } + + int type = buf.readUnsignedShortLE() & 0xf; + + // int sourceId = buf.readUnsignedShortLE(); + buf.skipBytes(2); + + switch (type) { + case PDUTYPE_DEMANDACTIVEPDU: + pushDataToPad("demand_active", buf); + break; + case PDUTYPE_CONFIRMACTIVEPDU: + throw new RuntimeException("Unexpected client CONFIRM ACTIVE PDU. Data: " + buf + "."); + case PDUTYPE_DEACTIVATEALLPDU: + // pushDataToPad("deactivate_all", buf); + /* ignore */buf.unref(); + break; + case PDUTYPE_DATAPDU: + handleDataPdu(buf); + break; + case PDUTYPE_SERVER_REDIR_PKT: + // pushDataToPad("server_redir", buf); + /* ignore */buf.unref(); + break; + default: + throw new RuntimeException("[" + this + "] ERROR: Unknown PDU type: " + type + ", data: " + buf + "."); + } + + } + + /** + * Graphics Update PDU. + */ + public static final int PDUTYPE2_UPDATE = 0x02; + + /** + * Control PDU. + */ + public static final int PDUTYPE2_CONTROL = 0x14; + + /** + * Pointer Update PDU. + */ + public static final int PDUTYPE2_POINTER = 0x1B; + + /** + * Input Event PDU. + */ + public static final int PDUTYPE2_INPUT = 0x1C; + + /** + * Synchronize PDU. + */ + public static final int PDUTYPE2_SYNCHRONIZE = 0x1F; + + /** + * Refresh Rect PDU. + */ + public static final int PDUTYPE2_REFRESH_RECT = 0x21; + + /** + * Play Sound PDU. + */ + public static final int PDUTYPE2_PLAY_SOUND = 0x22; + + /** + * Suppress Output PDU. + */ + public static final int PDUTYPE2_SUPPRESS_OUTPUT = 0x23; + + /** + * Shutdown Request PDU. + */ + public static final int PDUTYPE2_SHUTDOWN_REQUEST = 0x24; + + /** + * Shutdown Request Denied PDU. + */ + public static final int PDUTYPE2_SHUTDOWN_DENIED = 0x25; + + /** + * Save Session Info PDU. + */ + public static final int PDUTYPE2_SAVE_SESSION_INFO = 0x26; + + /** + * Font List PDU. + */ + public static final int PDUTYPE2_FONTLIST = 0x27; + + /** + * Font Map PDU. + */ + public static final int PDUTYPE2_FONTMAP = 0x28; + + /** + * Set Keyboard Indicators PDU. + */ + public static final int PDUTYPE2_SET_KEYBOARD_INDICATORS = 0x29; + + /** + * Persistent Key List PDU. + */ + public static final int PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST = 0x2B; + + /** + * Bitmap Cache Error PDU. + */ + public static final int PDUTYPE2_BITMAPCACHE_ERROR_PDU = 0x2C; + + /** + * Set Keyboard IME Status PDU. + */ + public static final int PDUTYPE2_SET_KEYBOARD_IME_STATUS = 0x2D; + + /** + * Offscreen Bitmap Cache Error PDU. + */ + public static final int PDUTYPE2_OFFSCRCACHE_ERROR_PDU = 0x2E; + + /** + * Set Error Info PDU. + */ + public static final int PDUTYPE2_SET_ERROR_INFO_PDU = 0x2F; + + /** + * DrawNineGrid Cache Error PDU. + */ + public static final int PDUTYPE2_DRAWNINEGRID_ERROR_PDU = 0x30; + + /** + * GDI+ Error PDU. + */ + public static final int PDUTYPE2_DRAWGDIPLUS_ERROR_PDU = 0x31; + + /** + * Auto-Reconnect Status PDU. + */ + public static final int PDUTYPE2_ARC_STATUS_PDU = 0x32; + + /** + * Status Info PDU. + */ + public static final int PDUTYPE2_STATUS_INFO_PDU = 0x36; + + /** + * Monitor Layout PDU. + */ + public static final int PDUTYPE2_MONITOR_LAYOUT_PDU = 0x37; + + /** + * Indicates an Orders Update. + */ + public static final int UPDATETYPE_ORDERS = 0x0000; + + /** + * Indicates a Bitmap Graphics Update. + */ + public static final int UPDATETYPE_BITMAP = 0x0001; + + /** + * Indicates a Palette Update. + */ + public static final int UPDATETYPE_PALETTE = 0x0002; + + /** + * Indicates a Synchronize Update. + */ + public static final int UPDATETYPE_SYNCHRONIZE = 0x0003; + + /** + * @see http://msdn.microsoft.com/en-us/library/cc240577.aspx + */ + protected void handleDataPdu(ByteBuffer buf) { + + // (4 bytes): A 32-bit, unsigned integer. Share identifier for the packet. + long shareId = buf.readUnsignedIntLE(); + if (shareId != state.serverShareId) + throw new RuntimeException("Unexpected share ID: " + shareId + "."); +// buf.skipBytes(4); + + // Padding. + buf.skipBytes(1); + + // (1 byte): An 8-bit, unsigned integer. The stream identifier for the + // packet. + // int streamId = buf.readUnsignedByte(); + buf.skipBytes(1); + + // (2 bytes): A 16-bit, unsigned integer. The uncompressed length of the + // packet in bytes. + int uncompressedLength = buf.readUnsignedShortLE(); + + // (1 byte): An 8-bit, unsigned integer. The type of Data PDU. + int type2 = buf.readUnsignedByte(); + + // (1 byte): An 8-bit, unsigned integer. The compression type and flags + // specifying the data following the Share Data Header + int compressedType = buf.readUnsignedByte(); + if (compressedType != 0) + throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + "."); + + // (2 bytes): A 16-bit, unsigned integer. The compressed length of the + // packet in bytes. + int compressedLength = buf.readUnsignedShortLE(); + if (compressedLength != 0) + throw new RuntimeException("Compression of protocol packets is not supported. Data: " + buf + "."); + + ByteBuffer data = buf.readBytes(uncompressedLength - 18); + buf.unref(); + + switch (type2) { + + case PDUTYPE2_UPDATE: { + + // (2 bytes): A 16-bit, unsigned integer. Type of the graphics update. + int updateType = data.readUnsignedShortLE(); + ByteBuffer payload = data.readBytes(data.length - data.cursor); + data.unref(); + + switch (updateType) { + case UPDATETYPE_ORDERS: + pushDataToPad("orders", payload); + break; + case UPDATETYPE_BITMAP: + pushDataToPad("bitmap", payload); + break; + case UPDATETYPE_PALETTE: + pushDataToPad("palette", payload); + break; + case UPDATETYPE_SYNCHRONIZE: + // Ignore + payload.unref(); + break; + } + + break; + } + case PDUTYPE2_CONTROL: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_CONTROL ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_POINTER: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_POINTER ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_INPUT: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_INPUT ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SYNCHRONIZE: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SYNCHRONIZE ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_REFRESH_RECT: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_REFRESH_RECT ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_PLAY_SOUND: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_PLAY_SOUND ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SUPPRESS_OUTPUT: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SUPPRESS_OUTPUT ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SHUTDOWN_REQUEST: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_REQUEST ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SHUTDOWN_DENIED: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SHUTDOWN_DENIED ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SAVE_SESSION_INFO: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SAVE_SESSION_INFO ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_FONTLIST: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTLIST ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_FONTMAP: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_FONTMAP ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SET_KEYBOARD_INDICATORS: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_INDICATORS ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_PERSISTENT_LIST ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_BITMAPCACHE_ERROR_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_BITMAPCACHE_ERROR_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SET_KEYBOARD_IME_STATUS: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_KEYBOARD_IME_STATUS ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_OFFSCRCACHE_ERROR_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_OFFSCRCACHE_ERROR_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_SET_ERROR_INFO_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_SET_ERROR_INFO_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_DRAWNINEGRID_ERROR_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWNINEGRID_ERROR_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_DRAWGDIPLUS_ERROR_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_DRAWGDIPLUS_ERROR_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_ARC_STATUS_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_ARC_STATUS_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_STATUS_INFO_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_STATUS_INFO_PDU ignored."); + // Ignore + data.unref(); + break; + case PDUTYPE2_MONITOR_LAYOUT_PDU: + if (verbose) + System.out.println("[" + this + "] INFO: Packet PDUTYPE2_MONITOR_LAYOUT_PDU ignored."); + // Ignore + data.unref(); + break; + + default: + throw new RuntimeException("Unknow data PDU type: " + type2 + ", data: " + buf + "."); + } + } + + /** + * Example. + * + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + byte[] packet = new byte[] { + // TPKT + (byte)0x03, (byte)0x00, // TPKT Header: TPKT version = 3 + (byte)0x00, (byte)0x1B, // TPKT length: 27 bytes + + // X224 + (byte)0x02, // X224 Length: 2 bytes + (byte)0xF0, // X224 Type: Data + (byte)0x80, // X224 EOT + + // MCS + // Type: send data indication: 26 (0x1a, top 6 bits) + (byte)0x68, // ?? + + (byte)0x00, (byte)0x01, // User ID: 1002 (1001+1) + (byte)0x03, (byte)0xEB, // Channel ID: 1003 + (byte)0x70, // Data priority: high, segmentation: begin|end + (byte)0x0D, // Payload length: 13 bytes + + // Deactivate all PDU + (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) + + // - PDUType: (0x16, LE) + // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU + // ProtocolVersion: (000000000001....) 1 + (byte)0x16, (byte)0x00, + + (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) + (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 + + (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) + (byte)0x00, // Source descriptor (should be set to 0): 0 + }; + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + RdpState rdpState = new RdpState() { + { + serverShareId = 66538; + } + }; + Element channel1003 = new ServerIOChannelRouter("channel_1003", rdpState); + Element mcs = new ServerMCSPDU("mcs"); + Element tpkt = new ServerTpkt("tpkt"); + Element x224 = new ServerX224DataPdu("x224"); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { + // Deactivate all PDU + (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) + + // - PDUType: 22 (0x16, LE) + // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU + // ProtocolVersion: (000000000001....) 1 + (byte)0x16, (byte)0x00, + + (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) + (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 + + (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) + (byte)0x00, // Source descriptor (should be set to 0): 0 + })); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, tpkt, x224, mcs, channel1003, sink); + pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "channel_1003 >deactivate_all", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerLicenseErrorPDUValidClient.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerLicenseErrorPDUValidClient.java index 7384c95a212..22e809442dc --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerLicenseErrorPDUValidClient.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSAttachUserConfirmPDU.java old mode 100644 new mode 100755 similarity index 75% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSAttachUserConfirmPDU.java index b4462534c5a..cbefcb93b69 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSAttachUserConfirmPDU.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * Server response to MCS Attach User request. @@ -56,8 +56,7 @@ public class ServerMCSAttachUserConfirmPDU extends OneTimeSwitch { int flags = typeAndFlags & 0x3; if (type != MCS_ATTACH_USER_CONFIRM_PDU) - throw new RuntimeException("[" + this + "] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 11, actual value: " + type + ", data: " + buf + - "."); + throw new RuntimeException("[" + this + "] ERROR: Incorrect type of MCS AttachUserConfirm PDU. Expected value: 11, actual value: " + type + ", data: " + buf + "."); if (flags != INITIATOR_PRESENT) throw new RuntimeException("Initator field is not present in MCS AttachUserConfirm PDU. Data: " + buf + "."); @@ -92,11 +91,11 @@ public class ServerMCSAttachUserConfirmPDU extends OneTimeSwitch { // System.setProperty("streamer.Pipeline.debug", "true"); byte[] packet = new byte[] {(byte)0x2E, // MCS user confirm (001011.., - // 0xb), InitiatorPresent: 1 - // (......01, 0x1) - (byte)0x00, // RT successfull (0000...., 0x0) - // Initiator: 1001+3 = 1004 - (byte)0x00, (byte)0x03,}; + // 0xb), InitiatorPresent: 1 + // (......01, 0x1) + (byte)0x00, // RT successfull (0000...., 0x0) + // Initiator: 1001+3 = 1004 + (byte)0x00, (byte)0x03,}; RdpState rdpState = new RdpState(); MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet, new byte[] {1, 2, 3})); @@ -115,20 +114,3 @@ public class ServerMCSAttachUserConfirmPDU extends OneTimeSwitch { } } - -/* - * 03 00 00 0B 02 F0 80 2E 00 00 03. - * - * Frame: Number = 18, Captured Frame Length = 68, MediaType = - * DecryptedPayloadHeader + DecryptedPayloadHeader: FrameCount = 1, ErrorStatus - * = SUCCESS TLSSSLData: Transport Layer Security (TLS) Payload Data + TLS: TLS - * Rec Layer-1 SSL Application Data ISOTS: TPKTCount = 1 - TPKT: version: 3, - * Length: 11 version: 3 (0x3) Reserved: 0 (0x0) PacketLength: 11 (0xB) - X224: - * Data Length: 2 (0x2) Type: Data EOT: 128 (0x80) - T125: Attach User Confirm, - * Result = rt-successful, Indicator = 0x3ec - MCSHeader: Type=Attach User - * Confirm - Type: Attach User Confirm - RootIndex: 11 Value: (001011..) 0xb - - * MCSAttachUserConfirm: Result = rt-successful, Indicator = 0x3ec - * InitiatorPresent: 1 (0x1) - Result: rt-successful - Result: rt-successful - - * RootIndex: 0 Value: (0000....) 0x0 - Initiator: 0x3ec - UserID: 0x3ec - - * ChannelId: 1004 - Align: No Padding Padding5: (00000...) 0x0 Value: 3 (0x3) - */ diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSChannelJoinConfirmPDU.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSChannelJoinConfirmPDU.java index e753cd9a99f..d3a5954b4e8 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSChannelJoinConfirmPDU.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSConnectResponse.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSConnectResponse.java index 030edbf683e..a62ae8b9518 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSConnectResponse.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; @@ -193,7 +193,7 @@ public class ServerMCSConnectResponse extends OneTimeSwitch { TagValue: (...00100) 4 - AsnLen: Length = 54, LengthOfLength = 0 Length: 54 bytes, LengthOfLength = 0 - - AsnBerObjectIdentifier: Generic Conference Contro (0.0.20.124.0.1) + - AsnBerObjectIdentifier: Generic Conference Control (0.0.20.124.0.1) - AsnObjectIdentifierHeader: - AsnId: Reserved for use by the encoding rules (Universal 0) - LowTag: diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSPDU.java new file mode 100755 index 00000000000..d28d0a09f70 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerMCSPDU.java @@ -0,0 +1,149 @@ +// 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 rdpclient.rdp; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; + +public class ServerMCSPDU extends BaseElement { + + public ServerMCSPDU(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + byte headerByte = buf.readSignedByte(); + int type = headerByte >> 2; + + switch (type) { + // Expected type: send data indication: 26 (0x1a, top 6 bits, or 0x68) + case 0x1a: { + // int userId = buf.readUnsignedShort() + 1001; // User ID: 1002 (1001+1) + buf.skipBytes(2); // Ignore user ID + + int channelId = buf.readUnsignedShort(); // Channel ID: 1003 + + int flags = buf.readSignedByte(); + if ((flags & 0x30) != 0x30) + throw new RuntimeException("Fragmented MCS packets are not supported."); + + int payloadLength = buf.readVariableUnsignedShort(); + + ByteBuffer data = buf.readBytes(payloadLength); + + buf.unref(); + + pushDataToPad("channel_" + channelId, data); + break; + } + + case 0x8: { + // Disconnection sequence. + buf.unref(); + break; + } + + default: + throw new RuntimeException("Unsupported MCS packet type: " + type + "(" + headerByte + "), data: " + buf + "."); + } + + } + + /** + * Example. + * + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + // System.setProperty("streamer.Element.debug", "true"); + // System.setProperty("streamer.Pipeline.debug", "true"); + + byte[] packet = new byte[] { + // TPKT + (byte)0x03, (byte)0x00, // TPKT Header: TPKT version = 3 + (byte)0x00, (byte)0x1B, // TPKT length: 27 bytes + + // X224 + (byte)0x02, // X224 Length: 2 bytes + (byte)0xF0, // X224 Type: Data + (byte)0x80, // X224 EOT + + // MCS + // Type: send data indication: 26 (0x1a, top 6 bits) + (byte)0x68, // ?? + + (byte)0x00, (byte)0x01, // User ID: 1002 (1001+1) + (byte)0x03, (byte)0xEB, // Channel ID: 1003 + (byte)0x70, // Data priority: high, segmentation: begin|end + (byte)0x0D, // Payload length: 13 bytes + + // Deactivate all PDU + (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) + + // - PDUType: 22 (0x16, LE) + // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU + // ProtocolVersion: (000000000001....) 1 + (byte)0x16, (byte)0x00, + + (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) + (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 + + (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) + (byte)0x00, // Source descriptor (should be set to 0): 0 + }; + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element mcs = new ServerMCSPDU("mcs") { + { + verbose = true; + } + }; + Element tpkt = new ServerTpkt("tpkt"); + Element x224 = new ServerX224DataPdu("x224"); + Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { + // Deactivate all PDU + (byte)0x0D, (byte)0x00, // Length: 13 bytes (LE) + + // - PDUType: 22 (0x16, LE) + // Type: (............0110) TS_PDUTYPE_DEACTIVATEALLPDU + // ProtocolVersion: (000000000001....) 1 + (byte)0x16, (byte)0x00, + + (byte)0xEA, (byte)0x03, // PDU source: 1002 (LE) + (byte)0xEA, (byte)0x03, (byte)0x01, (byte)0x00, // ShareID = 66538 + + (byte)0x01, (byte)0x00, // Length if source descriptor: 1 (LE) + (byte)0x00, // Source descriptor (should be set to 0): 0 + })); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, tpkt, x224, mcs, sink); + pipeline.link("source", "tpkt", "x224", "mcs >channel_1003", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerPaletteUpdate.java old mode 100644 new mode 100755 similarity index 96% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerPaletteUpdate.java index 8fb2fe0498b..606bd89fa84 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerPaletteUpdate.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import java.awt.image.IndexColorModel; @@ -57,8 +57,8 @@ public class ServerPaletteUpdate extends BaseElement { // in an 8 bpp palette). int numberColors = (int)buf.readUnsignedIntLE(); if (numberColors != 256) - throw new RuntimeException("Unexpected value for number of color field in server Palette Update packet. Expected value: 256 colors, actual value: " + - numberColors + ", data: " + buf + "."); + throw new RuntimeException("Unexpected value for number of color field in server Palette Update packet. Expected value: 256 colors, actual value: " + + numberColors + ", data: " + buf + "."); // (variable): An array of palette entries in RGB triplet format packed on // byte boundaries. The number of triplet entries is given by the diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerSynchronizePDU.java old mode 100644 new mode 100755 similarity index 99% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerSynchronizePDU.java index ad8704deb92..f464a4a887d --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerSynchronizePDU.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Link; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerTpkt.java old mode 100644 new mode 100755 similarity index 98% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerTpkt.java index 240c6243459..d9662d216f9 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerTpkt.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224ConnectionConfirmPDU.java old mode 100644 new mode 100755 similarity index 72% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224ConnectionConfirmPDU.java index 7dd70eb4575..5a8dfaf4eeb --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224ConnectionConfirmPDU.java @@ -14,16 +14,16 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.ByteBuffer; import streamer.Element; import streamer.Link; -import streamer.MockSink; -import streamer.MockSource; import streamer.OneTimeSwitch; import streamer.Pipeline; import streamer.PipelineImpl; +import streamer.debug.MockSink; +import streamer.debug.MockSource; /** * Once the External Security Protocol handshake has run to completion, the @@ -100,8 +100,8 @@ public class ServerX224ConnectionConfirmPDU extends OneTimeSwitch { int x224Type = buf.readUnsignedByte(); if (x224Type != X224_TPDU_CONNECTION_CONFIRM) - throw new RuntimeException("Unexpected type of packet. Expected type: " + X224_TPDU_CONNECTION_CONFIRM + " (CONNECTION CONFIRM), actual type: " + x224Type + - ", length: " + x224Length + ", buf: " + buf + "."); + throw new RuntimeException("Unexpected type of packet. Expected type: " + X224_TPDU_CONNECTION_CONFIRM + " (CONNECTION CONFIRM), actual type: " + + x224Type + ", length: " + x224Length + ", buf: " + buf + "."); // Ignore destination reference, because client side has only one node buf.skipBytes(2); @@ -134,39 +134,36 @@ public class ServerX224ConnectionConfirmPDU extends OneTimeSwitch { int errorCode = protocol; String message = "Unknown error."; switch (errorCode) { - case SSL_REQUIRED_BY_SERVER: - message = - "The server requires that the client support Enhanced RDP Security with either TLS 1.0, 1.1 or 1.2 or CredSSP. If only CredSSP was requested then the server only supports TLS."; - break; + case SSL_REQUIRED_BY_SERVER: + message = "The server requires that the client support Enhanced RDP Security with either TLS 1.0, 1.1 or 1.2 or CredSSP. If only CredSSP was requested then the server only supports TLS."; + break; - case SSL_NOT_ALLOWED_BY_SERVER: - message = "The server is configured to only use Standard RDP Security mechanisms and does not support any External Security Protocols."; - break; + case SSL_NOT_ALLOWED_BY_SERVER: + message = "The server is configured to only use Standard RDP Security mechanisms and does not support any External Security Protocols."; + break; - case SSL_CERT_NOT_ON_SERVER: - message = "The server does not possess a valid authentication certificate and cannot initialize the External Security Protocol Provider."; - break; + case SSL_CERT_NOT_ON_SERVER: + message = "The server does not possess a valid authentication certificate and cannot initialize the External Security Protocol Provider."; + break; - case INCONSISTENT_FLAGS: - message = - "The list of requested security protocols is not consistent with the current security protocol in effect. This error is only possible when the Direct Approach is used and an External Security Protocolis already being used."; - break; + case INCONSISTENT_FLAGS: + message = "The list of requested security protocols is not consistent with the current security protocol in effect. This error is only possible when the Direct Approach is used and an External Security Protocolis already being used."; + break; - case HYBRID_REQUIRED_BY_SERVER: - message = "The server requires that the client support Enhanced RDP Security with CredSSP."; - break; + case HYBRID_REQUIRED_BY_SERVER: + message = "The server requires that the client support Enhanced RDP Security with CredSSP."; + break; - case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER: - message = "The server requires that the client support Enhanced RDP Security with TLS 1.0, 1.1 or 1.2 and certificate-based client authentication."; - break; + case SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER: + message = "The server requires that the client support Enhanced RDP Security with TLS 1.0, 1.1 or 1.2 and certificate-based client authentication."; + break; } throw new RuntimeException("Connection failure: " + message); } - if (protocol != RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL) - throw new RuntimeException("Unexpected protocol type. Expected protocol type: " + RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL + " (SSL), actual response type: " + - protocol + ", RDP NEG buf: " + buf + "."); + if (protocol != RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL && protocol != RdpConstants.RDP_NEG_REQ_PROTOCOL_HYBRID) + throw new RuntimeException("Unexpected protocol type (nor SSL, nor HYBRID (SSL+CredSSP)): " + protocol + ", RDP NEG buf: " + buf + "."); if (verbose) System.out.println("[" + this + "] INFO: RDP Negotiation response. Type: " + negType + ", protocol: " + protocol + "."); @@ -206,19 +203,19 @@ public class ServerX224ConnectionConfirmPDU extends OneTimeSwitch { // 03 00 00 13 0e d0 00 00 12 34 00 03 00 08 00 05 00 00 00 byte[] packet = new byte[] { - 0x03, // -> TPKT Header: TPKT version = 3 - 0x00, // TPKT Header: Reserved = 0 - 0x00, 0x13, // TPKT Header: Packet length - (total = 19 bytes) - 0x0e, // X.224: Length indicator (14 bytes) - (byte)0xd0, // X.224: Type (high nibble) = 0xd = CC TPDU; credit - // (low nibble) = 0 - 0x00, 0x00, // X.224: Destination reference = 0 - 0x12, 0x34, // X.224: Source reference = 0x1234 (bogus value) - 0x00, // X.224: Class and options = 0 - (byte)0x03, // Failure - (byte)0x00, // RDP_NEG_RSP::flags (0) - (byte)0x08, (byte)0x00, // RDP_NEG_RSP::length (8 bytes) - (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, // Code: HYBRID_REQUIRED_BY_SERVER + 0x03, // -> TPKT Header: TPKT version = 3 + 0x00, // TPKT Header: Reserved = 0 + 0x00, 0x13, // TPKT Header: Packet length - (total = 19 bytes) + 0x0e, // X.224: Length indicator (14 bytes) + (byte)0xd0, // X.224: Type (high nibble) = 0xd = CC TPDU; credit + // (low nibble) = 0 + 0x00, 0x00, // X.224: Destination reference = 0 + 0x12, 0x34, // X.224: Source reference = 0x1234 (bogus value) + 0x00, // X.224: Class and options = 0 + (byte)0x03, // Failure + (byte)0x00, // RDP_NEG_RSP::flags (0) + (byte)0x08, (byte)0x00, // RDP_NEG_RSP::length (8 bytes) + (byte)0x05, (byte)0x00, (byte)0x00, (byte)0x00, // Code: HYBRID_REQUIRED_BY_SERVER }; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224DataPdu.java old mode 100644 new mode 100755 similarity index 88% rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java rename to services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224DataPdu.java index 62b0f557adf..2c0087e0fe1 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/rdp/ServerX224DataPdu.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package rdpclient; +package rdpclient.rdp; import streamer.BaseElement; import streamer.ByteBuffer; @@ -46,14 +46,14 @@ public class ServerX224DataPdu extends BaseElement { int type = buf.readUnsignedByte(); // High nibble: type, low nibble: if ((type & 0xf0) != X224_TPDU_DATA) - throw new RuntimeException("[" + this + "] ERROR: Unexepcted X224 packet type. Expected packet type: " + X224_TPDU_DATA + - " (X224_TPDU_DATA), actual packet type: " + type + ", buf: " + buf + "."); + throw new RuntimeException("[" + this + "] ERROR: Unexepcted X224 packet type. Expected packet type: " + X224_TPDU_DATA + + " (X224_TPDU_DATA), actual packet type: " + type + ", buf: " + buf + "."); int options = buf.readUnsignedByte(); if ((options & X224_TPDU_LAST_DATA_UNIT) != X224_TPDU_LAST_DATA_UNIT) - throw new RuntimeException("Unexepcted X224 packet options. Expected options: " + X224_TPDU_LAST_DATA_UNIT + - " (X224_TPDU_LAST_DATA_UNIT), actual packet options: " + options + ", buf: " + buf + "."); + throw new RuntimeException("Unexepcted X224 packet options. Expected options: " + X224_TPDU_LAST_DATA_UNIT + + " (X224_TPDU_LAST_DATA_UNIT), actual packet options: " + options + ", buf: " + buf + "."); ByteBuffer payload = buf.readBytes(buf.length - buf.cursor); diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java old mode 100644 new mode 100755 index dd2b541b202..1ca68ceaa80 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java @@ -21,6 +21,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import streamer.debug.FakeSink; +import streamer.debug.FakeSource; + public class BaseElement implements Element { protected String id; @@ -78,11 +81,11 @@ public class BaseElement implements Element { @Override public Set getPads(Direction direction) { switch (direction) { - case IN: - return inputPads.keySet(); + case IN: + return inputPads.keySet(); - case OUT: - return outputPads.keySet(); + case OUT: + return outputPads.keySet(); } return null; } @@ -103,24 +106,24 @@ public class BaseElement implements Element { @Override public void setLink(String padName, Link link, Direction direction) { switch (direction) { - case IN: - if (inputPads.get(padName) != null) - throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: " + - link + ", existing link: " + inputPads.get(padName) + "."); - inputPads.put(padName, link); - link.setSink(this); + case IN: + if (inputPads.get(padName) != null) + throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: " + + link + ", existing link: " + inputPads.get(padName) + "."); + inputPads.put(padName, link); + link.setSink(this); - break; + break; - case OUT: - if (outputPads.get(padName) != null) - throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: " + - link + ", existing link: " + outputPads.get(padName) + "."); + case OUT: + if (outputPads.get(padName) != null) + throw new RuntimeException("Cannot link more than one wire to same pad. Element: " + this + ", pad: " + padName + ":" + direction + ", new link: " + + link + ", existing link: " + outputPads.get(padName) + "."); - outputPads.put(padName, link); - link.setSource(this); + outputPads.put(padName, link); + link.setSource(this); - break; + break; } } @@ -162,9 +165,6 @@ public class BaseElement implements Element { */ @Override public void handleData(ByteBuffer buf, Link link) { - if (buf == null) - return; - if (verbose) System.out.println("[" + this + "] INFO: Data received: " + buf + "."); @@ -177,7 +177,8 @@ public class BaseElement implements Element { protected final void pushDataToAllOuts(ByteBuffer buf) { if (buf == null) - return; + throw new NullPointerException(); + //return; if (outputPads.size() == 0) throw new RuntimeException("Number of outgoing connection is zero. Cannot send data to output. Data: " + buf + "."); @@ -218,18 +219,19 @@ public class BaseElement implements Element { * By default, do nothing with incoming event and retransmit event to all * pads. */ + @SuppressWarnings("incomplete-switch") @Override public void handleEvent(Event event, Direction direction) { if (verbose) System.out.println("[" + this + "] INFO: Event " + event + ":" + direction + " is received."); switch (event) { - case STREAM_CLOSE: - onClose(); - break; - case STREAM_START: - onStart(); - break; + case STREAM_CLOSE: + onClose(); + break; + case STREAM_START: + onStart(); + break; } sendEventToAllPads(event, direction); @@ -261,18 +263,18 @@ public class BaseElement implements Element { System.out.println("[" + this + "] INFO: Sending event " + event + ":" + direction + "."); switch (direction) { - case IN: - // Send event to all pads with IN direction - for (DataSource in : inputPads.values()) { - in.sendEvent(event, direction); - } - break; - case OUT: - // Send event to all pads with OUT direction - for (DataSink out : outputPads.values()) { - out.sendEvent(event, direction); - } - break; + case IN: + // Send event to all pads with IN direction + for (DataSource in : inputPads.values()) { + in.sendEvent(event, direction); + } + break; + case OUT: + // Send event to all pads with OUT direction + for (DataSink out : outputPads.values()) { + out.sendEvent(event, direction); + } + break; } } @@ -290,7 +292,7 @@ public class BaseElement implements Element { * source link, to push unnecessary data back * @param fromCursor * if true, then position will be included into calculation - * @return true, + * @return true, if buffer is long enough, false otherwise */ public boolean cap(ByteBuffer buf, int minLength, int maxLength, Link link, boolean fromCursor) { @@ -384,16 +386,16 @@ public class BaseElement implements Element { public static void main(String args[]) { Element source = new FakeSource("source") { { - this.verbose = true; - this.numBuffers = 10; - this.incommingBufLength = 3; - this.delay = 100; + verbose = true; + numBuffers = 10; + incommingBufLength = 3; + delay = 100; } }; Element sink = new FakeSink("sink") { { - this.verbose = true; + verbose = true; } }; @@ -411,7 +413,9 @@ public class BaseElement implements Element { // Links between t3-t4-sink will operate in push mode. // Link between t2-t3 will run main loop (pull from source and push to // sink). - pipeline.getLink("t3", STDOUT).run(); + Link link = pipeline.getLink("t3", STDOUT); + link.sendEvent(Event.STREAM_START, Direction.IN); + link.run(); } } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java old mode 100644 new mode 100755 index 8543a5aa7ab..3a718ba0bd5 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java @@ -19,7 +19,9 @@ package streamer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * This class represents a slice in a buffer. @@ -45,9 +47,9 @@ public class ByteBuffer { */ public ByteBuffer(int minLength) { // Get buffer of acceptable size from buffer pool - this.data = BufferPool.allocateNewBuffer(minLength); - this.offset = 0; - this.length = minLength; + data = BufferPool.allocateNewBuffer(minLength); + offset = 0; + length = minLength; } public ByteBuffer(byte data[]) { @@ -55,8 +57,8 @@ public class ByteBuffer { throw new NullPointerException("Data must be non-null."); this.data = data; - this.offset = 0; - this.length = data.length; + offset = 0; + length = data.length; } public ByteBuffer(byte[] data, int offset, int length) { @@ -74,9 +76,9 @@ public class ByteBuffer { */ public ByteBuffer(int minLength, boolean reserveSpaceForHeader) { // Get buffer of acceptable size from buffer pool - this.data = BufferPool.allocateNewBuffer(128 + minLength); - this.offset = 128; // 100 bytes should be enough for headers - this.length = minLength; + data = BufferPool.allocateNewBuffer(128 + minLength); + offset = 128; // 100 bytes should be enough for headers + length = minLength; } /** @@ -96,7 +98,7 @@ public class ByteBuffer { @Override public String toString() { - return toString(100); + return toString(length); } /** @@ -106,8 +108,8 @@ public class ByteBuffer { * number of bytes to show in string */ public String toString(int maxLength) { - return "ByteRange(){offset=" + offset + ", length=" + length + ", cursor=" + cursor + ", data=" + ((data == null) ? "null" : toHexString(maxLength)) + - ((metadata == null || metadata.size() == 0) ? "" : ", metadata=" + metadata) + "}"; + return "ByteRange(){offset=" + offset + ", length=" + length + ", cursor=" + cursor + ", data=" + ((data == null) ? "null" : toHexString(maxLength)) + + ((metadata == null || metadata.size() == 0) ? "" : ", metadata=" + metadata) + "}"; } /** @@ -143,13 +145,68 @@ public class ByteBuffer { if (i > 0) builder.append(" "); int b = data[offset + i] & 0xff; - builder.append(((b < 16) ? "0" : "") + Integer.toString(b, 16)); + builder.append(String.format("%02x", b)); } return builder.toString(); } - public void dump() { - System.out.println(toString(length)); + /** + * Return string representation of this byte buffer as dump, e.g. + * "0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 .................". + * + * @param maxLength + * number of bytes to show in string + */ + public String dump() { + StringBuilder builder = new StringBuilder(length * 4); + int i = 0; + for (; i < length && i < length; i++) { + if (i % 16 == 0) { + builder.append(String.format("%04x", i)); + } + + builder.append(' '); + int b = data[offset + i] & 0xff; + builder.append(String.format("%02x", b)); + + if (i % 16 == 15) { + builder.append(' '); + builder.append(toASCIIString(i - 15, i)); + builder.append('\n'); + } + } + int end = i - 1; + if (end % 16 != 15) { + int begin = end & ~0xf; + for (int j = 0; j < (15 - (end % 16)); j++) { + builder.append(" "); + } + builder.append(' '); + builder.append(toASCIIString(begin, end)); + builder.append('\n'); + } + return builder.toString(); + } + + private String toASCIIString(int start, int finish) { + StringBuffer sb = new StringBuffer(16); + for (int i = start; i <= finish; i++) { + char ch = (char)data[offset + i]; + if (ch < ' ' || ch >= 0x7f) { + sb.append('.'); + } else { + sb.append(ch); + } + } + return sb.toString(); + } + + /** + * Return string representation of this byte buffer as hexadecimal numbers, + * e.g. "01 02". + */ + public String toPlainHexString() { + return toPlainHexString(length); } public void extend(int newLength) { @@ -190,12 +247,12 @@ public class ByteBuffer { ref(); if (this.length < (offset + length)) - throw new RuntimeException("Length of region is larger that length of this buffer. Buffer length: " + this.length + ", offset: " + offset + - ", new region length: " + length + "."); + throw new RuntimeException("Length of region is larger that length of this buffer. Buffer length: " + this.length + ", offset: " + offset + + ", new region length: " + length + "."); ByteBuffer slice = new ByteBuffer(data, this.offset + offset, length); - if (copyMetadata && this.metadata != null) + if (copyMetadata && metadata != null) slice.metadata = new HashMap(metadata); return slice; @@ -251,8 +308,8 @@ public class ByteBuffer { public short[] toShortArray() { if (length % 2 != 0) - throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 2 without remainder. Array length: " + length + ", remainder: " + - (length % 2) + "."); + throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 2 without remainder. Array length: " + length + ", remainder: " + + (length % 2) + "."); short[] buf = new short[length / 2]; @@ -267,8 +324,8 @@ public class ByteBuffer { */ public int[] toIntLEArray() { if (length % 4 != 0) - throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 4 without remainder. Array length: " + length + ", remainder: " + - (length % 4) + "."); + throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 4 without remainder. Array length: " + length + ", remainder: " + + (length % 4) + "."); int[] buf = new int[length / 4]; @@ -279,12 +336,13 @@ public class ByteBuffer { } /** - * Return array of int's in little endian order, but use only 3 bytes per int (3RGB). + * Return array of int's in little endian order, but use only 3 bytes per int + * (3RGB). */ public int[] toInt3LEArray() { if (length % 3 != 0) - throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 3 without remainder. Array length: " + length + ", remainder: " + - (length % 3) + "."); + throw new ArrayIndexOutOfBoundsException("Length of byte array must be dividable by 3 without remainder. Array length: " + length + ", remainder: " + + (length % 3) + "."); int[] buf = new int[length / 3]; @@ -315,8 +373,8 @@ public class ByteBuffer { if (cursor + 4 > length) throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + "."); - int result = - (((data[offset + cursor] & 0xff) << 24) + ((data[offset + cursor + 1] & 0xff) << 16) + ((data[offset + cursor + 2] & 0xff) << 8) + (data[offset + cursor + 3] & 0xff)); + int result = (((data[offset + cursor] & 0xff) << 24) + ((data[offset + cursor + 1] & 0xff) << 16) + ((data[offset + cursor + 2] & 0xff) << 8) + (data[offset + + cursor + 3] & 0xff)); cursor += 4; return result; } @@ -328,8 +386,8 @@ public class ByteBuffer { if (cursor + 4 > length) throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + "."); - int result = - (((data[offset + cursor + 3] & 0xff) << 24) + ((data[offset + cursor + 2] & 0xff) << 16) + ((data[offset + cursor + 1] & 0xff) << 8) + (data[offset + cursor] & 0xff)); + int result = (((data[offset + cursor + 3] & 0xff) << 24) + ((data[offset + cursor + 2] & 0xff) << 16) + ((data[offset + cursor + 1] & 0xff) << 8) + (data[offset + + cursor] & 0xff)); cursor += 4; return result; } @@ -341,9 +399,21 @@ public class ByteBuffer { if (cursor + 4 > length) throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + "."); - long result = - (((long)(data[offset + cursor + 3] & 0xff) << 24) + ((long)(data[offset + cursor + 2] & 0xff) << 16) + ((long)(data[offset + cursor + 1] & 0xff) << 8) + (data[offset + - cursor] & 0xff)); + long result = (((long)(data[offset + cursor + 3] & 0xff) << 24) + ((long)(data[offset + cursor + 2] & 0xff) << 16) + + ((long)(data[offset + cursor + 1] & 0xff) << 8) + (data[offset + cursor + 0] & 0xff)); + cursor += 4; + return result; + } + + /** + * Read unsigned int in network order. Cursor is advanced by 4. + */ + public long readUnsignedInt() { + if (cursor + 4 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 4 bytes from this buffer: " + this + "."); + + long result = (((long)(data[offset + cursor + 0] & 0xff) << 24) + ((long)(data[offset + cursor + 1] & 0xff) << 16) + + ((long)(data[offset + cursor + 2] & 0xff) << 8) + (data[offset + cursor + 3] & 0xff)); cursor += 4; return result; } @@ -379,19 +449,19 @@ public class ByteBuffer { int firstByte = readUnsignedByte(); int result; switch (firstByte & 0xc0) { - default: - case 0x00: - result = firstByte & 0x3f; - break; - case 0x40: - result = (firstByte & 0x3f << 8) | readUnsignedByte(); - break; - case 0x80: - result = (((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte(); - break; - case 0xc0: - result = ((((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte() << 8) | readUnsignedByte(); - break; + default: + case 0x00: + result = firstByte & 0x3f; + break; + case 0x40: + result = (firstByte & 0x3f << 8) | readUnsignedByte(); + break; + case 0x80: + result = (((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte(); + break; + case 0xc0: + result = ((((firstByte & 0x3f << 8) | readUnsignedByte()) << 8) | readUnsignedByte() << 8) | readUnsignedByte(); + break; } return result; @@ -445,6 +515,18 @@ public class ByteBuffer { return result; } + /** + * Read signed short in network order. Cursor is advanced by 2. + */ + public short readSignedShort() { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + "."); + + short result = (short)(((data[offset + cursor + 0] & 0xff) << 8) | (data[offset + cursor + 1] & 0xff)); + cursor += 2; + return result; + } + /** * Read unsigned short in network order in variable length format. Cursor is * advanced by 1 or 2 bytes. @@ -456,9 +538,9 @@ public class ByteBuffer { int firstByte = readUnsignedByte(); int result; - if ((firstByte & 0x80) == 0) + if ((firstByte & 0x80) == 0) { result = firstByte & 0x7f; - else { + } else { int secondByte = readUnsignedByte(); result = (((firstByte & 0x7f) << 8) | secondByte); } @@ -466,6 +548,174 @@ public class ByteBuffer { return result; } + /** + * Read integer in BER format. + * + * Most significant bit of first byte indicates type of date in first byte: if + * 0, then byte contains length (up to 7f), if 1, then byte contains number of + * following bytes with value in network order. Value 0x80 means unlimited + * length, which ends with two zero bytes (0x00 0x00) sequence. + * + * If -1 is returned by this method, then caller must seek two consecutive + * zeroes in buffer and consume all that data from buffer, including these two + * zeroes, but caller should not parse these two zeroes. + * + * @return length or -1, for unlimited length + */ + public long readBerLength() { + int firstByte = readUnsignedByte(); + + long result; + if ((firstByte & 0x80) == 0) { + result = firstByte & 0x7f; + } else { + int intLength = firstByte & 0x7f; + if (intLength != 0) + result = readUnsignedVarInt(intLength); + else + return -1; + } + + return result; + } + + /** + * Read integer in BER format. + * + * Most significant bit of first byte indicates type of date in first byte: if + * 0, then byte contains length (up to 7f), if 1, then byte contains number of + * following bytes with value in network order. + */ + public void writeBerLength(long length) { + if (length < 0) + throw new RuntimeException("Length cannot be less than zero: " + length + ". Data: " + this + "."); + + if (length < 0x80) { + writeByte((int)length); + } else { + if (length < 0xff) { + writeByte(0x81); + writeByte((int)length); + } else if (length <= 0xffFF) { + writeByte(0x82); + writeShort((int)length); + } else if (length <= 0xffFFff) { + writeByte(0x83); + writeByte((int)(length >> 16)); + writeShort((int)length); + } else if (length <= 0xffFFffFFL) { + writeByte(0x84); + writeInt((int)length); + } else if (length <= 0xffFFffFFffL) { + writeByte(0x85); + writeByte((int)(length >> 32)); + writeInt((int)length); + } else if (length <= 0xffFFffFFffFFL) { + writeByte(0x86); + writeShort((int)(length >> 32)); + writeInt((int)length); + } else if (length <= 0xffFFffFFffFFffL) { + writeByte(0x87); + writeByte((int)(length >> (32 + 16))); + writeShort((int)(length >> 32)); + writeInt((int)length); + } else { + writeByte(0x88); + writeInt((int)(length >> 32)); + writeInt((int)length); + } + } + + } + + /** + * Read signed variable length integers in network order. + * + * @param len + * length of integer + */ + public long readSignedVarInt(int len) { + long value = 0; + switch (len) { + case 0: + value = 0; + break; + case 1: + value = readSignedByte(); + break; + case 2: + value = readSignedShort(); + break; + case 3: + value = (readSignedByte() << 16) | readUnsignedShort(); + break; + case 4: + value = readSignedInt(); + break; + case 5: + value = (readSignedByte() << 32) | readUnsignedInt(); + break; + case 6: + value = (readSignedShort() << 32) | readUnsignedInt(); + break; + case 7: + value = (readSignedByte() << 32 + 24) | (readUnsignedShort() << 32) | readUnsignedInt(); + break; + case 8: + value = readSignedLong(); + break; + default: + throw new RuntimeException("Cannot read integers which are more than 8 bytes long. Length: " + len + ". Data: " + this + "."); + } + + return value; + } + + /** + * Read unsigned variable length integers in network order. Values, which are + * larger than 0x7FffFFffFFffFFff cannot be parsed by this method. + */ + public long readUnsignedVarInt(int len) { + long value = 0; + switch (len) { + case 0: + value = 0; + break; + case 1: + value = readUnsignedByte(); + break; + case 2: + value = readUnsignedShort(); + break; + case 3: + value = (readUnsignedByte() << 16) | readUnsignedShort(); + break; + case 4: + value = readUnsignedInt(); + break; + case 5: + value = (readUnsignedByte() << 32) | readUnsignedInt(); + break; + case 6: + value = (readUnsignedShort() << 32) | readUnsignedInt(); + break; + case 7: + value = (readUnsignedByte() << 32 + 16) | (readUnsignedShort() << 32) | readUnsignedInt(); + break; + case 8: + value = readSignedLong(); + if (value < 0) + throw new RuntimeException( + "Cannot read 64 bit integers which are larger than 0x7FffFFffFFffFFff, because of lack of unsinged long type in Java. Value: " + value + ". Data: " + + this + "."); + break; + default: + throw new RuntimeException("Cannot read integers which are more than 8 bytes long. Length: " + len + ". Data: " + this + "."); + } + + return value; + } + /** * Read unsigned short in little endian order. Cursor is advanced by 2. */ @@ -535,6 +785,13 @@ public class ByteBuffer { return ((readSignedIntLE()) & 0xffFFffFFL) | (((long)readSignedIntLE()) << 32); } + /** + * Read signed long in network order. Cursor is advanced by 8 bytes. + */ + public long readSignedLong() { + return (((long)readSignedInt()) << 32) | ((readSignedInt()) & 0xffFFffFFL); + } + /** * Read string from buffer. Cursor is advanced by string length. */ @@ -547,6 +804,38 @@ public class ByteBuffer { return string; } + /** + * Read string with '\0' character at end. + */ + public String readVariableString(Charset charset) { + + int start = cursor; + + // Find end of string + while (readUnsignedByte() != 0) { + } + + String string = new String(data, offset + start, cursor - start - 1, charset); + + return string; + } + + /** + * Read wide string with wide '\0' character at end. + */ + public String readVariableWideString(Charset charset) { + + int start = cursor; + + // Find end of string + while (readUnsignedShortLE() != 0) { + } + + String string = new String(data, offset + start, cursor - start - 2, charset); + + return string; + } + /** * Get bytes as lightweight slice. Cursor is advanced by data length. */ @@ -682,8 +971,8 @@ public class ByteBuffer { */ public void prepend(byte[] data, int offset, int length) { if (!isSoleOwner()) { - throw new RuntimeException("Create full copy of this byte buffer data for modification. refCount: " + refCount + ", parentByteBuffer: " + parentByteBuffer + - "."); + throw new RuntimeException("Create full copy of this byte buffer data for modification. refCount: " + refCount + ", parentByteBuffer: " + + parentByteBuffer + "."); } // If there is no enough space for header to prepend @@ -697,9 +986,13 @@ public class ByteBuffer { // Extend byte range to include header this.offset -= length; this.length += length; - this.cursor += length; + cursor += length; } + /** + * Write byte representation of given string, without string terminators (zero + * or zeroes at end of string). + */ public void writeString(String str, Charset charset) { writeBytes(str.getBytes(charset)); } @@ -725,44 +1018,10 @@ public class ByteBuffer { } public void writeBytes(byte[] bytes, int offset, int length) { - System.arraycopy(bytes, offset, this.data, this.offset + this.cursor, length); + System.arraycopy(bytes, offset, data, this.offset + cursor, length); cursor += length; } - // /** - // * Write BER encoded definite long variant of the ASN.1 length field. - // */ - // public void writeBerLength(int value) { - // int fieldLength; - // if (value > 0xFFffFF) - // fieldLength = 4; - // else if (value > 0xFFff) - // fieldLength = 3; - // else if (value > 0xFF) - // fieldLength = 2; - // else - // fieldLength = 1; - // - // if (cursor + fieldLength + 1 > length) - // throw new ArrayIndexOutOfBoundsException("Cannot write " + (fieldLength + - // 1) + " byte(s) to this buffer: " + this + "."); - // - // // Write length of length field itself - // writeByte(0x80 | fieldLength); - // - // switch (fieldLength) { - // case 4: - // data[offset + cursor++] = (byte) (value >> 24); - // case 3: - // data[offset + cursor++] = (byte) (value >> 16); - // case 2: - // data[offset + cursor++] = (byte) (value >> 8); - // case 1: - // data[offset + cursor++] = (byte) value; - // } - // - // } - /** * Reduce length of buffer to cursor position. */ @@ -771,7 +1030,7 @@ public class ByteBuffer { } /** - * Rewind cursor to beginning of buffer. + * Rewind cursor to beginning of the buffer. */ public void rewindCursor() { cursor = 0; @@ -808,8 +1067,11 @@ public class ByteBuffer { public boolean equals(Object obj) { if (this == obj) return true; + if (obj == null) return false; + + // Does not work in case of anonymous type(s) if (getClass() != obj.getClass()) return false; @@ -824,4 +1086,38 @@ public class ByteBuffer { return true; } + /** + * Return length of data left after cursor. + */ + public int remainderLength() { + if (length >= cursor) + return length - cursor; + else + throw new RuntimeException("Inconsistent state of buffer: cursor is after end of buffer: " + this + "."); + } + + public Set getMetadataKeys() { + if (metadata != null) + return metadata.keySet(); + else + return new HashSet(0); + } + + /** + * Return unsigned value of byte at given position relative to cursor. Cursor + * is not advanced. + */ + public int peekUnsignedByte(int i) { + return data[offset + cursor + i] & 0xff; + } + + /** + * Trim few first bytes. + */ + public void trimHeader(int length) { + offset += length; + this.length -= length; + rewindCursor(); + } + } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java old mode 100644 new mode 100755 index d489b182393..e78e3012856 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java @@ -19,7 +19,7 @@ package streamer; import java.util.Set; /** - * Element is for processing of data. It has one or more contact pads, which can + * Element is basic building block for constructing data processing pipes. It has one or more contact pads, which can * be wired with other elements using links. */ public interface Element { diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java old mode 100644 new mode 100755 index 9808f62b684..998274cf1d0 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.java @@ -17,7 +17,8 @@ package streamer; public enum Event { - STREAM_START, STREAM_CLOSE, + STREAM_START, + STREAM_CLOSE, /** * Upgrade socket to SSL. diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java old mode 100644 new mode 100755 index 45155e185fb..0c8c97df690 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java @@ -20,13 +20,15 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import streamer.debug.FakeSink; + /** * Source element, which reads data from InputStream. */ public class InputStreamSource extends BaseElement { protected InputStream is; - protected SocketWrapper socketWrapper; + protected SocketWrapperImpl socketWrapper; public InputStreamSource(String id) { super(id); @@ -37,7 +39,7 @@ public class InputStreamSource extends BaseElement { this.is = is; } - public InputStreamSource(String id, SocketWrapper socketWrapper) { + public InputStreamSource(String id, SocketWrapperImpl socketWrapper) { super(id); this.socketWrapper = socketWrapper; } @@ -45,27 +47,27 @@ public class InputStreamSource extends BaseElement { @Override public void handleEvent(Event event, Direction direction) { switch (event) { - case SOCKET_UPGRADE_TO_SSL: - socketWrapper.upgradeToSsl(); - break; - default: - super.handleEvent(event, direction); + case SOCKET_UPGRADE_TO_SSL: + socketWrapper.upgradeToSsl(); + break; + default: + super.handleEvent(event, direction); } } @Override public void setLink(String padName, Link link, Direction direction) { switch (direction) { - case OUT: - super.setLink(padName, link, direction); + case OUT: + super.setLink(padName, link, direction); - if (is == null) { - // Pause links until data stream will be ready - link.pause(); - } - break; - case IN: - throw new RuntimeException("Cannot assign link to input pad in source element. Element: " + this + ", pad: " + padName + ", link: " + link + "."); + if (is == null) { + // Pause links until data stream will be ready + link.pause(); + } + break; + case IN: + throw new RuntimeException("Cannot assign link to input pad in source element. Element: " + this + ", pad: " + padName + ", link: " + link + "."); } } diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java deleted file mode 100644 index 8094c82b7b3..00000000000 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.java +++ /dev/null @@ -1,113 +0,0 @@ -// 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 streamer; - -import java.util.Arrays; - -/** - * Compare incoming packets with expected packets. - */ -public class MockSink extends BaseElement { - - protected ByteBuffer bufs[] = null; - - public MockSink(String id) { - super(id); - } - - public MockSink(String id, ByteBuffer bufs[]) { - super(id); - this.bufs = bufs; - } - - @Override - public void handleData(ByteBuffer buf, Link link) { - if (verbose) - System.out.println("[" + this + "] INFO: Received buf #" + (packetNumber) + " " + buf + "."); - - if (buf == null) - return; - - if (packetNumber >= bufs.length) - throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not expected. Number of expected buffers: " + bufs.length + - ", unexpected buffer: " + buf + "."); - - // Compare incoming buffer with expected buffer - if (!Arrays.equals(bufs[packetNumber].toByteArray(), buf.toByteArray())) - throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not equal to expected buffer.\n Actual bufer: " + buf + - ",\n expected buffer: " + bufs[packetNumber] + "."); - - if (verbose) - System.out.println("[" + this + "] INFO: buffers are equal."); - - // Use packetNumber variable to count incoming packets - packetNumber++; - - buf.unref(); - } - - @Override - protected void onClose() { - super.onClose(); - - if (packetNumber != bufs.length) - throw new AssertionError("[" + this + "] Number of expected buffers: " + bufs.length + ", number of actual buffers: " + packetNumber + "."); - } - - @Override - public String toString() { - return "MockSink(" + id + ")"; - } - - /** - * Example. - */ - public static void main(String args[]) { - - Element mockSource = new MockSource("source") { - { - this.bufs = - new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}), new ByteBuffer(new byte[] {3, 1, 2, 3}), - new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})}; - this.verbose = true; - this.delay = 100; - this.numBuffers = this.bufs.length; - } - }; - - Element mockSink = new MockSink("sink") { - { - this.bufs = - new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}), new ByteBuffer(new byte[] {3, 1, 2, 3}), - new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})}; - this.verbose = true; - } - }; - - Link link = new SyncLink() { - { - this.verbose = true; - } - }; - - mockSource.setLink(STDOUT, link, Direction.OUT); - mockSink.setLink(STDIN, link, Direction.IN); - - link.run(); - } - -} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java old mode 100644 new mode 100755 index d50995eae72..9cccb5bf2c2 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.java @@ -87,13 +87,16 @@ public abstract class OneTimeSwitch extends BaseElement { // Wake up next peer(s) sendEventToAllPads(Event.STREAM_START, Direction.OUT); + // Disconnect our stdin from this element stdin.setSink(null); inputPads.remove(STDIN); + // Replace next peer stdin (our stdout) by our stdin Element nextPeer = stdout.getSink(); nextPeer.replaceLink(stdout, stdin); stdout.drop(); + // Drop all other links for (Object link : inputPads.values().toArray()) ((Link)link).drop(); for (Object link : outputPads.values().toArray()) diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.java old mode 100644 new mode 100755 diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java old mode 100644 new mode 100755 index 7a55ea81bea..e66899df8fc --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java @@ -20,10 +20,12 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import streamer.debug.FakeSource; + public class OutputStreamSink extends BaseElement { protected OutputStream os; - protected SocketWrapper socketWrapper; + protected SocketWrapperImpl socketWrapper; public OutputStreamSink(String id) { super(id); @@ -34,7 +36,7 @@ public class OutputStreamSink extends BaseElement { this.os = os; } - public OutputStreamSink(String id, SocketWrapper socketWrapper) { + public OutputStreamSink(String id, SocketWrapperImpl socketWrapper) { super(id); this.socketWrapper = socketWrapper; } @@ -68,26 +70,26 @@ public class OutputStreamSink extends BaseElement { @Override public void handleEvent(Event event, Direction direction) { switch (event) { - case SOCKET_UPGRADE_TO_SSL: - socketWrapper.upgradeToSsl(); - break; - default: - super.handleEvent(event, direction); + case SOCKET_UPGRADE_TO_SSL: + socketWrapper.upgradeToSsl(); + break; + default: + super.handleEvent(event, direction); } } @Override public void setLink(String padName, Link link, Direction direction) { switch (direction) { - case IN: - super.setLink(padName, link, direction); + case IN: + super.setLink(padName, link, direction); - if (os == null) - // Pause links until data stream will be ready - link.pause(); - break; - case OUT: - throw new RuntimeException("Cannot assign link to output pad in sink element. Element: " + this + ", pad: " + padName + ", link: " + link + "."); + if (os == null) + // Pause links until data stream will be ready + link.pause(); + break; + case OUT: + throw new RuntimeException("Cannot assign link to output pad in sink element. Element: " + this + ", pad: " + padName + ", link: " + link + "."); } } @@ -126,10 +128,10 @@ public class OutputStreamSink extends BaseElement { public static void main(String args[]) { Element source = new FakeSource("source") { { - this.verbose = true; - this.numBuffers = 3; - this.incommingBufLength = 5; - this.delay = 100; + verbose = true; + numBuffers = 3; + incommingBufLength = 5; + delay = 100; } }; diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java old mode 100644 new mode 100755 index d2e52ddbb27..0f6089bcf23 --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java @@ -48,6 +48,8 @@ public interface Pipeline extends Element { * so when pipeline will be connected with other elements, outside of this * pipeline, they will be connected to IN and OUT elements. * + * Empty names are skipped. + * * Example: * *
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
      old mode 100644
      new mode 100755
      index 4e6fc0a44ca..299d0a466f7
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java
      @@ -16,10 +16,15 @@
       // under the License.
       package streamer;
       
      +import java.util.ArrayList;
       import java.util.HashMap;
      +import java.util.List;
       import java.util.Map;
       import java.util.Set;
       
      +import streamer.debug.FakeSink;
      +import streamer.debug.FakeSource;
      +
       public class PipelineImpl implements Pipeline {
       
           protected String id;
      @@ -51,11 +56,11 @@ public class PipelineImpl implements Pipeline {
           @Override
           public Set getPads(Direction direction) {
               switch (direction) {
      -            case IN:
      -                return elements.get(IN).getPads(direction);
      +        case IN:
      +            return elements.get(IN).getPads(direction);
       
      -            case OUT:
      -                return elements.get(OUT).getPads(direction);
      +        case OUT:
      +            return elements.get(OUT).getPads(direction);
               }
               return null;
           }
      @@ -71,8 +76,8 @@ public class PipelineImpl implements Pipeline {
                   int outPadsNumber = element.getPads(Direction.OUT).size();
                   int inPadsNumber = element.getPads(Direction.IN).size();
                   if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0))
      -                throw new RuntimeException("[ " + this + "] Pads of input element of pipeline are not balanced. Element: " + element + ", output pads: " +
      -                    element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
      +                throw new RuntimeException("[ " + this + "] Pads of input element of pipeline are not balanced. Element: " + element + ", output pads: "
      +                        + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
               }
       
               // Check OUT element
      @@ -81,8 +86,8 @@ public class PipelineImpl implements Pipeline {
                   int outPadsNumber = element.getPads(Direction.OUT).size();
                   int inPadsNumber = element.getPads(Direction.IN).size();
                   if ((outPadsNumber | inPadsNumber) > 0 && (outPadsNumber == 0 || inPadsNumber == 0))
      -                throw new RuntimeException("[ " + this + "] Pads of output element of pipeline are not balanced. Element: " + element + ", output pads: " +
      -                    element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
      +                throw new RuntimeException("[ " + this + "] Pads of output element of pipeline are not balanced. Element: " + element + ", output pads: "
      +                        + element.getPads(Direction.OUT).toString() + ", input pads: " + element.getPads(Direction.IN).toString() + ".");
               }
       
           }
      @@ -127,12 +132,12 @@ public class PipelineImpl implements Pipeline {
           @Override
           public void handleEvent(Event event, Direction direction) {
               switch (direction) {
      -            case IN:
      -                get(IN).handleEvent(event, direction);
      -                break;
      -            case OUT:
      -                get(OUT).handleEvent(event, direction);
      -                break;
      +        case IN:
      +            get(IN).handleEvent(event, direction);
      +            break;
      +        case OUT:
      +            get(OUT).handleEvent(event, direction);
      +            break;
               }
           }
       
      @@ -142,8 +147,8 @@ public class PipelineImpl implements Pipeline {
                   String id = element.getId();
       
                   if (this.elements.containsKey(id))
      -                throw new RuntimeException("This pipeline already contains element with same ID. New element: " + element + ", existing element: " +
      -                    this.elements.get(id) + ".");
      +                throw new RuntimeException("This pipeline already contains element with same ID. New element: " + element + ", existing element: "
      +                        + this.elements.get(id) + ".");
       
                   this.elements.put(id, element);
               }
      @@ -152,6 +157,8 @@ public class PipelineImpl implements Pipeline {
           @Override
           public void link(String... elementNames) {
       
      +        elementNames = filterOutEmptyStrings(elementNames);
      +
               if (elementNames.length < 2)
                   throw new RuntimeException("At least two elements are necessary to create link between them.");
       
      @@ -180,8 +187,8 @@ public class PipelineImpl implements Pipeline {
                   elements[i] = get(elementName);
       
                   if (elements[i] == null)
      -                throw new RuntimeException("Cannot find element by name in this pipeline. Element name: \"" + elementName + "\" (" + elementNames[i] + "), pipeline: " +
      -                    this + ".");
      +                throw new RuntimeException("Cannot find element by name in this pipeline. Element name: \"" + elementName + "\" (" + elementNames[i] + "), pipeline: "
      +                        + this + ".");
       
                   i++;
               }
      @@ -204,6 +211,30 @@ public class PipelineImpl implements Pipeline {
               }
           }
       
      +    /**
      +     * Filter out empty strings from array and return new array with non-empty
      +     * elements only. If array contains no empty string, returns same array.
      +     */
      +    private String[] filterOutEmptyStrings(String[] strings) {
      +
      +        boolean found = false;
      +        for (String string : strings) {
      +            if (string == null || string.isEmpty()) {
      +                found = true;
      +                break;
      +            }
      +        }
      +
      +        if (!found)
      +            return strings;
      +
      +        List filteredStrings = new ArrayList(strings.length);
      +        for (String string : strings)
      +            if (string != null && !string.isEmpty())
      +                filteredStrings.add(string);
      +        return filteredStrings.toArray(new String[filteredStrings.size()]);
      +    }
      +
           @Override
           public void addAndLink(Element... elements) {
               add(elements);
      @@ -281,20 +312,20 @@ public class PipelineImpl implements Pipeline {
               // Create elements
               pipeline.add(new FakeSource("source") {
                   {
      -                this.incommingBufLength = 3;
      -                this.numBuffers = 10;
      -                this.delay = 100;
      +                incommingBufLength = 3;
      +                numBuffers = 10;
      +                delay = 100;
                   }
               });
               pipeline.add(new BaseElement("tee"));
               pipeline.add(new FakeSink("sink") {
                   {
      -                this.verbose = true;
      +                verbose = true;
                   }
               });
               pipeline.add(new FakeSink("sink2") {
                   {
      -                this.verbose = true;
      +                verbose = true;
                   }
               });
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
      old mode 100644
      new mode 100755
      index 23c57e0add8..910e073a058
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java
      @@ -19,6 +19,9 @@ package streamer;
       import java.util.concurrent.LinkedBlockingQueue;
       import java.util.concurrent.TimeUnit;
       
      +import streamer.debug.FakeSink;
      +import streamer.debug.FakeSource;
      +
       /**
        * Message queue for safe transfer of packets between threads.
        */
      @@ -30,7 +33,6 @@ public class Queue extends BaseElement {
               super(id);
           }
       
      -    @SuppressWarnings("incomplete-switch")
           @Override
           public void poll(boolean block) {
               try {
      @@ -67,12 +69,12 @@ public class Queue extends BaseElement {
           @Override
           public void handleEvent(Event event, Direction direction) {
               switch (event) {
      -            case LINK_SWITCH_TO_PULL_MODE:
      -                // Do not propagate this event, because this element is boundary between
      -                // threads
      -                break;
      -            default:
      -                super.handleEvent(event, direction);
      +        case LINK_SWITCH_TO_PULL_MODE:
      +            // Do not propagate this event, because this element is boundary between
      +            // threads
      +            break;
      +        default:
      +            super.handleEvent(event, direction);
               }
           }
       
      @@ -104,17 +106,17 @@ public class Queue extends BaseElement {
       
               Element source1 = new FakeSource("source1") {
                   {
      -                this.delay = 100;
      -                this.numBuffers = 10;
      -                this.incommingBufLength = 10;
      +                delay = 100;
      +                numBuffers = 10;
      +                incommingBufLength = 10;
                   }
               };
       
               Element source2 = new FakeSource("source2") {
                   {
      -                this.delay = 100;
      -                this.numBuffers = 10;
      -                this.incommingBufLength = 10;
      +                delay = 100;
      +                numBuffers = 10;
      +                incommingBufLength = 10;
                   }
               };
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
      old mode 100644
      new mode 100755
      index dfffd35944d..22d436e5c67
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java
      @@ -16,224 +16,20 @@
       // under the License.
       package streamer;
       
      -import static rdpclient.MockServer.Packet.PacketType.CLIENT;
      -import static rdpclient.MockServer.Packet.PacketType.SERVER;
      -
       import java.io.IOException;
      -import java.io.InputStream;
      -import java.io.OutputStream;
       import java.net.InetSocketAddress;
      -import java.net.Socket;
      -import java.util.HashMap;
       
      -import javax.net.SocketFactory;
      -import javax.net.ssl.SSLContext;
      -import javax.net.ssl.SSLSocket;
      -import javax.net.ssl.SSLSocketFactory;
      -import javax.net.ssl.TrustManager;
      -
      -import rdpclient.MockServer;
      -import rdpclient.MockServer.Packet;
      -import rdpclient.TrustAllX509TrustManager;
      -
      -public class SocketWrapper extends PipelineImpl {
      -
      -    protected InputStreamSource source;
      -    protected OutputStreamSink sink;
      -    protected Socket socket;
      -    protected InetSocketAddress address;
      -
      -    protected SSLSocket sslSocket;
      -
      -    //protected String SSL_VERSION_TO_USE = "TLSv1.2";
      -    /*DEBUG*/protected static final String SSL_VERSION_TO_USE = "TLSv1";
      -
      -    public SocketWrapper(String id) {
      -        super(id);
      -    }
      -
      -    @Override
      -    protected HashMap initElementMap(String id) {
      -        HashMap map = new HashMap();
      -
      -        source = new InputStreamSource(id + "." + OUT, this);
      -        sink = new OutputStreamSink(id + "." + IN, this);
      -
      -        // Pass requests to read data to socket input stream
      -        map.put(OUT, source);
      -
      -        // All incoming data, which is sent to this socket wrapper, will be sent
      -        // to socket remote
      -        map.put(IN, sink);
      -
      -        return map;
      -    }
      +public interface SocketWrapper extends Element {
       
           /**
            * Connect this socket wrapper to remote server and start main loop on
      -     * IputStreamSource stdout link, to watch for incoming data, and
      -     * OutputStreamSink stdin link, to pull for outgoing data.
      -     *
      -     * @param address
      -     * @throws IOException
      +     * Source stdout link, to watch for incoming data, and
      +     * Sink stdin link, to pull for outgoing data.
            */
      -    public void connect(InetSocketAddress address) throws IOException {
      -        this.address = address;
      +    public void connect(InetSocketAddress address) throws IOException;
       
      -        // Connect socket to server
      -        socket = SocketFactory.getDefault().createSocket();
      -        try {
      -            socket.connect(address);
      +    public void shutdown();
       
      -            InputStream is = socket.getInputStream();
      -            source.setInputStream(is);
      +    void upgradeToSsl();
       
      -            OutputStream os = socket.getOutputStream();
      -            sink.setOutputStream(os);
      -
      -            // Start polling for data to send to remote sever
      -            runMainLoop(IN, STDIN, true, true);
      -
      -            // Push incoming data from server to handlers
      -            runMainLoop(OUT, STDOUT, false, false);
      -
      -        } finally {
      -            socket.close();
      -        }
      -    }
      -
      -    @Override
      -    public void handleEvent(Event event, Direction direction) {
      -        switch (event) {
      -            case SOCKET_UPGRADE_TO_SSL:
      -                upgradeToSsl();
      -                break;
      -            default:
      -                super.handleEvent(event, direction);
      -                break;
      -        }
      -    }
      -
      -    public void upgradeToSsl() {
      -
      -        if (sslSocket != null)
      -            // Already upgraded
      -            return;
      -
      -        if (verbose)
      -            System.out.println("[" + this + "] INFO: Upgrading socket to SSL.");
      -
      -        try {
      -            // Use most secure implementation of SSL available now.
      -            // JVM will try to negotiate TLS1.2, then will fallback to TLS1.0, if
      -            // TLS1.2 is not supported.
      -            SSLContext sslContext = SSLContext.getInstance(SSL_VERSION_TO_USE);
      -
      -            // Trust all certificates (FIXME: insecure)
      -            sslContext.init(null, new TrustManager[] {new TrustAllX509TrustManager()}, null);
      -
      -            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
      -            sslSocket = (SSLSocket)sslSocketFactory.createSocket(socket, address.getHostName(), address.getPort(), true);
      -            sslSocket.startHandshake();
      -
      -            InputStream sis = sslSocket.getInputStream();
      -            source.setInputStream(sis);
      -
      -            OutputStream sos = sslSocket.getOutputStream();
      -            sink.setOutputStream(sos);
      -
      -        } catch (Exception e) {
      -            throw new RuntimeException("Cannot upgrade socket to SSL: " + e.getMessage(), e);
      -        }
      -
      -    }
      -
      -    @Override
      -    public void validate() {
      -        for (Element element : elements.values())
      -            element.validate();
      -
      -        if (get(IN).getPads(Direction.IN).size() == 0)
      -            throw new RuntimeException("[ " + this + "] Input of socket is not connected.");
      -
      -        if (get(OUT).getPads(Direction.OUT).size() == 0)
      -            throw new RuntimeException("[ " + this + "] Output of socket is not connected.");
      -
      -    }
      -
      -    public void shutdown() {
      -        try {
      -            handleEvent(Event.STREAM_CLOSE, Direction.IN);
      -        } catch (Exception e) {
      -        }
      -        try {
      -            handleEvent(Event.STREAM_CLOSE, Direction.OUT);
      -        } catch (Exception e) {
      -        }
      -        try {
      -            if (sslSocket != null)
      -                sslSocket.close();
      -        } catch (Exception e) {
      -        }
      -        try {
      -            socket.close();
      -        } catch (Exception e) {
      -        }
      -    }
      -
      -    @Override
      -    public String toString() {
      -        return "SocketWrapper(" + id + ")";
      -    }
      -
      -    /**
      -     * Example.
      -     */
      -    public static void main(String args[]) {
      -        try {
      -            System.setProperty("streamer.Link.debug", "true");
      -            System.setProperty("streamer.Element.debug", "true");
      -            System.setProperty("rdpclient.MockServer.debug", "true");
      -
      -            Pipeline pipeline = new PipelineImpl("echo client");
      -
      -            SocketWrapper socketWrapper = new SocketWrapper("socket");
      -
      -            pipeline.add(socketWrapper);
      -            pipeline.add(new BaseElement("echo"));
      -
      -            pipeline.link("socket", "echo", "socket");
      -
      -            final byte[] mockData = new byte[] {0x01, 0x02, 0x03};
      -            MockServer server = new MockServer(new Packet[] {new Packet("Server hello") {
      -                {
      -                    type = SERVER;
      -                    data = mockData;
      -                }
      -            }, new Packet("Client hello") {
      -                {
      -                    type = CLIENT;
      -                    data = mockData;
      -                }
      -            }, new Packet("Server hello") {
      -                {
      -                    type = SERVER;
      -                    data = mockData;
      -                }
      -            }, new Packet("Client hello") {
      -                {
      -                    type = CLIENT;
      -                    data = mockData;
      -                }
      -            }});
      -            server.start();
      -            InetSocketAddress address = server.getAddress();
      -
      -            socketWrapper.connect(address);
      -
      -        } catch (IOException e) {
      -            e.printStackTrace(System.err);
      -        }
      -
      -    }
       }
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapperImpl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapperImpl.java
      new file mode 100755
      index 00000000000..da89a0d0417
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapperImpl.java
      @@ -0,0 +1,249 @@
      +// 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 streamer;
      +
      +import static streamer.debug.MockServer.Packet.PacketType.CLIENT;
      +import static streamer.debug.MockServer.Packet.PacketType.SERVER;
      +
      +import java.io.IOException;
      +import java.io.InputStream;
      +import java.io.OutputStream;
      +import java.net.InetSocketAddress;
      +import java.net.Socket;
      +import java.util.HashMap;
      +
      +import javax.net.SocketFactory;
      +import javax.net.ssl.SSLContext;
      +import javax.net.ssl.SSLSocket;
      +import javax.net.ssl.SSLSocketFactory;
      +import javax.net.ssl.TrustManager;
      +
      +import streamer.debug.MockServer;
      +import streamer.debug.MockServer.Packet;
      +import streamer.ssl.SSLState;
      +import streamer.ssl.TrustAllX509TrustManager;
      +
      +public class SocketWrapperImpl extends PipelineImpl implements SocketWrapper {
      +
      +    protected InputStreamSource source;
      +    protected OutputStreamSink sink;
      +    protected Socket socket;
      +    protected InetSocketAddress address;
      +
      +    protected SSLSocket sslSocket;
      +
      +    protected String sslVersionToUse = "TLSv1.2";
      +
      +    protected SSLState sslState;
      +
      +    public SocketWrapperImpl(String id, SSLState sslState) {
      +        super(id);
      +        this.sslState = sslState;
      +    }
      +
      +    @Override
      +    protected HashMap initElementMap(String id) {
      +        HashMap map = new HashMap();
      +
      +        source = new InputStreamSource(id + "." + OUT, this);
      +        sink = new OutputStreamSink(id + "." + IN, this);
      +
      +        // Pass requests to read data to socket input stream
      +        map.put(OUT, source);
      +
      +        // All incoming data, which is sent to this socket wrapper, will be sent
      +        // to socket remote
      +        map.put(IN, sink);
      +
      +        return map;
      +    }
      +
      +    /**
      +     * Connect this socket wrapper to remote server and start main loop on
      +     * IputStreamSource stdout link, to watch for incoming data, and
      +     * OutputStreamSink stdin link, to pull for outgoing data.
      +     *
      +     * @param address
      +     * @throws IOException
      +     */
      +    @Override
      +    public void connect(InetSocketAddress address) throws IOException {
      +        this.address = address;
      +
      +        // Connect socket to server
      +        socket = SocketFactory.getDefault().createSocket();
      +        try {
      +            socket.connect(address);
      +
      +            InputStream is = socket.getInputStream();
      +            source.setInputStream(is);
      +
      +            OutputStream os = socket.getOutputStream();
      +            sink.setOutputStream(os);
      +
      +            // Start polling for data to send to remote sever
      +            runMainLoop(IN, STDIN, true, true);
      +
      +            // Push incoming data from server to handlers
      +            runMainLoop(OUT, STDOUT, false, false);
      +
      +        } finally {
      +            socket.close();
      +        }
      +    }
      +
      +    @Override
      +    public void handleEvent(Event event, Direction direction) {
      +        switch (event) {
      +        case SOCKET_UPGRADE_TO_SSL:
      +            upgradeToSsl();
      +            break;
      +        default:
      +            super.handleEvent(event, direction);
      +            break;
      +        }
      +    }
      +
      +    @Override
      +    public void upgradeToSsl() {
      +
      +        if (sslSocket != null)
      +            // Already upgraded
      +            return;
      +
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Upgrading socket to SSL.");
      +
      +        try {
      +            // Use most secure implementation of SSL available now.
      +            // JVM will try to negotiate TLS1.2, then will fallback to TLS1.0, if
      +            // TLS1.2 is not supported.
      +            SSLContext sslContext = SSLContext.getInstance(sslVersionToUse);
      +
      +            // Trust all certificates (FIXME: insecure)
      +            sslContext.init(null, new TrustManager[] {new TrustAllX509TrustManager(sslState)}, null);
      +
      +            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
      +            sslSocket = (SSLSocket)sslSocketFactory.createSocket(socket, address.getHostName(), address.getPort(), true);
      +
      +            sslSocket.startHandshake();
      +
      +            InputStream sis = sslSocket.getInputStream();
      +            source.setInputStream(sis);
      +
      +            OutputStream sos = sslSocket.getOutputStream();
      +            sink.setOutputStream(sos);
      +
      +        } catch (Exception e) {
      +            throw new RuntimeException("Cannot upgrade socket to SSL: " + e.getMessage(), e);
      +        }
      +
      +    }
      +
      +    @Override
      +    public void validate() {
      +        for (Element element : elements.values())
      +            element.validate();
      +
      +        if (get(IN).getPads(Direction.IN).size() == 0)
      +            throw new RuntimeException("[ " + this + "] Input of socket is not connected.");
      +
      +        if (get(OUT).getPads(Direction.OUT).size() == 0)
      +            throw new RuntimeException("[ " + this + "] Output of socket is not connected.");
      +
      +    }
      +
      +    @Override
      +    public void shutdown() {
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.IN);
      +        } catch (Exception e) {
      +        }
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.OUT);
      +        } catch (Exception e) {
      +        }
      +        try {
      +            if (sslSocket != null)
      +                sslSocket.close();
      +        } catch (Exception e) {
      +        }
      +        try {
      +            socket.close();
      +        } catch (Exception e) {
      +        }
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "SocketWrapper(" + id + ")";
      +    }
      +
      +    /**
      +     * Example.
      +     */
      +    public static void main(String args[]) {
      +
      +        try {
      +            System.setProperty("streamer.Link.debug", "true");
      +            System.setProperty("streamer.Element.debug", "true");
      +            System.setProperty("rdpclient.MockServer.debug", "true");
      +
      +            Pipeline pipeline = new PipelineImpl("echo client");
      +
      +            SocketWrapperImpl socketWrapper = new SocketWrapperImpl("socket", null);
      +
      +            pipeline.add(socketWrapper);
      +            pipeline.add(new BaseElement("echo"));
      +            pipeline.add(new Queue("queue")); // To decouple input and output
      +
      +            pipeline.link("socket", "echo", "queue", "socket");
      +
      +            final byte[] mockData = new byte[] {0x01, 0x02, 0x03};
      +            MockServer server = new MockServer(new Packet[] {new Packet("Server hello") {
      +                {
      +                    type = SERVER;
      +                    data = mockData;
      +                }
      +            }, new Packet("Client hello") {
      +                {
      +                    type = CLIENT;
      +                    data = mockData;
      +                }
      +            }, new Packet("Server hello") {
      +                {
      +                    type = SERVER;
      +                    data = mockData;
      +                }
      +            }, new Packet("Client hello") {
      +                {
      +                    type = CLIENT;
      +                    data = mockData;
      +                }
      +            }});
      +            server.start();
      +            InetSocketAddress address = server.getAddress();
      +
      +            /*DEBUG*/System.out.println("Address: " + address);
      +            socketWrapper.connect(address);
      +
      +        } catch (Exception e) {
      +            e.printStackTrace(System.err);
      +        }
      +
      +    }
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
      old mode 100644
      new mode 100755
      index 2bd4919e5f4..94281d21c9d
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java
      @@ -27,7 +27,7 @@ public class SyncLink implements Link {
            * avoid consuming of 100% of CPU in main loop in cases when link is pauses or
            * source element cannot produce data right now.
            */
      -    protected static final long STANDARD_DELAY_FOR_EMPTY_PACKET = 10; // Milliseconds
      +    public static final long STANDARD_DELAY_FOR_EMPTY_PACKET = 10; // Milliseconds
       
           /**
            * Delay for null packets in poll method when blocking is requested, in
      @@ -46,7 +46,8 @@ public class SyncLink implements Link {
           protected String id = null;
       
           /**
      -     * Buffer with data to hold because link is paused, or data is pushed back.
      +     * Buffer with data to hold because link is paused, on hold, or data is pushed
      +     * back from output element.
            */
           protected ByteBuffer cacheBuffer = null;
       
      @@ -61,11 +62,6 @@ public class SyncLink implements Link {
            */
           protected int packetNumber = 0;
       
      -    /**
      -     * Set to true to hold all data in link until it will be set to false again.
      -     */
      -    protected boolean paused = false;
      -
           /**
            * Element to pull data from, when in pull mode.
            */
      @@ -87,12 +83,24 @@ public class SyncLink implements Link {
            * Indicates that event STREAM_START is passed over this link, so main loop
            * can be started to pull data from source element.
            */
      -    protected boolean start;
      +    protected boolean started = false;
       
           /**
      -     * Operate in pull mode.
      +     * Set to true to hold all data in link until it will be set to false again.
            */
      -    protected boolean pullMode;
      +    protected boolean paused = false;
      +
      +    /**
      +     * Used by pull() method to hold all data in this link to avoid recursion when
      +     * source element is asked to push new data to it outputs.
      +     */
      +    protected boolean hold = false;
      +
      +    /**
      +     * Operate in pull mode instead of default push mode. In pull mode, link will
      +     * ask it source element for new data.
      +     */
      +    protected boolean pullMode = false;
       
           public SyncLink() {
           }
      @@ -139,7 +147,7 @@ public class SyncLink implements Link {
            */
           @Override
           public void sendData(ByteBuffer buf) {
      -        if (!paused && pullMode)
      +        if (!hold && pullMode)
                   throw new RuntimeException("[" + this + "] ERROR: link is not in push mode.");
       
               if (verbose)
      @@ -162,7 +170,7 @@ public class SyncLink implements Link {
               // When data pushed back and length of data is less than length of full
               // packet, then feed data to sink element immediately
               while (cacheBuffer != null) {
      -            if (paused) {
      +            if (paused || hold) {
                       if (verbose)
                           System.out.println("[" + this + "] INFO: Transfer is paused. Data in cache buffer: " + cacheBuffer + ".");
       
      @@ -172,8 +180,8 @@ public class SyncLink implements Link {
       
                   if (expectedPacketSize > 0 && cacheBuffer.length < expectedPacketSize) {
                       if (verbose)
      -                    System.out.println("[" + this + "] INFO: Transfer is suspended because available data is less than expected packet size. Expected packet size: " +
      -                        expectedPacketSize + ", data in cache buffer: " + cacheBuffer + ".");
      +                    System.out.println("[" + this + "] INFO: Transfer is suspended because available data is less than expected packet size. Expected packet size: "
      +                            + expectedPacketSize + ", data in cache buffer: " + cacheBuffer + ".");
       
                       // Wait until rest of packet will be read
                       return;
      @@ -203,43 +211,46 @@ public class SyncLink implements Link {
       
               // Shutdown main loop (if any) when STREAM_CLOSE event is received.
               switch (event) {
      -            case STREAM_START: {
      -                if (!start)
      -                    start = true;
      -                else
      -                    // Event already sent trough this link
      -                    return;
      -                break;
      -            }
      -            case STREAM_CLOSE: {
      -                if (!shutdown)
      -                    shutdown = true;
      -                else
      -                    // Event already sent trough this link
      -                    return;
      -                break;
      -            }
      -            case LINK_SWITCH_TO_PULL_MODE: {
      -                setPullMode();
      -                break;
      -            }
      +        case STREAM_START: {
      +            if (!started)
      +                started = true;
      +            else
      +                // Event already sent trough this link
      +                return;
      +            break;
      +        }
      +        case STREAM_CLOSE: {
      +            if (!shutdown)
      +                shutdown = true;
      +            else
      +                // Event already sent trough this link
      +                return;
      +            break;
      +        }
      +        case LINK_SWITCH_TO_PULL_MODE: {
      +            setPullMode();
      +            break;
      +        }
       
               }
       
               switch (direction) {
      -            case IN:
      -                source.handleEvent(event, direction);
      -                break;
      -            case OUT:
      -                sink.handleEvent(event, direction);
      -                break;
      +        case IN:
      +            source.handleEvent(event, direction);
      +            break;
      +        case OUT:
      +            sink.handleEvent(event, direction);
      +            break;
               }
           }
       
           @Override
           public ByteBuffer pull(boolean block) {
               if (!pullMode)
      -            throw new RuntimeException("This link is not in pull mode.");
      +            throw new RuntimeException("[" + this + "] ERROR: This link is not in pull mode.");
      +
      +        if (hold)
      +            throw new RuntimeException("[" + this + "] ERROR: This link is already on hold, waiting for data to be pulled in. Circular reference?");
       
               if (paused) {
                   if (verbose)
      @@ -269,9 +280,12 @@ public class SyncLink implements Link {
       
               // Pause this link, so incoming data will not be sent to sink
               // immediately, then ask source element for more data
      -        pause();
      -        source.poll(block);
      -        resume();
      +        try {
      +            hold = true;
      +            source.poll(block);
      +        } finally {
      +            hold = false;
      +        }
       
               // Can return something only when data was stored in buffer
               if (cacheBuffer != null && (expectedPacketSize == 0 || (expectedPacketSize > 0 && cacheBuffer.length >= expectedPacketSize))) {
      @@ -289,10 +303,11 @@ public class SyncLink implements Link {
           @Override
           public Element setSink(Element sink) {
               if (sink != null && this.sink != null)
      -            throw new RuntimeException("This link sink element is already set. Link: " + this + ", new sink: " + sink + ", existing sink: " + this.sink + ".");
      +            throw new RuntimeException("[" + this + "] ERROR: This link sink element is already set. Link: " + this + ", new sink: " + sink + ", existing sink: "
      +                    + this.sink + ".");
       
               if (sink == null && cacheBuffer != null)
      -            throw new RuntimeException("Cannot drop link: cache is not empty. Link: " + this + ", cache: " + cacheBuffer);
      +            throw new RuntimeException("[" + this + "] ERROR: Cannot drop link: cache is not empty. Link: " + this + ", cache: " + cacheBuffer);
       
               this.sink = sink;
       
      @@ -302,7 +317,8 @@ public class SyncLink implements Link {
           @Override
           public Element setSource(Element source) {
               if (this.source != null && source != null)
      -            throw new RuntimeException("This link source element is already set. Link: " + this + ", new source: " + source + ", existing source: " + this.source + ".");
      +            throw new RuntimeException("[" + this + "] ERROR: This link source element is already set. Link: " + this + ", new source: " + source
      +                    + ", existing source: " + this.source + ".");
       
               this.source = source;
               return source;
      @@ -321,7 +337,7 @@ public class SyncLink implements Link {
           @Override
           public void pause() {
               if (paused)
      -            throw new RuntimeException("Link is already paused.");
      +            throw new RuntimeException("[" + this + "] ERROR: Link is already paused.");
       
               paused = true;
       
      @@ -343,7 +359,7 @@ public class SyncLink implements Link {
           @Override
           public void run() {
               // Wait until even STREAM_START will arrive
      -        while (!start) {
      +        while (!started) {
                   delay();
               }
       
      @@ -374,8 +390,7 @@ public class SyncLink implements Link {
               try {
                   Thread.sleep(delay);
               } catch (InterruptedException e) {
      -            e.printStackTrace(System.err);
      -            throw new RuntimeException("Interrupted in main loop.", e);
      +            throw new RuntimeException("[" + this + "] ERROR: Interrupted in main loop.", e);
               }
           }
       
      @@ -384,16 +399,16 @@ public class SyncLink implements Link {
               if (verbose)
                   System.out.println("[" + this + "] INFO: Switching to PULL mode.");
       
      -        this.pullMode = true;
      +        pullMode = true;
           }
       
           @Override
           public void drop() {
               if (pullMode)
      -            throw new RuntimeException("Cannot drop link in pull mode.");
      +            throw new RuntimeException("[" + this + "] ERROR: Cannot drop link in pull mode.");
       
               if (cacheBuffer != null)
      -            throw new RuntimeException("Cannot drop link when cache conatains data: " + cacheBuffer + ".");
      +            throw new RuntimeException("[" + this + "] ERROR: Cannot drop link when cache conatains data: " + cacheBuffer + ".");
       
               source.dropLink(this);
               sink.dropLink(this);
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSink.java
      new file mode 100755
      index 00000000000..edfe8dbc752
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSink.java
      @@ -0,0 +1,129 @@
      +// 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 streamer.apr;
      +
      +import org.apache.tomcat.jni.Socket;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.DataSource;
      +import streamer.Direction;
      +import streamer.Event;
      +import streamer.Link;
      +
      +public class AprSocketSink extends BaseElement {
      +
      +    protected AprSocketWrapperImpl socketWrapper;
      +    protected Long socket;
      +
      +    public AprSocketSink(String id) {
      +        super(id);
      +    }
      +
      +    public AprSocketSink(String id, AprSocketWrapperImpl socketWrapper) {
      +        super(id);
      +        this.socketWrapper = socketWrapper;
      +    }
      +
      +    public void setSocket(long socket) {
      +        this.socket = socket;
      +
      +        // Resume links
      +        resumeLinks();
      +    }
      +
      +    /**
      +     * Send incoming data to stream.
      +     */
      +    @Override
      +    public void handleData(ByteBuffer buf, Link link) {
      +        if (buf == null)
      +            return;
      +
      +        if (socketWrapper.shutdown)
      +            return;
      +
      +        try {
      +            if (verbose)
      +                System.out.println("[" + this + "] INFO: Writing data to stream: " + buf + ".");
      +
      +            // FIXME: If pull is destroyed or socket is closed, segfault will happen here
      +            Socket.send(socket, buf.data, buf.offset, buf.length);
      +        } catch (Exception e) {
      +            System.err.println("[" + this + "] ERROR: " + e.getMessage());
      +            closeStream();
      +        }
      +    }
      +
      +    @Override
      +    public void handleEvent(Event event, Direction direction) {
      +        switch (event) {
      +        case SOCKET_UPGRADE_TO_SSL:
      +            socketWrapper.upgradeToSsl();
      +            break;
      +        case LINK_SWITCH_TO_PULL_MODE:
      +            throw new RuntimeException("[" + this + "] ERROR: Unexpected event: sink recived LINK_SWITCH_TO_PULL_MODE event.");
      +        default:
      +            super.handleEvent(event, direction);
      +        }
      +    }
      +
      +    @Override
      +    public void setLink(String padName, Link link, Direction direction) {
      +        switch (direction) {
      +        case IN:
      +            super.setLink(padName, link, direction);
      +
      +            if (socket == null)
      +                // Pause links until data stream will be ready
      +                link.pause();
      +            break;
      +        case OUT:
      +            throw new RuntimeException("Cannot assign link to output pad in sink element. Element: " + this + ", pad: " + padName + ", link: " + link + ".");
      +        }
      +    }
      +
      +    private void resumeLinks() {
      +        for (DataSource source : inputPads.values())
      +            ((Link)source).resume();
      +    }
      +
      +    @Override
      +    protected void onClose() {
      +        closeStream();
      +    }
      +
      +    private void closeStream() {
      +        if (socketWrapper.shutdown)
      +            return;
      +
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Closing stream.");
      +
      +        try {
      +            sendEventToAllPads(Event.STREAM_CLOSE, Direction.IN);
      +        } catch (Exception e) {
      +        }
      +        socketWrapper.shutdown();
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "AprSocketSink(" + id + ")";
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSource.java
      new file mode 100755
      index 00000000000..02983492e8d
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketSource.java
      @@ -0,0 +1,171 @@
      +// 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 streamer.apr;
      +
      +import org.apache.tomcat.jni.Socket;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.DataSink;
      +import streamer.Direction;
      +import streamer.Event;
      +import streamer.Link;
      +
      +/**
      + * Source element, which reads data from InputStream.
      + */
      +public class AprSocketSource extends BaseElement {
      +
      +    protected AprSocketWrapperImpl socketWrapper;
      +    protected Long socket;
      +
      +    public AprSocketSource(String id) {
      +        super(id);
      +    }
      +
      +    public AprSocketSource(String id, AprSocketWrapperImpl socketWrapper) {
      +        super(id);
      +        this.socketWrapper = socketWrapper;
      +    }
      +
      +    @Override
      +    public void handleEvent(Event event, Direction direction) {
      +        switch (event) {
      +        case SOCKET_UPGRADE_TO_SSL:
      +            socketWrapper.upgradeToSsl();
      +            break;
      +        case LINK_SWITCH_TO_PULL_MODE:
      +            // Do nothing - this is the source
      +            break;
      +        default:
      +            super.handleEvent(event, direction);
      +        }
      +    }
      +
      +    @Override
      +    public void setLink(String padName, Link link, Direction direction) {
      +        switch (direction) {
      +        case OUT:
      +            super.setLink(padName, link, direction);
      +
      +            if (socket == null) {
      +                // Pause links until data stream will be ready
      +                link.pause();
      +            }
      +            break;
      +        case IN:
      +            throw new RuntimeException("Cannot assign link to input pad in source element. Element: " + this + ", pad: " + padName + ", link: " + link + ".");
      +        }
      +    }
      +
      +    public void setSocket(long socket) {
      +        this.socket = socket;
      +
      +        // Resume links
      +        resumeLinks();
      +    }
      +
      +    private void resumeLinks() {
      +        for (DataSink sink : outputPads.values())
      +            ((Link)sink).resume();
      +    }
      +
      +    /**
      +     * Read data from input stream.
      +     */
      +    @Override
      +    public void poll(boolean block) {
      +        if (socketWrapper.shutdown) {
      +            socketWrapper.destroyPull();
      +            return;
      +        }
      +
      +        try {
      +            // Create buffer of recommended size and with default offset
      +            ByteBuffer buf = new ByteBuffer(incommingBufLength);
      +
      +            if (verbose)
      +                System.out.println("[" + this + "] INFO: Reading data from stream.");
      +
      +            // FIXME: If pull is destroyed or socket is closed, segfault will happen here
      +            int actualLength = (block) ? // Blocking read
      +                    Socket.recv(socket, buf.data, buf.offset, buf.data.length - buf.offset)
      +                    : // Non-blocking read
      +                        Socket.recvt(socket, buf.data, buf.offset, buf.data.length - buf.offset, 1);
      +
      +                    if (socketWrapper.shutdown) {
      +                        socketWrapper.destroyPull();
      +                        return;
      +                    }
      +
      +                    if (actualLength < 0) {
      +                        if (verbose)
      +                            System.out.println("[" + this + "] INFO: End of stream.");
      +
      +                        buf.unref();
      +                        closeStream();
      +                        sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
      +                        return;
      +                    }
      +
      +                    if (actualLength == 0) {
      +                        if (verbose)
      +                            System.out.println("[" + this + "] INFO: Empty buffer is read from stream.");
      +
      +                        buf.unref();
      +                        return;
      +                    }
      +
      +                    buf.length = actualLength;
      +
      +                    if (verbose)
      +                        System.out.println("[" + this + "] INFO: Data read from stream: " + buf + ".");
      +
      +                    pushDataToAllOuts(buf);
      +
      +        } catch (Exception e) {
      +            System.err.println("[" + this + "] ERROR: " + e.getMessage());
      +            closeStream();
      +        }
      +    }
      +
      +    @Override
      +    protected void onClose() {
      +        closeStream();
      +    }
      +
      +    private void closeStream() {
      +
      +        if (socketWrapper.shutdown)
      +            return;
      +
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Closing stream.");
      +
      +        try {
      +            sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT);
      +        } catch (Exception e) {
      +        }
      +        socketWrapper.shutdown();
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "AprSocketSource(" + id + ")";
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketWrapperImpl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketWrapperImpl.java
      new file mode 100755
      index 00000000000..2ee426b89c3
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/apr/AprSocketWrapperImpl.java
      @@ -0,0 +1,281 @@
      +// 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 streamer.apr;
      +
      +import static streamer.debug.MockServer.Packet.PacketType.CLIENT;
      +import static streamer.debug.MockServer.Packet.PacketType.SERVER;
      +
      +import java.io.IOException;
      +import java.net.InetSocketAddress;
      +import java.util.HashMap;
      +
      +import org.apache.tomcat.jni.Address;
      +import org.apache.tomcat.jni.Error;
      +import org.apache.tomcat.jni.Library;
      +import org.apache.tomcat.jni.Pool;
      +import org.apache.tomcat.jni.SSL;
      +import org.apache.tomcat.jni.SSLContext;
      +import org.apache.tomcat.jni.SSLSocket;
      +import org.apache.tomcat.jni.Socket;
      +
      +import streamer.BaseElement;
      +import streamer.Direction;
      +import streamer.Element;
      +import streamer.Event;
      +import streamer.Pipeline;
      +import streamer.PipelineImpl;
      +import streamer.Queue;
      +import streamer.SocketWrapper;
      +import streamer.debug.MockServer;
      +import streamer.debug.MockServer.Packet;
      +import streamer.ssl.SSLState;
      +import sun.security.x509.X509CertImpl;
      +
      +public class AprSocketWrapperImpl extends PipelineImpl implements SocketWrapper {
      +
      +    static {
      +        try {
      +            Library.initialize(null);
      +            SSL.initialize(null);
      +        } catch (Exception e) {
      +            throw new RuntimeException("Cannot load Tomcat Native Library (Apache Portable Runtime).", e);
      +        }
      +    }
      +
      +    private final SSLState sslState;
      +
      +    final long pool = Pool.create(0);
      +    long inetAddress;
      +    long socket;
      +    private AprSocketSource source;
      +    private AprSocketSink sink;
      +
      +    boolean shutdown = false;
      +
      +    private final boolean shutdowned = false;
      +
      +    public AprSocketWrapperImpl(String id, SSLState sslState) {
      +        super(id);
      +        this.sslState = sslState;
      +    }
      +
      +    @Override
      +    protected HashMap initElementMap(String id) {
      +        HashMap map = new HashMap();
      +
      +        source = new AprSocketSource(id + "." + OUT, this);
      +        sink = new AprSocketSink(id + "." + IN, this);
      +
      +        // Pass requests to read data to socket input stream
      +        map.put(OUT, source);
      +
      +        // All incoming data, which is sent to this socket wrapper, will be sent
      +        // to socket remote
      +        map.put(IN, sink);
      +
      +        return map;
      +    }
      +
      +    /**
      +     * Connect this socket wrapper to remote server and start main loop on
      +     * AprSocketSource stdout link, to watch for incoming data, and
      +     * AprSocketSink stdin link, to pull for outgoing data.
      +     */
      +    @Override
      +    public void connect(InetSocketAddress address) throws IOException {
      +        try {
      +            inetAddress = Address.info(address.getHostName(), Socket.APR_UNSPEC, address.getPort(), 0, pool);
      +            socket = Socket.create(Address.getInfo(inetAddress).family, Socket.SOCK_STREAM, Socket.APR_PROTO_TCP, pool);
      +        } catch (Exception e) {
      +            throw new IOException("[" + this + "] ERROR: Cannot create socket for \"" + address + "\".", e);
      +        }
      +
      +        int ret = Socket.connect(socket, inetAddress);
      +        if (ret != 0)
      +            throw new IOException("[" + this + "] ERROR: Cannot connect to remote host \"" + address + "\": " + Error.strerror(ret));
      +
      +        source.setSocket(socket);
      +        sink.setSocket(socket);
      +
      +        // Start polling for data to send to remote sever
      +        runMainLoop(IN, STDIN, true, true);
      +
      +        // Push incoming data from server to handlers
      +        runMainLoop(OUT, STDOUT, false, false);
      +
      +    }
      +
      +    @Override
      +    public void handleEvent(Event event, Direction direction) {
      +        switch (event) {
      +        case SOCKET_UPGRADE_TO_SSL:
      +            upgradeToSsl();
      +            break;
      +        default:
      +            super.handleEvent(event, direction);
      +            break;
      +        }
      +    }
      +
      +    @Override
      +    public void validate() {
      +
      +        if (getPads(Direction.IN).size() == 0)
      +            throw new RuntimeException("[ " + this + "] BUG: Input of socket is not connected.");
      +
      +        if (getPads(Direction.OUT).size() == 0)
      +            throw new RuntimeException("[ " + this + "] BUG: Output of socket is not connected.");
      +
      +    }
      +
      +    @Override
      +    public void upgradeToSsl() {
      +
      +        try {
      +            long sslContext;
      +            try {
      +                sslContext = SSLContext.make(pool, SSL.SSL_PROTOCOL_TLSV1, SSL.SSL_MODE_CLIENT);
      +            } catch (Exception e) {
      +                throw new RuntimeException("Cannot create SSL context using Tomcat native library.", e);
      +            }
      +
      +            SSLContext.setOptions(sslContext, SSL.SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS | SSL.SSL_OP_TLS_BLOCK_PADDING_BUG | SSL.SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
      +                    | SSL.SSL_OP_MSIE_SSLV2_RSA_PADDING);
      +            // FIXME: verify certificate by default
      +            SSLContext.setVerify(sslContext, SSL.SSL_CVERIFY_NONE, 0);
      +            int ret;
      +            try {
      +                ret = SSLSocket.attach(sslContext, socket);
      +            } catch (Exception e) {
      +                throw new RuntimeException("[" + this + "] ERROR: Cannot attach SSL context to socket: ", e);
      +            }
      +            if (ret != 0)
      +                throw new RuntimeException("[" + this + "] ERROR: Cannot attach SSL context to socket(" + ret + "): " + SSL.getLastError());
      +
      +            try {
      +                ret = SSLSocket.handshake(socket);
      +            } catch (Exception e) {
      +                throw new RuntimeException("[" + this + "] ERROR: Cannot make SSL handshake with server: ", e);
      +            }
      +            if (ret != 0 && ret != 20014) // 20014: bad certificate signature FIXME: show prompt for self signed certificate
      +                throw new RuntimeException("[" + this + "] ERROR: Cannot make SSL handshake with server(" + ret + "): " + SSL.getLastError());
      +
      +            try {
      +                byte[] key = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
      +                //*DEBUG*/System.out.println("DEBUG: Server cert:\n"+new ByteBuffer(key).dump());
      +                sslState.serverCertificateSubjectPublicKeyInfo = new X509CertImpl(key).getPublicKey().getEncoded();
      +            } catch (Exception e) {
      +                throw new RuntimeException("[" + this + "] ERROR: Cannot get server public key: ", e);
      +            }
      +
      +        } catch (RuntimeException e) {
      +            shutdown();
      +            throw e;
      +        }
      +    }
      +
      +    @Override
      +    public void shutdown() {
      +        if (shutdown)
      +            return;
      +
      +        shutdown = true;
      +
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.IN);
      +        } catch (Exception e) {
      +        }
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.OUT);
      +        } catch (Exception e) {
      +        }
      +    }
      +
      +    void destroyPull() {
      +        if (shutdowned)
      +            return;
      +
      +        // Causes segfault in AprSocketSource.poll() method, so this function must be called from it
      +        try {
      +            Socket.close(socket);
      +            // or
      +            // Socket.shutdown(socket, Socket.APR_SHUTDOWN_READWRITE);
      +            Pool.destroy(pool);
      +        } catch (Exception e) {
      +        }
      +
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "AprSocketWrapper(" + id + ")";
      +    }
      +
      +    /**
      +     * Example.
      +     */
      +    public static void main(String args[]) {
      +
      +        try {
      +            System.setProperty("streamer.Link.debug", "true");
      +            System.setProperty("streamer.Element.debug", "true");
      +            System.setProperty("rdpclient.MockServer.debug", "true");
      +
      +            Pipeline pipeline = new PipelineImpl("echo client");
      +
      +            AprSocketWrapperImpl socketWrapper = new AprSocketWrapperImpl("socket", null);
      +
      +            pipeline.add(socketWrapper);
      +            pipeline.add(new BaseElement("echo"));
      +            pipeline.add(new Queue("queue")); // To decouple input and output
      +
      +            pipeline.link("socket", "echo", "queue", "socket");
      +
      +            final byte[] mockData = new byte[] {0x01, 0x02, 0x03};
      +            MockServer server = new MockServer(new Packet[] {new Packet("Server hello") {
      +                {
      +                    type = SERVER;
      +                    data = mockData;
      +                }
      +            }, new Packet("Client hello") {
      +                {
      +                    type = CLIENT;
      +                    data = mockData;
      +                }
      +            }, new Packet("Server hello") {
      +                {
      +                    type = SERVER;
      +                    data = mockData;
      +                }
      +            }, new Packet("Client hello") {
      +                {
      +                    type = CLIENT;
      +                    data = mockData;
      +                }
      +            }});
      +            server.start();
      +            InetSocketAddress address = server.getAddress();
      +
      +            socketWrapper.connect(address);
      +
      +        } catch (Exception e) {
      +            e.printStackTrace(System.err);
      +        }
      +
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/bco/BcoSocketWrapperImpl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/bco/BcoSocketWrapperImpl.java
      new file mode 100755
      index 00000000000..67e2dbd019e
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/bco/BcoSocketWrapperImpl.java
      @@ -0,0 +1,119 @@
      +// 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 streamer.bco;
      +
      +import java.io.IOException;
      +import java.io.InputStream;
      +import java.io.OutputStream;
      +import java.security.SecureRandom;
      +import java.security.Security;
      +
      +import org.bouncycastle.asn1.x509.X509CertificateStructure;
      +import org.bouncycastle.crypto.tls.CertificateVerifyer;
      +import org.bouncycastle.crypto.tls.TlsProtocolHandler;
      +import org.bouncycastle.jce.provider.BouncyCastleProvider;
      +
      +import streamer.Direction;
      +import streamer.Event;
      +import streamer.SocketWrapperImpl;
      +import streamer.ssl.SSLState;
      +
      +@SuppressWarnings("deprecation")
      +public class BcoSocketWrapperImpl extends SocketWrapperImpl {
      +
      +    static {
      +        Security.addProvider(new BouncyCastleProvider());
      +    }
      +
      +    private TlsProtocolHandler bcoSslSocket;
      +
      +    public BcoSocketWrapperImpl(String id, SSLState sslState) {
      +        super(id, sslState);
      +    }
      +
      +    @Override
      +    public void upgradeToSsl() {
      +
      +        if (sslSocket != null)
      +            // Already upgraded
      +            return;
      +
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Upgrading socket to SSL.");
      +
      +        try {
      +
      +            SecureRandom secureRandom = new SecureRandom();
      +            bcoSslSocket = new TlsProtocolHandler(socket.getInputStream(), socket.getOutputStream(), secureRandom);
      +
      +            CertificateVerifyer client = new CertificateVerifyer() {
      +
      +                @Override
      +                public boolean isValid(X509CertificateStructure[] chain) {
      +
      +                    try {
      +                        if (sslState != null) {
      +                            sslState.serverCertificateSubjectPublicKeyInfo = chain[0].getSubjectPublicKeyInfo().getEncoded();
      +                        }
      +                    } catch (IOException e) {
      +                        throw new RuntimeException("Cannot get server public key.", e);
      +                    }
      +
      +                    return true;
      +                }
      +            };
      +            bcoSslSocket.connect(client);
      +
      +            InputStream sis = bcoSslSocket.getInputStream();
      +            source.setInputStream(sis);
      +
      +            OutputStream sos = bcoSslSocket.getOutputStream();
      +            sink.setOutputStream(sos);
      +
      +        } catch (Exception e) {
      +            throw new RuntimeException("Cannot upgrade socket to SSL: " + e.getMessage(), e);
      +        }
      +
      +    }
      +
      +    @Override
      +    public void shutdown() {
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.IN);
      +        } catch (Exception e) {
      +        }
      +        try {
      +            handleEvent(Event.STREAM_CLOSE, Direction.OUT);
      +        } catch (Exception e) {
      +        }
      +        try {
      +            if (bcoSslSocket != null)
      +                bcoSslSocket.close();
      +        } catch (Exception e) {
      +        }
      +        try {
      +            socket.close();
      +        } catch (Exception e) {
      +        }
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "BcoSocketWrapper(" + id + ")";
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/AssertingByteBuffer.java
      old mode 100644
      new mode 100755
      similarity index 98%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/AssertingByteBuffer.java
      index fe9cc50ad60..15449c315ba
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/AssertingByteBuffer.java
      @@ -14,10 +14,12 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package streamer;
      +package streamer.debug;
       
       import java.nio.charset.Charset;
       
      +import streamer.ByteBuffer;
      +
       /**
        * Assert that writes to this buffer are matching expected data.
        */
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/Dumper.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/Dumper.java
      new file mode 100755
      index 00000000000..c884cfbdfa7
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/Dumper.java
      @@ -0,0 +1,28 @@
      +// Licensed to the Apache Software Foundation (ASF) under one
      +// or more contributor license agreements.  See the NOTICE file
      +// distributed with this work for additional information
      +// regarding copyright ownership.  The ASF licenses this file
      +// to you under the Apache License, Version 2.0 (the
      +// "License"); you may not use this file except in compliance
      +// with the License.  You may obtain a copy of the License at
      +//
      +//   http://www.apache.org/licenses/LICENSE-2.0
      +//
      +// Unless required by applicable law or agreed to in writing,
      +// software distributed under the License is distributed on an
      +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
      +// KIND, either express or implied.  See the License for the
      +// specific language governing permissions and limitations
      +// under the License.
      +package streamer.debug;
      +
      +import streamer.ByteBuffer;
      +
      +public interface Dumper {
      +
      +    /**
      +     * Parse and dump content of buffer.
      +     */
      +    void dump(ByteBuffer buf);
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSink.java
      old mode 100644
      new mode 100755
      similarity index 90%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSink.java
      index e373b19a10c..93154886503
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSink.java
      @@ -14,7 +14,15 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package streamer;
      +package streamer.debug;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.Direction;
      +import streamer.Element;
      +import streamer.Event;
      +import streamer.Link;
      +import streamer.SyncLink;
       
       public class FakeSink extends BaseElement {
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSource.java
      old mode 100644
      new mode 100755
      similarity index 86%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSource.java
      index 9056852855b..7f0c554cab0
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/FakeSource.java
      @@ -14,7 +14,15 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package streamer;
      +package streamer.debug;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.Direction;
      +import streamer.Element;
      +import streamer.Event;
      +import streamer.Link;
      +import streamer.SyncLink;
       
       public class FakeSource extends BaseElement {
       
      @@ -40,7 +48,8 @@ public class FakeSource extends BaseElement {
               ByteBuffer buf = initializeData();
       
               // Push it to output(s)
      -        pushDataToAllOuts(buf);
      +        if (buf != null)
      +            pushDataToAllOuts(buf);
       
               // Make slight delay when blocking input was requested (to avoid
               // consuming of 100% in parent loop)
      @@ -85,26 +94,29 @@ public class FakeSource extends BaseElement {
               return "FakeSource(" + id + ")";
           }
       
      +    /**
      +     * Example.
      +     */
           public static void main(String args[]) {
       
               Element fakeSource = new FakeSource("source 3/10/100") {
                   {
                       verbose = true;
      -                this.incommingBufLength = 3;
      -                this.numBuffers = 10;
      -                this.delay = 100;
      +                incommingBufLength = 3;
      +                numBuffers = 10;
      +                delay = 100;
                   }
               };
       
               Element fakeSink = new FakeSink("sink") {
                   {
      -                this.verbose = true;
      +                verbose = true;
                   }
               };
       
               Element fakeSink2 = new FakeSink("sink2") {
                   {
      -                this.verbose = true;
      +                verbose = true;
                   }
               };
       
      @@ -118,6 +130,7 @@ public class FakeSource extends BaseElement {
               fakeSource.setLink("out2", link2, Direction.OUT);
               fakeSink2.setLink(STDIN, link2, Direction.IN);
       
      +        link.sendEvent(Event.STREAM_START, Direction.IN);
               link.run();
       
           }
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockServer.java
      old mode 100644
      new mode 100755
      similarity index 57%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockServer.java
      index 72e4a6f182c..348a006c1aa
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockServer.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package rdpclient;
      +package streamer.debug;
       
       import java.io.IOException;
       import java.io.InputStream;
      @@ -51,7 +51,9 @@ public class MockServer implements Runnable {
               exception = null;
               shutdowned = false;
       
      -        new Thread(this).start();
      +        Thread thread = new Thread(this);
      +        thread.setDaemon(true);
      +        thread.start();
           }
       
           @Override
      @@ -60,6 +62,9 @@ public class MockServer implements Runnable {
               try {
                   Socket socket = serverSocket.accept();
       
      +            if (verbose)
      +                System.out.println("[" + this + "] INFO: Clien connected: " + socket.getRemoteSocketAddress() + ".");
      +
                   InputStream is = socket.getInputStream();
                   OutputStream os = socket.getOutputStream();
       
      @@ -68,58 +73,58 @@ public class MockServer implements Runnable {
       
                           Packet packet = packets[i];
                           switch (packet.type) {
      -                        case CLIENT: {
      -                            // Read client data and compare it with mock data
      -                            // (unless "ignore" option is set)
      -                            byte actualData[] = new byte[packet.data.length];
      -                            int actualDataLength = is.read(actualData);
      +                    case CLIENT: {
      +                        // Read client data and compare it with mock data
      +                        // (unless "ignore" option is set)
      +                        byte actualData[] = new byte[packet.data.length];
      +                        int actualDataLength = is.read(actualData);
       
      -                            if (verbose)
      -                                System.out.println("[" + this + "] INFO: Data is read: {" + Arrays.toString(Arrays.copyOf(actualData, actualDataLength)) + "}.");
      +                        if (verbose)
      +                            System.out.println("[" + this + "] INFO: Data is read: {" + Arrays.toString(Arrays.copyOf(actualData, actualDataLength)) + "}.");
       
      -                            if (!packet.ignore) {
      -                                // Compare actual data with expected data
      -                                if (actualDataLength != packet.data.length) {
      -                                    throw new AssertionError("Actual length of client request for packet #" + (i + 1) + " (\"" + packet.id + "\")" +
      -                                        " does not match length of expected client request. Actual length: " + actualDataLength + ", expected legnth: " +
      -                                        packet.data.length + ".");
      -                                }
      +                        if (!packet.ignore) {
      +                            // Compare actual data with expected data
      +                            if (actualDataLength != packet.data.length) {
      +                                throw new AssertionError("Actual length of client request for packet #" + (i + 1) + " (\"" + packet.id + "\")"
      +                                        + " does not match length of expected client request. Actual length: " + actualDataLength + ", expected legnth: " + packet.data.length
      +                                        + ".");
      +                            }
       
      -                                for (int j = 0; j < packet.data.length; j++) {
      +                            for (int j = 0; j < packet.data.length; j++) {
       
      -                                    if (packet.data[j] != actualData[j]) {
      -                                        throw new AssertionError("Actual byte #" + (j + 1) + " of client request for packet #" + (i + 1) + " (\"" + packet.id + "\")" +
      -                                            " does not match corresponding byte of expected client request. Actual byte: " + actualData[j] + ", expected byte: " +
      -                                            packet.data[j] + ".");
      -                                    }
      +                                if (packet.data[j] != actualData[j]) {
      +                                    throw new AssertionError("Actual byte #" + (j + 1) + " of client request for packet #" + (i + 1) + " (\"" + packet.id + "\")"
      +                                            + " does not match corresponding byte of expected client request. Actual byte: " + actualData[j] + ", expected byte: " + packet.data[j]
      +                                                    + ".");
                                       }
                                   }
      -                            break;
                               }
      -                        case SERVER: {
      -                            // Send mock data to client
      -                            os.write(packet.data);
      +                        break;
      +                    }
      +                    case SERVER: {
      +                        // Send mock data to client
      +                        os.write(packet.data);
       
      -                            if (verbose)
      -                                System.out.println("[" + this + "] INFO: Data is written: {" + Arrays.toString(packet.data) + "}.");
      +                        if (verbose)
      +                            System.out.println("[" + this + "] INFO: Data is written: {" + Arrays.toString(packet.data) + "}.");
       
      -                            break;
      -                        }
      -                        case UPGRADE_TO_SSL: {
      -                            // Attach SSL context to socket
      +                        break;
      +                    }
      +                    case UPGRADE_TO_SSL: {
      +                        // Attach SSL context to socket
       
      -                            final SSLSocketFactory sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
      -                            SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(socket, null, serverSocket.getLocalPort(), true);
      -                            sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
      -                            sslSocket.setUseClientMode(false);
      -                            sslSocket.startHandshake();
      -                            is = sslSocket.getInputStream();
      -                            os = sslSocket.getOutputStream();
      +                        final SSLSocketFactory sslSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
      +                        SSLSocket sslSocket = (SSLSocket)sslSocketFactory.createSocket(socket, null, serverSocket.getLocalPort(), true);
      +                        sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites());
      +                        sslSocket.setUseClientMode(false);
      +                        sslSocket.startHandshake();
      +                        is = sslSocket.getInputStream();
      +                        os = sslSocket.getOutputStream();
       
      -                            break;
      -                        }
      -                        default:
      -                            throw new RuntimeException("Unknown packet type: " + packet.type);
      +                        break;
      +                    }
      +                    default:
      +                        throw new RuntimeException("Unknown packet type: " + packet.type);
                           }
       
                       }
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSink.java
      new file mode 100755
      index 00000000000..fe038a96454
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSink.java
      @@ -0,0 +1,154 @@
      +// 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 streamer.debug;
      +
      +import java.util.Arrays;
      +import java.util.Set;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.Direction;
      +import streamer.Element;
      +import streamer.Link;
      +import streamer.SyncLink;
      +
      +/**
      + * Compare incoming packets with expected packets.
      + */
      +public class MockSink extends BaseElement {
      +
      +    protected ByteBuffer bufs[];
      +    protected Dumper dumper;
      +
      +    public MockSink(String id) {
      +        super(id);
      +    }
      +
      +    public MockSink(String id, ByteBuffer bufs[]) {
      +        super(id);
      +        this.bufs = bufs;
      +    }
      +
      +    public MockSink(String id, ByteBuffer bufs[], Dumper dumper) {
      +        super(id);
      +        this.bufs = bufs;
      +        this.dumper = dumper;
      +    }
      +
      +    @Override
      +    public void handleData(ByteBuffer buf, Link link) {
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Received buf #" + (packetNumber) + " " + buf + ".");
      +
      +        if (buf == null)
      +            return;
      +
      +        if (packetNumber >= bufs.length)
      +            throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not expected. Number of expected buffers: " + bufs.length
      +                    + ", unexpected buffer: " + buf + ".");
      +
      +        // Compare incoming buffer with expected buffer
      +        ByteBuffer expectedBuf = bufs[packetNumber];
      +        if (!Arrays.equals(expectedBuf.toByteArray(), buf.toByteArray())) {
      +            dump(buf, expectedBuf);
      +            throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not equal to expected buffer.\n  Actual bufer: " + buf
      +                    + ",\n  expected buffer: " + expectedBuf + ".");
      +        }
      +
      +        // If expected buffer has metadata, then compare it too
      +        Set metadataKeys = expectedBuf.getMetadataKeys();
      +        if (metadataKeys.size() > 0) {
      +            for (String key : metadataKeys) {
      +                Object expectedValue = expectedBuf.getMetadata(key);
      +                Object actualValue = buf.getMetadata(key);
      +                if (actualValue == null)
      +                    throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not equal to expected buffer in metadata for key \"" + key
      +                            + "\".\n  Actual metadata value: " + actualValue + ",\n  expected value: \"" + expectedValue + "\".");
      +
      +                if (!expectedValue.equals(actualValue))
      +                    throw new AssertionError("[" + this + "] Incoming buffer #" + packetNumber + " is not equal to expected buffer in metadata for key \"" + key
      +                            + "\".\n  Actual metadata value: \"" + actualValue + "\",\n  expected value: \"" + expectedValue + "\".");
      +            }
      +        }
      +
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: buffers are equal.");
      +
      +        // Use packetNumber variable to count incoming packets
      +        packetNumber++;
      +
      +        buf.unref();
      +    }
      +
      +    private void dump(ByteBuffer actualData, ByteBuffer expectedData) {
      +        if (dumper != null) {
      +            System.out.println("[" + this + "] INFO: Actual data:");
      +            dumper.dump(actualData);
      +            System.out.println("[" + this + "] INFO: Expected data:");
      +            dumper.dump(expectedData);
      +        }
      +    }
      +
      +    @Override
      +    protected void onClose() {
      +        super.onClose();
      +
      +        if (packetNumber != bufs.length)
      +            throw new AssertionError("[" + this + "] Number of expected buffers: " + bufs.length + ", number of actual buffers: " + packetNumber + ".");
      +    }
      +
      +    @Override
      +    public String toString() {
      +        return "MockSink(" + id + ")";
      +    }
      +
      +    /**
      +     * Example.
      +     */
      +    public static void main(String args[]) {
      +
      +        Element mockSource = new MockSource("source") {
      +            {
      +                bufs = new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}),
      +                        new ByteBuffer(new byte[] {3, 1, 2, 3}), new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})};
      +                verbose = true;
      +                delay = 100;
      +                numBuffers = bufs.length;
      +            }
      +        };
      +
      +        Element mockSink = new MockSink("sink") {
      +            {
      +                bufs = new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}),
      +                        new ByteBuffer(new byte[] {3, 1, 2, 3}), new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})};
      +                verbose = true;
      +            }
      +        };
      +
      +        Link link = new SyncLink() {
      +            {
      +                verbose = true;
      +            }
      +        };
      +
      +        mockSource.setLink(STDOUT, link, Direction.OUT);
      +        mockSink.setLink(STDIN, link, Direction.IN);
      +
      +        link.run();
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSource.java
      old mode 100644
      new mode 100755
      similarity index 80%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSource.java
      index f758bab6aff..a77f1d4a84b
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/debug/MockSource.java
      @@ -14,7 +14,14 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package streamer;
      +package streamer.debug;
      +
      +import streamer.ByteBuffer;
      +import streamer.Direction;
      +import streamer.Element;
      +import streamer.Event;
      +import streamer.Link;
      +import streamer.SyncLink;
       
       public class MockSource extends FakeSource {
       
      @@ -64,18 +71,17 @@ public class MockSource extends FakeSource {
       
               Element mockSource = new MockSource("source") {
                   {
      -                this.bufs =
      -                    new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}), new ByteBuffer(new byte[] {3, 1, 2, 3}),
      -                        new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})};
      -                this.verbose = true;
      -                this.delay = 100;
      +                bufs = new ByteBuffer[] {new ByteBuffer(new byte[] {1, 1, 2, 3, 4, 5}), new ByteBuffer(new byte[] {2, 1, 2, 3, 4}),
      +                        new ByteBuffer(new byte[] {3, 1, 2, 3}), new ByteBuffer(new byte[] {4, 1, 2}), new ByteBuffer(new byte[] {5, 1})};
      +                verbose = true;
      +                delay = 100;
                       // this.numBuffers = this.bufs.length;
                   }
               };
       
               Element fakeSink = new FakeSink("sink") {
                   {
      -                this.verbose = true;
      +                verbose = true;
                   }
               };
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/SSLState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/SSLState.java
      new file mode 100755
      index 00000000000..f4050887329
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/SSLState.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 streamer.ssl;
      +
      +public class SSLState {
      +
      +    /**
      +     * Server public certificate in ASN.1 BER format.
      +     */
      +    public byte[] serverCertificateSubjectPublicKeyInfo;
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/TrustAllX509TrustManager.java
      old mode 100644
      new mode 100755
      similarity index 77%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/TrustAllX509TrustManager.java
      index 088094a38e6..4d9eac27d6a
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/TrustAllX509TrustManager.java
      @@ -14,21 +14,33 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package rdpclient;
      +package streamer.ssl;
       
       import java.security.cert.X509Certificate;
       
       import javax.net.ssl.X509TrustManager;
       
       public class TrustAllX509TrustManager implements X509TrustManager {
      +    private SSLState sslState;
      +
      +    public TrustAllX509TrustManager(SSLState sslState) {
      +        this.sslState = sslState;
      +    }
      +
           @Override
           public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
               // TODO: ask user to confirm self-signed certificates
      +        // Trust all (insecure)
           }
       
           @Override
           public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
               // TODO: ask user to confirm self-signed certificates
      +        // Trust all (insecure)
      +
      +        // Store public certificates to use for NTLMSSP negotiation
      +        if (sslState != null)
      +            sslState.serverCertificateSubjectPublicKeyInfo = chain[0].getPublicKey().getEncoded();
           }
       
           @Override
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/UpgradeSocketToSSL.java
      old mode 100644
      new mode 100755
      similarity index 98%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/UpgradeSocketToSSL.java
      index 72677081859..9d7c7087492
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ssl/UpgradeSocketToSSL.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package rdpclient;
      +package streamer.ssl;
       
       import streamer.ByteBuffer;
       import streamer.Direction;
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java
      deleted file mode 100644
      index a804e262e59..00000000000
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java
      +++ /dev/null
      @@ -1,368 +0,0 @@
      -// 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 vncclient;
      -
      -import java.awt.event.KeyEvent;
      -
      -import streamer.BaseElement;
      -import streamer.ByteBuffer;
      -import streamer.Link;
      -import common.KeyOrder;
      -
      -public class AwtKeyboardEventToVncAdapter extends BaseElement {
      -
      -    protected boolean sh = false;
      -    protected boolean caps = false;
      -    protected boolean num = false;
      -
      -    public AwtKeyboardEventToVncAdapter(String id) {
      -        super(id);
      -    }
      -
      -    @Override
      -    public void handleData(ByteBuffer buf, Link link) {
      -        if (verbose)
      -            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
      -
      -        KeyOrder order = (KeyOrder)buf.getOrder();
      -        buf.unref();
      -
      -        ByteBuffer outBuf = new ByteBuffer(8);
      -        outBuf.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT);
      -
      -        outBuf.writeByte((order.pressed) ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP);
      -        outBuf.writeShort(0); // padding
      -        outBuf.writeInt(map_en_us(order));
      -
      -        pushDataToAllOuts(outBuf);
      -    }
      -
      -    /**
      -     * Return key scan code (in lower byte) and extended flags (in second byte).
      -     */
      -    private int map_en_us(KeyOrder order) {
      -
      -        switch (order.event.getKeyCode()) {
      -        // Functional keys
      -            case KeyEvent.VK_ESCAPE:
      -                return 0xff1b;
      -            case KeyEvent.VK_F1:
      -                return 0xffbe;
      -            case KeyEvent.VK_F2:
      -                return 0xffbf;
      -            case KeyEvent.VK_F3:
      -                return 0xffc0;
      -            case KeyEvent.VK_F4:
      -                return 0xffc1;
      -            case KeyEvent.VK_F5:
      -                return 0xffc2;
      -            case KeyEvent.VK_F6:
      -                return 0xffc3;
      -            case KeyEvent.VK_F7:
      -                return 0xffc4;
      -            case KeyEvent.VK_F8:
      -                return 0xffc5;
      -            case KeyEvent.VK_F9:
      -                return 0xffc6;
      -            case KeyEvent.VK_F10:
      -                return 0xffc7;
      -            case KeyEvent.VK_F11:
      -                return 0xffc8;
      -            case KeyEvent.VK_F12:
      -                return 0xffc9;
      -
      -                // Row #1
      -            case KeyEvent.VK_BACK_QUOTE:
      -                return (sh) ? '~' : '`';
      -            case KeyEvent.VK_1:
      -                return (sh) ? '!' : '1';
      -            case KeyEvent.VK_2:
      -                return (sh) ? '@' : '2';
      -            case KeyEvent.VK_3:
      -                return (sh) ? '#' : '3';
      -            case KeyEvent.VK_4:
      -                return (sh) ? '$' : '4';
      -            case KeyEvent.VK_5:
      -                return (sh) ? '%' : '5';
      -            case KeyEvent.VK_6:
      -                return (sh) ? '^' : '6';
      -            case KeyEvent.VK_7:
      -                return (sh) ? '&' : '7';
      -            case KeyEvent.VK_8:
      -                return (sh) ? '*' : '8';
      -            case KeyEvent.VK_9:
      -                return (sh) ? '(' : '9';
      -            case KeyEvent.VK_0:
      -                return (sh) ? ')' : '0';
      -            case KeyEvent.VK_MINUS:
      -                return (sh) ? '_' : '-';
      -            case KeyEvent.VK_EQUALS:
      -                return (sh) ? '+' : '=';
      -            case KeyEvent.VK_BACK_SPACE:
      -                return 0xff08;
      -
      -                // Row #2
      -            case KeyEvent.VK_TAB:
      -                return 0xff09;
      -            case KeyEvent.VK_Q:
      -                return (sh ^ caps) ? 'Q' : 'q';
      -            case KeyEvent.VK_W:
      -                return (sh ^ caps) ? 'W' : 'w';
      -            case KeyEvent.VK_E:
      -                return (sh ^ caps) ? 'E' : 'e';
      -            case KeyEvent.VK_R:
      -                return (sh ^ caps) ? 'R' : 'r';
      -            case KeyEvent.VK_T:
      -                return (sh ^ caps) ? 'T' : 't';
      -            case KeyEvent.VK_Y:
      -                return (sh ^ caps) ? 'Y' : 'y';
      -            case KeyEvent.VK_U:
      -                return (sh ^ caps) ? 'U' : 'u';
      -            case KeyEvent.VK_I:
      -                return (sh ^ caps) ? 'I' : 'i';
      -            case KeyEvent.VK_O:
      -                return (sh ^ caps) ? 'O' : 'o';
      -            case KeyEvent.VK_P:
      -                return (sh ^ caps) ? 'P' : 'p';
      -            case KeyEvent.VK_OPEN_BRACKET:
      -                return (sh) ? '{' : '[';
      -            case KeyEvent.VK_CLOSE_BRACKET:
      -                return (sh) ? '{' : ']';
      -            case KeyEvent.VK_ENTER:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_STANDARD:
      -                        return 0xff0d;
      -                    case KeyEvent.KEY_LOCATION_NUMPAD:
      -                        return 0xff8d;
      -                }
      -
      -                // Row #3
      -            case KeyEvent.VK_CAPS_LOCK:
      -                if (order.pressed)
      -                    caps = !caps;
      -                return 0xFFE5;
      -            case KeyEvent.VK_A:
      -                return (sh ^ caps) ? 'A' : 'a';
      -            case KeyEvent.VK_S:
      -                return (sh ^ caps) ? 'S' : 's';
      -            case KeyEvent.VK_D:
      -                return (sh ^ caps) ? 'D' : 'd';
      -            case KeyEvent.VK_F:
      -                return (sh ^ caps) ? 'F' : 'f';
      -            case KeyEvent.VK_G:
      -                return (sh ^ caps) ? 'G' : 'g';
      -            case KeyEvent.VK_H:
      -                return (sh ^ caps) ? 'H' : 'h';
      -            case KeyEvent.VK_J:
      -                return (sh ^ caps) ? 'J' : 'j';
      -            case KeyEvent.VK_K:
      -                return (sh ^ caps) ? 'K' : 'k';
      -            case KeyEvent.VK_L:
      -                return (sh ^ caps) ? 'L' : 'l';
      -            case KeyEvent.VK_SEMICOLON:
      -                return (sh) ? ':' : ';';
      -            case KeyEvent.VK_QUOTE:
      -                return (sh) ? '"' : '\'';
      -
      -                // Row #4
      -            case KeyEvent.VK_SHIFT:
      -                sh = !sh;
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xffe1;
      -                    case KeyEvent.KEY_LOCATION_RIGHT:
      -                        return 0xffe2;
      -                }
      -            case KeyEvent.VK_BACK_SLASH:
      -                return (sh) ? '|' : '\\';
      -            case KeyEvent.VK_Z:
      -                return (sh ^ caps) ? 'Z' : 'z';
      -            case KeyEvent.VK_X:
      -                return (sh ^ caps) ? 'X' : 'x';
      -            case KeyEvent.VK_C:
      -                return (sh ^ caps) ? 'C' : 'c';
      -            case KeyEvent.VK_V:
      -                return (sh ^ caps) ? 'V' : 'v';
      -            case KeyEvent.VK_B:
      -                return (sh ^ caps) ? 'B' : 'b';
      -            case KeyEvent.VK_N:
      -                return (sh ^ caps) ? 'N' : 'n';
      -            case KeyEvent.VK_M:
      -                return (sh ^ caps) ? 'M' : 'M';
      -            case KeyEvent.VK_COMMA:
      -                return (sh) ? '<' : ',';
      -            case KeyEvent.VK_PERIOD:
      -                return (sh) ? '>' : '.';
      -            case KeyEvent.VK_SLASH:
      -                return (sh) ? '?' : '/';
      -
      -                //
      -                // Bottom row
      -            case KeyEvent.VK_CONTROL:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xFFE3;
      -                    case KeyEvent.KEY_LOCATION_RIGHT:
      -                        return 0xFFE4;
      -                }
      -            case KeyEvent.VK_WINDOWS:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xFFED; // HyperL
      -                    case KeyEvent.KEY_LOCATION_RIGHT:
      -                        return 0xFFEE; // HyperR
      -                }
      -            case KeyEvent.VK_META:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xFFE7; // MetaL
      -                    case KeyEvent.KEY_LOCATION_RIGHT:
      -                        return 0xFFE8; // MetaR
      -                }
      -
      -            case KeyEvent.VK_ALT:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xFFE9;
      -                    case KeyEvent.KEY_LOCATION_RIGHT:
      -                        return 0xFFEA;
      -                }
      -            case KeyEvent.VK_ALT_GRAPH:
      -                return 0xfe03;
      -
      -            case KeyEvent.VK_SPACE:
      -                return ' ';
      -
      -            case KeyEvent.VK_CONTEXT_MENU:
      -                return 0xff67;
      -
      -                //
      -                // Special keys
      -            case KeyEvent.VK_PRINTSCREEN:
      -                return (sh) ? 0xFF15/* SysRq */: 0xFF61 /* Print */;
      -            case KeyEvent.VK_SCROLL_LOCK:
      -                return 0xFF14;
      -            case KeyEvent.VK_PAUSE:
      -                return (sh) ? 0xFF6B/* Break */: 0xFF13/* Pause */;
      -
      -                // Text navigation keys
      -            case KeyEvent.VK_INSERT:
      -                return 0xff63;
      -            case KeyEvent.VK_DELETE:
      -                return 0xffff;
      -            case KeyEvent.VK_HOME:
      -                return 0xff50;
      -            case KeyEvent.VK_END:
      -                return 0xff57;
      -            case KeyEvent.VK_PAGE_UP:
      -                return 0xff55;
      -            case KeyEvent.VK_PAGE_DOWN:
      -                return 0xff56;
      -
      -                // Cursor keys
      -            case KeyEvent.VK_LEFT:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xff51;
      -                    case KeyEvent.KEY_LOCATION_NUMPAD:
      -                        return 0xFF96;
      -                }
      -            case KeyEvent.VK_UP:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xff52;
      -                    case KeyEvent.KEY_LOCATION_NUMPAD:
      -                        return 0xFF97;
      -                }
      -            case KeyEvent.VK_RIGHT:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xff53;
      -                    case KeyEvent.KEY_LOCATION_NUMPAD:
      -                        return 0xFF98;
      -                }
      -            case KeyEvent.VK_DOWN:
      -                switch (order.event.getKeyLocation()) {
      -                    default:
      -                    case KeyEvent.KEY_LOCATION_LEFT:
      -                        return 0xff54;
      -                    case KeyEvent.KEY_LOCATION_NUMPAD:
      -                        return 0xFF99;
      -                }
      -
      -                // Keypad
      -            case KeyEvent.VK_NUM_LOCK:
      -                if (order.pressed)
      -                    num = !num;
      -                return 0xFF6F;
      -            case KeyEvent.VK_DIVIDE:
      -                return 0xFFAF;
      -            case KeyEvent.VK_MULTIPLY:
      -                return 0xFFAA;
      -            case KeyEvent.VK_SUBTRACT:
      -                return 0xFFAD;
      -            case KeyEvent.VK_ADD:
      -                return 0xFFAB;
      -
      -            case KeyEvent.VK_KP_LEFT:
      -                return 0xFF96;
      -            case KeyEvent.VK_KP_UP:
      -                return 0xFF97;
      -            case KeyEvent.VK_KP_RIGHT:
      -                return 0xFF98;
      -            case KeyEvent.VK_KP_DOWN:
      -                return 0xFF99;
      -
      -            case KeyEvent.VK_NUMPAD0:
      -                return 0xFFB0;
      -            case KeyEvent.VK_NUMPAD1:
      -                return 0xFFB1;
      -            case KeyEvent.VK_NUMPAD2:
      -                return 0xFFB2;
      -            case KeyEvent.VK_NUMPAD3:
      -                return 0xFFB3;
      -            case KeyEvent.VK_NUMPAD4:
      -                return 0xFFB4;
      -            case KeyEvent.VK_NUMPAD5:
      -                return 0xFFB5;
      -            case KeyEvent.VK_NUMPAD6:
      -                return 0xFFB6;
      -            case KeyEvent.VK_NUMPAD7:
      -                return 0xFFB7;
      -            case KeyEvent.VK_NUMPAD8:
      -                return 0xFFB8;
      -            case KeyEvent.VK_NUMPAD9:
      -                return 0xFFB9;
      -            case KeyEvent.VK_DECIMAL:
      -                return 0xFFAE;
      -
      -            default:
      -                System.err.println("Key is not mapped: " + order + ".");
      -                return ' '; // Space
      -        }
      -    }
      -
      -}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
      old mode 100644
      new mode 100755
      index c035b52fc4e..1c76656e0d9
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java
      @@ -18,13 +18,23 @@ package vncclient;
       
       import streamer.PipelineImpl;
       import streamer.Queue;
      -import common.AwtBellAdapter;
      -import common.AwtCanvasAdapter;
      -import common.AwtClipboardAdapter;
      +import vncclient.adapter.AwtVncKeyboardAdapter;
      +import vncclient.adapter.AwtVncMouseAdapter;
      +import vncclient.vnc.EncodingsMessage;
      +import vncclient.vnc.FrameBufferUpdateRequest;
      +import vncclient.vnc.RGB888LE32PixelFormatRequest;
      +import vncclient.vnc.RfbConstants;
      +import vncclient.vnc.VncInitializer;
      +import vncclient.vnc.VncMessageHandler;
      +import vncclient.vnc.Vnc33Authentication;
      +import vncclient.vnc.Vnc33Hello;
       import common.AwtKeyEventSource;
       import common.AwtMouseEventSource;
       import common.BufferedImageCanvas;
       import common.ScreenDescription;
      +import common.adapter.AwtBellAdapter;
      +import common.adapter.AwtCanvasAdapter;
      +import common.adapter.AwtClipboardAdapter;
       
       public class VncClient extends PipelineImpl {
       
      @@ -44,41 +54,41 @@ public class VncClient extends PipelineImpl {
               canvas.addKeyListener(keyEventSource);
       
               add(
      -            // Handshake
      +                // Handshake
       
      -            // RFB protocol version exchanger
      -            new Vnc33Hello("hello"),
      -            // Authenticator
      -            new Vnc33Authentication("auth", password),
      -            // Initializer
      -            new VncInitializer("init", true, screen),
      +                // RFB protocol version exchanger
      +                new Vnc33Hello("hello"),
      +                // Authenticator
      +                new Vnc33Authentication("auth", password),
      +                // Initializer
      +                new VncInitializer("init", true, screen),
       
      -            new EncodingsMessage("encodings", RfbConstants.SUPPORTED_ENCODINGS_ARRAY),
      +                new EncodingsMessage("encodings", RfbConstants.SUPPORTED_ENCODINGS_ARRAY),
       
      -            new RGB888LE32PixelFormatRequest("pixel_format", screen),
      +                new RGB888LE32PixelFormatRequest("pixel_format", screen),
       
      -            // Main
      +                // Main
       
      -            // Packet receiver
      -            new VncMessageHandler("message_handler", screen),
      +                // Packet receiver
      +                new VncMessageHandler("message_handler", screen),
       
      -            new AwtBellAdapter("bell"),
      +                new AwtBellAdapter("bell"),
       
      -            new AwtClipboardAdapter("clipboard"),
      +                new AwtClipboardAdapter("clipboard"),
       
      -            new AwtCanvasAdapter("pixels", canvas, screen),
      +                new AwtCanvasAdapter("pixels", canvas, screen),
       
      -            new Queue("queue"),
      +                new Queue("queue"),
       
      -            new FrameBufferUpdateRequest("fbur", screen),
      +                new FrameBufferUpdateRequest("fbur", screen),
       
      -            new AwtKeyboardEventToVncAdapter("keyboard_adapter"),
      +                new AwtVncKeyboardAdapter("keyboard_adapter"),
       
      -            new AwtMouseEventToVncAdapter("mouse_adapter"),
      +                new AwtVncMouseAdapter("mouse_adapter"),
       
      -            mouseEventSource, keyEventSource
      +                mouseEventSource, keyEventSource
       
      -        );
      +                );
       
               // Link handshake elements
               link("IN", "hello", "auth", "init", "message_handler");
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncKeyboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncKeyboardAdapter.java
      new file mode 100755
      index 00000000000..6b50e35eafd
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncKeyboardAdapter.java
      @@ -0,0 +1,369 @@
      +// 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 vncclient.adapter;
      +
      +import java.awt.event.KeyEvent;
      +
      +import streamer.BaseElement;
      +import streamer.ByteBuffer;
      +import streamer.Link;
      +import vncclient.vnc.RfbConstants;
      +import common.KeyOrder;
      +
      +public class AwtVncKeyboardAdapter extends BaseElement {
      +
      +    protected boolean sh = false;
      +    protected boolean caps = false;
      +    protected boolean num = false;
      +
      +    public AwtVncKeyboardAdapter(String id) {
      +        super(id);
      +    }
      +
      +    @Override
      +    public void handleData(ByteBuffer buf, Link link) {
      +        if (verbose)
      +            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
      +
      +        KeyOrder order = (KeyOrder)buf.getOrder();
      +        buf.unref();
      +
      +        ByteBuffer outBuf = new ByteBuffer(8);
      +        outBuf.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT);
      +
      +        outBuf.writeByte((order.pressed) ? RfbConstants.KEY_DOWN : RfbConstants.KEY_UP);
      +        outBuf.writeShort(0); // padding
      +        outBuf.writeInt(map_en_us(order));
      +
      +        pushDataToAllOuts(outBuf);
      +    }
      +
      +    /**
      +     * Return key scan code (in lower byte) and extended flags (in second byte).
      +     */
      +    private int map_en_us(KeyOrder order) {
      +
      +        switch (order.event.getKeyCode()) {
      +        // Functional keys
      +        case KeyEvent.VK_ESCAPE:
      +            return 0xff1b;
      +        case KeyEvent.VK_F1:
      +            return 0xffbe;
      +        case KeyEvent.VK_F2:
      +            return 0xffbf;
      +        case KeyEvent.VK_F3:
      +            return 0xffc0;
      +        case KeyEvent.VK_F4:
      +            return 0xffc1;
      +        case KeyEvent.VK_F5:
      +            return 0xffc2;
      +        case KeyEvent.VK_F6:
      +            return 0xffc3;
      +        case KeyEvent.VK_F7:
      +            return 0xffc4;
      +        case KeyEvent.VK_F8:
      +            return 0xffc5;
      +        case KeyEvent.VK_F9:
      +            return 0xffc6;
      +        case KeyEvent.VK_F10:
      +            return 0xffc7;
      +        case KeyEvent.VK_F11:
      +            return 0xffc8;
      +        case KeyEvent.VK_F12:
      +            return 0xffc9;
      +
      +            // Row #1
      +        case KeyEvent.VK_BACK_QUOTE:
      +            return (sh) ? '~' : '`';
      +        case KeyEvent.VK_1:
      +            return (sh) ? '!' : '1';
      +        case KeyEvent.VK_2:
      +            return (sh) ? '@' : '2';
      +        case KeyEvent.VK_3:
      +            return (sh) ? '#' : '3';
      +        case KeyEvent.VK_4:
      +            return (sh) ? '$' : '4';
      +        case KeyEvent.VK_5:
      +            return (sh) ? '%' : '5';
      +        case KeyEvent.VK_6:
      +            return (sh) ? '^' : '6';
      +        case KeyEvent.VK_7:
      +            return (sh) ? '&' : '7';
      +        case KeyEvent.VK_8:
      +            return (sh) ? '*' : '8';
      +        case KeyEvent.VK_9:
      +            return (sh) ? '(' : '9';
      +        case KeyEvent.VK_0:
      +            return (sh) ? ')' : '0';
      +        case KeyEvent.VK_MINUS:
      +            return (sh) ? '_' : '-';
      +        case KeyEvent.VK_EQUALS:
      +            return (sh) ? '+' : '=';
      +        case KeyEvent.VK_BACK_SPACE:
      +            return 0xff08;
      +
      +            // Row #2
      +        case KeyEvent.VK_TAB:
      +            return 0xff09;
      +        case KeyEvent.VK_Q:
      +            return (sh ^ caps) ? 'Q' : 'q';
      +        case KeyEvent.VK_W:
      +            return (sh ^ caps) ? 'W' : 'w';
      +        case KeyEvent.VK_E:
      +            return (sh ^ caps) ? 'E' : 'e';
      +        case KeyEvent.VK_R:
      +            return (sh ^ caps) ? 'R' : 'r';
      +        case KeyEvent.VK_T:
      +            return (sh ^ caps) ? 'T' : 't';
      +        case KeyEvent.VK_Y:
      +            return (sh ^ caps) ? 'Y' : 'y';
      +        case KeyEvent.VK_U:
      +            return (sh ^ caps) ? 'U' : 'u';
      +        case KeyEvent.VK_I:
      +            return (sh ^ caps) ? 'I' : 'i';
      +        case KeyEvent.VK_O:
      +            return (sh ^ caps) ? 'O' : 'o';
      +        case KeyEvent.VK_P:
      +            return (sh ^ caps) ? 'P' : 'p';
      +        case KeyEvent.VK_OPEN_BRACKET:
      +            return (sh) ? '{' : '[';
      +        case KeyEvent.VK_CLOSE_BRACKET:
      +            return (sh) ? '{' : ']';
      +        case KeyEvent.VK_ENTER:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_STANDARD:
      +                return 0xff0d;
      +            case KeyEvent.KEY_LOCATION_NUMPAD:
      +                return 0xff8d;
      +            }
      +
      +            // Row #3
      +        case KeyEvent.VK_CAPS_LOCK:
      +            if (order.pressed)
      +                caps = !caps;
      +            return 0xFFE5;
      +        case KeyEvent.VK_A:
      +            return (sh ^ caps) ? 'A' : 'a';
      +        case KeyEvent.VK_S:
      +            return (sh ^ caps) ? 'S' : 's';
      +        case KeyEvent.VK_D:
      +            return (sh ^ caps) ? 'D' : 'd';
      +        case KeyEvent.VK_F:
      +            return (sh ^ caps) ? 'F' : 'f';
      +        case KeyEvent.VK_G:
      +            return (sh ^ caps) ? 'G' : 'g';
      +        case KeyEvent.VK_H:
      +            return (sh ^ caps) ? 'H' : 'h';
      +        case KeyEvent.VK_J:
      +            return (sh ^ caps) ? 'J' : 'j';
      +        case KeyEvent.VK_K:
      +            return (sh ^ caps) ? 'K' : 'k';
      +        case KeyEvent.VK_L:
      +            return (sh ^ caps) ? 'L' : 'l';
      +        case KeyEvent.VK_SEMICOLON:
      +            return (sh) ? ':' : ';';
      +        case KeyEvent.VK_QUOTE:
      +            return (sh) ? '"' : '\'';
      +
      +            // Row #4
      +        case KeyEvent.VK_SHIFT:
      +            sh = !sh;
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xffe1;
      +            case KeyEvent.KEY_LOCATION_RIGHT:
      +                return 0xffe2;
      +            }
      +        case KeyEvent.VK_BACK_SLASH:
      +            return (sh) ? '|' : '\\';
      +        case KeyEvent.VK_Z:
      +            return (sh ^ caps) ? 'Z' : 'z';
      +        case KeyEvent.VK_X:
      +            return (sh ^ caps) ? 'X' : 'x';
      +        case KeyEvent.VK_C:
      +            return (sh ^ caps) ? 'C' : 'c';
      +        case KeyEvent.VK_V:
      +            return (sh ^ caps) ? 'V' : 'v';
      +        case KeyEvent.VK_B:
      +            return (sh ^ caps) ? 'B' : 'b';
      +        case KeyEvent.VK_N:
      +            return (sh ^ caps) ? 'N' : 'n';
      +        case KeyEvent.VK_M:
      +            return (sh ^ caps) ? 'M' : 'M';
      +        case KeyEvent.VK_COMMA:
      +            return (sh) ? '<' : ',';
      +        case KeyEvent.VK_PERIOD:
      +            return (sh) ? '>' : '.';
      +        case KeyEvent.VK_SLASH:
      +            return (sh) ? '?' : '/';
      +
      +            //
      +            // Bottom row
      +        case KeyEvent.VK_CONTROL:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xFFE3;
      +            case KeyEvent.KEY_LOCATION_RIGHT:
      +                return 0xFFE4;
      +            }
      +        case KeyEvent.VK_WINDOWS:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xFFED; // HyperL
      +            case KeyEvent.KEY_LOCATION_RIGHT:
      +                return 0xFFEE; // HyperR
      +            }
      +        case KeyEvent.VK_META:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xFFE7; // MetaL
      +            case KeyEvent.KEY_LOCATION_RIGHT:
      +                return 0xFFE8; // MetaR
      +            }
      +
      +        case KeyEvent.VK_ALT:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xFFE9;
      +            case KeyEvent.KEY_LOCATION_RIGHT:
      +                return 0xFFEA;
      +            }
      +        case KeyEvent.VK_ALT_GRAPH:
      +            return 0xfe03;
      +
      +        case KeyEvent.VK_SPACE:
      +            return ' ';
      +
      +        case KeyEvent.VK_CONTEXT_MENU:
      +            return 0xff67;
      +
      +            //
      +            // Special keys
      +        case KeyEvent.VK_PRINTSCREEN:
      +            return (sh) ? 0xFF15/* SysRq */: 0xFF61 /* Print */;
      +        case KeyEvent.VK_SCROLL_LOCK:
      +            return 0xFF14;
      +        case KeyEvent.VK_PAUSE:
      +            return (sh) ? 0xFF6B/* Break */: 0xFF13/* Pause */;
      +
      +            // Text navigation keys
      +        case KeyEvent.VK_INSERT:
      +            return 0xff63;
      +        case KeyEvent.VK_DELETE:
      +            return 0xffff;
      +        case KeyEvent.VK_HOME:
      +            return 0xff50;
      +        case KeyEvent.VK_END:
      +            return 0xff57;
      +        case KeyEvent.VK_PAGE_UP:
      +            return 0xff55;
      +        case KeyEvent.VK_PAGE_DOWN:
      +            return 0xff56;
      +
      +            // Cursor keys
      +        case KeyEvent.VK_LEFT:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xff51;
      +            case KeyEvent.KEY_LOCATION_NUMPAD:
      +                return 0xFF96;
      +            }
      +        case KeyEvent.VK_UP:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xff52;
      +            case KeyEvent.KEY_LOCATION_NUMPAD:
      +                return 0xFF97;
      +            }
      +        case KeyEvent.VK_RIGHT:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xff53;
      +            case KeyEvent.KEY_LOCATION_NUMPAD:
      +                return 0xFF98;
      +            }
      +        case KeyEvent.VK_DOWN:
      +            switch (order.event.getKeyLocation()) {
      +            default:
      +            case KeyEvent.KEY_LOCATION_LEFT:
      +                return 0xff54;
      +            case KeyEvent.KEY_LOCATION_NUMPAD:
      +                return 0xFF99;
      +            }
      +
      +            // Keypad
      +        case KeyEvent.VK_NUM_LOCK:
      +            if (order.pressed)
      +                num = !num;
      +            return 0xFF6F;
      +        case KeyEvent.VK_DIVIDE:
      +            return 0xFFAF;
      +        case KeyEvent.VK_MULTIPLY:
      +            return 0xFFAA;
      +        case KeyEvent.VK_SUBTRACT:
      +            return 0xFFAD;
      +        case KeyEvent.VK_ADD:
      +            return 0xFFAB;
      +
      +        case KeyEvent.VK_KP_LEFT:
      +            return 0xFF96;
      +        case KeyEvent.VK_KP_UP:
      +            return 0xFF97;
      +        case KeyEvent.VK_KP_RIGHT:
      +            return 0xFF98;
      +        case KeyEvent.VK_KP_DOWN:
      +            return 0xFF99;
      +
      +        case KeyEvent.VK_NUMPAD0:
      +            return 0xFFB0;
      +        case KeyEvent.VK_NUMPAD1:
      +            return 0xFFB1;
      +        case KeyEvent.VK_NUMPAD2:
      +            return 0xFFB2;
      +        case KeyEvent.VK_NUMPAD3:
      +            return 0xFFB3;
      +        case KeyEvent.VK_NUMPAD4:
      +            return 0xFFB4;
      +        case KeyEvent.VK_NUMPAD5:
      +            return 0xFFB5;
      +        case KeyEvent.VK_NUMPAD6:
      +            return 0xFFB6;
      +        case KeyEvent.VK_NUMPAD7:
      +            return 0xFFB7;
      +        case KeyEvent.VK_NUMPAD8:
      +            return 0xFFB8;
      +        case KeyEvent.VK_NUMPAD9:
      +            return 0xFFB9;
      +        case KeyEvent.VK_DECIMAL:
      +            return 0xFFAE;
      +
      +        default:
      +            System.err.println("Key is not mapped: " + order + ".");
      +            return ' '; // Space
      +        }
      +    }
      +
      +}
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncMouseAdapter.java
      old mode 100644
      new mode 100755
      similarity index 85%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncMouseAdapter.java
      index 8af0195c22e..59216d3f2ce
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/adapter/AwtVncMouseAdapter.java
      @@ -14,18 +14,19 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.adapter;
       
       import java.awt.event.InputEvent;
       
       import streamer.BaseElement;
       import streamer.ByteBuffer;
       import streamer.Link;
      +import vncclient.vnc.RfbConstants;
       import common.MouseOrder;
       
      -public class AwtMouseEventToVncAdapter extends BaseElement {
      +public class AwtVncMouseAdapter extends BaseElement {
       
      -    public AwtMouseEventToVncAdapter(String id) {
      +    public AwtVncMouseAdapter(String id) {
               super(id);
           }
       
      @@ -62,9 +63,8 @@ public class AwtMouseEventToVncAdapter extends BaseElement {
            * @return VNC mouse button mask
            */
           public static int mapAwtModifiersToVncButtonMask(int modifiers) {
      -        int mask =
      -            (((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) |
      -                (((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0);
      +        int mask = (((modifiers & InputEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & InputEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0)
      +                | (((modifiers & InputEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0);
               return mask;
           }
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
      old mode 100644
      new mode 100755
      similarity index 98%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
      index 81540d66412..5dfa83101b1
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/EncodingsMessage.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import streamer.BaseElement;
       import streamer.ByteBuffer;
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
      old mode 100644
      new mode 100755
      similarity index 89%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
      index 0081827bd01..ee94c60cadc
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/FrameBufferUpdateRequest.java
      @@ -14,16 +14,16 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import streamer.BaseElement;
       import streamer.ByteBuffer;
       import streamer.Element;
       import streamer.Link;
      -import streamer.MockSink;
      -import streamer.MockSource;
       import streamer.Pipeline;
       import streamer.PipelineImpl;
      +import streamer.debug.MockSink;
      +import streamer.debug.MockSource;
       import common.ScreenDescription;
       
       public class FrameBufferUpdateRequest extends BaseElement {
      @@ -95,18 +95,18 @@ public class FrameBufferUpdateRequest extends BaseElement {
               Element adapter = new FrameBufferUpdateRequest("renderer", screen);
       
               Element sink = new MockSink("sink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
      -            // Request
      -            RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST,
      -            // Full update (redraw area)
      -            RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST,
      -            // X
      -            0, 1,
      -            // Y
      -            0, 2,
      -            // Width
      -            0, 3,
      -            // Height
      -            0, 4}));
      +                // Request
      +                RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST,
      +                // Full update (redraw area)
      +                RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST,
      +                // X
      +                0, 1,
      +                // Y
      +                0, 2,
      +                // Width
      +                0, 3,
      +                // Height
      +                0, 4}));
       
               ByteBuffer buf = new ByteBuffer(new byte[0]);
               buf.putMetadata(TARGET_X, 1);
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
      old mode 100644
      new mode 100755
      similarity index 96%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
      index 32de903d706..2350f1483f4
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RGB888LE32PixelFormatRequest.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import streamer.BaseElement;
       import streamer.ByteBuffer;
      @@ -80,8 +80,8 @@ public class RGB888LE32PixelFormatRequest extends BaseElement {
               outBuf.writeByte(0);
               outBuf.writeByte(0);
       
      -        screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColourFlag == RfbConstants.TRUE_COLOR, redMax, greenMax, blueMax,
      -            redShift, greenShift, blueShift);
      +        screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColourFlag == RfbConstants.TRUE_COLOR, redMax, greenMax,
      +                blueMax, redShift, greenShift, blueShift);
       
               pushDataToAllOuts(outBuf);
           }
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
      old mode 100644
      new mode 100755
      similarity index 99%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
      index 7359cd30099..a3895d401ff
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/RfbConstants.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import java.nio.charset.Charset;
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Authentication.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Authentication.java
      old mode 100644
      new mode 100755
      similarity index 76%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Authentication.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Authentication.java
      index de1036c62f8..6fdb3b1f69e
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Authentication.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Authentication.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import java.security.spec.KeySpec;
       
      @@ -25,13 +25,13 @@ import javax.crypto.spec.DESKeySpec;
       
       import streamer.ByteBuffer;
       import streamer.Element;
      -import streamer.FakeSink;
       import streamer.Link;
      -import streamer.MockSink;
      -import streamer.MockSource;
       import streamer.OneTimeSwitch;
       import streamer.Pipeline;
       import streamer.PipelineImpl;
      +import streamer.debug.FakeSink;
      +import streamer.debug.MockSink;
      +import streamer.debug.MockSource;
       
       public class Vnc33Authentication extends OneTimeSwitch {
       
      @@ -64,13 +64,13 @@ public class Vnc33Authentication extends OneTimeSwitch {
                   System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
       
               switch (stage) {
      -            case 0: // Read security with optional challenge and response
      -                stage0(buf, link);
      +        case 0: // Read security with optional challenge and response
      +            stage0(buf, link);
       
      -                break;
      -            case 1: // Read authentication response
      -                stage1(buf, link);
      -                break;
      +            break;
      +        case 1: // Read authentication response
      +            stage1(buf, link);
      +            break;
               }
       
           }
      @@ -91,30 +91,30 @@ public class Vnc33Authentication extends OneTimeSwitch {
               int authType = buf.readSignedInt();
       
               switch (authType) {
      -            case RfbConstants.CONNECTION_FAILED: {
      -                // Server forbids to connect. Read reason and throw exception
      +        case RfbConstants.CONNECTION_FAILED: {
      +            // Server forbids to connect. Read reason and throw exception
       
      -                int length = buf.readSignedInt();
      -                String reason = new String(buf.data, buf.offset, length, RfbConstants.US_ASCII_CHARSET);
      +            int length = buf.readSignedInt();
      +            String reason = new String(buf.data, buf.offset, length, RfbConstants.US_ASCII_CHARSET);
       
      -                throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
      -            }
      +            throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
      +        }
       
      -            case RfbConstants.NO_AUTH: {
      -                // Client can connect without authorization. Nothing to do.
      -                // Switch off this element from circuit
      -                switchOff();
      -                break;
      -            }
      +        case RfbConstants.NO_AUTH: {
      +            // Client can connect without authorization. Nothing to do.
      +            // Switch off this element from circuit
      +            switchOff();
      +            break;
      +        }
       
      -            case RfbConstants.VNC_AUTH: {
      -                // Read challenge and generate response
      -                responseToChallenge(buf, link);
      -                break;
      -            }
      +        case RfbConstants.VNC_AUTH: {
      +            // Read challenge and generate response
      +            responseToChallenge(buf, link);
      +            break;
      +        }
       
      -            default:
      -                throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
      +        default:
      +            throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
               }
       
           }
      @@ -226,21 +226,21 @@ public class Vnc33Authentication extends OneTimeSwitch {
               int authResult = buf.readSignedInt();
       
               switch (authResult) {
      -            case RfbConstants.VNC_AUTH_OK: {
      -                // Nothing to do
      -                if (verbose)
      -                    System.out.println("[" + this + "] INFO: Authentication successfull.");
      -                break;
      -            }
      +        case RfbConstants.VNC_AUTH_OK: {
      +            // Nothing to do
      +            if (verbose)
      +                System.out.println("[" + this + "] INFO: Authentication successfull.");
      +            break;
      +        }
       
      -            case RfbConstants.VNC_AUTH_TOO_MANY:
      -                throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
      +        case RfbConstants.VNC_AUTH_TOO_MANY:
      +            throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
       
      -            case RfbConstants.VNC_AUTH_FAILED:
      -                throw new RuntimeException("Connection to VNC server failed: wrong password.");
      +        case RfbConstants.VNC_AUTH_FAILED:
      +            throw new RuntimeException("Connection to VNC server failed: wrong password.");
       
      -            default:
      -                throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
      +        default:
      +            throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
               }
       
               switchOff();
      @@ -265,10 +265,10 @@ public class Vnc33Authentication extends OneTimeSwitch {
               Element source = new MockSource("source") {
                   {
                       bufs = ByteBuffer.convertByteArraysToByteBuffers(
      -                    // Request authentication and send 16 byte challenge
      -                    new byte[] {0, 0, 0, RfbConstants.VNC_AUTH, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
      -                    // Respond to challenge with AUTH_OK
      -                    new byte[] {0, 0, 0, RfbConstants.VNC_AUTH_OK});
      +                        // Request authentication and send 16 byte challenge
      +                        new byte[] {0, 0, 0, RfbConstants.VNC_AUTH, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
      +                        // Respond to challenge with AUTH_OK
      +                        new byte[] {0, 0, 0, RfbConstants.VNC_AUTH_OK});
                   }
               };
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Hello.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Hello.java
      old mode 100644
      new mode 100755
      similarity index 99%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Hello.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Hello.java
      index 63d0caa3627..812c6a83644
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc33Hello.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/Vnc33Hello.java
      @@ -14,7 +14,7 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import java.io.ByteArrayInputStream;
       import java.io.ByteArrayOutputStream;
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
      old mode 100644
      new mode 100755
      similarity index 77%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
      index 25631c2a10d..0b96c7303e4
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncInitializer.java
      @@ -14,16 +14,16 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import streamer.ByteBuffer;
       import streamer.Element;
       import streamer.Link;
      -import streamer.MockSink;
      -import streamer.MockSource;
       import streamer.OneTimeSwitch;
       import streamer.Pipeline;
       import streamer.PipelineImpl;
      +import streamer.debug.MockSink;
      +import streamer.debug.MockSource;
       import common.ScreenDescription;
       
       public class VncInitializer extends OneTimeSwitch {
      @@ -113,13 +113,13 @@ public class VncInitializer extends OneTimeSwitch {
               String desktopName = buf.readString(length, RfbConstants.US_ASCII_CHARSET);
               buf.unref();
               if (verbose)
      -            System.out.println("[" + this + "] INFO: Desktop name: \"" + desktopName + "\", bpp: " + bitsPerPixel + ", depth: " + depth + ", screen size: " +
      -                framebufferWidth + "x" + framebufferHeight + ".");
      +            System.out.println("[" + this + "] INFO: Desktop name: \"" + desktopName + "\", bpp: " + bitsPerPixel + ", depth: " + depth + ", screen size: "
      +                    + framebufferWidth + "x" + framebufferHeight + ".");
       
               // Set screen properties
               screen.setFramebufferSize(framebufferWidth, framebufferHeight);
      -        screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColorFlag == RfbConstants.TRUE_COLOR, redMax, greenMax, blueMax,
      -            redShift, greenShift, blueShift);
      +        screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag != RfbConstants.LITTLE_ENDIAN, trueColorFlag == RfbConstants.TRUE_COLOR, redMax, greenMax, blueMax, redShift,
      +                greenShift, blueShift);
               screen.setDesktopName(desktopName);
       
               // If sever screen has different parameters than ours, then change it
      @@ -167,45 +167,45 @@ public class VncInitializer extends OneTimeSwitch {
               Element source = new MockSource("source") {
                   {
                       bufs = ByteBuffer.convertByteArraysToByteBuffers(
      -                    // Send screen description
      -                    new byte[] {
      -                        // Framebuffer width (short)
      -                        0, (byte)200,
      -                        // Framebuffer height (short)
      -                        0, 100,
      -                        // Bits per pixel
      -                        32,
      -                        // Depth,
      -                        24,
      -                        // Endianness flag
      -                        RfbConstants.LITTLE_ENDIAN,
      -                        // Truecolor flag
      -                        RfbConstants.TRUE_COLOR,
      -                        // Red max (short)
      -                        0, (byte)255,
      -                        // Green max (short)
      -                        0, (byte)255,
      -                        // Blue max (short)
      -                        0, (byte)255,
      -                        // Red shift
      -                        16,
      -                        // Green shift
      -                        8,
      -                        // Blue shift
      -                        0,
      -                        // Padding
      -                        0, 0, 0,
      -                        // Desktop name length (int)
      -                        0, 0, 0, 4,
      -                        // Desktop name ("test", 4 bytes)
      -                        't', 'e', 's', 't',
      +                        // Send screen description
      +                        new byte[] {
      +                                // Framebuffer width (short)
      +                                0, (byte)200,
      +                                // Framebuffer height (short)
      +                                0, 100,
      +                                // Bits per pixel
      +                                32,
      +                                // Depth,
      +                                24,
      +                                // Endianness flag
      +                                RfbConstants.LITTLE_ENDIAN,
      +                                // Truecolor flag
      +                                RfbConstants.TRUE_COLOR,
      +                                // Red max (short)
      +                                0, (byte)255,
      +                                // Green max (short)
      +                                0, (byte)255,
      +                                // Blue max (short)
      +                                0, (byte)255,
      +                                // Red shift
      +                                16,
      +                                // Green shift
      +                                8,
      +                                // Blue shift
      +                                0,
      +                                // Padding
      +                                0, 0, 0,
      +                                // Desktop name length (int)
      +                                0, 0, 0, 4,
      +                                // Desktop name ("test", 4 bytes)
      +                                't', 'e', 's', 't',
       
      -                        // Tail
      -                        1, 2, 3
      +                                // Tail
      +                                1, 2, 3
       
      -                    },
      -                    // Tail packet
      -                    new byte[] {4, 5, 6});
      +                        },
      +                        // Tail packet
      +                        new byte[] {4, 5, 6});
                   }
               };
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
      old mode 100644
      new mode 100755
      similarity index 74%
      rename from services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java
      rename to services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
      index 6421fb220d5..5914cb30f7a
      --- a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/vnc/VncMessageHandler.java
      @@ -14,20 +14,21 @@
       // KIND, either express or implied.  See the License for the
       // specific language governing permissions and limitations
       // under the License.
      -package vncclient;
      +package vncclient.vnc;
       
       import streamer.BaseElement;
       import streamer.ByteBuffer;
       import streamer.Element;
       import streamer.Link;
      -import streamer.MockSink;
      -import streamer.MockSource;
       import streamer.Pipeline;
       import streamer.PipelineImpl;
      +import streamer.debug.MockSink;
      +import streamer.debug.MockSource;
       import common.BitmapOrder;
       import common.BitmapRectangle;
       import common.CopyRectOrder;
       import common.ScreenDescription;
      +import common.adapter.AwtClipboardAdapter;
       
       public class VncMessageHandler extends BaseElement {
           protected ScreenDescription screen = null;
      @@ -39,7 +40,6 @@ public class VncMessageHandler extends BaseElement {
           public static final String FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD = "fbur";
       
           // Keys for metadata
      -    public static final String CLIPBOARD_CONTENT = "content";
           public static final String TARGET_X = "x";
           public static final String TARGET_Y = "y";
           public static final String WIDTH = "width";
      @@ -88,33 +88,33 @@ public class VncMessageHandler extends BaseElement {
                   // Invoke packet handler by packet type.
                   switch (messageType) {
       
      -                case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
      -                    // Handle frame buffer update
      -                    if (!handleFBU(buf, link))
      -                        return;
      +            case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
      +                // Handle frame buffer update
      +                if (!handleFBU(buf, link))
      +                    return;
       
      -                    // Frame buffer update is received and fully processed, send request for
      -                    // another frame buffer update to server.
      -                    sendFBUR();
      +                // Frame buffer update is received and fully processed, send request for
      +                // another frame buffer update to server.
      +                sendFBUR();
       
      -                    break;
      -                }
      +                break;
      +            }
       
      -                case RfbConstants.SERVER_BELL: {
      -                    if (!handleBell(buf, link))
      -                        return;
      -                    break;
      -                }
      +            case RfbConstants.SERVER_BELL: {
      +                if (!handleBell(buf, link))
      +                    return;
      +                break;
      +            }
       
      -                case RfbConstants.SERVER_CUT_TEXT: {
      -                    if (!handleClipboard(buf, link))
      -                        return;
      -                    break;
      -                }
      +            case RfbConstants.SERVER_CUT_TEXT: {
      +                if (!handleClipboard(buf, link))
      +                    return;
      +                break;
      +            }
       
      -                default:
      -                    // TODO: allow to extend functionality
      -                    throw new RuntimeException("Unknown server packet type: " + messageType + ".");
      +            default:
      +                // TODO: allow to extend functionality
      +                throw new RuntimeException("Unknown server packet type: " + messageType + ".");
                   }
       
                   // Cut tail, if any
      @@ -144,7 +144,7 @@ public class VncMessageHandler extends BaseElement {
       
               // Send content in metadata
               ByteBuffer outBuf = new ByteBuffer(0);
      -        outBuf.putMetadata(CLIPBOARD_CONTENT, content);
      +        outBuf.putMetadata(AwtClipboardAdapter.CLIPBOARD_CONTENT, content);
       
               pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf);
       
      @@ -186,8 +186,8 @@ public class VncMessageHandler extends BaseElement {
                   buf.cursor = (Integer)buf.getMetadata(SAVED_CURSOR_POSITION);
       
               if (verbose && numberOfProcessedRectangles > 0)
      -            System.out.println("[" + this + "] INFO: Restarting from saved point. Number of already processed rectangles: " + numberOfRectangles + ", cursor: " +
      -                buf.cursor + ".");
      +            System.out.println("[" + this + "] INFO: Restarting from saved point. Number of already processed rectangles: " + numberOfRectangles + ", cursor: "
      +                    + buf.cursor + ".");
       
               // For all new rectangles
               for (int i = numberOfProcessedRectangles; i < numberOfRectangles; i++) {
      @@ -209,27 +209,27 @@ public class VncMessageHandler extends BaseElement {
                   // Process rectangle
                   switch (encodingType) {
       
      -                case RfbConstants.ENCODING_RAW: {
      -                    if (!handleRawRectangle(buf, link, x, y, width, height))
      -                        return false;
      -                    break;
      -                }
      +            case RfbConstants.ENCODING_RAW: {
      +                if (!handleRawRectangle(buf, link, x, y, width, height))
      +                    return false;
      +                break;
      +            }
       
      -                case RfbConstants.ENCODING_COPY_RECT: {
      -                    if (!handleCopyRect(buf, link, x, y, width, height))
      -                        return false;
      -                    break;
      -                }
      +            case RfbConstants.ENCODING_COPY_RECT: {
      +                if (!handleCopyRect(buf, link, x, y, width, height))
      +                    return false;
      +                break;
      +            }
       
      -                case RfbConstants.ENCODING_DESKTOP_SIZE: {
      -                    if (!handleScreenSizeChangeRect(buf, link, x, y, width, height))
      -                        return false;
      -                    break;
      -                }
      +            case RfbConstants.ENCODING_DESKTOP_SIZE: {
      +                if (!handleScreenSizeChangeRect(buf, link, x, y, width, height))
      +                    return false;
      +                break;
      +            }
       
      -                default:
      -                    // TODO: allow to extend functionality
      -                    throw new RuntimeException("Unsupported ecnoding: " + encodingType + ".");
      +            default:
      +                // TODO: allow to extend functionality
      +                throw new RuntimeException("Unsupported ecnoding: " + encodingType + ".");
                   }
       
                   // Update information about processed rectangles to avoid handling of same
      @@ -270,8 +270,8 @@ public class VncMessageHandler extends BaseElement {
               order.height = height;
       
               if (verbose)
      -            System.out.println("[" + this + "] INFO: Copy rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", srcX: " + order.srcX +
      -                ", srcY: " + order.srcY + ".");
      +            System.out.println("[" + this + "] INFO: Copy rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", srcX: " + order.srcX
      +                    + ", srcY: " + order.srcY + ".");
       
               pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order));
       
      @@ -288,8 +288,8 @@ public class VncMessageHandler extends BaseElement {
                   return false;
       
               if (verbose)
      -            System.out.println("[" + this + "] INFO: Raw rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", data length: " + rectDataLength +
      -                ".");
      +            System.out.println("[" + this + "] INFO: Raw rect. X: " + x + ", y: " + y + ", width: " + width + ", height: " + height + ", data length: "
      +                    + rectDataLength + ".");
       
               BitmapRectangle rectangle = new BitmapRectangle();
               rectangle.x = x;
      @@ -337,8 +337,7 @@ public class VncMessageHandler extends BaseElement {
               Element source = new MockSource("source") {
                   {
                       // Split messages at random boundaries to check "pushback" logic
      -                bufs =
      -                    ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
      +                bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {
                               // Message type: server bell
                               RfbConstants.SERVER_BELL,
       
      @@ -349,7 +348,7 @@ public class VncMessageHandler extends BaseElement {
                               // Length (test)
                               0, 0, 0, 4,
       
      -                    }, new byte[] {
      +                }, new byte[] {
                               // Clipboard text
                               't', 'e', 's', 't',
       
      @@ -362,35 +361,35 @@ public class VncMessageHandler extends BaseElement {
       
                               new byte[] {
       
      -                            // x, y, width, height: 0x0@4x4
      -                            0, 0, 0, 0, 0, 4, 0, 4,
      -                            // Encoding: desktop size
      -                            (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 24) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 16) & 0xff),
      -                            (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 8) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 0) & 0xff),},
      +                        // x, y, width, height: 0x0@4x4
      +                        0, 0, 0, 0, 0, 4, 0, 4,
      +                        // Encoding: desktop size
      +                        (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 24) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 16) & 0xff),
      +                        (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 8) & 0xff), (byte)((RfbConstants.ENCODING_DESKTOP_SIZE >> 0) & 0xff),},
       
                               new byte[] {
       
      -                            // x, y, width, height: 0x0@4x4
      -                            0, 0, 0, 0, 0, 4, 0, 4,
      -                            // Encoding: raw rect
      -                            (byte)((RfbConstants.ENCODING_RAW >> 24) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 16) & 0xff),
      -                            (byte)((RfbConstants.ENCODING_RAW >> 8) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 0) & 0xff),
      -                            // Raw pixel data 4x4x1 bpp
      -                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10,}, new byte[] {11, 12, 13, 14, 15, 16,
      +                        // x, y, width, height: 0x0@4x4
      +                        0, 0, 0, 0, 0, 4, 0, 4,
      +                        // Encoding: raw rect
      +                        (byte)((RfbConstants.ENCODING_RAW >> 24) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 16) & 0xff),
      +                        (byte)((RfbConstants.ENCODING_RAW >> 8) & 0xff), (byte)((RfbConstants.ENCODING_RAW >> 0) & 0xff),
      +                        // Raw pixel data 4x4x1 bpp
      +                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,}, new byte[] {11, 12, 13, 14, 15, 16,
       
      -                            // x, y, width, height: 0x0@2x2
      -                            0, 0, 0, 0, 0, 2, 0, 2,
      -                            // Encoding: copy rect
      -                            (byte)((RfbConstants.ENCODING_COPY_RECT >> 24) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 16) & 0xff),
      -                            (byte)((RfbConstants.ENCODING_COPY_RECT >> 8) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 0) & 0xff),
      -                            // srcX, srcY: 2x2
      -                            0, 2, 0, 2,});
      +                        // x, y, width, height: 0x0@2x2
      +                        0, 0, 0, 0, 0, 2, 0, 2,
      +                        // Encoding: copy rect
      +                        (byte)((RfbConstants.ENCODING_COPY_RECT >> 24) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 16) & 0xff),
      +                        (byte)((RfbConstants.ENCODING_COPY_RECT >> 8) & 0xff), (byte)((RfbConstants.ENCODING_COPY_RECT >> 0) & 0xff),
      +                        // srcX, srcY: 2x2
      +                        0, 2, 0, 2,});
                   }
               };
       
               ScreenDescription screen = new ScreenDescription() {
                   {
      -                this.bytesPerPixel = 1;
      +                bytesPerPixel = 1;
                   }
               };
       
      @@ -401,7 +400,8 @@ public class VncMessageHandler extends BaseElement {
               Element bellSink = new MockSink("bell", emptyBuf);
               Element clipboardSink = new MockSink("clipboard", emptyBuf);
               Element desktopSizeChangeSink = new MockSink("desktop_size", emptyBuf);
      -        Element pixelsSink = new MockSink("pixels", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,}));
      +        Element pixelsSink = new MockSink("pixels",
      +                ByteBuffer.convertByteArraysToByteBuffers(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,}));
               Element copyRectSink = new MockSink("copy_rect", emptyBuf);
       
               Pipeline pipeline = new PipelineImpl("test");
      diff --git a/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt b/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt
      new file mode 100755
      index 00000000000..41376095acf
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/main/resources/jaas_ntlm_config.txt
      @@ -0,0 +1,21 @@
      +// 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.
      +
      +MyConfig { com.sun.security.auth.module.Krb5LoginModule required
      +  useTicketCache=true
      +  doNotPrompt=false;
      +};
      diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
      old mode 100644
      new mode 100755
      index dd4168373b1..704f5f52f16
      --- a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
      +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt
      @@ -30,3 +30,5 @@ fingerprints.
       
       File rdp-key.pem contains private key in PEM format for use with
       Wireshark.
      +
      +As alternative, mimikatz can be used to extract RDP private key.
      diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
      old mode 100644
      new mode 100755
      index 14a7bbd0f0a..4e19157e5d5
      --- a/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
      +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat
      @@ -111,6 +111,19 @@ rem Start TS service
       
       net start Termservice
       
      +rem Enable logs
      +
      +wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Admin /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Analytic /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Debug /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Admin /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Analytic /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Debug /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-TerminalServices-SessionBroker-Client/Operational /enabled:true /quiet:true
      +wevtutil sl Microsoft-Windows-NTLM/Operational /enabled:true /quiet:true
      +
      +
       
       rem For Network Monitor Decrypt Expert.
       
      diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/freerdp-debug-log.txt b/services/console-proxy-rdp/rdpconsole/src/test/doc/freerdp-debug-log.txt
      new file mode 100755
      index 00000000000..d3101d698ed
      --- /dev/null
      +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/freerdp-debug-log.txt
      @@ -0,0 +1,772 @@
      +// 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.
      +connected to 192.168.1.3:3389
      +NEGOTIATE_MESSAGE (length = 40)
      +0000 4e 54 4c 4d 53 53 50 00 01 00 00 00 b7 82 08 e2 NTLMSSP.........
      +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      +0020 06 01 b1 1d 00 00 00 0f                         ........
      +
      +VERSION =
      +{
      +	ProductMajorVersion: 6
      +	ProductMinorVersion: 1
      +	ProductBuild: 7601
      +	Reserved: 0x000000
      +	NTLMRevisionCurrent: 0x0F
      +ntlm_generate_client_challenge: ClientChallenge (random bytes): 
      +0000 01 02 03 04 05 06 07 08                         ........
      +
      +CHALLENGE_MESSAGE (length = 238)
      +0000 4e 54 4c 4d 53 53 50 00 02 00 00 00 1e 00 1e 00 NTLMSSP.........
      +0010 38 00 00 00 35 82 8a e2 4a 25 50 a5 11 9b d6 16 8...5...J%P.....
      +0020 00 00 00 00 00 00 00 00 98 00 98 00 56 00 00 00 ............V...
      +0030 06 03 d7 24 00 00 00 0f 57 00 49 00 4e 00 2d 00 ...$....W.I.N.-.
      +0040 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +0050 53 00 52 00 30 00 02 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0060 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0070 4c 00 53 00 52 00 30 00 01 00 1e 00 57 00 49 00 L.S.R.0.....W.I.
      +0080 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 N.-.L.O.4.1.9.B.
      +0090 32 00 4c 00 53 00 52 00 30 00 04 00 1e 00 57 00 2.L.S.R.0.....W.
      +00a0 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 I.N.-.L.O.4.1.9.
      +00b0 42 00 32 00 4c 00 53 00 52 00 30 00 03 00 1e 00 B.2.L.S.R.0.....
      +00c0 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +00d0 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 07 00 9.B.2.L.S.R.0...
      +00e0 08 00 a0 e8 85 2c e4 c9 ce 01 00 00 00 00       .....,........
      +
      +negotiateFlags "0xE28A8235"{
      +	NTLMSSP_NEGOTIATE_56 (0),
      +	NTLMSSP_NEGOTIATE_KEY_EXCH (1),
      +	NTLMSSP_NEGOTIATE_128 (2),
      +	NTLMSSP_NEGOTIATE_VERSION (6),
      +	NTLMSSP_NEGOTIATE_TARGET_INFO (8),
      +	NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY (12),
      +	NTLMSSP_TARGET_TYPE_SERVER (14),
      +	NTLMSSP_NEGOTIATE_ALWAYS_SIGN (16),
      +	NTLMSSP_NEGOTIATE_NTLM (22),
      +	NTLMSSP_NEGOTIATE_SEAL (26),
      +	NTLMSSP_NEGOTIATE_SIGN (27),
      +	NTLMSSP_REQUEST_TARGET (29),
      +	NTLMSSP_NEGOTIATE_UNICODE (31),
      +}
      +VERSION =
      +{
      +	ProductMajorVersion: 6
      +	ProductMinorVersion: 3
      +	ProductBuild: 9431
      +	Reserved: 0x000000
      +	NTLMRevisionCurrent: 0x0F
      +TargetName (Len: 30 MaxLen: 30 BufferOffset: 56)
      +0000 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0010 39 00 42 00 32 00 4c 00 53 00 52 00 30 00       9.B.2.L.S.R.0.
      +
      +TargetInfo (Len: 152 MaxLen: 152 BufferOffset: 86)
      +0000 02 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 ....W.I.N.-.L.O.
      +0010 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 4.1.9.B.2.L.S.R.
      +0020 30 00 01 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 0.....W.I.N.-.L.
      +0030 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 O.4.1.9.B.2.L.S.
      +0040 52 00 30 00 04 00 1e 00 57 00 49 00 4e 00 2d 00 R.0.....W.I.N.-.
      +0050 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +0060 53 00 52 00 30 00 03 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0070 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0080 4c 00 53 00 52 00 30 00 07 00 08 00 a0 e8 85 2c L.S.R.0........,
      +0090 e4 c9 ce 01 00 00 00 00                         ........
      +
      +ChallengeTargetInfo (152):
      +AV_PAIRs =
      +{
      +	MsvAvNbDomainName AvId: 2 AvLen: 30
      +0000 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0010 39 00 42 00 32 00 4c 00 53 00 52 00 30 00       9.B.2.L.S.R.0.
      +	MsvAvNbComputerName AvId: 1 AvLen: 30
      +0000 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0010 39 00 42 00 32 00 4c 00 53 00 52 00 30 00       9.B.2.L.S.R.0.
      +	MsvAvDnsDomainName AvId: 4 AvLen: 30
      +0000 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0010 39 00 42 00 32 00 4c 00 53 00 52 00 30 00       9.B.2.L.S.R.0.
      +	MsvAvDnsComputerName AvId: 3 AvLen: 30
      +0000 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0010 39 00 42 00 32 00 4c 00 53 00 52 00 30 00       9.B.2.L.S.R.0.
      +	MsvAvTimestamp AvId: 7 AvLen: 8
      +0000 a0 e8 85 2c e4 c9 ce 01                         ...,....
      +}
      +ntlm_generate_timestamp: timestamp
      +0000 a0 e8 85 2c e4 c9 ce 01                         ...,....
      +
      +ntlm_generate_timestamp: ChallengeTimestamp
      +0000 a0 e8 85 2c e4 c9 ce 01                         ...,....
      +
      +NTOWFv1W: Password:
      +0000 52 00 32 00 50 00 72 00 65 00 76 00 69 00 65 00 R.2.P.r.e.v.i.e.
      +0010 77 00 21 00                                     w.!.
      +
      +NTOWFv1W: NtHash (MD4(Password)):
      +0000 25 f3 39 c9 86 b5 c2 6f dc ab 91 34 93 a2 18 2a %.9....o...4...*
      +
      +NTOWFv2W: NtHashV1 (NTOWFv1W(Password)):
      +0000 25 f3 39 c9 86 b5 c2 6f dc ab 91 34 93 a2 18 2a %.9....o...4...*
      +
      +NTOWFv2W: User:
      +0000 41 00 64 00 6d 00 69 00 6e 00 69 00 73 00 74 00 A.d.m.i.n.i.s.t.
      +0010 72 00 61 00 74 00 6f 00 72 00                   r.a.t.o.r.
      +
      +NTOWFv2W: Domain:
      +0000 77 00 6f 00 72 00 6b 00 67 00 72 00 6f 00 75 00 w.o.r.k.g.r.o.u.
      +0010 70 00                                           p.
      +
      +NTOWFv2W: buffer (User+Domain):
      +0000 41 00 44 00 4d 00 49 00 4e 00 49 00 53 00 54 00 A.D.M.I.N.I.S.T.
      +0010 52 00 41 00 54 00 4f 00 52 00 77 00 6f 00 72 00 R.A.T.O.R.w.o.r.
      +0020 6b 00 67 00 72 00 6f 00 75 00 70 00             k.g.r.o.u.p.
      +
      +NTOWFv2W: NtHash (HMAC_MD5(NtHashV1, User+Domain)):
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_ntlm_v2_hash: hash (NTOWFv2W(Password, User, Domain)):
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_lm_v2_response: ntlm_v2_hash:
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_lm_v2_response: value (ServerChallenge + ClientChallenge):
      +0000 4a 25 50 a5 11 9b d6 16 01 02 03 04 05 06 07 08 J%P.............
      +
      +ntlm_compute_lm_v2_response: response (HMAC_MD5(ntlm_v2_hash, value) + ClientChallenge):
      +0000 7c c0 fd 08 c5 14 05 34 f3 12 9e 3e a3 09 bc c6 |......4...>....
      +0010 01 02 03 04 05 06 07 08                         ........
      +
      +NTOWFv1W: Password:
      +0000 52 00 32 00 50 00 72 00 65 00 76 00 69 00 65 00 R.2.P.r.e.v.i.e.
      +0010 77 00 21 00                                     w.!.
      +
      +NTOWFv1W: NtHash (MD4(Password)):
      +0000 25 f3 39 c9 86 b5 c2 6f dc ab 91 34 93 a2 18 2a %.9....o...4...*
      +
      +NTOWFv2W: NtHashV1 (NTOWFv1W(Password)):
      +0000 25 f3 39 c9 86 b5 c2 6f dc ab 91 34 93 a2 18 2a %.9....o...4...*
      +
      +NTOWFv2W: User:
      +0000 41 00 64 00 6d 00 69 00 6e 00 69 00 73 00 74 00 A.d.m.i.n.i.s.t.
      +0010 72 00 61 00 74 00 6f 00 72 00                   r.a.t.o.r.
      +
      +NTOWFv2W: Domain:
      +0000 77 00 6f 00 72 00 6b 00 67 00 72 00 6f 00 75 00 w.o.r.k.g.r.o.u.
      +0010 70 00                                           p.
      +
      +NTOWFv2W: buffer (User+Domain):
      +0000 41 00 44 00 4d 00 49 00 4e 00 49 00 53 00 54 00 A.D.M.I.N.I.S.T.
      +0010 52 00 41 00 54 00 4f 00 52 00 77 00 6f 00 72 00 R.A.T.O.R.w.o.r.
      +0020 6b 00 67 00 72 00 6f 00 75 00 70 00             k.g.r.o.u.p.
      +
      +NTOWFv2W: NtHash (HMAC_MD5(NtHashV1, User+Domain)):
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_ntlm_v2_hash: hash (NTOWFv2W(Password, User, Domain)):
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_ntlm_v2_response: Password (length = 20)
      +0000 52 00 32 00 50 00 72 00 65 00 76 00 69 00 65 00 R.2.P.r.e.v.i.e.
      +0010 77 00 21 00                                     w.!.
      +
      +ntlm_compute_ntlm_v2_response: Username (length = 26)
      +0000 41 00 64 00 6d 00 69 00 6e 00 69 00 73 00 74 00 A.d.m.i.n.i.s.t.
      +0010 72 00 61 00 74 00 6f 00 72 00                   r.a.t.o.r.
      +
      +ntlm_compute_ntlm_v2_response: Domain (length = 18)
      +0000 77 00 6f 00 72 00 6b 00 67 00 72 00 6f 00 75 00 w.o.r.k.g.r.o.u.
      +0010 70 00                                           p.
      +
      +ntlm_compute_ntlm_v2_response: Workstation (length = 12)
      +0000 61 00 70 00 6f 00 6c 00 6c 00 6f 00             a.p.o.l.l.o.
      +
      +ntlm_compute_ntlm_v2_response: TargetInfo (length = 234)
      +0000 02 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 ....W.I.N.-.L.O.
      +0010 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 4.1.9.B.2.L.S.R.
      +0020 30 00 01 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 0.....W.I.N.-.L.
      +0030 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 O.4.1.9.B.2.L.S.
      +0040 52 00 30 00 04 00 1e 00 57 00 49 00 4e 00 2d 00 R.0.....W.I.N.-.
      +0050 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +0060 53 00 52 00 30 00 03 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0070 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0080 4c 00 53 00 52 00 30 00 07 00 08 00 a0 e8 85 2c L.S.R.0........,
      +0090 e4 c9 ce 01 06 00 04 00 02 00 00 00 0a 00 10 00 ................
      +00a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      +00b0 09 00 26 00 54 00 45 00 52 00 4d 00 53 00 52 00 ..&.T.E.R.M.S.R.
      +00c0 56 00 2f 00 31 00 39 00 32 00 2e 00 31 00 36 00 V./.1.9.2...1.6.
      +00d0 38 00 2e 00 31 00 2e 00 33 00 00 00 00 00 00 00 8...1...3.......
      +00e0 00 00 00 00 00 00 00 00 00 00                   ..........
      +
      +ntlm_compute_ntlm_v2_response: ntlm_v2_hash (ntlm_compute_ntlm_v2_hash(context, (char*) ntlm_v2_hash))
      +0000 5f cc 4c 48 74 6b 94 ce b7 ae f1 0d c9 11 22 8f _.LHtk........".
      +
      +ntlm_compute_ntlm_v2_response: ntlm_v2_temp (0x0101 + reserved 6 bytes + Timestamp + ClientChallenge + TargetInfo)
      +0000 01 01 00 00 00 00 00 00 a0 e8 85 2c e4 c9 ce 01 ...........,....
      +0010 01 02 03 04 05 06 07 08 00 00 00 00 02 00 1e 00 ................
      +0020 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0030 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 01 00 9.B.2.L.S.R.0...
      +0040 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 ..W.I.N.-.L.O.4.
      +0050 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 1.9.B.2.L.S.R.0.
      +0060 04 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 ....W.I.N.-.L.O.
      +0070 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 4.1.9.B.2.L.S.R.
      +0080 30 00 03 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 0.....W.I.N.-.L.
      +0090 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 O.4.1.9.B.2.L.S.
      +00a0 52 00 30 00 07 00 08 00 a0 e8 85 2c e4 c9 ce 01 R.0........,....
      +00b0 06 00 04 00 02 00 00 00 0a 00 10 00 00 00 00 00 ................
      +00c0 00 00 00 00 00 00 00 00 00 00 00 00 09 00 26 00 ..............&.
      +00d0 54 00 45 00 52 00 4d 00 53 00 52 00 56 00 2f 00 T.E.R.M.S.R.V./.
      +00e0 31 00 39 00 32 00 2e 00 31 00 36 00 38 00 2e 00 1.9.2...1.6.8...
      +00f0 31 00 2e 00 33 00 00 00 00 00 00 00 00 00 00 00 1...3...........
      +0100 00 00 00 00 00 00                               ......
      +
      +ntlm_compute_ntlm_v2_response: ntlm_v2_chal (ServerChallenge + ntlm_v2_temp)
      +0000 4a 25 50 a5 11 9b d6 16 01 01 00 00 00 00 00 00 J%P.............
      +0010 a0 e8 85 2c e4 c9 ce 01 01 02 03 04 05 06 07 08 ...,............
      +0020 00 00 00 00 02 00 1e 00 57 00 49 00 4e 00 2d 00 ........W.I.N.-.
      +0030 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +0040 53 00 52 00 30 00 01 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0050 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0060 4c 00 53 00 52 00 30 00 04 00 1e 00 57 00 49 00 L.S.R.0.....W.I.
      +0070 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 N.-.L.O.4.1.9.B.
      +0080 32 00 4c 00 53 00 52 00 30 00 03 00 1e 00 57 00 2.L.S.R.0.....W.
      +0090 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 I.N.-.L.O.4.1.9.
      +00a0 42 00 32 00 4c 00 53 00 52 00 30 00 07 00 08 00 B.2.L.S.R.0.....
      +00b0 a0 e8 85 2c e4 c9 ce 01 06 00 04 00 02 00 00 00 ...,............
      +00c0 0a 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      +00d0 00 00 00 00 09 00 26 00 54 00 45 00 52 00 4d 00 ......&.T.E.R.M.
      +00e0 53 00 52 00 56 00 2f 00 31 00 39 00 32 00 2e 00 S.R.V./.1.9.2...
      +00f0 31 00 36 00 38 00 2e 00 31 00 2e 00 33 00 00 00 1.6.8...1...3...
      +0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00       ..............
      +
      +ntlm_compute_ntlm_v2_response: nt_proof_str ( HMAC_MD5(ntlm_v2_hash, ntlm_v2_temp_chal))
      +0000 19 4b eb ad da 24 d5 96 85 2e 24 94 d6 4a b8 5e .K...$....$..J.^
      +
      +ntlm_compute_ntlm_v2_response: NtChallengeResponse ( nt_proof_str + ntlm_v2_temp)
      +0000 19 4b eb ad da 24 d5 96 85 2e 24 94 d6 4a b8 5e .K...$....$..J.^
      +0010 01 01 00 00 00 00 00 00 a0 e8 85 2c e4 c9 ce 01 ...........,....
      +0020 01 02 03 04 05 06 07 08 00 00 00 00 02 00 1e 00 ................
      +0030 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +0040 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 01 00 9.B.2.L.S.R.0...
      +0050 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 ..W.I.N.-.L.O.4.
      +0060 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 1.9.B.2.L.S.R.0.
      +0070 04 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 4f 00 ....W.I.N.-.L.O.
      +0080 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 52 00 4.1.9.B.2.L.S.R.
      +0090 30 00 03 00 1e 00 57 00 49 00 4e 00 2d 00 4c 00 0.....W.I.N.-.L.
      +00a0 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 53 00 O.4.1.9.B.2.L.S.
      +00b0 52 00 30 00 07 00 08 00 a0 e8 85 2c e4 c9 ce 01 R.0........,....
      +00c0 06 00 04 00 02 00 00 00 0a 00 10 00 00 00 00 00 ................
      +00d0 00 00 00 00 00 00 00 00 00 00 00 00 09 00 26 00 ..............&.
      +00e0 54 00 45 00 52 00 4d 00 53 00 52 00 56 00 2f 00 T.E.R.M.S.R.V./.
      +00f0 31 00 39 00 32 00 2e 00 31 00 36 00 38 00 2e 00 1.9.2...1.6.8...
      +0100 31 00 2e 00 33 00 00 00 00 00 00 00 00 00 00 00 1...3...........
      +0110 00 00 00 00 00 00                               ......
      +
      +ntlm_compute_ntlm_v2_response: SessionBaseKey ( HMAC_MD5(ntlm_v2_hash, nt_proof_str)
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_generate_key_exchange_key: KeyExchangeKey (SessionBaseKey): 
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_generate_random_session_key: RandomSessionKey (random bytes): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_exported_session_key: ExportedSessionKey (RandomSessionKey): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_rc4k: key, 
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_rc4k: plaintext 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_rc4k: ciphertext (RC4K(key, plaintext)) 
      +0000 e4 e9 c2 ad 41 02 2f 3c f9 4c 72 84 c5 2a 7c 6f ....A./<.Lr..*|o
      +
      +ntlm_encrypt_random_session_key: KeyExchangeKey: 
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_encrypt_random_session_key: RandomSessionKey: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_encrypt_random_session_key: EncryptedRandomSessionKey (RC4K(KeyExchangeKey, RandomSessionKey)): 
      +0000 e4 e9 c2 ad 41 02 2f 3c f9 4c 72 84 c5 2a 7c 6f ....A./<.Lr..*|o
      +
      +ntlm_generate_signing_key: exported_session_key: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_signing_key: sign_magic: 
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0010 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0020 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: value ( exported_session_key + sign_magic ): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +0010 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0020 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0030 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0040 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: signing_key (MD5(value)): 
      +0000 f6 ae 96 cb 05 e2 ab 54 f6 dd 59 f3 c9 d9 a0 43 .......T..Y....C
      +
      +ntlm_generate_client_signing_key: ExportedSessionKey: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_client_signing_key: client_sign_magic: ":session key to client-to-server signing key magic constant"
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0010 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0020 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_client_signing_key: ClientSigningKey ( ntlm_generate_signing_key(context->ExportedSessionKey, &sign_magic, context->ClientSigningKey))
      +0000 f6 ae 96 cb 05 e2 ab 54 f6 dd 59 f3 c9 d9 a0 43 .......T..Y....C
      +
      +ntlm_generate_signing_key: exported_session_key: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_signing_key: sign_magic: 
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0010 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0020 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: value ( exported_session_key + sign_magic ): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +0010 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0020 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0030 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0040 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: signing_key (MD5(value)): 
      +0000 b6 58 c5 98 7a 25 f8 6e d8 e5 6c e9 3e 3c c0 88 .X..z%.n..l.><..
      +
      +ntlm_generate_server_signing_key: ExportedSessionKey: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_server_signing_key: server_sign_magic: ":session key to server-to-client signing key magic constant"
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0010 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0020 73 69 67 6e 69 6e 67 20 6b 65 79 20 6d 61 67 69 signing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_server_signing_key: ServerSigningKey (ntlm_generate_signing_key(context->ExportedSessionKey, &sign_magic, context->ServerSigningKey))
      +0000 b6 58 c5 98 7a 25 f8 6e d8 e5 6c e9 3e 3c c0 88 .X..z%.n..l.><..
      +
      +ntlm_generate_signing_key: exported_session_key: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_signing_key: sign_magic: 
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0010 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0020 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: value ( exported_session_key + sign_magic ): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +0010 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0020 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0030 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0040 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: signing_key (MD5(value)): 
      +0000 58 19 44 c2 7a c6 34 45 e4 b8 2b 55 b9 0b 1f b5 X.D.z.4E..+U....
      +
      +ntlm_generate_client_sealing_key: ExportedSessionKey: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_client_sealing_key: client_seal_magic: ":session key to client-to-server sealing key magic constant"
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 63 session key to c
      +0010 6c 69 65 6e 74 2d 74 6f 2d 73 65 72 76 65 72 20 lient-to-server 
      +0020 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_client_sealing_key: ClientSealingKey (ntlm_generate_signing_key(context->ExportedSessionKey, &seal_magic, context->ClientSealingKey))
      +0000 58 19 44 c2 7a c6 34 45 e4 b8 2b 55 b9 0b 1f b5 X.D.z.4E..+U....
      +
      +ntlm_generate_signing_key: exported_session_key: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_signing_key: sign_magic: 
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0010 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0020 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: value ( exported_session_key + sign_magic ): 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +0010 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0020 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0030 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0040 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_signing_key: signing_key (MD5(value)): 
      +0000 92 3a 73 5c 92 a7 04 34 be 9a a2 9f ed c1 e6 13 .:s\...4........
      +
      +ntlm_generate_server_sealing_key: ExportedSessionKey: 
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_generate_server_sealing_key: server_seal_magic: ":session key to server-to-client sealing key magic constant"
      +0000 73 65 73 73 69 6f 6e 20 6b 65 79 20 74 6f 20 73 session key to s
      +0010 65 72 76 65 72 2d 74 6f 2d 63 6c 69 65 6e 74 20 erver-to-client 
      +0020 73 65 61 6c 69 6e 67 20 6b 65 79 20 6d 61 67 69 sealing key magi
      +0030 63 20 63 6f 6e 73 74 61 6e 74 00                c constant.
      +
      +ntlm_generate_server_sealing_key: ServerSealingKey (ntlm_generate_signing_key(context->ExportedSessionKey, &seal_magic, context->ServerSealingKey))
      +0000 92 3a 73 5c 92 a7 04 34 be 9a a2 9f ed c1 e6 13 .:s\...4........
      +
      +ntlm_init_rc4_seal_states: SendSigningKey (ClientSigningKey)
      +0000 f6 ae 96 cb 05 e2 ab 54 f6 dd 59 f3 c9 d9 a0 43 .......T..Y....C
      +
      +ntlm_init_rc4_seal_states: RecvSigningKey (ServerSigningKey)
      +0000 b6 58 c5 98 7a 25 f8 6e d8 e5 6c e9 3e 3c c0 88 .X..z%.n..l.><..
      +
      +ntlm_init_rc4_seal_states: SendSealingKey (ServerSealingKey)
      +0000 92 3a 73 5c 92 a7 04 34 be 9a a2 9f ed c1 e6 13 .:s\...4........
      +
      +ntlm_init_rc4_seal_states: RecvSealingKey (ClientSealingKey)
      +0000 58 19 44 c2 7a c6 34 45 e4 b8 2b 55 b9 0b 1f b5 X.D.z.4E..+U....
      +
      +ntlm_init_rc4_seal_states: SendRc4Seal = 0xb6a28da0 (RC4_set_key(&context->SendRc4Seal, 16, context->ClientSealingKey))
      +ntlm_init_rc4_seal_states: RecvRc4Seal = 0xb6a291a8 (RC4_set_key(&context->RecvRc4Seal, 16, context->ServerSealingKey))
      +ntlm_read_ChallengeMessage: ClientChallenge
      +0000 01 02 03 04 05 06 07 08                         ........
      +
      +ntlm_read_ChallengeMessage: ServerChallenge
      +0000 4a 25 50 a5 11 9b d6 16                         J%P.....
      +
      +ntlm_read_ChallengeMessage: SessionBaseKey
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_read_ChallengeMessage: KeyExchangeKey
      +0000 8e 0f dd 12 4c 3b 11 7f 22 b9 4b 59 52 bc a7 18 ....L;..".KYR...
      +
      +ntlm_read_ChallengeMessage: ExportedSessionKey
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_read_ChallengeMessage: RandomSessionKey
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_read_ChallengeMessage: ClientSigningKey
      +0000 f6 ae 96 cb 05 e2 ab 54 f6 dd 59 f3 c9 d9 a0 43 .......T..Y....C
      +
      +ntlm_read_ChallengeMessage: ClientSealingKey
      +0000 58 19 44 c2 7a c6 34 45 e4 b8 2b 55 b9 0b 1f b5 X.D.z.4E..+U....
      +
      +ntlm_read_ChallengeMessage: ServerSigningKey
      +0000 b6 58 c5 98 7a 25 f8 6e d8 e5 6c e9 3e 3c c0 88 .X..z%.n..l.><..
      +
      +ntlm_read_ChallengeMessage: ServerSealingKey
      +0000 92 3a 73 5c 92 a7 04 34 be 9a a2 9f ed c1 e6 13 .:s\...4........
      +
      +ntlm_read_ChallengeMessage: Timestamp
      +0000 a0 e8 85 2c e4 c9 ce 01                         ...,....
      +
      +ntlm_compute_message_integrity_check: ExportedSessionKey
      +0000 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 ................
      +
      +ntlm_compute_message_integrity_check: NegotiateMessage
      +0000 4e 54 4c 4d 53 53 50 00 01 00 00 00 b7 82 08 e2 NTLMSSP.........
      +0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      +0020 06 01 b1 1d 00 00 00 0f                         ........
      +
      +ntlm_compute_message_integrity_check: ChallengeMessage
      +0000 4e 54 4c 4d 53 53 50 00 02 00 00 00 1e 00 1e 00 NTLMSSP.........
      +0010 38 00 00 00 35 82 8a e2 4a 25 50 a5 11 9b d6 16 8...5...J%P.....
      +0020 00 00 00 00 00 00 00 00 98 00 98 00 56 00 00 00 ............V...
      +0030 06 03 d7 24 00 00 00 0f 57 00 49 00 4e 00 2d 00 ...$....W.I.N.-.
      +0040 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +0050 53 00 52 00 30 00 02 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0060 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0070 4c 00 53 00 52 00 30 00 01 00 1e 00 57 00 49 00 L.S.R.0.....W.I.
      +0080 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 N.-.L.O.4.1.9.B.
      +0090 32 00 4c 00 53 00 52 00 30 00 04 00 1e 00 57 00 2.L.S.R.0.....W.
      +00a0 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 I.N.-.L.O.4.1.9.
      +00b0 42 00 32 00 4c 00 53 00 52 00 30 00 03 00 1e 00 B.2.L.S.R.0.....
      +00c0 57 00 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 W.I.N.-.L.O.4.1.
      +00d0 39 00 42 00 32 00 4c 00 53 00 52 00 30 00 07 00 9.B.2.L.S.R.0...
      +00e0 08 00 a0 e8 85 2c e4 c9 ce 01 00 00 00 00       .....,........
      +
      +ntlm_compute_message_integrity_check: AuthenticateMessage
      +0000 4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 00 NTLMSSP.........
      +0010 90 00 00 00 16 01 16 01 a8 00 00 00 12 00 12 00 ................
      +0020 58 00 00 00 1a 00 1a 00 6a 00 00 00 0c 00 0c 00 X.......j.......
      +0030 84 00 00 00 10 00 10 00 be 01 00 00 35 b2 88 e2 ............5...
      +0040 06 01 b1 1d 00 00 00 0f 00 00 00 00 00 00 00 00 ................
      +0050 00 00 00 00 00 00 00 00 77 00 6f 00 72 00 6b 00 ........w.o.r.k.
      +0060 67 00 72 00 6f 00 75 00 70 00 41 00 64 00 6d 00 g.r.o.u.p.A.d.m.
      +0070 69 00 6e 00 69 00 73 00 74 00 72 00 61 00 74 00 i.n.i.s.t.r.a.t.
      +0080 6f 00 72 00 61 00 70 00 6f 00 6c 00 6c 00 6f 00 o.r.a.p.o.l.l.o.
      +0090 7c c0 fd 08 c5 14 05 34 f3 12 9e 3e a3 09 bc c6 |......4...>....
      +00a0 01 02 03 04 05 06 07 08 19 4b eb ad da 24 d5 96 .........K...$..
      +00b0 85 2e 24 94 d6 4a b8 5e 01 01 00 00 00 00 00 00 ..$..J.^........
      +00c0 a0 e8 85 2c e4 c9 ce 01 01 02 03 04 05 06 07 08 ...,............
      +00d0 00 00 00 00 02 00 1e 00 57 00 49 00 4e 00 2d 00 ........W.I.N.-.
      +00e0 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 4c 00 L.O.4.1.9.B.2.L.
      +00f0 53 00 52 00 30 00 01 00 1e 00 57 00 49 00 4e 00 S.R.0.....W.I.N.
      +0100 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 32 00 -.L.O.4.1.9.B.2.
      +0110 4c 00 53 00 52 00 30 00 04 00 1e 00 57 00 49 00 L.S.R.0.....W.I.
      +0120 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 42 00 N.-.L.O.4.1.9.B.
      +0130 32 00 4c 00 53 00 52 00 30 00 03 00 1e 00 57 00 2.L.S.R.0.....W.
      +0140 49 00 4e 00 2d 00 4c 00 4f 00 34 00 31 00 39 00 I.N.-.L.O.4.1.9.
      +0150 42 00 32 00 4c 00 53 00 52 00 30 00 07 00 08 00 B.2.L.S.R.0.....
      +0160 a0 e8 85 2c e4 c9 ce 01 06 00 04 00 02 00 00 00 ...,............
      +0170 0a 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
      +0180 00 00 00 00 09 00 26 00 54 00 45 00 52 00 4d 00 ......&.T.E.R.M.
      +0190 53 00 52 00 56 00 2f 00 31 00 39 00 32 00 2e 00 S.R.V./.1.9.2...
      +01a0 31 00 36 00 38 00 2e 00 31 00 2e 00 33 00 00 00 1.6.8...1...3...
      +01b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e4 e9 ................
      +01c0 c2 ad 41 02 2f 3c f9 4c 72 84 c5 2a 7c 6f       ..A./<.Lr..*|o
      +
      +ntlm_compute_message_integrity_check: MessageIntegrityCheck (HMAC_MD5(ExportedSessionKey + NegotiateMessage + ChallengeMessage + AuthenticateMessage))
      +0000 d9 e9 bc 9b 6f a5 f9 c8 70 16 10 20 f8 f1 61 42 ....o...p.. ..aB
      +
      +credssp_encrypt_public_key_echo: Server public key (length = 270):
      +recv_seq_num: 0
      +Public key before encryption:
      +0000 30 82 01 0a 02 82 01 01 00 a8 56 65 d3 ce 8a 54 0.........Ve...T
      +0010 4d 9d b0 84 31 19 71 7f dd 42 fb 2a 7a 72 13 a1 M...1.q..B.*zr..
      +0020 b9 72 bb d3 08 ad 7d 6c 15 65 03 d1 c4 54 c5 33 .r....}l.e...T.3
      +0030 6b 7d 69 89 5e fe e0 01 c0 7e 9b cb 5d 65 36 cd k}i.^....~..]e6.
      +0040 77 5d f3 7a 5b 29 44 72 d5 38 e2 cf b1 c7 78 9b w].z[)Dr.8....x.
      +0050 58 b9 17 7c b7 d6 c7 c7 bf 90 4e 7c 39 93 cb 2e X..|......N|9...
      +0060 e0 c2 33 2d a5 7e e0 7b b6 f9 91 32 b7 d4 85 b7 ..3-.~.{...2....
      +0070 35 2d 2b 00 6d f8 ea 8c 97 5f 51 1d 68 04 3c 79 5-+.m...._Q.h.SendRc4Seal, length, data, data_buffer->pvBuffer))
      +0000 15 f7 f2 54 da a9 e5 ad 85 04 67 4d 0b cb f9 b1 ...T......gM....
      +0010 f8 02 8a 77 c2 63 ab d5 74 23 9f 9d 5d 1f d3 b3 ...w.c..t#..]...
      +0020 a0 ac 16 8a 4b 08 f5 47 70 58 10 b4 e7 87 b3 4b ....K..GpX.....K
      +0030 c9 a2 d5 d1 ca 0f d4 e3 8d 76 5a 60 28 f8 06 5d .........vZ`(..]
      +0040 e4 7e 21 c8 bb ac e5 79 85 30 9b 88 13 2f 8f fc .~!....y.0.../..
      +0050 04 52 fe 87 94 cf cb 49 4a da 6f dd ee 57 a5 e4 .R.....IJ.o..W..
      +0060 4d 0e 5c 3d 0b 63 1f f6 3d 1b ae 5a f6 42 2a 46 M.\=.c..=..Z.B*F
      +0070 fa 42 71 67 46 02 71 ea 51 98 f7 d4 43 bf 8e e8 .BqgF.q.Q...C...
      +0080 3c c8 fa 79 9d 8c fc c2 42 c9 bb d0 ab 81 c4 53 <..y....B......S
      +0090 fd 41 da ab 0f 25 79 5f bd a3 8c d3 f5 1b ab 20 .A...%y_....... 
      +00a0 d1 f4 d8 81 9c 18 4a a4 77 ee e1 51 ee 2a c1 94 ......J.w..Q.*..
      +00b0 37 c5 06 7a 3f 0f 25 5b 4e 6a dc 0b 62 6f 12 83 7..z?.%[Nj..bo..
      +00c0 03 ae 4e ce 2b 6e d4 d5 23 27 f6 a6 38 67 ec 95 ..N.+n..#'..8g..
      +00d0 82 c6 ba d4 f6 e6 22 7d b9 e4 81 97 24 ff 40 b2 ......"}....$.@.
      +00e0 42 3c 11 24 d0 3a 96 d9 c1 13 d6 62 45 21 60 5b B<.$.:.....bE!`[
      +00f0 7b 2b 62 44 f7 40 93 29 5b 44 b7 da 9c a6 a9 3b {+bD.@.)[D.....;
      +0100 e1 3b 9d 31 f2 21 53 0f b3 70 55 84 2c b4       .;.1.!S..pU.,.
      +
      +ntlm_EncryptMessage: Checksum ( RC4(&context->SendRc4Seal, 8, digest, checksum), first 8 bytes of digest only!):
      +0000 72 76 1e 57 49 b5 0f ad                         rv.WI...
      +ntlm_EncryptMessage: signature (version + checksum + seq_num)
      +0000 01 00 00 00 72 76 1e 57 49 b5 0f ad 00 00 00 00 ....rv.WI.......
      +
      +ntlm_EncryptMessage: SendSeqNum (after increase): 1, SeqNo: 0.
      +credssp_encrypt_public_key_echo: Server public key (length = 270):
      +recv_seq_num: 0
      +Public key after encryption:
      +0000 15 f7 f2 54 da a9 e5 ad 85 04 67 4d 0b cb f9 b1 ...T......gM....
      +0010 f8 02 8a 77 c2 63 ab d5 74 23 9f 9d 5d 1f d3 b3 ...w.c..t#..]...
      +0020 a0 ac 16 8a 4b 08 f5 47 70 58 10 b4 e7 87 b3 4b ....K..GpX.....K
      +0030 c9 a2 d5 d1 ca 0f d4 e3 8d 76 5a 60 28 f8 06 5d .........vZ`(..]
      +0040 e4 7e 21 c8 bb ac e5 79 85 30 9b 88 13 2f 8f fc .~!....y.0.../..
      +0050 04 52 fe 87 94 cf cb 49 4a da 6f dd ee 57 a5 e4 .R.....IJ.o..W..
      +0060 4d 0e 5c 3d 0b 63 1f f6 3d 1b ae 5a f6 42 2a 46 M.\=.c..=..Z.B*F
      +0070 fa 42 71 67 46 02 71 ea 51 98 f7 d4 43 bf 8e e8 .BqgF.q.Q...C...
      +0080 3c c8 fa 79 9d 8c fc c2 42 c9 bb d0 ab 81 c4 53 <..y....B......S
      +0090 fd 41 da ab 0f 25 79 5f bd a3 8c d3 f5 1b ab 20 .A...%y_....... 
      +00a0 d1 f4 d8 81 9c 18 4a a4 77 ee e1 51 ee 2a c1 94 ......J.w..Q.*..
      +00b0 37 c5 06 7a 3f 0f 25 5b 4e 6a dc 0b 62 6f 12 83 7..z?.%[Nj..bo..
      +00c0 03 ae 4e ce 2b 6e d4 d5 23 27 f6 a6 38 67 ec 95 ..N.+n..#'..8g..
      +00d0 82 c6 ba d4 f6 e6 22 7d b9 e4 81 97 24 ff 40 b2 ......"}....$.@.
      +00e0 42 3c 11 24 d0 3a 96 d9 c1 13 d6 62 45 21 60 5b B<.$.:.....bE!`[
      +00f0 7b 2b 62 44 f7 40 93 29 5b 44 b7 da 9c a6 a9 3b {+bD.@.)[D.....;
      +0100 e1 3b 9d 31 f2 21 53 0f b3 70 55 84 2c b4       .;.1.!S..pU.,.
      +credssp_decrypt_public_key_echo: Server public key (length = 270):
      +recv_seq_num: 0
      +Public key before decryption:
      +0000 03 12 dd ea 47 b3 ff e1 66 08 f6 6b a0 62 42 67 ....G...f..k.bBg
      +0010 bf 3d 59 60 ef 52 b0 26 95 ed 84 48 44 bb 8d 65 .=Y`.R.&...HD..e
      +0020 cf e4 8e 6f 69 ae ed 44 bb 49 1d 2a 40 29 2b 13 ...oi..D.I.*@)+.
      +0030 42 1c eb b1 6c 8a 3b 80 d1 70 fd dd 79 e4 93 0b B...l.;..p..y...
      +0040 47 bd 3a 7e 31 66 4b 65 8d 5c 2a cd c2 09 7a 3b G.:~1fKe.\*...z;
      +0050 b2 fd 09 52 09 47 05 a4 6f 32 d1 76 b2 d4 59 e0 ...R.G..o2.v..Y.
      +0060 85 f1 36 7d 76 50 21 0e 20 22 83 1a 08 c0 85 5d ..6}vP!. ".....]
      +0070 4f 5c 77 68 32 95 a9 a2 59 69 ea 19 34 08 ed 76 O\wh2...Yi..4..v
      +0080 a3 58 37 f2 0a 0c ba 4d bb 6f 82 94 d3 87 de c9 .X7....M.o......
      +0090 8f ef 34 2d 8f d0 0c 91 59 fd ea 6b cb bd a2 20 ..4-....Y..k... 
      +00a0 ed b9 76 d3 64 1b b3 3b f5 9b 61 d7 ab 26 9b 0d ..v.d..;..a..&..
      +00b0 a0 ea bf ad 2c ad 63 65 c6 70 c4 e5 8d 40 aa 08 ....,.ce.p...@..
      +00c0 45 66 e2 4d c9 46 00 33 43 e0 ba d6 80 29 21 5e Ef.M.F.3C....)!^
      +00d0 d1 9a bc 44 fa 4d 46 f9 25 80 40 b5 27 dd c5 02 ...D.MF.%.@.'...
      +00e0 f8 a4 9a cb cf 3f ef c7 cd 71 45 a5 35 b1 21 14 .....?...qE.5.!.
      +00f0 39 57 f8 0a 24 98 ea 15 e1 e3 cb 9d f2 4e ef 89 9W..$........N..
      +0100 97 c0 b2 96 9a 1e ad d0 9a 99 62 9f 13 2e       ..........b...
      +credssp_decrypt_public_key_echo: Signature for decryption:
      +0000 01 00 00 00 25 f8 2d 1e 4e 6a ec 4f 00 00 00 00 ....%.-.Nj.O....
      +ntlm_DecryptMessage: Data Buffer before decryption (length = 270)
      +0000 03 12 dd ea 47 b3 ff e1 66 08 f6 6b a0 62 42 67 ....G...f..k.bBg
      +0010 bf 3d 59 60 ef 52 b0 26 95 ed 84 48 44 bb 8d 65 .=Y`.R.&...HD..e
      +0020 cf e4 8e 6f 69 ae ed 44 bb 49 1d 2a 40 29 2b 13 ...oi..D.I.*@)+.
      +0030 42 1c eb b1 6c 8a 3b 80 d1 70 fd dd 79 e4 93 0b B...l.;..p..y...
      +0040 47 bd 3a 7e 31 66 4b 65 8d 5c 2a cd c2 09 7a 3b G.:~1fKe.\*...z;
      +0050 b2 fd 09 52 09 47 05 a4 6f 32 d1 76 b2 d4 59 e0 ...R.G..o2.v..Y.
      +0060 85 f1 36 7d 76 50 21 0e 20 22 83 1a 08 c0 85 5d ..6}vP!. ".....]
      +0070 4f 5c 77 68 32 95 a9 a2 59 69 ea 19 34 08 ed 76 O\wh2...Yi..4..v
      +0080 a3 58 37 f2 0a 0c ba 4d bb 6f 82 94 d3 87 de c9 .X7....M.o......
      +0090 8f ef 34 2d 8f d0 0c 91 59 fd ea 6b cb bd a2 20 ..4-....Y..k... 
      +00a0 ed b9 76 d3 64 1b b3 3b f5 9b 61 d7 ab 26 9b 0d ..v.d..;..a..&..
      +00b0 a0 ea bf ad 2c ad 63 65 c6 70 c4 e5 8d 40 aa 08 ....,.ce.p...@..
      +00c0 45 66 e2 4d c9 46 00 33 43 e0 ba d6 80 29 21 5e Ef.M.F.3C....)!^
      +00d0 d1 9a bc 44 fa 4d 46 f9 25 80 40 b5 27 dd c5 02 ...D.MF.%.@.'...
      +00e0 f8 a4 9a cb cf 3f ef c7 cd 71 45 a5 35 b1 21 14 .....?...qE.5.!.
      +00f0 39 57 f8 0a 24 98 ea 15 e1 e3 cb 9d f2 4e ef 89 9W..$........N..
      +0100 97 c0 b2 96 9a 1e ad d0 9a 99 62 9f 13 2e       ..........b...
      +
      +ntlm_DecryptMessage: RecvRc4Seal = 0xe
      +ntlm_DecryptMessage: Data Buffer after decryption (RC4(&context->RecvRc4Seal, length, data, data_buffer->pvBuffer))
      +0000 31 82 01 0a 02 82 01 01 00 a8 56 65 d3 ce 8a 54 1.........Ve...T
      +0010 4d 9d b0 84 31 19 71 7f dd 42 fb 2a 7a 72 13 a1 M...1.q..B.*zr..
      +0020 b9 72 bb d3 08 ad 7d 6c 15 65 03 d1 c4 54 c5 33 .r....}l.e...T.3
      +0030 6b 7d 69 89 5e fe e0 01 c0 7e 9b cb 5d 65 36 cd k}i.^....~..]e6.
      +0040 77 5d f3 7a 5b 29 44 72 d5 38 e2 cf b1 c7 78 9b w].z[)Dr.8....x.
      +0050 58 b9 17 7c b7 d6 c7 c7 bf 90 4e 7c 39 93 cb 2e X..|......N|9...
      +0060 e0 c2 33 2d a5 7e e0 7b b6 f9 91 32 b7 d4 85 b7 ..3-.~.{...2....
      +0070 35 2d 2b 00 6d f8 ea 8c 97 5f 51 1d 68 04 3c 79 5-+.m...._Q.h.<..
      +
      +ntlm_DecryptMessage: digest (HMAC_MD5(RecvSigningKey, seq_num + data)), seq_num: 0
      +0000 33 77 62 77 99 a9 14 40 1d 76 17 98 21 65 36 05 3wbw...@.v..!e6.
      +
      +ntlm_DecryptMessage: Checksum (RC4(&context->RecvRc4Seal, 8, digest, checksum), first 8 bytes of digest only!):
      +0000 25 f8 2d 1e 4e 6a ec 4f                         %.-.Nj.O
      +ntlm_DecryptMessage: Expected Signature (version + checksum + seq_num):
      +0000 01 00 00 00 25 f8 2d 1e 4e 6a ec 4f 00 00 00 00 ....%.-.Nj.O....
      +ntlm_DecryptMessage: RecvSeqNum (after increase): 1, SeqNo: 0.
      +credssp_decrypt_public_key_echo: Server public key (length = 270):
      +recv_seq_num: 1
      +Public key after decryption:
      +0000 30 82 01 0a 02 82 01 01 00 a8 56 65 d3 ce 8a 54 0.........Ve...T
      +0010 4d 9d b0 84 31 19 71 7f dd 42 fb 2a 7a 72 13 a1 M...1.q..B.*zr..
      +0020 b9 72 bb d3 08 ad 7d 6c 15 65 03 d1 c4 54 c5 33 .r....}l.e...T.3
      +0030 6b 7d 69 89 5e fe e0 01 c0 7e 9b cb 5d 65 36 cd k}i.^....~..]e6.
      +0040 77 5d f3 7a 5b 29 44 72 d5 38 e2 cf b1 c7 78 9b w].z[)Dr.8....x.
      +0050 58 b9 17 7c b7 d6 c7 c7 bf 90 4e 7c 39 93 cb 2e X..|......N|9...
      +0060 e0 c2 33 2d a5 7e e0 7b b6 f9 91 32 b7 d4 85 b7 ..3-.~.{...2....
      +0070 35 2d 2b 00 6d f8 ea 8c 97 5f 51 1d 68 04 3c 79 5-+.m...._Q.h.SendRc4Seal, length, data, data_buffer->pvBuffer))
      +0000 5a 26 69 b7 70 4b 41 55 82 43 a2 3a 72 6a e0 69 Z&i.pKAU.C.:rj.i
      +0010 9b 53 66 b6 70 75 c4 80 4f 61 e6 85 20 7e 3a 1b .Sf.pu..Oa.. ~:.
      +0020 a9 6a da 69 0b a5 4e 16 d9 da 71 5e ce e4 60 89 .j.i..N...q^..`.
      +0030 98 e6 30 59 d0 43 38 55 4a 97 e4 20 83 cd db 6b ..0Y.C8UJ.. ...k
      +0040 2b ca ac 9d 99 e6 2b ea 47 5f c1 4c 54 67 d1 75 +.....+.G_.LTg.u
      +0050 2c f7 d6 d6 04 8b 28 99 e9                      ,.....(..
      +
      +ntlm_EncryptMessage: Checksum ( RC4(&context->SendRc4Seal, 8, digest, checksum), first 8 bytes of digest only!):
      +0000 7a 7e b7 6c 49 3e 6b 7a                         z~.lI>kz
      +ntlm_EncryptMessage: signature (version + checksum + seq_num)
      +0000 01 00 00 00 7a 7e b7 6c 49 3e 6b 7a 01 00 00 00 ....z~.lI>kz....
      +
      +ntlm_EncryptMessage: SendSeqNum (after increase): 2, SeqNo: 1.
      +Unable to detect time zone
      +Closed from X11
      diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp.pfx b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp.pfx
      new file mode 100755
      index 00000000000..311ca43b71a
      Binary files /dev/null and b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp.pfx differ
      diff --git a/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
      old mode 100644
      new mode 100755
      index 283beac3456..a40cdb8ed79
      --- a/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
      +++ b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java
      @@ -16,9 +16,9 @@
       // under the License.
       package rdpclient;
       
      -import static rdpclient.MockServer.Packet.PacketType.CLIENT;
      -import static rdpclient.MockServer.Packet.PacketType.SERVER;
      -import static rdpclient.MockServer.Packet.PacketType.UPGRADE_TO_SSL;
      +import static streamer.debug.MockServer.Packet.PacketType.CLIENT;
      +import static streamer.debug.MockServer.Packet.PacketType.SERVER;
      +import static streamer.debug.MockServer.Packet.PacketType.UPGRADE_TO_SSL;
       
       import java.io.InputStream;
       import java.io.OutputStream;
      @@ -30,7 +30,8 @@ import javax.net.ssl.SSLSocket;
       import javax.net.ssl.SSLSocketFactory;
       
       import junit.framework.TestCase;
      -import rdpclient.MockServer.Packet;
      +import streamer.debug.MockServer;
      +import streamer.debug.MockServer.Packet;
       
       public class MockServerTest extends TestCase {
       
      diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
      index 56e2ea997fa..18d97d250af 100644
      --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
      +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyAjaxHandler.java
      @@ -79,6 +79,7 @@ public class ConsoleProxyAjaxHandler implements HttpHandler {
               String eventStr = queryMap.get("event");
               String console_url = queryMap.get("consoleurl");
               String console_host_session = queryMap.get("sessionref");
      +        String vm_locale = queryMap.get("locale");
       
               if (tag == null)
                   tag = "";
      @@ -126,6 +127,7 @@ public class ConsoleProxyAjaxHandler implements HttpHandler {
                   param.setTicket(ticket);
                   param.setClientTunnelUrl(console_url);
                   param.setClientTunnelSession(console_host_session);
      +            param.setLocale(vm_locale);
       
                   viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr);
               } catch (Exception e) {
      diff --git a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
      index daef040c9b1..eb38007de9a 100644
      --- a/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
      +++ b/services/console-proxy/server/src/com/cloud/consoleproxy/ConsoleProxyClientBase.java
      @@ -287,11 +287,11 @@ public abstract class ConsoleProxyClientBase implements ConsoleProxyClient, Cons
               }
       
               return getAjaxViewerPageContent(sbTileSequence.toString(), imgUrl, updateUrl, width, height, tileWidth, tileHeight, title,
      -            ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW, languages, guest);
      +            ConsoleProxy.keyboardType == ConsoleProxy.KEYBOARD_RAW, languages, guest, this.clientParam.getLocale());
           }
       
           private String getAjaxViewerPageContent(String tileSequence, String imgUrl, String updateUrl, int width, int height, int tileWidth, int tileHeight, String title,
      -        boolean rawKeyboard, List languages, String guest) {
      +        boolean rawKeyboard, List languages, String guest, String locale) {
       
               StringBuffer sbLanguages = new StringBuffer("");
               if (languages != null) {
      @@ -323,7 +323,7 @@ public abstract class ConsoleProxyClientBase implements ConsoleProxyClient, Cons
                       "
    • Japanese keyboard
    • ", "
    ", "
  • ", "
", "", "", "
", "