diff --git a/CHANGES b/CHANGES index 054e7b06615..e3e316e9dc9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,4 @@ + Apache CloudStack CHANGES ====================================== diff --git a/api/src/com/cloud/agent/api/HostVmStateReportEntry.java b/api/src/com/cloud/agent/api/HostVmStateReportEntry.java new file mode 100644 index 00000000000..7bcb50f361a --- /dev/null +++ b/api/src/com/cloud/agent/api/HostVmStateReportEntry.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.agent.api; + +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.PowerState; + +public class HostVmStateReportEntry { + VirtualMachine.PowerState state; + String host; + + public HostVmStateReportEntry() { + } + + public HostVmStateReportEntry(PowerState state, String host) { + this.state = state; + this.host = host; + } + + public PowerState getState() { + return state; + } + + public String getHost() { + return host; + } +} diff --git a/api/src/com/cloud/agent/api/to/LoadBalancerTO.java b/api/src/com/cloud/agent/api/to/LoadBalancerTO.java index df2f8a87490..4caa1b2f85c 100644 --- a/api/src/com/cloud/agent/api/to/LoadBalancerTO.java +++ b/api/src/com/cloud/agent/api/to/LoadBalancerTO.java @@ -33,6 +33,7 @@ import com.cloud.network.lb.LoadBalancingRule.LbCondition; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.utils.Pair; @@ -41,6 +42,7 @@ public class LoadBalancerTO { String srcIp; int srcPort; String protocol; + String lbProtocol; String algorithm; boolean revoked; boolean alreadyAdded; @@ -48,6 +50,7 @@ public class LoadBalancerTO { DestinationTO[] destinations; private StickinessPolicyTO[] stickinessPolicies; private HealthCheckPolicyTO[] healthCheckPolicies; + private LbSslCert sslCert; /* XXX: Should this be SslCertTO? */ private AutoScaleVmGroupTO autoScaleVmGroupTO; final static int MAX_STICKINESS_POLICIES = 1; final static int MAX_HEALTHCHECK_POLICIES = 1; @@ -66,6 +69,8 @@ public class LoadBalancerTO { this.inline = inline; this.destinations = new DestinationTO[destinations.size()]; this.stickinessPolicies = null; + this.sslCert = null; + this.lbProtocol = null; int i = 0; for (LbDestination destination : destinations) { this.destinations[i++] = new DestinationTO(destination.getIpAddress(), destination.getDestinationPortStart(), destination.isRevoked(), false); @@ -77,12 +82,12 @@ public class LoadBalancerTO { List stickinessPolicies) { this(id, srcIp, srcPort, protocol, algorithm, revoked, alreadyAdded, inline, arg_destinations, - stickinessPolicies, null); + stickinessPolicies, null, null, null); } public LoadBalancerTO(String id, String srcIp, int srcPort, String protocol, String algorithm, boolean revoked, boolean alreadyAdded, boolean inline, List arg_destinations, - List stickinessPolicies, List healthCheckPolicies) { + List stickinessPolicies, List healthCheckPolicies, LbSslCert sslCert, String lbProtocol) { this(id, srcIp, srcPort, protocol, algorithm, revoked, alreadyAdded, inline, arg_destinations); this.stickinessPolicies = null; this.healthCheckPolicies = null; @@ -117,6 +122,9 @@ public class LoadBalancerTO { if (index == 0) this.healthCheckPolicies = null; } + + this.sslCert = sslCert; + this.lbProtocol = lbProtocol; } protected LoadBalancerTO() { @@ -142,6 +150,10 @@ public class LoadBalancerTO { return protocol; } + public String getLbProtocol() { + return lbProtocol; + } + public boolean isRevoked() { return revoked; } @@ -178,6 +190,10 @@ public class LoadBalancerTO { return this.autoScaleVmGroupTO != null; } + public LbSslCert getSslCert(){ + return this.sslCert; + } + public static class StickinessPolicyTO { private String _methodName; private List> _paramsList; @@ -294,6 +310,8 @@ public class LoadBalancerTO { public String getMonitorState() { return monitorState; } + + } public static class CounterTO implements Serializable { private final String name; @@ -558,5 +576,4 @@ public class LoadBalancerTO { autoScaleVmGroupTO = new AutoScaleVmGroupTO(autoScaleVmGroup.getUuid(), autoScaleVmGroup.getMinMembers(), autoScaleVmGroup.getMaxMembers(), autoScaleVmGroup.getMemberPort(), autoScaleVmGroup.getInterval(), autoScalePolicyTOs, autoScaleVmProfileTO, autoScaleVmGroup.getState(), lbAutoScaleVmGroup.getCurrentState()); } - } diff --git a/api/src/com/cloud/agent/api/to/MonitorServiceTO.java b/api/src/com/cloud/agent/api/to/MonitorServiceTO.java new file mode 100644 index 00000000000..deced16eff9 --- /dev/null +++ b/api/src/com/cloud/agent/api/to/MonitorServiceTO.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 com.cloud.agent.api.to; + +import org.apache.cloudstack.api.InternalIdentity; + +public class MonitorServiceTO implements InternalIdentity { + long id; + String service; + String processname; + String serviceName; + String servicePath; + String pidFile; + boolean isDefault; + + protected MonitorServiceTO() { + } + + public MonitorServiceTO (String service, String processname, String serviceName, String servicepath, String pidFile, boolean isDefault) { + this.service = service; + this.processname = processname; + this.serviceName = serviceName; + this.servicePath = servicepath; + this.pidFile = pidFile; + this.isDefault = isDefault; + } + + + + public boolean isDefault() { + return isDefault; + } + + public String getPidFile() { + return pidFile; + } + + @Override + public long getId() { + return id; + } + + + public String getService() { + return service; + } + + public String getServiceName() { + return serviceName; + } + + public String getServicePath() { + return servicePath; + } + + public String getProcessname() { + return processname; + } + +} diff --git a/api/src/com/cloud/agent/manager/allocator/HostAllocator.java b/api/src/com/cloud/agent/manager/allocator/HostAllocator.java index 14525aa52de..f21455eeb31 100755 --- a/api/src/com/cloud/agent/manager/allocator/HostAllocator.java +++ b/api/src/com/cloud/agent/manager/allocator/HostAllocator.java @@ -38,7 +38,7 @@ public interface HostAllocator extends Adapter { /** * Determines which physical hosts are suitable to * allocate the guest virtual machines on - * + * * @param VirtualMachineProfile vmProfile * @param DeploymentPlan plan * @param GuestType type @@ -46,35 +46,57 @@ public interface HostAllocator extends Adapter { * @param int returnUpTo (use -1 to return all possible hosts) * @return List List of hosts that are suitable for VM allocation **/ - + public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo); - + /** - * Determines which physical hosts are suitable to - * allocate the guest virtual machines on - * - * @param VirtualMachineProfile vmProfile - * @param DeploymentPlan plan - * @param GuestType type - * @param ExcludeList avoid - * @param int returnUpTo (use -1 to return all possible hosts) - * @param boolean considerReservedCapacity (default should be true, set to false if host capacity calculation should not look at reserved capacity) - * @return List List of hosts that are suitable for VM allocation - **/ - + * Determines which physical hosts are suitable to allocate the guest + * virtual machines on + * + * Allocators must set any other hosts not considered for allocation in the + * ExcludeList avoid. Thus the avoid set and the list of hosts suitable, + * together must cover the entire host set in the cluster. + * + * @param VirtualMachineProfile + * vmProfile + * @param DeploymentPlan + * plan + * @param GuestType + * type + * @param ExcludeList + * avoid + * @param int returnUpTo (use -1 to return all possible hosts) + * @param boolean considerReservedCapacity (default should be true, set to + * false if host capacity calculation should not look at reserved + * capacity) + * @return List List of hosts that are suitable for VM allocation + **/ + public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity); /** - * Determines which physical hosts are suitable to - * allocate the guest virtual machines on + * Determines which physical hosts are suitable to allocate the guest + * virtual machines on * - * @param VirtualMachineProfile vmProfile - * @param DeploymentPlan plan - * @param GuestType type - * @param ExcludeList avoid - * @param List hosts + * Allocators must set any other hosts not considered for allocation in the + * ExcludeList avoid. Thus the avoid set and the list of hosts suitable, + * together must cover the entire host set in the cluster. + * + * + * @param VirtualMachineProfile + * vmProfile + * @param DeploymentPlan + * plan + * @param GuestType + * type + * @param ExcludeList + * avoid + * @param List + * hosts * @param int returnUpTo (use -1 to return all possible hosts) - * @param boolean considerReservedCapacity (default should be true, set to false if host capacity calculation should not look at reserved capacity) + * @param boolean considerReservedCapacity (default should be true, set to + * false if host capacity calculation should not look at reserved + * capacity) * @return List List of hosts that are suitable for VM allocation **/ public List allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List hosts, diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index d9f80eb2a2c..a3e45fadce2 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -133,6 +133,10 @@ public class EventTypes { public static final String EVENT_LB_HEALTHCHECKPOLICY_CREATE = "LB.HEALTHCHECKPOLICY.CREATE"; public static final String EVENT_LB_HEALTHCHECKPOLICY_DELETE = "LB.HEALTHCHECKPOLICY.DELETE"; public static final String EVENT_LOAD_BALANCER_UPDATE = "LB.UPDATE"; + public static final String EVENT_LB_CERT_UPLOAD = "LB.CERT.UPLOAD"; + public static final String EVENT_LB_CERT_DELETE = "LB.CERT.DELETE"; + public static final String EVENT_LB_CERT_ASSIGN = "LB.CERT.ASSIGN"; + public static final String EVENT_LB_CERT_REMOVE = "LB.CERT.REMOVE"; // Global Load Balancer rules public static final String EVENT_ASSIGN_TO_GLOBAL_LOAD_BALANCER_RULE = "GLOBAL.LB.ASSIGN"; @@ -511,6 +515,10 @@ public class EventTypes { entityEventDetails.put(EVENT_LB_STICKINESSPOLICY_CREATE, LoadBalancer.class.getName()); entityEventDetails.put(EVENT_LB_STICKINESSPOLICY_DELETE, LoadBalancer.class.getName()); entityEventDetails.put(EVENT_LOAD_BALANCER_UPDATE, LoadBalancer.class.getName()); + entityEventDetails.put(EVENT_LB_CERT_UPLOAD, LoadBalancer.class.getName()); + entityEventDetails.put(EVENT_LB_CERT_DELETE, LoadBalancer.class.getName()); + entityEventDetails.put(EVENT_LB_CERT_ASSIGN, LoadBalancer.class.getName()); + entityEventDetails.put(EVENT_LB_CERT_REMOVE, LoadBalancer.class.getName()); // Account events entityEventDetails.put(EVENT_ACCOUNT_DISABLE, Account.class.getName()); diff --git a/api/src/com/cloud/host/Host.java b/api/src/com/cloud/host/Host.java index 17b0ba86f7f..581bd47c5f4 100755 --- a/api/src/com/cloud/host/Host.java +++ b/api/src/com/cloud/host/Host.java @@ -106,6 +106,11 @@ public interface Host extends StateObject, Identity, InternalIdentity { */ Long getTotalMemory(); + /** + * @return # of cpu sockets in a machine. + */ + Integer getCpuSockets(); + /** * @return # of cores in a machine. Note two cpus with two cores each returns 4. */ diff --git a/api/src/com/cloud/network/MonitoringService.java b/api/src/com/cloud/network/MonitoringService.java new file mode 100644 index 00000000000..2a1754ab970 --- /dev/null +++ b/api/src/com/cloud/network/MonitoringService.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 com.cloud.network; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + + +/** + * Nic represents one nic on the VM. + */ +public interface MonitoringService extends ControlledEntity, Identity, InternalIdentity { + /** + * @return id in the CloudStack database + */ + enum Service { + Dhcp, + LoadBalancing, + Ssh, + Webserver, + } + long getId(); + String getService(); + String getServiceName(); + String getPidFile(); + String getServicePath(); +} diff --git a/api/src/com/cloud/network/Network.java b/api/src/com/cloud/network/Network.java index bda3326cb51..4eadd61aa25 100644 --- a/api/src/com/cloud/network/Network.java +++ b/api/src/com/cloud/network/Network.java @@ -116,6 +116,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Provider VirtualRouter = new Provider("VirtualRouter", false); public static final Provider JuniperContrail = new Provider("JuniperContrail", false); public static final Provider JuniperSRX = new Provider("JuniperSRX", true); + public static final Provider PaloAlto = new Provider("PaloAlto", true); public static final Provider F5BigIp = new Provider("F5BigIp", true); public static final Provider Netscaler = new Provider("Netscaler", true); public static final Provider ExternalDhcpServer = new Provider("ExternalDhcpServer", true); @@ -180,6 +181,7 @@ public interface Network extends ControlledEntity, StateObject, I public static final Capability SupportedTrafficDirection = new Capability("SupportedTrafficDirection"); public static final Capability SupportedEgressProtocols = new Capability("SupportedEgressProtocols"); public static final Capability HealthCheckPolicy = new Capability("HealthCheckPolicy"); + public static final Capability SslTermination = new Capability("SslTermination"); public static final Capability LbSchemes = new Capability("LbSchemes"); public static final Capability DhcpAccrossMultipleSubnets = new Capability("DhcpAccrossMultipleSubnets"); diff --git a/api/src/com/cloud/network/NetworkService.java b/api/src/com/cloud/network/NetworkService.java index 8f81697158d..b91a63d75f1 100755 --- a/api/src/com/cloud/network/NetworkService.java +++ b/api/src/com/cloud/network/NetworkService.java @@ -116,11 +116,11 @@ public interface NetworkService { long findPhysicalNetworkId(long zoneId, String tag, TrafficType trafficType); PhysicalNetworkTrafficType addTrafficTypeToPhysicalNetwork(Long physicalNetworkId, String trafficType, - String xenLabel, String kvmLabel, String vmwareLabel, String simulatorLabel, String vlan); + String xenLabel, String kvmLabel, String vmwareLabel, String simulatorLabel, String vlan, String hypervLabel); PhysicalNetworkTrafficType getPhysicalNetworkTrafficType(Long id); - PhysicalNetworkTrafficType updatePhysicalNetworkTrafficType(Long id, String xenLabel, String kvmLabel, String vmwareLabel); + PhysicalNetworkTrafficType updatePhysicalNetworkTrafficType(Long id, String xenLabel, String kvmLabel, String vmwareLabel, String hypervLabel); boolean deletePhysicalNetworkTrafficType(Long id); diff --git a/api/src/com/cloud/network/PhysicalNetworkTrafficType.java b/api/src/com/cloud/network/PhysicalNetworkTrafficType.java index a385b533af8..ad3b93d4406 100644 --- a/api/src/com/cloud/network/PhysicalNetworkTrafficType.java +++ b/api/src/com/cloud/network/PhysicalNetworkTrafficType.java @@ -37,4 +37,6 @@ public interface PhysicalNetworkTrafficType extends InternalIdentity, Identity { String getVmwareNetworkLabel(); String getSimulatorNetworkLabel(); + + String getHypervNetworkLabel(); } diff --git a/api/src/com/cloud/network/VirtualNetworkApplianceService.java b/api/src/com/cloud/network/VirtualNetworkApplianceService.java index 58eead2af07..fb5d12aed99 100644 --- a/api/src/com/cloud/network/VirtualNetworkApplianceService.java +++ b/api/src/com/cloud/network/VirtualNetworkApplianceService.java @@ -23,6 +23,9 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.router.VirtualRouter; import com.cloud.user.Account; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; + +import java.util.List; public interface VirtualNetworkApplianceService { /** @@ -66,4 +69,7 @@ public interface VirtualNetworkApplianceService { VirtualRouter findRouter(long routerId); + List upgradeRouterTemplate(UpgradeRouterTemplateCmd cmd); + + public static final String _minVRVersion = "4.2.0"; } diff --git a/api/src/com/cloud/network/lb/CertService.java b/api/src/com/cloud/network/lb/CertService.java new file mode 100644 index 00000000000..69d77d6ff4d --- /dev/null +++ b/api/src/com/cloud/network/lb/CertService.java @@ -0,0 +1,32 @@ +// 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.lb; + + +import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd; +import org.apache.cloudstack.api.response.SslCertResponse; + +import java.util.List; + +public interface CertService { + + public SslCertResponse uploadSslCert(UploadSslCertCmd certCmd); + public void deleteSslCert(DeleteSslCertCmd deleteSslCertCmd); + public List listSslCerts(ListSslCertsCmd listSslCertCmd); +} \ No newline at end of file diff --git a/api/src/com/cloud/network/lb/LoadBalancingRule.java b/api/src/com/cloud/network/lb/LoadBalancingRule.java index 4b37782a8c7..39c969c7001 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRule.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRule.java @@ -36,9 +36,11 @@ public class LoadBalancingRule { private List stickinessPolicies; private LbAutoScaleVmGroup autoScaleVmGroup; private List healthCheckPolicies; + private LbSslCert sslCert; + private String lbProtocol; public LoadBalancingRule(LoadBalancer lb, List destinations, - List stickinessPolicies, List healthCheckPolicies, Ip sourceIp) { + List stickinessPolicies, List healthCheckPolicies, Ip sourceIp) { this.lb = lb; this.destinations = destinations; this.stickinessPolicies = stickinessPolicies; @@ -46,6 +48,17 @@ public class LoadBalancingRule { this.sourceIp = sourceIp; } + public LoadBalancingRule(LoadBalancer lb, List destinations, + List stickinessPolicies, List healthCheckPolicies, Ip sourceIp, LbSslCert sslCert, String lbProtocol) { + this.lb = lb; + this.destinations = destinations; + this.stickinessPolicies = stickinessPolicies; + this.healthCheckPolicies = healthCheckPolicies; + this.sourceIp = sourceIp; + this.sslCert = sslCert; + this.lbProtocol = lbProtocol; + } + public long getId() { return lb.getId(); } @@ -90,6 +103,10 @@ public class LoadBalancingRule { return lb.getProtocol(); } + public String getLbProtocol() { + return this.lbProtocol; + } + public FirewallRule.Purpose getPurpose() { return FirewallRule.Purpose.LoadBalancing; } @@ -123,6 +140,10 @@ public class LoadBalancingRule { return healthCheckPolicies; } + public LbSslCert getLbSslCert(){ + return sslCert; + } + public interface Destination { String getIpAddress(); @@ -415,6 +436,44 @@ public class LoadBalancingRule { } } + public static class LbSslCert { + private String cert; + private String key; + private String password=null; + private String chain=null; + private boolean revoked; + + + public LbSslCert(String cert, String key, String password, String chain, boolean revoked) { + this.cert = cert; + this.key = key; + this.password = password; + this.chain = chain; + this.revoked = revoked; + } + + public String getCert() { + + return cert; + } + + public String getKey() { + return key; + } + + public String getPassword() { + return password; + } + + public String getChain() { + return chain; + } + + public boolean isRevoked(){ + return revoked; + } + } + public Ip getSourceIp() { return sourceIp; } diff --git a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java index 59d5c8dec05..298426b27b4 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRulesService.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRulesService.java @@ -52,7 +52,7 @@ public interface LoadBalancingRulesService { */ LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd, Long ipAddrId, String protocol, String algorithm, - long networkId, long lbOwnerId, boolean openFirewall) throws NetworkRuleConflictException, InsufficientAddressCapacityException; + long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol) throws NetworkRuleConflictException, InsufficientAddressCapacityException; LoadBalancer updateLoadBalancerRule(UpdateLoadBalancerRuleCmd cmd); @@ -94,10 +94,16 @@ public interface LoadBalancingRulesService { */ boolean assignToLoadBalancer(long lbRuleId, List vmIds); + boolean assignSSLCertToLoadBalancerRule(Long lbRuleId, String certName, String publicCert, String privateKey); + boolean removeFromLoadBalancer(long lbRuleId, List vmIds); boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException; + boolean assignCertToLoadBalancer(long lbRuleId, Long CertId); + boolean removeCertFromLoadBalancer(long lbRuleId); + + /** * List instances that have either been applied to a load balancer or are eligible to be assigned to a load * balancer. diff --git a/api/src/com/cloud/network/lb/SslCert.java b/api/src/com/cloud/network/lb/SslCert.java new file mode 100644 index 00000000000..f7a7c4b790e --- /dev/null +++ b/api/src/com/cloud/network/lb/SslCert.java @@ -0,0 +1,16 @@ +package com.cloud.network.lb; + + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface SslCert extends InternalIdentity, Identity, ControlledEntity { + + public String getCertificate(); + public String getKey() ; + public String getChain(); + public String getPassword(); + public String getFingerPrint(); + +} diff --git a/api/src/com/cloud/network/router/VirtualRouter.java b/api/src/com/cloud/network/router/VirtualRouter.java index 9e90e8e262f..a87521876fc 100755 --- a/api/src/com/cloud/network/router/VirtualRouter.java +++ b/api/src/com/cloud/network/router/VirtualRouter.java @@ -41,4 +41,5 @@ public interface VirtualRouter extends VirtualMachine { * @return */ Long getVpcId(); + String getTemplateVersion(); } diff --git a/api/src/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/com/cloud/network/vpc/VpcProvisioningService.java index 70676ce07ab..e8094bca98a 100644 --- a/api/src/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/com/cloud/network/vpc/VpcProvisioningService.java @@ -20,20 +20,21 @@ import java.util.List; import java.util.Map; public interface VpcProvisioningService { - + public VpcOffering getVpcOffering(long vpcOfferingId); - - public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders); - + + public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, + Map> serviceProviders, Long serviceOfferingId); + List listVpcOfferings(Long id, String name, String displayText, List supportedServicesStr, Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal); - + /** * @param offId * @return */ public boolean deleteVpcOffering(long offId); - + /** * @param vpcOffId * @param vpcOfferingName diff --git a/api/src/com/cloud/offering/DiskOffering.java b/api/src/com/cloud/offering/DiskOffering.java index a4c7dc30e00..fa6d66b1ab0 100644 --- a/api/src/com/cloud/offering/DiskOffering.java +++ b/api/src/com/cloud/offering/DiskOffering.java @@ -89,4 +89,7 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId Long getIopsWriteRate(); + void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve); + + Integer getHypervisorSnapshotReserve(); } diff --git a/api/src/com/cloud/offering/ServiceOffering.java b/api/src/com/cloud/offering/ServiceOffering.java index 9f7bf8e1315..e4c65cdaf0c 100755 --- a/api/src/com/cloud/offering/ServiceOffering.java +++ b/api/src/com/cloud/offering/ServiceOffering.java @@ -61,17 +61,17 @@ public interface ServiceOffering extends DiskOffering, InfrastructureEntity, Int /** * @return # of cpu. */ - int getCpu(); + Integer getCpu(); /** * @return speed in mhz */ - int getSpeed(); + Integer getSpeed(); /** * @return ram size in megabytes */ - int getRamSize(); + Integer getRamSize(); /** * @return Does this service plan offer HA? @@ -117,4 +117,6 @@ public interface ServiceOffering extends DiskOffering, InfrastructureEntity, Int String getSystemVmType(); String getDeploymentPlanner(); + + boolean isDynamic(); } diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index 57e04944d26..dd59f12f4d4 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -185,4 +185,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba void setReservationId(String reserv); Storage.ImageFormat getFormat(); Long getVmSnapshotChainSize(); + + void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve); + + Integer getHypervisorSnapshotReserve(); } diff --git a/api/src/com/cloud/storage/VolumeApiService.java b/api/src/com/cloud/storage/VolumeApiService.java index 4806ae7c06f..28656281236 100644 --- a/api/src/com/cloud/storage/VolumeApiService.java +++ b/api/src/com/cloud/storage/VolumeApiService.java @@ -79,7 +79,7 @@ public interface VolumeApiService { Volume detachVolumeFromVM(DetachVolumeCmd cmmd); - Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) + Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm) throws ResourceAllocationException; Snapshot allocSnapshot(Long volumeId, Long policyId) diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 0b142e83b72..6fc8b54cfa1 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -136,6 +136,7 @@ public interface UserVmService { * Creates a Basic Zone User VM in the database and returns the VM to the * caller. * + * * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -181,13 +182,15 @@ public interface UserVmService { * @param displayVm * - Boolean flag whether to the display the vm to the end user or not * @param affinityGroupIdList - * * @param accountName * - an optional account for the virtual machine. Must be used * with domainId * @param domainId * - an optional domainId for the virtual machine. If the account * parameter is used, domainId must also be used + * @param cpuSpeed + * @param memory + * @param cpuNumber * @return UserVm object if successful. * * @throws InsufficientCapacityException @@ -201,15 +204,16 @@ public interface UserVmService { * @throws InsufficientResourcesException */ 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 defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList) + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, + IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootdisksize) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is enabled) in * the database and returns the VM to the caller. * + * * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -257,13 +261,15 @@ public interface UserVmService { * @param displayVm * - Boolean flag whether to the display the vm to the end user or not * @param affinityGroupIdList - * * @param accountName * - an optional account for the virtual machine. Must be used * with domainId * @param domainId * - an optional domainId for the virtual machine. If the account * parameter is used, domainId must also be used + * @param CpuSpeed + * @param memory + * @param cpuNumber * @return UserVm object if successful. * * @throws InsufficientCapacityException @@ -277,14 +283,15 @@ public interface UserVmService { * @throws InsufficientResourcesException */ 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) + 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, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootdisksize) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Creates a User VM in Advanced Zone (Security Group feature is disabled) * in the database and returns the VM to the caller. * + * * @param zone * - availability zone for the virtual machine * @param serviceOffering @@ -329,15 +336,17 @@ public interface UserVmService { * @param displayVm * - Boolean flag whether to the display the vm to the end user or not * @param affinityGroupIdList - * * @param accountName * - an optional account for the virtual machine. Must be used * with domainId * @param domainId * - an optional domainId for the virtual machine. If the account * parameter is used, domainId must also be used + * @param cpuSpeed + * @param memory + * @param cpuNumber * @return UserVm object if successful. - * + * * @throws InsufficientCapacityException * if there is insufficient capacity to deploy the VM. * @throws ConcurrentOperationException @@ -349,9 +358,9 @@ public interface UserVmService { * @throws InsufficientResourcesException */ 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) + 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, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootdkisksize) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java index 0a968bc415f..9a8d88330c7 100755 --- a/api/src/com/cloud/vm/VirtualMachine.java +++ b/api/src/com/cloud/vm/VirtualMachine.java @@ -33,6 +33,12 @@ import com.cloud.utils.fsm.StateObject; */ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, InternalIdentity, StateObject { + public enum PowerState { + PowerUnknown, + PowerOn, + PowerOff, + } + public enum State { Starting(true, "VM is being started. At this state, you should find host id filled which means it's being started on that host."), Running(false, "VM is running. host id has the host that it is running on."), @@ -111,6 +117,15 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I s_fsm.addTransition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging); s_fsm.addTransition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging); s_fsm.addTransition(State.Error, VirtualMachine.Event.ExpungeOperation, State.Expunging); + + s_fsm.addTransition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); + s_fsm.addTransition(State.Stopped, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); + s_fsm.addTransition(State.Running, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); + s_fsm.addTransition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOnReport, State.Running); + s_fsm.addTransition(State.Starting, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); + s_fsm.addTransition(State.Stopping, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); + s_fsm.addTransition(State.Running, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); + s_fsm.addTransition(State.Migrating, VirtualMachine.Event.FollowAgentPowerOffReport, State.Stopped); } public static boolean isVmStarted(State oldState, Event e, State newState) { @@ -179,6 +194,10 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I AgentReportMigrated, RevertRequested, SnapshotRequested, + + // added for new VMSync logic + FollowAgentPowerOnReport, + FollowAgentPowerOffReport, }; public enum Type { diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 3b2bc67534f..82b2af69759 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -36,6 +36,9 @@ public class ApiConstants { public static final String CATEGORY = "category"; public static final String CAN_REVERT = "canrevert"; public static final String CERTIFICATE = "certificate"; + public static final String CERTIFICATE_CHAIN = "certchain"; + public static final String CERTIFICATE_FINGERPRINT = "fingerprint"; + public static final String CERTIFICATE_ID = "certid"; public static final String PRIVATE_KEY = "privatekey"; public static final String DOMAIN_SUFFIX = "domainsuffix"; public static final String DNS_SEARCH_ORDER = "dnssearchorder"; @@ -54,12 +57,14 @@ public class ApiConstants { public static final String CUSTOMIZED_IOPS = "customizediops"; public static final String MIN_IOPS = "miniops"; public static final String MAX_IOPS = "maxiops"; + public static final String HYPERVISOR_SNAPSHOT_RESERVE = "hypervisorsnapshotreserve"; public static final String DESCRIPTION = "description"; public static final String DESTINATION_ZONE_ID = "destzoneid"; public static final String DETAILS = "details"; public static final String DEVICE_ID = "deviceid"; public static final String DISK_OFFERING_ID = "diskofferingid"; public static final String DISK_SIZE = "disksize"; + public static final String ROOT_DISK_SIZE = "rootdisksize"; public static final String DISPLAY_NAME = "displayname"; public static final String DISPLAY_NETWORK = "displaynetwork"; public static final String DISPLAY_NIC = "displaynic"; @@ -208,6 +213,7 @@ public class ApiConstants { public static final String SNAPSHOT_ID = "snapshotid"; public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid"; public static final String SNAPSHOT_TYPE = "snapshottype"; + public static final String SNAPSHOT_QUIESCEVM = "quiescevm"; public static final String SOURCE_ZONE_ID = "sourcezoneid"; public static final String START_DATE = "startdate"; public static final String START_IP = "startip"; @@ -349,6 +355,7 @@ public class ApiConstants { public static final String XEN_NETWORK_LABEL = "xennetworklabel"; public static final String KVM_NETWORK_LABEL = "kvmnetworklabel"; public static final String VMWARE_NETWORK_LABEL = "vmwarenetworklabel"; + public static final String HYPERV_NETWORK_LABEL = "hypervnetworklabel"; public static final String NETWORK_SERVICE_PROVIDER_ID = "nspid"; public static final String SERVICE_LIST = "servicelist"; public static final String CAN_ENABLE_INDIVIDUAL_SERVICE = "canenableindividualservice"; @@ -526,6 +533,7 @@ public class ApiConstants { public static final String EXPUNGE = "expunge"; public static final String FOR_DISPLAY = "fordisplay"; public static final String PASSIVE = "passive"; + public static final String VERSION = "version"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/ResponseGenerator.java b/api/src/org/apache/cloudstack/api/ResponseGenerator.java index a8de31d8c2d..832f6e3a010 100644 --- a/api/src/org/apache/cloudstack/api/ResponseGenerator.java +++ b/api/src/org/apache/cloudstack/api/ResponseGenerator.java @@ -60,6 +60,7 @@ import org.apache.cloudstack.api.response.IpForwardingRuleResponse; import org.apache.cloudstack.api.response.IsolationMethodResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; @@ -99,6 +100,7 @@ import org.apache.cloudstack.api.response.TemplatePermissionsResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.TrafficMonitorResponse; import org.apache.cloudstack.api.response.TrafficTypeResponse; +import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -446,4 +448,6 @@ public interface ResponseGenerator { IsolationMethodResponse createIsolationMethodResponse(IsolationType method); + ListResponse createUpgradeRouterTemplateResponse(List jobIds); + } diff --git a/api/src/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java index 95b9ac33799..c6f1e70ae9c 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/host/ListHostsCmd.java @@ -21,6 +21,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Map; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiCommandJobType; import org.apache.cloudstack.api.ApiConstants; @@ -88,6 +89,9 @@ public class ListHostsCmd extends BaseListCmd { @Parameter(name=ApiConstants.HA_HOST, type=CommandType.BOOLEAN, description="if true, list only hosts dedicated to HA") private Boolean haHost; + @Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, description="hypervisor type of host: XenServer,KVM,VMware,Hyperv,BareMetal,Simulator") + private String hypervisor; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -128,6 +132,10 @@ public class ListHostsCmd extends BaseListCmd { return virtualMachineId; } + public HypervisorType getHypervisor() { + return HypervisorType.getType(hypervisor); + } + public EnumSet getDetails() throws InvalidParameterValueException { EnumSet dv; if (viewDetails==null || viewDetails.size() <=0){ diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java index 4983255389d..a7906f41522 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/AddNetworkDeviceCmd.java @@ -47,8 +47,9 @@ public class AddNetworkDeviceCmd extends BaseCmd { // ////////////// API parameters ///////////////////// // /////////////////////////////////////////////////// + @Inject ExternalNetworkDeviceManager nwDeviceMgr; - @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall") + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") diff --git a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java index 0b7836de3a8..5278ba9efd7 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/network/ListNetworkDeviceCmd.java @@ -51,7 +51,7 @@ public class ListNetworkDeviceCmd extends BaseListCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall") + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, description = "Network device type, now supports ExternalDhcp, PxeServer, NetscalerMPXLoadBalancer, NetscalerVPXLoadBalancer, NetscalerSDXLoadBalancer, F5BigIpLoadBalancer, JuniperSRXFirewall, PaloAltoFirewall") private String type; @Parameter(name = ApiConstants.NETWORK_DEVICE_PARAMETER_LIST, type = CommandType.MAP, description = "parameters for network device") diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index 47415911e9b..77aeb542819 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -86,6 +86,9 @@ public class CreateDiskOfferingCmd extends BaseCmd { @Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering") private Long maxIops; + @Parameter(name=ApiConstants.HYPERVISOR_SNAPSHOT_RESERVE, type=CommandType.INTEGER, required=false, description="Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)") + private Integer hypervisorSnapshotReserve; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -150,6 +153,10 @@ public class CreateDiskOfferingCmd extends BaseCmd { return displayOffering; } + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index decac29ae6b..63f19cc7057 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -41,17 +41,17 @@ public class CreateServiceOfferingCmd extends BaseCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.CPU_NUMBER, type=CommandType.LONG, required=true, description="the CPU number of the service offering") - private Long cpuNumber; + @Parameter(name=ApiConstants.CPU_NUMBER, type=CommandType.INTEGER, required=false, description="the CPU number of the service offering") + private Integer cpuNumber; - @Parameter(name=ApiConstants.CPU_SPEED, type=CommandType.LONG, required=true, description="the CPU speed of the service offering in MHz.") - private Long cpuSpeed; + @Parameter(name=ApiConstants.CPU_SPEED, type=CommandType.INTEGER, required=false, description="the CPU speed of the service offering in MHz.") + private Integer cpuSpeed; @Parameter(name=ApiConstants.DISPLAY_TEXT, type=CommandType.STRING, required=true, description="the display text of the service offering") private String displayText; - @Parameter(name=ApiConstants.MEMORY, type=CommandType.LONG, required=true, description="the total memory of the service offering in MB") - private Long memory; + @Parameter(name=ApiConstants.MEMORY, type=CommandType.INTEGER, required=false, description="the total memory of the service offering in MB") + private Integer memory; @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, required=true, description="the name of the service offering") private String serviceOfferingName; @@ -109,11 +109,11 @@ public class CreateServiceOfferingCmd extends BaseCmd { /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// - public Long getCpuNumber() { + public Integer getCpuNumber() { return cpuNumber; } - public Long getCpuSpeed() { + public Integer getCpuSpeed() { return cpuSpeed; } @@ -121,7 +121,7 @@ public class CreateServiceOfferingCmd extends BaseCmd { return displayText; } - public Long getMemory() { + public Integer getMemory() { return memory; } @@ -173,6 +173,11 @@ public class CreateServiceOfferingCmd extends BaseCmd { return deploymentPlanner; } + public boolean getCustomized() { + return (cpuNumber == null || memory == null || cpuSpeed == null); + } + + public Map getDetails() { if (details == null || details.isEmpty()) { return null; diff --git a/api/src/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java b/api/src/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java index d7f59b96e63..2752a13b176 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/router/ListRoutersCmd.java @@ -75,7 +75,10 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { @Parameter(name=ApiConstants.FOR_VPC, type=CommandType.BOOLEAN, description="if true is passed for this parameter, list only VPC routers") private Boolean forVpc; - + + @Parameter(name=ApiConstants.VERSION, type=CommandType.STRING, description="list virtual router elements by version") + private String version; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -115,7 +118,11 @@ public class ListRoutersCmd extends BaseListProjectAndAccountResourcesCmd { public Boolean getForVpc() { return forVpc; } - + + public String getVersion() { + return version; + } + public String getRole() { return Role.VIRTUAL_ROUTER.toString(); } diff --git a/api/src/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java b/api/src/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.java new file mode 100644 index 00000000000..1db22bc0842 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/router/UpgradeRouterTemplateCmd.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 org.apache.cloudstack.api.command.admin.router; + + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.router.VirtualRouter; +import com.cloud.user.Account; +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.BaseResponse; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.AccountResponse; +import org.apache.cloudstack.api.response.ClusterResponse; +import org.apache.cloudstack.api.response.DomainResponse; +import org.apache.cloudstack.api.response.DomainRouterResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.PodResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; + +import java.util.List; +import java.util.logging.Logger; + +@APICommand(name = "upgradeRouterTemplate", description="Upgrades router to use newer template", responseObject=BaseResponse.class) +public class UpgradeRouterTemplateCmd extends org.apache.cloudstack.api.BaseCmd { + public static final Logger s_logger = Logger.getLogger(UpgradeRouterTemplateCmd.class.getName()); + private static final String s_name = "upgraderoutertemplateresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name= ApiConstants.ID, type=CommandType.UUID, entityType = DomainRouterResponse.class, + description="upgrades router with the specified Id") + private Long id; + + @Parameter(name=ApiConstants.CLUSTER_ID, type=CommandType.UUID, entityType= ClusterResponse.class, + description="upgrades all routers within the specified cluster") + private Long clusterId; + + @Parameter(name=ApiConstants.POD_ID, type=CommandType.UUID, entityType=PodResponse.class, + description="upgrades all routers within the specified pod") + private Long podId; + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType=ZoneResponse.class, + description="upgrades all routers within the specified zone") + private Long zoneId; + + @Parameter(name=ApiConstants.ACCOUNT_ID, type=CommandType.UUID, entityType = AccountResponse.class, + description="upgrades all routers owned by the specified account") + private Long accountId; + + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, + description="upgrades all routers owned by the specified domain") + private Long domainId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + public Long getClusterId() { + return clusterId; + } + + public Long getPodId() { + return podId; + } + + public Long getZoneId() { + return zoneId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getDomainId() { + return domainId; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + public ApiCommandJobType getInstanceType() { + return ApiCommandJobType.DomainRouter; + } + + public Long getInstanceId() { + return getId(); + } + + @Override + public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + CallContext.current().setEventDetails("Upgrading router template"); + List result = _routerService.upgradeRouterTemplate(this); + if (result != null){ + ListResponse response = _responseGenerator.createUpgradeRouterTemplateResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to upgrade router template"); + } + } +} diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java index 3957e6948f3..f6e4319fdee 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/usage/AddTrafficTypeCmd.java @@ -60,6 +60,9 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { @Parameter(name=ApiConstants.VMWARE_NETWORK_LABEL, type=CommandType.STRING, description="The network name label of the physical device dedicated to this traffic on a VMware host") private String vmwareLabel; + @Parameter(name=ApiConstants.HYPERV_NETWORK_LABEL, type=CommandType.STRING, description="The network name label of the physical device dedicated to this traffic on a Hyperv host") + private String hypervLabel; + @Parameter(name=ApiConstants.VLAN, type=CommandType.STRING, description="The VLAN id to be used for Management traffic by VMware host") private String vlan; @@ -88,6 +91,10 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { return vmwareLabel; } + public String getHypervLabel() { + return hypervLabel; + } + public String getSimulatorLabel() { //simulators will have no labels return null; @@ -130,7 +137,7 @@ public class AddTrafficTypeCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - PhysicalNetworkTrafficType result = _networkService.addTrafficTypeToPhysicalNetwork(getPhysicalNetworkId(), getTrafficType(), getXenLabel(), getKvmLabel(), getVmwareLabel(), getSimulatorLabel(), getVlan()); + PhysicalNetworkTrafficType result = _networkService.addTrafficTypeToPhysicalNetwork(getPhysicalNetworkId(), getTrafficType(), getXenLabel(), getKvmLabel(), getVmwareLabel(), getSimulatorLabel(), getVlan(), getHypervLabel()); if (result != null) { setEntityId(result.getId()); setEntityUuid(result.getUuid()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java b/api/src/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java index e54b87c0591..c1e36aae802 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/usage/UpdateTrafficTypeCmd.java @@ -23,6 +23,7 @@ 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.BaseCmd.CommandType; import org.apache.cloudstack.api.response.TrafficTypeResponse; import org.apache.log4j.Logger; @@ -53,6 +54,8 @@ public class UpdateTrafficTypeCmd extends BaseAsyncCmd { @Parameter(name=ApiConstants.VMWARE_NETWORK_LABEL, type=CommandType.STRING, description="The network name label of the physical device dedicated to this traffic on a VMware host") private String vmwareLabel; + @Parameter(name=ApiConstants.HYPERV_NETWORK_LABEL, type=CommandType.STRING, description="The network name label of the physical device dedicated to this traffic on a Hyperv host") + private String hypervLabel; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -73,6 +76,10 @@ public class UpdateTrafficTypeCmd extends BaseAsyncCmd { return vmwareLabel; } + public String getHypervLabel() { + return hypervLabel; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -89,7 +96,7 @@ public class UpdateTrafficTypeCmd extends BaseAsyncCmd { @Override public void execute(){ - PhysicalNetworkTrafficType result = _networkService.updatePhysicalNetworkTrafficType(getId(), getXenLabel(), getKvmLabel(), getVmwareLabel()); + PhysicalNetworkTrafficType result = _networkService.updatePhysicalNetworkTrafficType(getId(), getXenLabel(), getKvmLabel(), getVmwareLabel(), getHypervLabel()); if (result != null) { TrafficTypeResponse response = _responseGenerator.createTrafficTypeResponse(result); response.setResponseName(getCommandName()); diff --git a/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java index 6da4b6c9034..6b3e4ddf3ad 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vm/AssignVMCmd.java @@ -107,7 +107,7 @@ public class AssignVMCmd extends BaseCmd { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to move vm"); } UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", userVm).get(0); - response.setResponseName(DeployVMCmd.getResultObjectName()); + response.setResponseName(getCommandName()); this.setResponseObject(response); }catch (Exception e){ e.printStackTrace(); diff --git a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index 4a3a92a211c..7a8debab6b3 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -16,7 +16,14 @@ // under the License. package org.apache.cloudstack.api.command.admin.vpc; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -24,8 +31,8 @@ import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.BaseAsyncCreateCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.VpcOfferingResponse; -import org.apache.log4j.Logger; import com.cloud.event.EventTypes; import com.cloud.exception.ResourceAllocationException; @@ -56,6 +63,9 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ "If not specified, the provider for the service will be mapped to the default provider on the physical network") private Map serviceProviderList; + @Parameter(name = ApiConstants.SERVICE_OFFERING_ID, type = CommandType.UUID, entityType = ServiceOfferingResponse.class, description = "the ID of the service offering for the VPC router appliance") + private Long serviceOfferingId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -96,12 +106,17 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ return serviceProviderMap; } + public Long getServiceOfferingId() { + return serviceOfferingId; + } + @Override public void create() throws ResourceAllocationException { - VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(getVpcOfferingName(), getDisplayText(), getSupportedServices(), getServiceProviders()); + VpcOffering vpcOff = _vpcProvSvc.createVpcOffering(getVpcOfferingName(), getDisplayText(), getSupportedServices(), + getServiceProviders(), getServiceOfferingId()); if (vpcOff != null) { - this.setEntityId(vpcOff.getId()); - this.setEntityUuid(vpcOff.getUuid()); + setEntityId(vpcOff.getId()); + setEntityUuid(vpcOff.getUuid()); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create a VPC offering"); } @@ -109,11 +124,11 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ @Override public void execute() { - VpcOffering vpc = _vpcProvSvc.getVpcOffering(this.getEntityId()); + VpcOffering vpc = _vpcProvSvc.getVpcOffering(getEntityId()); if (vpc != null) { VpcOfferingResponse response = _responseGenerator.createVpcOfferingResponse(vpc); response.setResponseName(getCommandName()); - this.setResponseObject(response); + setResponseObject(response); } else { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create VPC offering"); } @@ -122,7 +137,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ @Override public String getEventType() { - return EventTypes.EVENT_VPC_OFFERING_CREATE; + return EventTypes.EVENT_VPC_OFFERING_CREATE; } @Override @@ -137,7 +152,7 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd{ @Override public long getEntityOwnerId() { - return Account.ACCOUNT_ID_SYSTEM; + return Account.ACCOUNT_ID_SYSTEM; } } diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java new file mode 100644 index 00000000000..253c015469e --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/AssignCertToLoadBalancerCmd.java @@ -0,0 +1,92 @@ +// 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.user.loadbalancer; + +import com.cloud.event.EventTypes; +import com.cloud.exception.*; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SslCertResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +@APICommand(name = "assignCertToLoadBalancer", description = "Assigns a certificate to a Load Balancer Rule", responseObject = SuccessResponse.class) +public class AssignCertToLoadBalancerCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger + .getLogger(AssignCertToLoadBalancerCmd.class.getName()); + + private static final String s_name = "assignCertToLoadBalancer"; + + + @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, + required = true, description = "the ID of the load balancer rule") + Long lbRuleId; + + @Parameter(name = ApiConstants.CERTIFICATE_ID, type = CommandType.UUID, entityType = SslCertResponse.class, + required = true, description = "the ID of the certificate") + Long certId; + + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + //To change body of implemented methods use File | Settings | File Templates. + if ( _lbService.assignCertToLoadBalancer( getLbRuleId(), getCertId()) ) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to assign certificate to loadbalancer"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_CERT_ASSIGN; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public String getEventDescription() { + return "Assigining a certificate to a loadbalancer"; + } + + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + if (lb == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return lb.getAccountId(); + } + + + + public Long getCertId(){ + return certId; + } + + public Long getLbRuleId(){ + return lbRuleId; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java index a36843634cc..e4cc0e613db 100644 --- a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/CreateLoadBalancerRuleCmd.java @@ -102,6 +102,9 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements "rule will be created for. Required when public Ip address is not associated with any Guest network yet (VPC case)") private Long networkId; + @Parameter(name=ApiConstants.PROTOCOL, type=CommandType.STRING, description="The protocol for the LB") + private String lbProtocol; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -227,6 +230,10 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements return null; } + public String getLbProtocol(){ + return lbProtocol; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -282,7 +289,7 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCreateCmd /*implements try { LoadBalancer result = _lbService.createPublicLoadBalancerRule(getXid(), getName(), getDescription(), getSourcePortStart(), getSourcePortEnd(), getDefaultPortStart(), getDefaultPortEnd(), getSourceIpAddressId(), getProtocol(), getAlgorithm(), - getNetworkId(), getEntityOwnerId(), getOpenFirewall()); + getNetworkId(), getEntityOwnerId(), getOpenFirewall(), getLbProtocol()); this.setEntityId(result.getId()); this.setEntityUuid(result.getUuid()); } catch (NetworkRuleConflictException e) { diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/DeleteSslCertCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/DeleteSslCertCmd.java new file mode 100644 index 00000000000..19e36d2b7b6 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/DeleteSslCertCmd.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 org.apache.cloudstack.api.command.user.loadbalancer; + + +import com.cloud.network.lb.CertService; +import org.apache.cloudstack.api.response.SslCertResponse; +import com.cloud.exception.*; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +@APICommand(name = "deleteSslCert", description="Delete a certificate to cloudstack", responseObject=SuccessResponse.class) +public class DeleteSslCertCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteSslCertCmd.class.getName()); + + private static final String s_name = "deletesslcertresponse"; + + @Inject + CertService _certService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name= ApiConstants.ID, type=CommandType.UUID, entityType = SslCertResponse.class, required=true, description="Id of SSL certificate") + private Long id; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + try { + _certService.deleteSslCert(this); + SuccessResponse rsp = new SuccessResponse(); + rsp.setResponseName(getCommandName()); + rsp.setObjectName("success"); + this.setResponseObject(rsp); + } catch (Exception e) { + throw new CloudRuntimeException(e); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java new file mode 100644 index 00000000000..3df28a39b80 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/ListSslCertsCmd.java @@ -0,0 +1,106 @@ +// 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.user.loadbalancer; + + +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SslCertResponse; +import com.cloud.network.lb.CertService; +import com.cloud.utils.exception.CloudRuntimeException; +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.response.AccountResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; +import java.util.List; + +@APICommand(name = "listSslCerts", description="Lists SSL certificates", responseObject=SslCertResponse.class) +public class ListSslCertsCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteSslCertCmd.class.getName()); + + private static final String s_name = "listsslcertsresponse"; + + @Inject + CertService _certService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name= ApiConstants.CERTIFICATE_ID, type=CommandType.UUID, entityType = SslCertResponse.class, required=false, description="Id of SSL certificate") + private Long certId; + + @Parameter(name= ApiConstants.ACCOUNT_ID, type=CommandType.UUID, entityType = AccountResponse.class, required=false, description="Account Id") + private Long accountId; + + @Parameter(name= ApiConstants.LBID, type=CommandType.UUID, entityType = FirewallRuleResponse.class, required=false, description="Loadbalancer Rule Id") + private Long lbId; + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getCertId() { + return certId; + } + + public Long getAccountId() { + return accountId; + } + + public Long getLbId(){ + return lbId; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute(){ + + + try { + List certResponseList = _certService.listSslCerts(this); + ListResponse response = new ListResponse(); + + response.setResponses(certResponseList); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + + } catch (Exception e) { + throw new CloudRuntimeException(e); + } + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.java new file mode 100644 index 00000000000..059d16320f6 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/RemoveCertFromLoadBalancerCmd.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 org.apache.cloudstack.api.command.user.loadbalancer; + + +import com.cloud.event.EventTypes; +import com.cloud.exception.*; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.Account; +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + + +@APICommand(name = "removeCertFromLoadBalancer", description = "Removes a certificate from a Load Balancer Rule", responseObject = SuccessResponse.class) +public class RemoveCertFromLoadBalancerCmd extends BaseAsyncCmd{ + + public static final Logger s_logger = Logger.getLogger(RemoveCertFromLoadBalancerCmd.class.getName()); + + private static final String s_name = "removeCertFromLoadBalancer"; + + + @Parameter(name = ApiConstants.LBID, type = CommandType.UUID, entityType = FirewallRuleResponse.class, + required = true, description = "the ID of the load balancer rule") + Long lbRuleId; + + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + boolean result = _lbService.removeCertFromLoadBalancer(getLbRuleId()); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove certificate from load balancer rule"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_LB_CERT_REMOVE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public String getEventDescription() { + return "Removing a certificate from a loadbalancer with ID " + getLbRuleId(); + } + + + @Override + public long getEntityOwnerId() { + LoadBalancer lb = _entityMgr.findById(LoadBalancer.class, getLbRuleId()); + if (lb == null) { + return Account.ACCOUNT_ID_SYSTEM; // bad id given, parent this command to SYSTEM so ERROR events are tracked + } + return lb.getAccountId(); + } + + public Long getLbRuleId(){ + return this.lbRuleId; + } +} diff --git a/api/src/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java new file mode 100644 index 00000000000..897d66b73c1 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/loadbalancer/UploadSslCertCmd.java @@ -0,0 +1,103 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.loadbalancer; + + +import org.apache.cloudstack.api.response.SslCertResponse; +import com.cloud.exception.*; +import com.cloud.network.lb.CertService; +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; + +import javax.inject.Inject; + +@APICommand(name = "uploadSslCert", description="Upload a certificate to cloudstack", responseObject=SslCertResponse.class) +public class UploadSslCertCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(UploadSslCertCmd.class.getName()); + + private static final String s_name = "uploadsslcertresponse"; + + @Inject CertService _certService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name= ApiConstants.CERTIFICATE, type=CommandType.STRING, required=true, description="SSL certificate",length=16384) + private String cert; + + @Parameter(name=ApiConstants.PRIVATE_KEY, type=CommandType.STRING, required=true, description="Private key", length=16384) + private String key; + + @Parameter(name=ApiConstants.CERTIFICATE_CHAIN, type=CommandType.STRING, description="Certificate chain of trust", length=2097152) + private String chain; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="Password for the private key") + private String password; + + + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public String getCert() { + return cert; + } + + public String getKey() { + return key; + } + + public String getChain() { + return chain; + } + + public String getPassword() { + return password; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + + try{ + SslCertResponse response = _certService.uploadSslCert(this); + setResponseObject(response); + response.setResponseName(getCommandName()); + } catch (Exception e){ + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } + +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 9fe5ffe1956..fc40fb2b72b 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -65,12 +65,22 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { description = "policy id of the snapshot, if this is null, then use MANUAL_POLICY.") private Long policyId; + @Parameter(name = ApiConstants.SNAPSHOT_QUIESCEVM, type = CommandType.BOOLEAN, required = false, description = "quiesce vm if true") + private Boolean quiescevm; + private String syncObjectType = BaseAsyncCmd.snapshotHostSyncObject; // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// + public Boolean getQuiescevm() { + if (quiescevm == null) { + return false; + } else { + return quiescevm; + } + } public String getAccountName() { return accountName; @@ -168,7 +178,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { CallContext.current().setEventDetails("Volume Id: "+getVolumeId()); Snapshot snapshot; try { - snapshot = _volumeService.takeSnapshot(this.getVolumeId(), this.getPolicyId(), this.getEntityId(), _accountService.getAccount(getEntityOwnerId())); + snapshot = _volumeService.takeSnapshot(this.getVolumeId(), this.getPolicyId(), this.getEntityId(), _accountService.getAccount(getEntityOwnerId()), getQuiescevm()); if (snapshot != null) { SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); response.setResponseName(getCommandName()); 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 8a6cea79091..3643f91de6c 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 @@ -188,6 +188,18 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @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.CPU_SPEED, type = CommandType.INTEGER, since="4.3", description = "optional field to specify the cpu speed when using dynamic compute offering.") + private Integer cpuSpeed; + + @Parameter(name=ApiConstants.MEMORY, type = CommandType.INTEGER, since="4.3", description = "optional field to specify the memory when using dynamic compute offering") + private Integer memory; + + @Parameter(name=ApiConstants.CPU_NUMBER, type=CommandType.INTEGER, since="4.3", description = "optional field to specify the number of cpu cores when using dynamic offering.") + private Integer cpuNumber; + + @Parameter(name=ApiConstants.ROOT_DISK_SIZE, type=CommandType.LONG, since="4.3", description = "optional field to specify the number of cpu cores when using dynamic offering.") + private Long rootdisksize; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -227,6 +239,22 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { return displayVm; } + public Integer getMemory() { + return memory; + } + + public Integer getCpuSpeed() { + return cpuSpeed; + } + + public Integer getCpuNumber() { + return cpuNumber; + } + + public Long getRootdisksize() { + return rootdisksize; + } + public List getSecurityGroupIdList() { if (securityGroupNameList != null && securityGroupIdList != null) { throw new InvalidParameterValueException("securitygroupids parameter is mutually exclusive with securitygroupnames parameter"); @@ -495,19 +523,19 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { 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()); + displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), cpuSpeed, memory, cpuNumber, rootdisksize); } } 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()); + owner, name, displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), cpuSpeed, memory, cpuNumber, rootdisksize ); } 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()); + diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), cpuSpeed, memory, cpuNumber, rootdisksize); } } diff --git a/api/src/org/apache/cloudstack/api/response/DomainRouterResponse.java b/api/src/org/apache/cloudstack/api/response/DomainRouterResponse.java index 1d31b58a481..5ea28b95a2a 100644 --- a/api/src/org/apache/cloudstack/api/response/DomainRouterResponse.java +++ b/api/src/org/apache/cloudstack/api/response/DomainRouterResponse.java @@ -144,8 +144,8 @@ public class DomainRouterResponse extends BaseResponse implements ControlledView @SerializedName("redundantstate") @Param(description="the state of redundant virtual router") private String redundantState; - @SerializedName("templateversion") @Param(description="the version of template") - private String templateVersion; + @SerializedName("version") @Param(description="the version of template") + private String version; @SerializedName("scriptsversion") @Param(description="the version of scripts") private String scriptsVersion; @@ -160,6 +160,9 @@ public class DomainRouterResponse extends BaseResponse implements ControlledView responseObject = NicResponse.class, since="4.0") private Set nics; + @SerializedName("requiresupgrade") @Param(description="true if the router template requires upgrader") + private boolean requiresUpgrade; + public DomainRouterResponse(){ nics = new LinkedHashSet(); } @@ -308,12 +311,12 @@ public class DomainRouterResponse extends BaseResponse implements ControlledView this.isRedundantRouter = isRedundantRouter; } - public String getTemplateVersion() { - return this.templateVersion; + public String getVersion() { + return this.version; } - public void setTemplateVersion(String templateVersion) { - this.templateVersion = templateVersion; + public void setVersion(String version) { + this.version = version; } public String getScriptsVersion() { @@ -364,4 +367,12 @@ public class DomainRouterResponse extends BaseResponse implements ControlledView public void setRole(String role) { this.role = role; } + + public boolean requiresUpgrade() { + return requiresUpgrade; + } + + public void setRequiresUpgrade(boolean requiresUpgrade) { + this.requiresUpgrade = requiresUpgrade; + } } diff --git a/api/src/org/apache/cloudstack/api/response/HostResponse.java b/api/src/org/apache/cloudstack/api/response/HostResponse.java index 687687d37fc..7b6b5348a91 100644 --- a/api/src/org/apache/cloudstack/api/response/HostResponse.java +++ b/api/src/org/apache/cloudstack/api/response/HostResponse.java @@ -72,6 +72,9 @@ public class HostResponse extends BaseResponse { @SerializedName(ApiConstants.HYPERVISOR) @Param(description="the host hypervisor") private HypervisorType hypervisor; + @SerializedName("cpusockets") @Param(description="the number of CPU sockets on the host") + private Integer cpuSockets; + @SerializedName("cpunumber") @Param(description="the CPU number of the host") private Integer cpuNumber; @@ -225,6 +228,10 @@ public class HostResponse extends BaseResponse { this.hypervisor = hypervisor; } + public void setCpuSockets(Integer cpuSockets) { + this.cpuSockets = cpuSockets; + } + public void setCpuNumber(Integer cpuNumber) { this.cpuNumber = cpuNumber; } diff --git a/api/src/org/apache/cloudstack/api/response/SslCertResponse.java b/api/src/org/apache/cloudstack/api/response/SslCertResponse.java new file mode 100644 index 00000000000..3e8b7ae559d --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/SslCertResponse.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 org.apache.cloudstack.api.response; + +import com.cloud.network.lb.SslCert; +import com.cloud.serializer.Param; +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 java.util.List; +//import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value= SslCert.class) +public class SslCertResponse extends BaseResponse { + + @SerializedName(ApiConstants.ID) + @Param(description = "SSL certificate ID") + private String id; + + @SerializedName(ApiConstants.CERTIFICATE) + @Param(description = "certificate") + private String certificate; + + @SerializedName(ApiConstants.PRIVATE_KEY) + @Param(description = "private key") + private String privatekey; + + @SerializedName(ApiConstants.ACCOUNT) + @Param(description = "account for the certificate") + private String accountName; + + @SerializedName(ApiConstants.CERTIFICATE_CHAIN) + @Param(description = "certificate chain") + private String certchain; + + @SerializedName(ApiConstants.CERTIFICATE_FINGERPRINT) + @Param(description = "certificate fingerprint") + private String fingerprint; + + @SerializedName(ApiConstants.LOAD_BALANCER_RULE_LIST) + @Param(description = "List of loabalancers this certificate is bound to") + List lbIds; + + public SslCertResponse() { + } + + public void setId(String id) { + this.id = id; + } + + public void setCertificate(String cert) { + this.certificate = cert; + } + + public void setPrivatekey(String key) { + this.privatekey = key; + } + + public void setAccountName(String accountName) { + this.accountName = accountName; + } + + public void setCertchain(String chain) { + this.certchain = chain; + } + + public void setFingerprint(String fingerprint){ + this.fingerprint = fingerprint; + } + + public void setLbIds(List lbIds){ + this.lbIds = lbIds; + } +} diff --git a/api/src/org/apache/cloudstack/api/response/TrafficTypeResponse.java b/api/src/org/apache/cloudstack/api/response/TrafficTypeResponse.java index 494048e347f..0205daa8cff 100644 --- a/api/src/org/apache/cloudstack/api/response/TrafficTypeResponse.java +++ b/api/src/org/apache/cloudstack/api/response/TrafficTypeResponse.java @@ -45,6 +45,9 @@ public class TrafficTypeResponse extends BaseResponse { @SerializedName(ApiConstants.VMWARE_NETWORK_LABEL) @Param(description="The network name label of the physical device dedicated to this traffic on a VMware host") private String vmwareNetworkLabel; + @SerializedName(ApiConstants.HYPERV_NETWORK_LABEL) @Param(description="The network name label of the physical device dedicated to this traffic on a HyperV host") + private String hypervNetworkLabel; + @Override public String getObjectId() { @@ -82,10 +85,17 @@ public class TrafficTypeResponse extends BaseResponse { public String getKvmLabel() { return kvmNetworkLabel; } + public String getHypervLabel() { + return hypervNetworkLabel; + } public void setXenLabel(String xenLabel) { this.xenNetworkLabel = xenLabel; } + + public void setHypervLabel(String hypervLabel) { + this.hypervNetworkLabel = hypervLabel; + } public void setKvmLabel(String kvmLabel) { this.kvmNetworkLabel = kvmLabel; diff --git a/api/src/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java b/api/src/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.java new file mode 100644 index 00000000000..ba0049bbaa1 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/response/UpgradeRouterTemplateResponse.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.api.response; + +import com.cloud.serializer.Param; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.State; +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.jobs.JobInfo; + +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.Set; + +@EntityReference(value = JobInfo.class) +@SuppressWarnings("unused") +public class UpgradeRouterTemplateResponse extends BaseResponse { + @SerializedName(ApiConstants.JOB_ID) @Param(description="the id of AsyncJob") + private String asyncJobId; + + public String getAsyncJobId() { + return asyncJobId; + } + + public void setAsyncJobId(String asyncJobId) { + this.asyncJobId = asyncJobId; + } +} diff --git a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java index 29ce2e3971d..32f13f80f22 100644 --- a/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java +++ b/api/src/org/apache/cloudstack/network/ExternalNetworkDeviceManager.java @@ -42,6 +42,7 @@ public interface ExternalNetworkDeviceManager extends Manager { public static final NetworkDevice NetscalerSDXLoadBalancer = new NetworkDevice("NetscalerSDXLoadBalancer", Network.Provider.Netscaler.getName()); public static final NetworkDevice F5BigIpLoadBalancer = new NetworkDevice("F5BigIpLoadBalancer", Network.Provider.F5BigIp.getName()); public static final NetworkDevice JuniperSRXFirewall = new NetworkDevice("JuniperSRXFirewall", Network.Provider.JuniperSRX.getName()); + 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()); diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index dbafee2a912..9ef6a69cf3b 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -43,6 +43,7 @@ label.custom.disk.iops=Custom IOPS label.disk.iops.min=Min IOPS label.disk.iops.max=Max IOPS label.disk.iops.total=IOPS Total +label.hypervisor.snapshot.reserve=Hypervisor Snapshot Reserve label.view.secondary.ips=View secondary IPs message.validate.invalid.characters=Invalid characters found; please correct. message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.
NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine. @@ -308,6 +309,7 @@ label.add.new.F5=Add new F5 label.add.new.gateway=Add new gateway label.add.new.NetScaler=Add new NetScaler label.add.new.SRX=Add new SRX +label.add.new.PA=Add new Palo Alto label.add.new.tier=Add new tier label.add.NiciraNvp.device=Add Nvp Controller label.add.physical.network=Add physical network @@ -322,6 +324,7 @@ label.add.secondary.storage=Add Secondary Storage label.add.security.group=Add Security Group label.add.service.offering=Add Service Offering label.add.SRX.device=Add SRX device +label.add.PA.device=Add Palo Alto device label.add.static.nat.rule=Add static NAT rule label.add.static.route=Add static route label.add.system.service.offering=Add System Service Offering @@ -483,6 +486,7 @@ label.delete.NetScaler=Delete NetScaler label.delete.NiciraNvp=Remove Nvp Controller label.delete.project=Delete project label.delete.SRX=Delete SRX +label.delete.PA=Delete Palo Alto label.delete.VPN.connection=delete VPN connection label.delete.VPN.customer.gateway=delete VPN Customer Gateway label.delete.VPN.gateway=delete VPN Gateway @@ -880,6 +884,8 @@ label.os.type=OS Type label.owned.public.ips=Owned Public IP Addresses label.owner.account=Owner Account label.owner.domain=Owner Domain +label.PA.log.profile=Palo Alto Log Profile +label.PA.threat.profile=Palo Alto Threat Profile label.parent.domain=Parent Domain label.password.enabled=Password Enabled label.password=Password @@ -1052,6 +1058,7 @@ label.specify.vlan=Specify VLAN label.specify.vxlan=Specify VXLAN label.SR.name = SR Name-Label label.srx=SRX +label.PA=Palo Alto label.start.IP=Start IP label.start.port=Start Port label.start.reserved.system.IP=Start Reserved system IP @@ -1370,6 +1377,7 @@ message.confirm.action.force.reconnect=Please confirm that you want to force rec message.confirm.delete.F5=Please confirm that you would like to delete F5 message.confirm.delete.NetScaler=Please confirm that you would like to delete NetScaler message.confirm.delete.SRX=Please confirm that you would like to delete SRX +message.confirm.delete.PA=Please confirm that you would like to delete Palo Alto message.confirm.destroy.router=Please confirm that you would like to destroy this router message.confirm.disable.provider=Please confirm that you would like to disable this provider message.confirm.enable.provider=Please confirm that you would like to enable this provider diff --git a/client/pom.xml b/client/pom.xml index 54cb667514b..a15a409a33c 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -90,6 +90,11 @@ cloud-plugin-network-contrail ${project.version} + + org.apache.cloudstack + cloud-plugin-network-palo-alto + ${project.version} + org.apache.cloudstack cloud-plugin-network-ovs diff --git a/client/tomcatconf/cloudmanagementserver.keystore b/client/tomcatconf/cloudmanagementserver.keystore.in similarity index 100% rename from client/tomcatconf/cloudmanagementserver.keystore rename to client/tomcatconf/cloudmanagementserver.keystore.in diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index 8cbe972cb81..cb9dcf0b071 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -165,6 +165,14 @@ deleteLBHealthCheckPolicy=15 listLoadBalancerRuleInstances=15 updateLoadBalancerRule=15 +##### SSL offload commands + +uploadSslCert=15 +deleteSslCert=15 +listSslCerts=15 +assignCertToLoadBalancer=15 +removeCertFromLoadBalancer=15 + #### autoscale commands createCounter=1 createCondition=15 @@ -197,6 +205,7 @@ listRouters=7 listVirtualRouterElements=7 configureVirtualRouterElement=7 createVirtualRouterElement=7 +upgradeRouterTemplate=1 #### system vm commands startSystemVm=1 @@ -533,6 +542,17 @@ configureSrxFirewall=1 listSrxFirewalls=1 listSrxFirewallNetworks=1 +#### Palo Alto firewall commands +addExternalFirewall=1 +deleteExternalFirewall=1 +listExternalFirewalls=1 + +addPaloAltoFirewall=1 +deletePaloAltoFirewall=1 +configurePaloAltoFirewall=1 +listPaloAltoFirewalls=1 +listPaloAltoFirewallNetworks=1 + ####Netapp integration commands createVolumeOnFiler=15 destroyVolumeOnFiler=15 diff --git a/client/tomcatconf/db.properties.in b/client/tomcatconf/db.properties.in index b0cb22b26f0..31e08033370 100644 --- a/client/tomcatconf/db.properties.in +++ b/client/tomcatconf/db.properties.in @@ -32,7 +32,6 @@ db.cloud.name=cloud db.cloud.maxActive=250 db.cloud.maxIdle=30 db.cloud.maxWait=10000 -db.cloud.autoReconnect=true db.cloud.validationQuery=SELECT 1 db.cloud.testOnBorrow=true db.cloud.testWhileIdle=true @@ -63,7 +62,6 @@ db.usage.name=cloud_usage db.usage.maxActive=100 db.usage.maxIdle=30 db.usage.maxWait=10000 -db.usage.autoReconnect=true db.usage.url.params= # awsapi database settings @@ -84,3 +82,25 @@ db.simulator.maxIdle=30 db.simulator.maxWait=10000 db.simulator.autoReconnect=true + +# High Availability And Cluster Properties +db.ha.enabled=false +# cloud stack Database +db.cloud.slaves=localhost,localhost +db.cloud.autoReconnect=true +db.cloud.failOverReadOnly=false +db.cloud.reconnectAtTxEnd=true +db.cloud.autoReconnectForPools=true +db.cloud.secondsBeforeRetryMaster=3600 +db.cloud.queriesBeforeRetryMaster=5000 +db.cloud.initialTimeout=3600 + +#usage Database +db.usage.slaves=localhost,localhost +db.usage.autoReconnect=true +db.usage.failOverReadOnly=false +db.usage.reconnectAtTxEnd=true +db.usage.autoReconnectForPools=true +db.usage.secondsBeforeRetryMaster=3600 +db.usage.queriesBeforeRetryMaster=5000 +db.usage.initialTimeout=3600 diff --git a/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java b/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java index 1e5fd6c9a68..b744ced57e3 100644 --- a/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java +++ b/core/src/com/cloud/agent/api/RevertToVMSnapshotCommand.java @@ -18,7 +18,6 @@ package com.cloud.agent.api; import java.util.List; -import com.cloud.agent.api.to.VolumeTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; public class RevertToVMSnapshotCommand extends VMSnapshotBaseCommand { @@ -27,4 +26,18 @@ public class RevertToVMSnapshotCommand extends VMSnapshotBaseCommand { super(vmName, snapshot, volumeTOs, guestOSType); } + public RevertToVMSnapshotCommand(String vmName, VMSnapshotTO snapshot, List volumeTOs, String guestOSType, boolean reloadVm) { + this(vmName, snapshot, volumeTOs, guestOSType); + setReloadVm(reloadVm); + } + + private boolean reloadVm = false; + + public boolean isReloadVm() { + return reloadVm; + } + + public void setReloadVm(boolean reloadVm) { + this.reloadVm = reloadVm; + } } diff --git a/core/src/com/cloud/agent/api/StartupRoutingCommand.java b/core/src/com/cloud/agent/api/StartupRoutingCommand.java index d52666b7d9d..f312e0fc72d 100755 --- a/core/src/com/cloud/agent/api/StartupRoutingCommand.java +++ b/core/src/com/cloud/agent/api/StartupRoutingCommand.java @@ -42,6 +42,7 @@ public class StartupRoutingCommand extends StartupCommand { return host; } } + Integer cpuSockets; int cpus; long speed; long memory; @@ -133,6 +134,10 @@ getHostDetails().put(RouterPrivateIpStrategy.class.getCanonicalName(), privIpStr _clusterVMStates = allStates; } + public Integer getCpuSockets() { + return cpuSockets; + } + public int getCpus() { return cpus; } @@ -165,6 +170,10 @@ getHostDetails().put(RouterPrivateIpStrategy.class.getCanonicalName(), privIpStr this.speed = speed; } + public void setCpuSockets(Integer cpuSockets) { + this.cpuSockets = cpuSockets; + } + public void setCpus(int cpus) { this.cpus = cpus; } diff --git a/core/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java b/core/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java index 3a51e8ad6be..a6a46d55665 100644 --- a/core/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java +++ b/core/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java @@ -33,6 +33,7 @@ public class LoadBalancerConfigCommand extends NetworkElementCommand { public String lbStatsAuth = "admin1:AdMiN123"; public String lbStatsUri = "/admin?stats"; public String maxconn =""; + public String lbProtocol; public boolean keepAliveEnabled = false; NicTO nic; Long vpcId; diff --git a/core/src/com/cloud/agent/api/routing/SetMonitorServiceCommand.java b/core/src/com/cloud/agent/api/routing/SetMonitorServiceCommand.java new file mode 100644 index 00000000000..109daf2c2b6 --- /dev/null +++ b/core/src/com/cloud/agent/api/routing/SetMonitorServiceCommand.java @@ -0,0 +1,56 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.api.routing; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import com.cloud.agent.api.to.MonitorServiceTO; + +/** + * + * AccessDetails allow different components to put in information about + * how to access the components inside the command. + */ +public class SetMonitorServiceCommand extends NetworkElementCommand { + MonitorServiceTO[] services; + + protected SetMonitorServiceCommand() { + } + + public SetMonitorServiceCommand(List services) { + this.services = services.toArray(new MonitorServiceTO[services.size()]); + } + + public MonitorServiceTO[] getRules() { + return services; + } + + public String getConfiguration() { + + StringBuilder sb = new StringBuilder(); + for (MonitorServiceTO service: services) { + sb.append("[").append(service.getService()).append("]").append(":"); + sb.append("processname=").append(service.getProcessname()).append(":"); + sb.append("servicename=").append(service.getServiceName()).append(":"); + sb.append("pidfile=").append(service.getPidFile()).append(":"); + sb.append(","); + } + + return sb.toString(); + } +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 4c2ee505117..b124aa68f5f 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -818,6 +818,12 @@ public class VirtualRoutingResource implements Manager { return new ConsoleProxyLoadAnswer(cmd, proxyVmId, proxyVmName, success, result); } + public String configureMonitor(final String routerIP, final String config) { + + String args= " -c " + config; + return routerProxy("monitor_service.sh", routerIP, args); + } + public String assignGuestNetwork(final String dev, final String routerIP, final String routerGIP, final String gateway, final String cidr, final String netmask, final String dns, final String domainName) { diff --git a/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index bacc0f9e5d0..34e977c6421 100644 --- a/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/core/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -33,6 +33,7 @@ public class SnapshotObjectTO implements DataTO { private String name; private HypervisorType hypervisorType; private long id; + private boolean quiescevm; public SnapshotObjectTO() { @@ -54,6 +55,7 @@ public class SnapshotObjectTO implements DataTO { this.dataStore = snapshot.getDataStore().getTO(); this.setName(snapshot.getName()); this.hypervisorType = snapshot.getHypervisorType(); + this.quiescevm = false; } @Override @@ -129,6 +131,14 @@ public class SnapshotObjectTO implements DataTO { this.hypervisorType = hypervisorType; } + public boolean getquiescevm() { + return this.quiescevm; + } + + public void setQuiescevm(boolean quiescevm) { + this.quiescevm = quiescevm; + } + @Override public String toString() { return new StringBuilder("SnapshotTO[datastore=").append(dataStore).append("|volume=").append(volume).append("|path") 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 1b682761996..031ef5cfea8 100644 --- a/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java +++ b/core/test/org/apache/cloudstack/api/agent/test/CheckOnHostCommandTest.java @@ -86,6 +86,10 @@ public class CheckOnHostCommandTest { public Long getTotalMemory() { return 100000000000L; + } + + public Integer getCpuSockets() { + return 1; }; public Integer getCpus() { @@ -289,6 +293,12 @@ public class CheckOnHostCommandTest { assertTrue(m == 100000000000L); } + @Test + public void testGetCpuSockets() { + Integer cpuSockets = host.getCpuSockets(); + assertTrue(cpuSockets == 1); + } + @Test public void testGetCpus() { int cpus = host.getCpus(); diff --git a/debian/cloudstack-management.postinst b/debian/cloudstack-management.postinst index a1219cc789f..8d99f6a1cce 100644 --- a/debian/cloudstack-management.postinst +++ b/debian/cloudstack-management.postinst @@ -35,7 +35,7 @@ if [ "$1" = configure ]; then OLDCONFDIR="/etc/cloud/management" NEWCONFDIR="/etc/cloudstack/management" - CONFFILES="db.properties cloud.keystore key" + CONFFILES="db.properties key" # Copy old configuration so the admin doesn't have to do that # Only do so when we are installing for the first time @@ -45,6 +45,9 @@ if [ "$1" = configure ]; then cp -a $OLDCONFDIR/$FILE $NEWCONFDIR/$FILE fi done + if [ -f "$OLDCONFDIR/cloud.keystore" ]; then + cp -a $OLDCONFDIR/cloud.keystore $NEWCONFDIR/cloudmanagementserver.keystore + fi fi chmod 0640 /etc/cloudstack/management/db.properties diff --git a/engine/api/src/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/com/cloud/vm/VirtualMachineManager.java index afac6f39012..cb6c62dd69d 100644 --- a/engine/api/src/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/com/cloud/vm/VirtualMachineManager.java @@ -46,7 +46,11 @@ import com.cloud.utils.fsm.NoTransitionException; * Manages allocating resources to vms. */ public interface VirtualMachineManager extends Manager { - + + public interface Topics { + public static final String VM_POWER_STATE = "vm.powerstate"; + } + /** * Allocates a new virtual machine instance in the CloudStack DB. This * orchestrates the creation of all virtual resources needed in CloudStack diff --git a/engine/api/src/org/apache/cloudstack/engine/Rules.java b/engine/api/src/org/apache/cloudstack/engine/Rules.java deleted file mode 100755 index b700fa5fcb7..00000000000 --- a/engine/api/src/org/apache/cloudstack/engine/Rules.java +++ /dev/null @@ -1,85 +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.engine; - -import java.util.ArrayList; -import java.util.List; - -import com.cloud.utils.StringUtils; - -/** - * Rules specifies all rules about developing and using CloudStack Orchestration - * Platforms APIs. This class is not actually used in CloudStack Orchestration - * Platform but must be read by all who wants to use and develop against - * CloudStack Orchestration Platform. - * - * Make sure to make changes here when there are changes to how the APIs should - * be used and developed. - * - * Changes to this class must be approved by the maintainer of this project. - * - */ -public class Rules { - public static List whenUsing() { - List rules = new ArrayList(); - rules.add("Always be prepared to handle RuntimeExceptions."); - return rules; - } - - public static List whenWritingNewApis() { - List rules = new ArrayList(); - rules.add("You may think you're the greatest developer in the " + - "world but every change to the API must be reviewed and approved. "); - rules.add("Every API must have unit tests written against it. And not it's unit tests"); - rules.add(""); - - - return rules; - } - - private static void printRule(String rule) { - System.out.print("API Rule: "); - String skip = ""; - int brk = 0; - while (true) { - int stop = StringUtils.formatForOutput(rule, brk, 75 - skip.length(), ' '); - if (stop < 0) { - break; - } - System.out.print(skip); - skip = " "; - System.out.println(rule.substring(brk, stop).trim()); - brk = stop; - } - } - - public static void main(String[] args) { - System.out.println("When developing against the CloudStack Orchestration Platform, you must following the following rules:"); - for (String rule : whenUsing()) { - printRule(rule); - } - System.out.println(""); - System.out.println("When writing APIs, you must follow these rules:"); - for (String rule : whenWritingNewApis()) { - printRule(rule); - } - } - -} - 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 a773ac42a3a..ad957d5ac8f 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 @@ -86,7 +86,7 @@ public interface VolumeOrchestrationService { boolean canVmRestartOnAnotherServer(long vmId); - DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, VirtualMachineTemplate template, VirtualMachine vm, Account owner); + DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, Long rootDisksize, VirtualMachineTemplate template, VirtualMachine vm, Account owner); String getVmNameFromVolumeId(long volumeId); diff --git a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java index 64ef063d096..8c582276ade 100755 --- a/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java +++ b/engine/api/src/org/apache/cloudstack/engine/service/api/OrchestrationService.java @@ -55,6 +55,7 @@ public interface OrchestrationService { * @param computeTags tags for the compute * @param rootDiskTags tags for the root disk * @param networks networks that this VM should join + * @param rootDiskSize size the root disk in case of templates. * @return VirtualMachineEntity */ @POST @@ -73,7 +74,8 @@ public interface OrchestrationService { @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, @QueryParam("network-nic-map") Map networkNicMap, - @QueryParam("deploymentplan") DeploymentPlan plan + @QueryParam("deploymentplan") DeploymentPlan plan, + @QueryParam("root-disk-size") Long rootDiskSize ) throws InsufficientCapacityException; @POST diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java index 855f0854103..7b5f8d967ee 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreProvider.java @@ -27,7 +27,7 @@ public interface DataStoreProvider { static final String S3_IMAGE = "S3"; static final String SWIFT_IMAGE = "Swift"; static final String SAMPLE_IMAGE = "Sample"; - + static final String SMB = "NFS"; static final String DEFAULT_PRIMARY = "DefaultPrimary"; static enum DataStoreProviderType { diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java index a0ef7dd1273..c0043341b61 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java @@ -28,6 +28,7 @@ public interface SnapshotInfo extends DataObject, Snapshot { VolumeInfo getBaseVolume(); void addPayload(Object data); + Object getPayload(); Long getDataCenterId(); diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java index 54808c195c5..e8ade44f475 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StoragePoolAllocator.java @@ -30,6 +30,11 @@ import com.cloud.vm.VirtualMachineProfile; public interface StoragePoolAllocator extends Adapter { /** * Determines which storage pools are suitable for the guest virtual machine + * and returns a list of pools suitable. + * + * Allocators must set any other pools not considered for allocation in the + * ExcludeList avoid. Thus the avoid set and the list of pools suitable, + * together must cover the entire pool set in the cluster. * * @param DiskProfile * dskCh diff --git a/engine/components-api/src/com/cloud/event/UsageEventUtils.java b/engine/components-api/src/com/cloud/event/UsageEventUtils.java index b44ed3280db..a4be4dd58c6 100644 --- a/engine/components-api/src/com/cloud/event/UsageEventUtils.java +++ b/engine/components-api/src/com/cloud/event/UsageEventUtils.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.event.dao.UsageEventDetailsDao; import org.apache.log4j.Logger; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -38,6 +39,8 @@ import com.cloud.event.dao.UsageEventDao; import com.cloud.user.Account; import com.cloud.user.dao.AccountDao; import com.cloud.utils.component.ComponentContext; +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.Autowired; public class UsageEventUtils { @@ -103,6 +106,19 @@ public class UsageEventUtils { publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID); } + public static void publishUsageEvent(String usageType, long accountId, long zoneId, long resourceId, + String resourceName, Long offeringId, Long templateId, String resourceType, + String entityType, String entityUUID, Map details) { + saveUsageEvent(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, resourceType, details); + publishUsageEvent(usageType, accountId, zoneId, entityType, entityUUID); + } + + private static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, String resourceType, Map details) { + UsageEventVO usageEvent = new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, resourceType); + _usageEventDao.persist(usageEvent); + _usageEventDao.saveDetails(usageEvent.getId(), details); + } + public static void saveUsageEvent(String usageType, long accountId, long zoneId, long resourceId, String resourceName, Long offeringId, Long templateId, Long size) { _usageEventDao.persist( new UsageEventVO(usageType, accountId, zoneId, resourceId, resourceName, offeringId, templateId, size)); } diff --git a/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java b/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java index 3e325859a9a..22830d5a06a 100644 --- a/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java +++ b/engine/components-api/src/com/cloud/network/lb/LoadBalancingRulesManager.java @@ -25,6 +25,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.rules.LbStickinessMethod; import com.cloud.network.rules.LoadBalancer; import com.cloud.network.rules.LoadBalancerContainer.Scheme; @@ -33,7 +34,8 @@ import com.cloud.user.Account; public interface LoadBalancingRulesManager { LoadBalancer createPublicLoadBalancer(String xId, String name, String description, - int srcPort, int destPort, long sourceIpId, String protocol, String algorithm, boolean openFirewall, CallContext caller) + int srcPort, int destPort, long sourceIpId, String protocol, String algorithm, + boolean openFirewall, CallContext caller, String lbProtocol) throws NetworkRuleConflictException; boolean removeAllLoadBalanacersForIp(long ipId, Account caller, long callerUserId); @@ -42,6 +44,7 @@ public interface LoadBalancingRulesManager { List getStickinessPolicies(long lbId); List getStickinessMethods(long networkid); List getHealthCheckPolicies(long lbId); + LbSslCert getLbSslCert(long lbId); /** * Remove vm from all load balancers diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index b74b4c55bac..555a58faaa9 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -356,7 +356,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else if (template.getFormat() == ImageFormat.BAREMETAL) { // Do nothing } else { - volumeMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOffering.first(), template, vmFinal, owner); + volumeMgr.allocateTemplatedVolume(Type.ROOT, "ROOT-" + vmFinal.getId(), rootDiskOffering.first(), rootDiskOffering.second(), template, vmFinal, owner); } for (Map.Entry offering : dataDiskOfferingsFinal.entrySet()) { @@ -674,7 +674,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac ItWorkVO work = start.third(); VMInstanceVO startedVm = null; - ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, vm.getTemplateId()); if (s_logger.isDebugEnabled()) { @@ -2771,7 +2771,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public void checkIfCanUpgrade(VirtualMachine vmInstance, long newServiceOfferingId) { - ServiceOfferingVO newServiceOffering = _offeringDao.findById(newServiceOfferingId); + ServiceOfferingVO newServiceOffering = _offeringDao.findById(vmInstance.getId(), newServiceOfferingId); if (newServiceOffering == null) { throw new InvalidParameterValueException("Unable to find a service offering with id " + newServiceOfferingId); } @@ -2793,7 +2793,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac newServiceOffering.getName() + ")"); } - ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering // NOTE: With the new network refactoring in 2.2, we shouldn't need the check for same guest IP type anymore. diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSync.java b/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSync.java new file mode 100644 index 00000000000..7a23ddd81e9 --- /dev/null +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSync.java @@ -0,0 +1,32 @@ +// 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.util.Map; + +import com.cloud.agent.api.HostVmStateReportEntry; +import com.cloud.vm.VirtualMachine.PowerState; + +public interface VirtualMachinePowerStateSync { + + void resetHostSyncState(long hostId); + + void processHostVmStateReport(long hostId, Map report); + + // to adapt legacy ping report + void processHostVmStatePingReport(long hostId, Map report); +} diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSyncImpl.java new file mode 100644 index 00000000000..9c47727ed51 --- /dev/null +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachinePowerStateSyncImpl.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 com.cloud.vm; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.framework.messagebus.PublishScope; + +import com.cloud.agent.api.HostVmStateReportEntry; +import com.cloud.vm.VirtualMachine.PowerState; +import com.cloud.vm.dao.VMInstanceDao; + +public class VirtualMachinePowerStateSyncImpl implements VirtualMachinePowerStateSync { + private static final Logger s_logger = Logger.getLogger(VirtualMachinePowerStateSyncImpl.class); + + @Inject MessageBus _messageBus; + @Inject VMInstanceDao _instanceDao; + @Inject VirtualMachineManager _vmMgr; + + public VirtualMachinePowerStateSyncImpl() { + } + + @Override + public void resetHostSyncState(long hostId) { + s_logger.info("Reset VM power state sync for host: " + hostId); + _instanceDao.resetHostPowerStateTracking(hostId); + } + + @Override + public void processHostVmStateReport(long hostId, Map report) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Process host VM state report from ping process. host: " + hostId); + + Map translatedInfo = convertToInfos(report); + processReport(hostId, translatedInfo); + } + + @Override + public void processHostVmStatePingReport(long hostId, Map report) { + if(s_logger.isDebugEnabled()) + s_logger.debug("Process host VM state report from ping process. host: " + hostId); + + Map translatedInfo = convertHostPingInfos(report); + processReport(hostId, translatedInfo); + } + + private void processReport(long hostId, Map translatedInfo) { + + for(Map.Entry entry : translatedInfo.entrySet()) { + + if(s_logger.isDebugEnabled()) + s_logger.debug("VM state report. host: " + hostId + ", vm id: " + entry.getKey() + ", power state: " + entry.getValue()); + + if(_instanceDao.updatePowerState(entry.getKey(), hostId, entry.getValue())) { + + if(s_logger.isDebugEnabled()) + s_logger.debug("VM state report is updated. host: " + hostId + ", vm id: " + entry.getKey() + ", power state: " + entry.getValue()); + + _messageBus.publish(null, VirtualMachineManager.Topics.VM_POWER_STATE, PublishScope.GLOBAL, entry.getKey()); + } + } + } + + private Map convertHostPingInfos(Map states) { + final HashMap map = new HashMap(); + if (states == null) { + return map; + } + + for (Map.Entry entry : states.entrySet()) { + VMInstanceVO vm = findVM(entry.getKey()); + if(vm != null) { + map.put(vm.getId(), entry.getValue()); + break; + } else { + s_logger.info("Unable to find matched VM in CloudStack DB. name: " + entry.getKey()); + } + } + + return map; + } + + private Map convertToInfos(Map states) { + final HashMap map = new HashMap(); + if (states == null) { + return map; + } + + for (Map.Entry entry : states.entrySet()) { + VMInstanceVO vm = findVM(entry.getKey()); + if(vm != null) { + map.put(vm.getId(), entry.getValue().getState()); + break; + } else { + s_logger.info("Unable to find matched VM in CloudStack DB. name: " + entry.getKey()); + } + } + + return map; + } + + private VMInstanceVO findVM(String vmName) { + return _instanceDao.findVMByInstanceName(vmName); + } +} diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java index 249c2db01c0..e8f42b49b1b 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/datacenter/entity/api/db/EngineHostVO.java @@ -325,6 +325,9 @@ public class EngineHostVO implements EngineHost, Identity { @Column(name="pod_id") private Long podId; + @Column(name="cpu_sockets") + private Integer cpuSockets; + @Column(name="cpus") private Integer cpus; @@ -648,6 +651,11 @@ public class EngineHostVO implements EngineHost, Identity { this.guid = guid; } + @Override + public Integer getCpuSockets() { + return cpuSockets; + } + @Override public Integer getCpus() { return cpus; diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java index 2fd10b66e32..5dd15ce0bb4 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java @@ -164,7 +164,7 @@ public class CloudOrchestrator implements OrchestrationService { Long diskSize, List computeTags, List rootDiskTags, - Map networkNicMap, DeploymentPlan plan) throws InsufficientCapacityException { + Map networkNicMap, DeploymentPlan plan, Long rootDiskSize) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); @@ -191,8 +191,9 @@ public class CloudOrchestrator implements OrchestrationService { Pair rootDiskOffering = new Pair(null, null); LinkedHashMap dataDiskOfferings = new LinkedHashMap(); - ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); rootDiskOffering.first(offering); + rootDiskOffering.second(rootDiskSize); if(vm.getDiskOfferingId() != null){ DiskOfferingVO diskOffering = _diskOfferingDao.findById(vm.getDiskOfferingId()); @@ -238,7 +239,7 @@ public class CloudOrchestrator implements OrchestrationService { VMInstanceVO vm = _vmDao.findByUuid(id); Pair rootDiskOffering = new Pair(null, null); - ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); rootDiskOffering.first(offering); LinkedHashMap dataDiskOfferings = new LinkedHashMap(); 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 8d841d8fd1b..f839d6cf343 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -406,6 +406,7 @@ 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); @@ -435,6 +436,29 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati throw new CloudRuntimeException("create volume failed even after template re-deploy"); } + // For managed storage on Xen and VMware, we need to potentially make space for hypervisor snapshots. + // The disk offering can collect this information and pass it on to the volume that's about to be created. + // Ex. if you want a 10 GB CloudStack volume to reside on managed storage on Xen, this leads to an SR + // that is a total size of (10 GB * (hypervisorSnapshotReserveSpace / 100) + 10 GB). + private VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, VolumeInfo volumeInfo, HypervisorType hyperType) { + Integer hypervisorSnapshotReserve = diskOffering.getHypervisorSnapshotReserve(); + + if (hyperType == HypervisorType.KVM) { + hypervisorSnapshotReserve = null; + } + else if (hypervisorSnapshotReserve == null || hypervisorSnapshotReserve < 0) { + hypervisorSnapshotReserve = 0; + } + + VolumeVO volume = _volsDao.findById(volumeInfo.getId()); + + volume.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + + _volsDao.update(volume.getId(), volume); + + return volFactory.getVolume(volume.getId()); + } + public String getRandomVolumeName() { return UUID.randomUUID().toString(); } @@ -562,11 +586,13 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } @Override - public DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering offering, 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(), 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 e811cce2362..c71190b5cf5 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 @@ -155,6 +155,7 @@ + @@ -188,6 +189,7 @@ + @@ -251,6 +253,7 @@ + @@ -262,6 +265,7 @@ + diff --git a/engine/schema/src/com/cloud/event/UsageEventDetailsVO.java b/engine/schema/src/com/cloud/event/UsageEventDetailsVO.java new file mode 100644 index 00000000000..d6d118b220f --- /dev/null +++ b/engine/schema/src/com/cloud/event/UsageEventDetailsVO.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.event; + + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name="usage_event_details") +public class UsageEventDetailsVO { + + @Id + @Column(name="id") + long id; + + @Column(name = "usage_event_id", nullable = false) + long usageEventId; + + @Column(name = "name", nullable = false) + String key; + + @Column(name = "value") + String value; + + public UsageEventDetailsVO() { + } + + + public UsageEventDetailsVO(long usageEventId, String key, String value) { + this.key = key; + this.value = value; + this.usageEventId = usageEventId; + } + + public long getId() { + return id; + } + + public void setUsageEventId(long usageEventId) { + this.usageEventId = usageEventId; + } + + public long getUsageEventId() { + return usageEventId; + } + + public String getKey() { + return this.key; + } + + public String getValue() { + return this.value; + } + + } diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDao.java b/engine/schema/src/com/cloud/event/dao/UsageEventDao.java index 01979e18ea3..dc6f8242de0 100644 --- a/engine/schema/src/com/cloud/event/dao/UsageEventDao.java +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDao.java @@ -18,6 +18,7 @@ package com.cloud.event.dao; import java.util.Date; import java.util.List; +import java.util.Map; import com.cloud.event.UsageEventVO; import com.cloud.utils.db.GenericDao; @@ -32,4 +33,6 @@ public interface UsageEventDao extends GenericDao { List listDirectIpEvents(Date startDate, Date endDate, long zoneId); + void saveDetails(long eventId, Map details); + } \ No newline at end of file diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java b/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java index 43339038c53..370afbafa4b 100644 --- a/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDaoImpl.java @@ -20,9 +20,11 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.TimeZone; import javax.ejb.Local; +import javax.inject.Inject; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -51,7 +53,7 @@ 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 MAX_EVENT = "select max(id) from cloud.usage_event where created <= ?"; - + @Inject protected UsageEventDetailsDao usageEventDetailsDao; public UsageEventDaoImpl () { latestEventsSearch = createSearchBuilder(); @@ -184,4 +186,9 @@ public class UsageEventDaoImpl extends GenericDaoBase implem return listBy(sc, filter); } + @Override + public void saveDetails(long eventId, Map details) { + usageEventDetailsDao.persist(eventId, details); + } + } diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.java new file mode 100644 index 00000000000..fb9c0e27b09 --- /dev/null +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDao.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 com.cloud.event.dao; +import java.util.Map; + +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); + + UsageEventDetailsVO findDetail(long eventId, String key); + + void deleteDetails(long eventId); +} diff --git a/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.java new file mode 100644 index 00000000000..a4382c47a3c --- /dev/null +++ b/engine/schema/src/com/cloud/event/dao/UsageEventDetailsDaoImpl.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 com.cloud.event.dao; + +import com.cloud.event.UsageEventDetailsVO; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.TransactionLegacy; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@Local(value={UsageEventDetailsDao.class}) +public class UsageEventDetailsDaoImpl extends GenericDaoBase implements UsageEventDetailsDao { + public static final Logger s_logger = Logger.getLogger(UsageEventDetailsDaoImpl.class.getName()); + + protected final SearchBuilder EventDetailsSearch; + protected final SearchBuilder DetailSearch; + + public UsageEventDetailsDaoImpl() { + + EventDetailsSearch =createSearchBuilder(); + EventDetailsSearch.and("eventId", EventDetailsSearch.entity().getUsageEventId(), SearchCriteria.Op.EQ); + EventDetailsSearch.done(); + + DetailSearch = createSearchBuilder(); + DetailSearch.and("eventId", DetailSearch.entity().getUsageEventId(), SearchCriteria.Op.EQ); + DetailSearch.and("key", DetailSearch.entity().getKey(), SearchCriteria.Op.EQ); + DetailSearch.done(); + + + } + + @Override + public void deleteDetails(long eventId) { + SearchCriteria sc = EventDetailsSearch.create(); + sc.setParameters("eventId", eventId); + + List results = search(sc, null); + for (UsageEventDetailsVO result : results) { + remove(result.getId()); + } + } + + @Override + public UsageEventDetailsVO findDetail(long eventId, String key) { + SearchCriteria sc = DetailSearch.create(); + sc.setParameters("eventId", eventId); + sc.setParameters("key", key); + + return findOneBy(sc); + } + + @Override + public Map findDetails(long eventId) { + SearchCriteria sc = EventDetailsSearch.create(); + sc.setParameters("eventId", eventId); + + List results = search(sc, null); + Map details = new HashMap(results.size()); + for (UsageEventDetailsVO result : results) { + details.put(result.getKey(), result.getValue()); + } + + return details; + } + + @Override + public void persist(long eventId, Map details) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + SearchCriteria sc = EventDetailsSearch.create(); + sc.setParameters("eventId", eventId); + expunge(sc); + + for (Map.Entry detail : details.entrySet()) { + UsageEventDetailsVO vo = new UsageEventDetailsVO(eventId, detail.getKey(), detail.getValue()); + persist(vo); + } + txn.commit(); + } + + +} diff --git a/engine/schema/src/com/cloud/host/HostVO.java b/engine/schema/src/com/cloud/host/HostVO.java index c814b94a426..2ac3d07948e 100755 --- a/engine/schema/src/com/cloud/host/HostVO.java +++ b/engine/schema/src/com/cloud/host/HostVO.java @@ -321,6 +321,9 @@ public class HostVO implements Host { @Column(name="pod_id") private Long podId; + @Column(name="cpu_sockets") + private Integer cpuSockets; + @Column(name="cpus") private Integer cpus; @@ -501,6 +504,10 @@ public class HostVO implements Host { this.privateIpAddress = ipAddress; } + public void setCpuSockets(Integer cpuSockets) { + this.cpuSockets = cpuSockets; + } + public void setCpus(Integer cpus) { this.cpus = cpus; } @@ -621,6 +628,11 @@ public class HostVO implements Host { this.guid = guid; } + @Override + public Integer getCpuSockets() { + return cpuSockets; + } + @Override public Integer getCpus() { return cpus; diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDao.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDao.java new file mode 100644 index 00000000000..21c01d9c15e --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDao.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 com.cloud.network.dao; + +import com.cloud.network.LBHealthCheckPolicyVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + + +public interface LoadBalancerCertMapDao extends GenericDao { + List listByCertId(Long certId); + List listByAccountId(Long accountId); + LoadBalancerCertMapVO findByLbRuleId(Long id); +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDaoImpl.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDaoImpl.java new file mode 100644 index 00000000000..57506f9a6f0 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapDaoImpl.java @@ -0,0 +1,79 @@ +// 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.dao; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import javax.ejb.Local; +import javax.inject.Inject; +import java.util.List; + +@Local(value = {LoadBalancerCertMapDao.class}) +public class LoadBalancerCertMapDaoImpl extends GenericDaoBase implements LoadBalancerCertMapDao { + + private final SearchBuilder listByCertId; + private final SearchBuilder findByLbRuleId; + + + @Inject SslCertDao _sslCertDao; + + public LoadBalancerCertMapDaoImpl() { + + listByCertId = createSearchBuilder(); + listByCertId.and("certificateId", listByCertId.entity().getCertId(), SearchCriteria.Op.EQ); + listByCertId.done(); + + findByLbRuleId = createSearchBuilder(); + findByLbRuleId.and("loadBalancerId", findByLbRuleId.entity().getLbId(), SearchCriteria.Op.EQ); + findByLbRuleId.done(); + + } + + @Override + public List listByCertId(Long certId) { + SearchCriteria sc = listByCertId.create(); + sc.setParameters("certificateId", certId); + return listBy(sc); + } + + @Override + public LoadBalancerCertMapVO findByLbRuleId(Long LbId) { + SearchCriteria sc = findByLbRuleId.create(); + sc.setParameters("loadBalancerId", LbId); + return findOneBy(sc); + } + + @Override + public List listByAccountId(Long accountId) { + + SearchBuilder listByAccountId; + SearchBuilder certsForAccount; + + listByAccountId = createSearchBuilder(); + certsForAccount = _sslCertDao.createSearchBuilder(); + certsForAccount.and("accountId", certsForAccount.entity().getAccountId(), SearchCriteria.Op.EQ); + listByAccountId.join("certsForAccount", certsForAccount, certsForAccount.entity().getId(), listByAccountId.entity().getLbId(), JoinBuilder.JoinType.INNER); + certsForAccount.done(); + listByAccountId.done(); + + SearchCriteria sc = listByAccountId.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapVO.java new file mode 100644 index 00000000000..535964757ec --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerCertMapVO.java @@ -0,0 +1,96 @@ +// 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.dao; + + +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + + +@Entity +@Table(name="load_balancer_cert_map") +public class LoadBalancerCertMapVO implements InternalIdentity { + + @Id + @Column(name="id") + private Long id; + + @Column(name="uuid") + private String uuid; + + @Column(name="load_balancer_id") + private Long lbId; + + @Column(name="certificate_id") + private Long certId; + + @Column(name="revoke") + private boolean revoke = false; + + + public LoadBalancerCertMapVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public LoadBalancerCertMapVO(Long lbId, Long certId, boolean revoke) { + + this.lbId = lbId; + this.certId = certId; + this.revoke = revoke; + this.uuid = UUID.randomUUID().toString(); + } + + // Getters + @Override + public long getId() { + return id; + } + + public String getUuid() { + return uuid; + } + + public Long getLbId() { + return lbId; + } + + public Long getCertId() { + return certId; + } + + public boolean isRevoke() { + return revoke; + } + + //Setters + public void setLbId(Long lbId) { + this.lbId = lbId; + } + + public void setCertId(Long certId) { + this.certId = certId; + } + + public void setRevoke(boolean revoke) { + this.revoke = revoke; + } +} diff --git a/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java b/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java index fee88cf7b0a..625eb6f651a 100644 --- a/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java +++ b/engine/schema/src/com/cloud/network/dao/LoadBalancerVO.java @@ -59,10 +59,15 @@ public class LoadBalancerVO extends FirewallRuleVO implements LoadBalancer { @Column(name="scheme") Scheme scheme = Scheme.Public; + @Column(name="lb_protocol") + String lbProtocol; + + public LoadBalancerVO() { } - public LoadBalancerVO(String xId, String name, String description, long srcIpId, int srcPort, int dstPort, String algorithm, long networkId, long accountId, long domainId) { + public LoadBalancerVO(String xId, String name, String description, long srcIpId, int srcPort, int dstPort, String algorithm, long networkId, + long accountId, long domainId, String lbProtocol) { super(xId, srcIpId, srcPort, NetUtils.TCP_PROTO, networkId, accountId, domainId, Purpose.LoadBalancing, null, null, null, null); this.name = name; this.description = description; @@ -70,6 +75,7 @@ public class LoadBalancerVO extends FirewallRuleVO implements LoadBalancer { this.defaultPortStart = dstPort; this.defaultPortEnd = dstPort; this.scheme = Scheme.Public; + this.lbProtocol = lbProtocol; } @Override @@ -101,6 +107,14 @@ public class LoadBalancerVO extends FirewallRuleVO implements LoadBalancer { this.name = name; } + public String getLbProtocol(){ + return lbProtocol; + } + + public void setLbProtocol(String lbProtocol){ + this.lbProtocol = lbProtocol; + } + public void setAlgorithm(String algorithm) { this.algorithm = algorithm; } diff --git a/engine/schema/src/com/cloud/network/dao/MonitoringServiceDao.java b/engine/schema/src/com/cloud/network/dao/MonitoringServiceDao.java new file mode 100644 index 00000000000..7cc7cc08b68 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/MonitoringServiceDao.java @@ -0,0 +1,31 @@ +// 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.dao; + +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface MonitoringServiceDao extends GenericDao { + + List listAllServices(); + List listDefaultServices(boolean isDefault); + + MonitoringServiceVO getServiceByName(String service); + +} diff --git a/engine/schema/src/com/cloud/network/dao/MonitoringServiceDaoImpl.java b/engine/schema/src/com/cloud/network/dao/MonitoringServiceDaoImpl.java new file mode 100644 index 00000000000..e0b2c20ad35 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/MonitoringServiceDaoImpl.java @@ -0,0 +1,68 @@ +// 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.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import javax.ejb.Local; +import java.util.List; + +@Component +@Local(value=MonitoringServiceDao.class) +public class MonitoringServiceDaoImpl extends GenericDaoBase implements MonitoringServiceDao { + private final SearchBuilder AllFieldsSearch; + + public MonitoringServiceDaoImpl() { + super(); + AllFieldsSearch = createSearchBuilder(); + AllFieldsSearch.and("isDefault", AllFieldsSearch.entity().isDefaultService(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("service", AllFieldsSearch.entity().getService(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("processname", AllFieldsSearch.entity().getProcessname(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("servicename", AllFieldsSearch.entity().getServiceName(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("servicepath", AllFieldsSearch.entity().getServicePath(), SearchCriteria.Op.EQ); + AllFieldsSearch.and("servicePidFile", AllFieldsSearch.entity().getPidFile(), SearchCriteria.Op.EQ); + + AllFieldsSearch.done(); + } + + + + @Override + public List listAllServices() { + return null; + } + + @Override + public List listDefaultServices(boolean isDefault) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("isDefault", isDefault); + return listBy(sc); + } + + @Override + public MonitoringServiceVO getServiceByName(String service) { + SearchCriteria sc = AllFieldsSearch.create(); + sc.setParameters("service", service); + return findOneBy(sc); + } +} \ No newline at end of file diff --git a/engine/schema/src/com/cloud/network/dao/MonitoringServiceVO.java b/engine/schema/src/com/cloud/network/dao/MonitoringServiceVO.java new file mode 100644 index 00000000000..ebd1f840b36 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/MonitoringServiceVO.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 com.cloud.network.dao; + +import com.cloud.network.MonitoringService; + +import javax.persistence.*; +import java.util.UUID; + +@Entity +@Table(name = "monitoring_services") +public class MonitoringServiceVO implements MonitoringService { + + public MonitoringServiceVO(String service, String processname, String serviceName, String servicePath, + String pidFile, boolean defaultService) { + this.service = service; + this.processname = processname; + this.servicename = serviceName; + this.servicePath = servicePath; + this.servicePidFile= pidFile; + this.defaultService = defaultService; + + } + + protected MonitoringServiceVO() { + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "service") + String service; + + @Column(name="process_name", updatable=false) + String processname; + + @Column(name="service_name", updatable=false) + String servicename; + + @Column(name="service_path", updatable=false) + private String servicePath; + + @Column(name="pidFile", updatable=false) + private String servicePidFile; + + @Column(name="isDefault") + private boolean defaultService; + + + @Column(name = "uuid") + String uuid = UUID.randomUUID().toString(); + + public long getId() { + return id; + } + + @Override + public String getService() { + return this.service; + } + + @Override + public String getServiceName() { + return this.servicename; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public String getPidFile() { + return this.servicePidFile; + + } + + @Override + public String getServicePath() { + return this.servicePidFile; + } + + @Override + public String getUuid() { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public long getAccountId() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public long getDomainId() { + return 0; //To change body of implemented methods use File | Settings | File Templates. + } + + public boolean isDefaultService() { + return defaultService; + } + + public String getProcessname() { + return processname; + } +} diff --git a/engine/schema/src/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java b/engine/schema/src/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java index 2a712a6127d..8f7b319546d 100755 --- a/engine/schema/src/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/PhysicalNetworkTrafficTypeDaoImpl.java @@ -41,6 +41,7 @@ public class PhysicalNetworkTrafficTypeDaoImpl extends GenericDaoBase vmWareAllFieldsSearch; final GenericSearchBuilder simulatorAllFieldsSearch; final GenericSearchBuilder ovmAllFieldsSearch; + final GenericSearchBuilder hypervAllFieldsSearch; protected PhysicalNetworkTrafficTypeDaoImpl() { super(); @@ -55,6 +56,13 @@ public class PhysicalNetworkTrafficTypeDaoImpl extends GenericDaoBase { + List listByAccountId(Long id); +} \ No newline at end of file diff --git a/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java b/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java new file mode 100644 index 00000000000..483c28d295b --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java @@ -0,0 +1,31 @@ +package com.cloud.network.dao; + +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; + +import javax.ejb.Local; +import java.util.List; + + + +@Local(value = {SslCertDao.class}) +public class SslCertDaoImpl extends GenericDaoBase implements SslCertDao { + + private final SearchBuilder listByAccountId; + + public SslCertDaoImpl() { + listByAccountId = createSearchBuilder(); + listByAccountId.and("accountId", listByAccountId.entity().getAccountId(), SearchCriteria.Op.EQ); + listByAccountId.done(); + } + + @Override + public List listByAccountId(Long accountId) { + SearchCriteria sc = listByAccountId.create(); + sc.setParameters("accountId", accountId); + return listBy(sc); + } + + +} diff --git a/engine/schema/src/com/cloud/network/dao/SslCertVO.java b/engine/schema/src/com/cloud/network/dao/SslCertVO.java new file mode 100644 index 00000000000..c26c2aa2740 --- /dev/null +++ b/engine/schema/src/com/cloud/network/dao/SslCertVO.java @@ -0,0 +1,132 @@ +// 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.dao; + + +import com.cloud.network.lb.SslCert; +import com.cloud.utils.db.Encrypt; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.api.InternalIdentity; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.UUID; + +@Entity +@Table(name="sslcerts") +public class SslCertVO implements SslCert { + + @Id + @Column(name="id") + private Long id; + + @Column(name="uuid") + private String uuid; + + @Column(name="certificate",length=16384) + private String certificate; + + + @Column(name="chain",length=2097152) + private String chain; + + @Encrypt + @Column(name="key",length=16384) + private String key; + + @Encrypt + @Column(name="password") + private String password; + + @Column(name="account_id") + private Long accountId; + + @Column(name = "domain_id") + long domainId; + + @Column(name = "fingerprint") + String fingerPrint; + + + + + public SslCertVO() { + this.uuid = UUID.randomUUID().toString(); + } + + public SslCertVO(String cert, String key, String password, String chain, Long accountId, Long domainId, String fingerPrint) { + this.certificate = cert; + this.key = key; + this.chain = chain; + this.password = password; + this.accountId = accountId; + this.domainId = domainId; + this.fingerPrint = fingerPrint; + this.uuid = UUID.randomUUID().toString(); + } + + + // Getters + @Override + public long getId() { + return id; + } + + @Override + public String getUuid() { + return uuid; + } + + @Override + public String getCertificate() { + return certificate; + } + + + @Override + public String getKey() { + return key; + } + + + @Override + public String getChain() { + return chain; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public String getFingerPrint() { + return fingerPrint; + } +} \ No newline at end of file diff --git a/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java b/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java index ba030e9a44a..74d37876263 100644 --- a/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java +++ b/engine/schema/src/com/cloud/network/vpc/dao/VpcDaoImpl.java @@ -68,7 +68,7 @@ public class VpcDaoImpl extends GenericDaoBase implements VpcDao{ CountByAccountId = createSearchBuilder(Long.class); CountByAccountId.select(null, Func.COUNT, CountByAccountId.entity().getId()); - CountByAccountId.and("offeringId", CountByAccountId.entity().getAccountId(), Op.EQ); + CountByAccountId.and("accountId", CountByAccountId.entity().getAccountId(), Op.EQ); CountByAccountId.and("removed", CountByAccountId.entity().getRemoved(), Op.NULL); CountByAccountId.done(); } diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java index 9a262c540b7..1e89addb519 100755 --- a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java @@ -18,12 +18,7 @@ package com.cloud.service; import java.util.Map; -import javax.persistence.Column; -import javax.persistence.DiscriminatorValue; -import javax.persistence.Entity; -import javax.persistence.PrimaryKeyJoinColumn; -import javax.persistence.Table; -import javax.persistence.Transient; +import javax.persistence.*; import com.cloud.offering.ServiceOffering; import com.cloud.storage.DiskOfferingVO; @@ -34,14 +29,18 @@ import com.cloud.vm.VirtualMachine; @DiscriminatorValue(value="Service") @PrimaryKeyJoinColumn(name="id") public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering { + public enum DynamicParameters { + cpuSpeed, cpuNumber, memory + }; + @Column(name="cpu") - private int cpu; + private Integer cpu; @Column(name="speed") - private int speed; + private Integer speed; @Column(name="ram_size") - private int ramSize; + private Integer ramSize; @Column(name="nw_rate") private Integer rateMbps; @@ -83,7 +82,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering super(); } - public ServiceOfferingVO(String name, int cpu, int ramSize, int speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, boolean defaultUse) { + public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, + boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, boolean defaultUse) { super(name, displayText, false, tags, recreatable, useLocalStorage, systemUse, true); this.cpu = cpu; this.ramSize = ramSize; @@ -97,7 +97,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering this.vm_type = vm_type == null ? null : vm_type.toString().toLowerCase(); } - public ServiceOfferingVO(String name, int cpu, int ramSize, int speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitCpuUse, boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId) { + public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitCpuUse, + boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId) { super(name, displayText, false, tags, recreatable, useLocalStorage, systemUse, true, domainId); this.cpu = cpu; this.ramSize = ramSize; @@ -110,12 +111,14 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering this.vm_type = vm_type == null ? null : vm_type.toString().toLowerCase(); } - public ServiceOfferingVO(String name, int cpu, int ramSize, int speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId, String hostTag) { - this(name, cpu, ramSize, speed, rateMbps, multicastRateMbps, offerHA, limitResourceUse, volatileVm, displayText, useLocalStorage, recreatable, tags, systemUse, vm_type, domainId); + public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitResourceUse, + boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId, String hostTag) { + this(name, cpu, ramSize, speed, rateMbps, multicastRateMbps, offerHA, limitResourceUse, volatileVm, displayText, useLocalStorage, recreatable, tags, systemUse, + vm_type, domainId); this.hostTag = hostTag; } - public ServiceOfferingVO(String name, int cpu, int ramSize, int speed, Integer rateMbps, Integer multicastRateMbps, + public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vm_type, Long domainId, String hostTag, String deploymentPlanner) { @@ -159,7 +162,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } @Override - public int getCpu() { + public Integer getCpu() { return cpu; } @@ -176,12 +179,12 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } @Override - public int getSpeed() { + public Integer getSpeed() { return speed; } @Override - public int getRamSize() { + public Integer getRamSize() { return ramSize; } @@ -252,4 +255,10 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering public void setDetails(Map details) { this.details = details; } + + public boolean isDynamic() { + return cpu == null || speed == null || ramSize == null; + } + + } diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java index 7da72088431..c5c4cff62c0 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDao.java @@ -17,6 +17,7 @@ package com.cloud.service.dao; import java.util.List; +import java.util.Map; import com.cloud.service.ServiceOfferingVO; import com.cloud.utils.db.GenericDao; @@ -33,4 +34,8 @@ public interface ServiceOfferingDao extends GenericDao ServiceOfferingVO persistDeafultServiceOffering(ServiceOfferingVO offering); void loadDetails(ServiceOfferingVO serviceOffering); void saveDetails(ServiceOfferingVO serviceOffering); + ServiceOfferingVO findById(Long vmId, long serviceOfferingId); + ServiceOfferingVO findByIdIncludingRemoved(Long vmId, long serviceOfferingId); + boolean isDynamic(long serviceOfferingId); + ServiceOfferingVO getcomputeOffering(long serviceOfferingId, Integer cpuCores, Integer cpuSpeed, Integer memory); } diff --git a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java index f807f0df565..917eaefb445 100644 --- a/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -25,6 +25,9 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.persistence.EntityExistsException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.dao.UserVmDetailsDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -41,13 +44,14 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase UniqueNameSearch; protected final SearchBuilder ServiceOfferingsByDomainIdSearch; protected final SearchBuilder SystemServiceOffering; protected final SearchBuilder ServiceOfferingsByKeywordSearch; protected final SearchBuilder PublicServiceOfferingSearch; - + public ServiceOfferingDaoImpl() { super(); @@ -134,7 +138,7 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase findPublicServiceOfferings(){ SearchCriteria sc = PublicServiceOfferingSearch.create(); sc.setParameters("system", false); - return listBy(sc); + return listBy(sc); } @Override @DB @@ -180,4 +184,48 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + offering.setCpu(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.cpuNumber.name()))); + offering.setSpeed(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.cpuSpeed.name()))); + offering.setRamSize(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.memory.name()))); + return offering; + } + return offering; + } + + public ServiceOfferingVO findByIdIncludingRemoved(Long vmId, long serviceOfferingId) { + ServiceOfferingVO offering = super.findByIdIncludingRemoved(serviceOfferingId); + if (offering.isDynamic()) { + if (vmId == null) { + throw new CloudRuntimeException("missing argument vmId"); + } + Map dynamicOffering = userVmDetailsDao.listDetailsKeyPairs(vmId); + offering.setCpu(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.cpuNumber.name()))); + offering.setSpeed(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.cpuSpeed.name()))); + offering.setRamSize(Integer.parseInt(dynamicOffering.get(ServiceOfferingVO.DynamicParameters.memory.name()))); + return offering; + + } + return offering; + } + + public boolean isDynamic(long serviceOfferingId) { + ServiceOfferingVO offering = super.findById(serviceOfferingId); + return offering.getCpu() == null || offering.getSpeed() == null || offering.getRamSize() == null; + } + + public ServiceOfferingVO getcomputeOffering(long serviceOfferingId, Integer cpuCores, Integer cpuSpeed, Integer memory) { + ServiceOfferingVO offering = super.findById(serviceOfferingId); + offering.setCpu(cpuCores); + offering.setSpeed(cpuSpeed); + offering.setRamSize(memory); + return offering; + } } diff --git a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java index 04064b64b56..b5b34519102 100755 --- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java @@ -127,6 +127,9 @@ public class DiskOfferingVO implements DiskOffering { @Column(name = "state") State state; + @Column(name="hv_ss_reserve") + Integer hypervisorSnapshotReserve; + public DiskOfferingVO() { uuid = UUID.randomUUID().toString(); } @@ -440,4 +443,12 @@ public class DiskOfferingVO implements DiskOffering { public Long getIopsWriteRate() { return iopsWriteRate; } + + public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { + this.hypervisorSnapshotReserve = hypervisorSnapshotReserve; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } } diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java index 1bdd09f9ab6..a130d89a86d 100755 --- a/engine/schema/src/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/com/cloud/storage/VolumeVO.java @@ -35,7 +35,6 @@ import javax.persistence.Transient; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; -import com.cloud.vm.VirtualMachine.State; @Entity @Table(name = "volumes") @@ -136,7 +135,7 @@ public class VolumeVO implements Volume { @Enumerated(value = EnumType.STRING) private State state; - @Column(name = "chain_info") + @Column(name = "chain_info",length=65535) String chainInfo; @Column(name = "uuid") @@ -161,6 +160,9 @@ public class VolumeVO implements Volume { // @Column(name="reservation") String reservationId; + @Column(name="hv_ss_reserve") + Integer hypervisorSnapshotReserve; + // Real Constructor public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size, @@ -580,4 +582,12 @@ public class VolumeVO implements Volume { public void setState(State state) { this.state = state; } + + public void setHypervisorSnapshotReserve(Integer hypervisorSnapshotReserve) { + this.hypervisorSnapshotReserve = hypervisorSnapshotReserve; + } + + public Integer getHypervisorSnapshotReserve() { + return hypervisorSnapshotReserve; + } } diff --git a/engine/schema/src/com/cloud/upgrade/dao/Upgrade420to421.java b/engine/schema/src/com/cloud/upgrade/dao/Upgrade420to421.java index 27704e8739e..d7eccc15efc 100644 --- a/engine/schema/src/com/cloud/upgrade/dao/Upgrade420to421.java +++ b/engine/schema/src/com/cloud/upgrade/dao/Upgrade420to421.java @@ -65,6 +65,60 @@ public class Upgrade420to421 implements DbUpgrade { @Override public void performDataMigration(Connection conn) { upgradeResourceCount(conn); + updateCpuOverprovisioning(conn); + } + + private void updateCpuOverprovisioning(Connection conn) { + PreparedStatement pstmt1 = null; + PreparedStatement pstmt2 = null; + PreparedStatement pstmt3 = null; + ResultSet result1 = null; + ResultSet result2 = null; + + // Get cpu overprovisioning factor from global setting and update user vm details table for all the vms if factor > 1 + + try { + pstmt1 = conn.prepareStatement("select value from `cloud`.`configuration` where name='cpu.overprovisioning.factor'"); + result1 = pstmt1.executeQuery(); + String overprov = "1"; + if(result1.next()){ + overprov = result1.getString(1); + } + // Need to populate only when overprovisioning factor doesn't pre exist. + s_logger.debug("Starting updating user_vm_details with cpu/memory overprovisioning factors"); + pstmt2 = conn.prepareStatement("select id from `cloud`.`vm_instance` where removed is null and id not in (select vm_id from `cloud`.`user_vm_details` where name='cpuOvercommitRatio')"); + pstmt3 = conn.prepareStatement("INSERT IGNORE INTO cloud.user_vm_details (vm_id, name, value) VALUES (?, ?, ?)"); + result2 = pstmt2.executeQuery(); + while (result2.next()) { + //For cpu + pstmt3.setLong(1, result2.getLong(1)); + pstmt3.setString(2, "cpuOvercommitRatio"); + pstmt3.setString(3, overprov); + pstmt3.executeUpdate(); + + // For memory + pstmt3.setLong(1, result2.getLong(1)); + pstmt3.setString(2, "memoryOvercommitRatio"); + pstmt3.setString(3, "1"); // memory overprovisioning didn't exist earlier. + pstmt3.executeUpdate(); + } + s_logger.debug("Done updating user_vm_details with cpu/memory overprovisioning factors"); + + + } catch (SQLException e) { + throw new CloudRuntimeException("Unable to update cpu/memory overprovisioning factors", e); + } finally { + try { + if (pstmt1 != null) + pstmt1.close(); + if (pstmt2 != null) + pstmt2.close(); + if (pstmt3 != null) + pstmt3.close(); + } catch (SQLException e) { + } + } + } private void upgradeResourceCount(Connection conn) { @@ -85,9 +139,9 @@ public class Upgrade420to421 implements DbUpgrade { long domain_id = rsAccount.getLong(2); // 1. update cpu,memory for all accounts pstmt2 = conn.prepareStatement( "SELECT SUM(service_offering.cpu), SUM(service_offering.ram_size)" + - " FROM `cloud`.`vm_instance`, `cloud`.`service_offering`" + - " WHERE vm_instance.service_offering_id = service_offering.id AND vm_instance.account_id = ?" + " AND vm_instance.removed is NULL" + - " AND vm_instance.vm_type='User' AND state not in ('Destroyed', 'Error', 'Expunging')"); + " FROM `cloud`.`vm_instance`, `cloud`.`service_offering`" + + " WHERE vm_instance.service_offering_id = service_offering.id AND vm_instance.account_id = ?" + " AND vm_instance.removed is NULL" + + " AND vm_instance.vm_type='User' AND state not in ('Destroyed', 'Error', 'Expunging')"); pstmt2.setLong(1, account_id); rsCount = pstmt2.executeQuery(); if (rsCount.next()) { @@ -126,7 +180,7 @@ public class Upgrade420to421 implements DbUpgrade { totalSnapshotsSize = rsCount.getLong(1); } pstmt4 = conn.prepareStatement("SELECT sum(template_store_ref.size) FROM `cloud`.`template_store_ref`,`cloud`.`vm_template` WHERE account_id = ?" + - " AND template_store_ref.template_id = vm_template.id AND download_state = 'DOWNLOADED' AND destroyed = false AND removed is NULL"); + " AND template_store_ref.template_id = vm_template.id AND download_state = 'DOWNLOADED' AND destroyed = false AND removed is NULL"); pstmt4.setLong(1, account_id); rsCount = pstmt4.executeQuery(); if (rsCount.next()) { @@ -202,7 +256,7 @@ public class Upgrade420to421 implements DbUpgrade { pstmt.executeUpdate(); pstmt.close(); } - + private static void upgradeResourceCountforDomain(Connection conn, Long domain_id, String type, Long resource_count) throws SQLException { //update or insert into resource_count table. PreparedStatement pstmt = null; diff --git a/engine/schema/src/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/com/cloud/vm/VMInstanceVO.java index 8cf7fd0dd87..96f534a4aac 100644 --- a/engine/schema/src/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/com/cloud/vm/VMInstanceVO.java @@ -163,6 +163,23 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject { * @param guestNetworkId */ void removeRouterFromGuestNetwork(long routerId, long guestNetworkId); - + + List listByClusterId(Long clusterId); } diff --git a/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java index 7676e2daf8d..5158e9857cd 100755 --- a/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/DomainRouterDaoImpl.java @@ -203,6 +203,13 @@ public class DomainRouterDaoImpl extends GenericDaoBase im return listBy(sc); } + @Override + public List listByClusterId(Long clusterId) { + SearchCriteria sc = AllFieldsSearch.create(); + //ToDo: Add cluster criteria + return listBy(sc); + } + @Override public List listByPodIdAndStates(Long podId, State... states) { SearchCriteria sc = AllFieldsSearch.create(); diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java index 2fe8140f1cd..830dea8aac7 100644 --- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDao.java @@ -122,4 +122,9 @@ public interface VMInstanceDao extends GenericDao, StateDao< List listStartingWithNoHostId(); + boolean updatePowerState(long instanceId, long powerHostId, VirtualMachine.PowerState powerState); + + void resetVmPowerStateTracking(long instanceId); + + void resetHostPowerStateTracking(long hostId); } diff --git a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java index 2c62376d642..e7f907e6545 100644 --- a/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -37,6 +37,7 @@ import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.tags.dao.ResourceTagDao; +import com.cloud.utils.DateUtil; import com.cloud.utils.Pair; import com.cloud.utils.db.Attribute; import com.cloud.utils.db.DB; @@ -63,7 +64,8 @@ import com.cloud.vm.VirtualMachine.Type; public class VMInstanceDaoImpl extends GenericDaoBase implements VMInstanceDao { public static final Logger s_logger = Logger.getLogger(VMInstanceDaoImpl.class); - + private static final int MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT = 3; + protected SearchBuilder VMClusterSearch; protected SearchBuilder LHVMClusterSearch; protected SearchBuilder IdStatesSearch; @@ -638,8 +640,6 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return listBy(sc); } - - @Override public List listDistinctHostNames(long networkId, VirtualMachine.Type... types) { SearchCriteria sc = DistinctHostNameSearch.create(); @@ -679,5 +679,63 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem sc.setParameters("state", State.Starting); return listBy(sc); } - + + @Override + public boolean updatePowerState(long instanceId, long powerHostId, VirtualMachine.PowerState powerState) { + boolean needToUpdate = false; + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + + VMInstanceVO instance = findById(instanceId); + if(instance != null) { + Long savedPowerHostId = instance.getPowerHostId(); + if(instance.getPowerState() != powerState || savedPowerHostId == null + || savedPowerHostId.longValue() != powerHostId) { + instance.setPowerState(powerState); + instance.setPowerHostId(powerHostId); + instance.setPowerStateUpdateCount(1); + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + needToUpdate = true; + update(instanceId, instance); + } else { + // to reduce DB updates, consecutive same state update for more than 3 times + if(instance.getPowerStateUpdateCount() < MAX_CONSECUTIVE_SAME_STATE_UPDATE_COUNT) { + instance.setPowerStateUpdateCount(instance.getPowerStateUpdateCount() + 1); + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + needToUpdate = true; + update(instanceId, instance); + } + } + } + + txn.commit(); + return needToUpdate; + } + + @Override + public void resetVmPowerStateTracking(long instanceId) { + TransactionLegacy txn = TransactionLegacy.currentTxn(); + txn.start(); + VMInstanceVO instance = findById(instanceId); + if(instance != null) { + instance.setPowerStateUpdateCount(0); + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + update(instanceId, instance); + } + + txn.commit(); + } + + + @Override @DB + public void resetHostPowerStateTracking(long hostId) { + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("powerHostId", SearchCriteria.Op.EQ, hostId); + + VMInstanceVO instance = this.createForUpdate(); + instance.setPowerStateUpdateCount(0); + instance.setPowerStateUpdateTime(DateUtil.currentGMTTime()); + + this.update(instance, sc); + } } diff --git a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java index 934dd92cca3..ee8765b4ee4 100644 --- a/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java +++ b/engine/schema/src/com/cloud/vm/snapshot/VMSnapshotDetailsVO.java @@ -18,27 +18,25 @@ */ package com.cloud.vm.snapshot; -import org.apache.cloudstack.api.InternalIdentity; - 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 javax.persistence.TableGenerator; + +import org.apache.cloudstack.api.ResourceDetail; @Entity @Table(name = "vm_snapshot_details") -public class VMSnapshotDetailsVO implements InternalIdentity { +public class VMSnapshotDetailsVO implements ResourceDetail { @Id - @TableGenerator(name = "vm_snapshot_details_seq", table = "sequence", pkColumnName = "name", valueColumnName = "value", pkColumnValue = "vm_snapshot_details_seq", allocationSize = 1) - @GeneratedValue(strategy = GenerationType.TABLE) + @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name = "id") private long id; @Column(name = "vm_snapshot_id") - Long vmSnapshotId; + private long resourceId; @Column(name = "name") String name; @@ -50,38 +48,34 @@ public class VMSnapshotDetailsVO implements InternalIdentity { } - public VMSnapshotDetailsVO(Long vmSnapshotId, String name, String value) { - this.vmSnapshotId = vmSnapshotId; + public VMSnapshotDetailsVO(long vmSnapshotId, String name, String value) { + this.resourceId = vmSnapshotId; this.name = name; this.value = value; } - public Long getVmSnapshotId() { - return this.vmSnapshotId; - } - - public void setVmSnapshotId(Long vmSnapshotId) { - this.vmSnapshotId = vmSnapshotId; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - + @Override + public String getName() { + return this.name; + } + + @Override + public String getValue() { + return this.value; + } + @Override public long getId() { - return id; + return id; + } + + @Override + public long getResourceId() { + return resourceId; + } + + @Override + public boolean isDisplay() { + return false; } } diff --git a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java index e84178cc4fd..ba2c52cc6be 100644 --- a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java +++ b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDao.java @@ -18,11 +18,10 @@ */ package com.cloud.vm.snapshot.dao; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDao; + import com.cloud.utils.db.GenericDao; import com.cloud.vm.snapshot.VMSnapshotDetailsVO; -import java.util.Map; - -public interface VMSnapshotDetailsDao extends GenericDao { - Map getDetails(Long vmSnapshotId); +public interface VMSnapshotDetailsDao extends GenericDao, ResourceDetailsDao { } diff --git a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java index b528b39290f..bfa912d0327 100644 --- a/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java +++ b/engine/schema/src/com/cloud/vm/snapshot/dao/VMSnapshotDetailsDaoImpl.java @@ -18,35 +18,14 @@ */ package com.cloud.vm.snapshot.dao; -import com.cloud.utils.db.GenericDaoBase; -import com.cloud.utils.db.SearchBuilder; -import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; + import com.cloud.vm.snapshot.VMSnapshotDetailsVO; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +public class VMSnapshotDetailsDaoImpl extends ResourceDetailsDaoBase implements VMSnapshotDetailsDao { -public class VMSnapshotDetailsDaoImpl extends GenericDaoBase implements VMSnapshotDetailsDao { - protected final SearchBuilder searchDetails; - - protected VMSnapshotDetailsDaoImpl() { - super(); - searchDetails = createSearchBuilder(); - searchDetails.and("vmsnapshotId", searchDetails.entity().getVmSnapshotId(), SearchCriteria.Op.EQ); - searchDetails.done(); - } @Override - public Map getDetails(Long vmSnapshotId) { - SearchCriteria sc = searchDetails.create(); - sc.setParameters("vmsnapshotId", vmSnapshotId); - - List details = listBy(sc); - Map detailsMap = new HashMap(); - for (VMSnapshotDetailsVO detail : details) { - detailsMap.put(detail.getName(), detail.getValue()); - } - - return detailsMap; + public void addDetail(long resourceId, String key, String value) { + super.addDetail(new VMSnapshotDetailsVO(resourceId, key, value)); } } diff --git a/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java b/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java index fc52f89f5d0..2a2ef9c201a 100644 --- a/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java +++ b/engine/storage/integration-test/test/com/cloud/vm/snapshot/dao/VmSnapshotDaoTest.java @@ -18,16 +18,19 @@ */ package com.cloud.vm.snapshot.dao; -import com.cloud.vm.snapshot.VMSnapshotDetailsVO; +import java.util.Map; + +import javax.inject.Inject; + import junit.framework.Assert; + import org.apache.cloudstack.storage.test.CloudStackTestNGBase; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import javax.inject.Inject; -import java.util.Map; +import com.cloud.vm.snapshot.VMSnapshotDetailsVO; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:/storageContext.xml") @@ -39,7 +42,7 @@ public class VmSnapshotDaoTest extends CloudStackTestNGBase { public void testVmSnapshotDetails() { VMSnapshotDetailsVO detailsVO = new VMSnapshotDetailsVO(1L, "test", "foo"); vmsnapshotDetailsDao.persist(detailsVO); - Map details = vmsnapshotDetailsDao.getDetails(1L); + Map details = vmsnapshotDetailsDao.listDetailsKeyPairs(1L); Assert.assertTrue(details.containsKey("test")); } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index daf6477be02..ee02eed8f6c 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -62,6 +62,7 @@ public class SnapshotObject implements SnapshotInfo { private static final Logger s_logger = Logger.getLogger(SnapshotObject.class); private SnapshotVO snapshot; private DataStore store; + private Object payload; @Inject protected SnapshotDao snapshotDao; @Inject @@ -358,6 +359,12 @@ public class SnapshotObject implements SnapshotInfo { @Override public void addPayload(Object data) { + this.payload = data; + } + + @Override + public Object getPayload() { + return this.payload; } @Override 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 ee00dd5d0e9..7b9cf957a07 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 @@ -203,12 +203,17 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase T findByUuidIncludingRemoved(Class entityType, String uuid) { // Finds and returns a unique VO using uuid, null if entity not found in db GenericDao dao = (GenericDao)GenericDaoBase.getDao(entityType); @@ -94,7 +95,7 @@ public class EntityManagerImpl extends ManagerBase implements EntityManager { _name = name; return true; - } + } @Override public boolean start() { diff --git a/framework/db/src/com/cloud/utils/db/ConnectionConcierge.java b/framework/db/src/com/cloud/utils/db/ConnectionConcierge.java index acb9cc6851b..bb76d0f03c8 100644 --- a/framework/db/src/com/cloud/utils/db/ConnectionConcierge.java +++ b/framework/db/src/com/cloud/utils/db/ConnectionConcierge.java @@ -144,8 +144,10 @@ public class ConnectionConcierge { PreparedStatement pstmt = null; try { if (conn != null) { - pstmt = conn.prepareStatement("SELECT 1"); - pstmt.executeQuery(); + synchronized (conn) { + pstmt = conn.prepareStatement("SELECT 1"); + pstmt.executeQuery(); + } } return null; } catch (Throwable th) { diff --git a/framework/db/src/com/cloud/utils/db/Merovingian2.java b/framework/db/src/com/cloud/utils/db/Merovingian2.java index 8544aab0384..15e3d24299f 100644 --- a/framework/db/src/com/cloud/utils/db/Merovingian2.java +++ b/framework/db/src/com/cloud/utils/db/Merovingian2.java @@ -253,10 +253,12 @@ public class Merovingian2 extends StandardMBean implements MerovingianMBean { s_logger.info("Cleaning up locks for " + msId); PreparedStatement pstmt = null; try { - pstmt = _concierge.conn().prepareStatement(CLEANUP_MGMT_LOCKS_SQL); - pstmt.setLong(1, msId); - int rows = pstmt.executeUpdate(); - s_logger.info("Released " + rows + " locks for " + msId); + synchronized (_concierge.conn()) { + pstmt = _concierge.conn().prepareStatement(CLEANUP_MGMT_LOCKS_SQL); + pstmt.setLong(1, msId); + int rows = pstmt.executeUpdate(); + s_logger.info("Released " + rows + " locks for " + msId); + } } catch (SQLException e) { throw new CloudRuntimeException("Unable to clear the locks", e); } finally { diff --git a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java index a874ce10a56..39893bd4eab 100755 --- a/framework/db/src/com/cloud/utils/db/TransactionLegacy.java +++ b/framework/db/src/com/cloud/utils/db/TransactionLegacy.java @@ -17,7 +17,6 @@ package com.cloud.utils.db; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.sql.Connection; import java.sql.PreparedStatement; @@ -41,12 +40,9 @@ import org.apache.commons.pool.KeyedObjectPoolFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory; import org.apache.log4j.Logger; -import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; -import org.jasypt.properties.EncryptableProperties; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; -import com.cloud.utils.crypt.EncryptionSecretKeyChecker; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.mgmt.JmxUtil; @@ -108,8 +104,6 @@ public class TransactionLegacy { private Statement _stmt; private String _creator; - private TransactionLegacy _prev = null; - public static TransactionLegacy currentTxn() { return currentTxn(true); } @@ -1016,6 +1010,7 @@ public class TransactionLegacy { private static DataSource s_usageDS; private static DataSource s_awsapiDS; private static DataSource s_simulatorDS; + private static boolean s_dbHAEnabled; static { // Initialize with assumed db.properties file @@ -1031,11 +1026,15 @@ public class TransactionLegacy { } } + @SuppressWarnings({ "rawtypes", "unchecked" }) public static void initDataSource(Properties dbProps) { try { if (dbProps.size() == 0) return; + s_dbHAEnabled = Boolean.valueOf(dbProps.getProperty("db.ha.enabled")); + s_logger.info("Is Data Base High Availiability enabled? Ans : " + s_dbHAEnabled); + // FIXME: If params are missing...default them???? final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive")); final int cloudMaxIdle = Integer.parseInt(dbProps.getProperty("db.cloud.maxIdle")); @@ -1071,6 +1070,14 @@ public class TransactionLegacy { final boolean cloudPoolPreparedStatements = Boolean.parseBoolean(dbProps.getProperty("db.cloud.poolPreparedStatements")); final String url = dbProps.getProperty("db.cloud.url.params"); + String cloudDbHAParams = null; + String cloudSlaves = null; + if(s_dbHAEnabled) { + cloudDbHAParams = getDBHAParams("cloud", dbProps); + cloudSlaves = dbProps.getProperty("db.cloud.slaves"); + s_logger.info("The slaves configured for Cloud Data base is/are : " + cloudSlaves); + } + final boolean useSSL = Boolean.parseBoolean(dbProps.getProperty("db.cloud.useSSL")); if (useSSL) { System.setProperty("javax.net.ssl.keyStore", dbProps.getProperty("db.cloud.keyStore")); @@ -1082,8 +1089,8 @@ public class TransactionLegacy { final GenericObjectPool cloudConnectionPool = new GenericObjectPool(null, cloudMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, cloudMaxWait, cloudMaxIdle, cloudTestOnBorrow, false, cloudTimeBtwEvictionRunsMillis, 1, cloudMinEvcitableIdleTimeMillis, cloudTestWhileIdle); - final ConnectionFactory cloudConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + ":" + cloudPort + "/" + cloudDbName + - "?autoReconnect=" + cloudAutoReconnect + (url != null ? "&" + url : "") + (useSSL ? "&useSSL=true" : ""), cloudUsername, cloudPassword); + final ConnectionFactory cloudConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + (s_dbHAEnabled ? "," + cloudSlaves : "") + ":" + cloudPort + "/" + cloudDbName + + "?autoReconnect=" + cloudAutoReconnect + (url != null ? "&" + url : "") + (useSSL ? "&useSSL=true" : "") + (s_dbHAEnabled ? "&" + cloudDbHAParams : ""), cloudUsername, cloudPassword); final KeyedObjectPoolFactory poolableObjFactory = (cloudPoolPreparedStatements ? new StackKeyedObjectPoolFactory() : null); @@ -1108,8 +1115,8 @@ public class TransactionLegacy { final GenericObjectPool usageConnectionPool = new GenericObjectPool(null, usageMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, usageMaxWait, usageMaxIdle); - final ConnectionFactory usageConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + usageHost + ":" + usagePort + "/" + usageDbName + - "?autoReconnect=" + usageAutoReconnect + (usageUrl != null ? "&" + usageUrl : ""), usageUsername, usagePassword); + final ConnectionFactory usageConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + usageHost + (s_dbHAEnabled ? "," + dbProps.getProperty("db.cloud.slaves") : "") + ":" + usagePort + "/" + usageDbName + + "?autoReconnect=" + usageAutoReconnect + (usageUrl != null ? "&" + usageUrl : "") + (s_dbHAEnabled ? "&" + getDBHAParams("usage", dbProps) : ""), usageUsername, usagePassword); final PoolableConnectionFactory usagePoolableConnectionFactory = new PoolableConnectionFactory(usageConnectionFactory, usageConnectionPool, new StackKeyedObjectPoolFactory(), null, false, false); @@ -1121,8 +1128,8 @@ public class TransactionLegacy { final String awsapiDbName = dbProps.getProperty("db.awsapi.name"); final GenericObjectPool awsapiConnectionPool = new GenericObjectPool(null, usageMaxActive, GenericObjectPool.DEFAULT_WHEN_EXHAUSTED_ACTION, usageMaxWait, usageMaxIdle); - final ConnectionFactory awsapiConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + ":" + cloudPort + "/" + awsapiDbName + - "?autoReconnect=" + usageAutoReconnect, cloudUsername, cloudPassword); + final ConnectionFactory awsapiConnectionFactory = new DriverManagerConnectionFactory("jdbc:mysql://" + cloudHost + (s_dbHAEnabled ? "," + cloudSlaves : "") + ":" + cloudPort + "/" + awsapiDbName + + "?autoReconnect=" + cloudAutoReconnect + (s_dbHAEnabled ? "&" + cloudDbHAParams : ""), cloudUsername, cloudPassword); final PoolableConnectionFactory awsapiPoolableConnectionFactory = new PoolableConnectionFactory(awsapiConnectionFactory, awsapiConnectionPool, new StackKeyedObjectPoolFactory(), null, false, false); @@ -1160,7 +1167,19 @@ public class TransactionLegacy { s_logger.warn("Unable to load db configuration, using defaults with 5 connections. Falling back on assumed datasource on localhost:3306 using username:password=cloud:cloud. Please check your configuration", e); } } + + private static String getDBHAParams(String dbName,Properties dbProps) { + StringBuilder sb = new StringBuilder(); + sb.append("failOverReadOnly=" + dbProps.getProperty("db." + dbName + ".failOverReadOnly")); + sb.append("&").append("reconnectAtTxEnd=" + dbProps.getProperty("db." + dbName + ".reconnectAtTxEnd")); + sb.append("&").append("autoReconnectForPools=" + dbProps.getProperty("db." + dbName + ".autoReconnectForPools")); + sb.append("&").append("secondsBeforeRetryMaster=" + dbProps.getProperty("db." + dbName + ".secondsBeforeRetryMaster")); + sb.append("&").append("queriesBeforeRetryMaster=" + dbProps.getProperty("db." + dbName + ".queriesBeforeRetryMaster")); + sb.append("&").append("initialTimeout=" + dbProps.getProperty("db." + dbName + ".initialTimeout")); + return sb.toString(); + } + @SuppressWarnings({ "unchecked", "rawtypes" }) private static DataSource getDefaultDataSource(final String database) { final GenericObjectPool connectionPool = new GenericObjectPool(null, 5); final ConnectionFactory connectionFactory = new DriverManagerConnectionFactory( diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDao.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDao.java new file mode 100644 index 00000000000..dfb063f21a6 --- /dev/null +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDao.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.framework.jobs.dao; + +import java.util.Date; +import java.util.List; + +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO.Step; + +import com.cloud.utils.db.GenericDao; +import com.cloud.vm.VirtualMachine; + +public interface VmWorkJobDao extends GenericDao { + VmWorkJobVO findPendingWorkJob(VirtualMachine.Type type, long instanceId); + List listPendingWorkJobs(VirtualMachine.Type type, long instanceId); + List listPendingWorkJobs(VirtualMachine.Type type, long instanceId, String jobCmd); + + void updateStep(long workJobId, Step step); + void expungeCompletedWorkJobs(Date cutDate); +} diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDaoImpl.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDaoImpl.java new file mode 100644 index 00000000000..77515a797fe --- /dev/null +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/dao/VmWorkJobDaoImpl.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 org.apache.cloudstack.framework.jobs.dao; + +import java.util.Date; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO; +import org.apache.cloudstack.framework.jobs.impl.VmWorkJobVO.Step; +import org.apache.cloudstack.jobs.JobInfo; + +import com.cloud.utils.DateUtil; +import com.cloud.utils.db.Filter; +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; +import com.cloud.vm.VirtualMachine; + +public class VmWorkJobDaoImpl extends GenericDaoBase implements VmWorkJobDao { + + protected SearchBuilder PendingWorkJobSearch; + protected SearchBuilder PendingWorkJobByCommandSearch; + protected SearchBuilder ExpungeWorkJobSearch; + + public VmWorkJobDaoImpl() { + } + + @PostConstruct + public void init() { + PendingWorkJobSearch = createSearchBuilder(); + PendingWorkJobSearch.and("jobStatus", PendingWorkJobSearch.entity().getStatus(), Op.EQ); + PendingWorkJobSearch.and("vmType", PendingWorkJobSearch.entity().getVmType(), Op.EQ); + PendingWorkJobSearch.and("vmInstanceId", PendingWorkJobSearch.entity().getVmInstanceId(), Op.EQ); + PendingWorkJobSearch.and("step", PendingWorkJobSearch.entity().getStep(), Op.NEQ); + PendingWorkJobSearch.done(); + + PendingWorkJobByCommandSearch = createSearchBuilder(); + PendingWorkJobByCommandSearch.and("jobStatus", PendingWorkJobByCommandSearch.entity().getStatus(), Op.EQ); + PendingWorkJobByCommandSearch.and("vmType", PendingWorkJobByCommandSearch.entity().getVmType(), Op.EQ); + PendingWorkJobByCommandSearch.and("vmInstanceId", PendingWorkJobByCommandSearch.entity().getVmInstanceId(), Op.EQ); + PendingWorkJobByCommandSearch.and("step", PendingWorkJobByCommandSearch.entity().getStep(), Op.NEQ); + PendingWorkJobByCommandSearch.and("cmd", PendingWorkJobByCommandSearch.entity().getCmd(), Op.EQ); + PendingWorkJobByCommandSearch.done(); + + ExpungeWorkJobSearch = createSearchBuilder(); + ExpungeWorkJobSearch.and("lastUpdated", ExpungeWorkJobSearch.entity().getLastUpdated(), Op.LT); + ExpungeWorkJobSearch.and("jobStatus", ExpungeWorkJobSearch.entity().getStatus(), Op.NEQ); + ExpungeWorkJobSearch.done(); + } + + @Override + public VmWorkJobVO findPendingWorkJob(VirtualMachine.Type type, long instanceId) { + + SearchCriteria sc = PendingWorkJobSearch.create(); + sc.setParameters("jobStatus", JobInfo. Status.IN_PROGRESS); + sc.setParameters("vmType", type); + sc.setParameters("vmInstanceId", instanceId); + + Filter filter = new Filter(VmWorkJobVO.class, "created", true, null, null); + List result = this.listBy(sc, filter); + if(result != null && result.size() > 0) + return result.get(0); + + return null; + } + + @Override + public List listPendingWorkJobs(VirtualMachine.Type type, long instanceId) { + + SearchCriteria sc = PendingWorkJobSearch.create(); + sc.setParameters("jobStatus", JobInfo.Status.IN_PROGRESS); + sc.setParameters("vmType", type); + sc.setParameters("vmInstanceId", instanceId); + + Filter filter = new Filter(VmWorkJobVO.class, "created", true, null, null); + return this.listBy(sc, filter); + } + + @Override + public List listPendingWorkJobs(VirtualMachine.Type type, long instanceId, String jobCmd) { + + SearchCriteria sc = PendingWorkJobByCommandSearch.create(); + sc.setParameters("jobStatus", JobInfo.Status.IN_PROGRESS); + sc.setParameters("vmType", type); + sc.setParameters("vmInstanceId", instanceId); + sc.setParameters("cmd", jobCmd); + + Filter filter = new Filter(VmWorkJobVO.class, "created", true, null, null); + return this.listBy(sc, filter); + } + + @Override + public void updateStep(long workJobId, Step step) { + VmWorkJobVO jobVo = findById(workJobId); + jobVo.setStep(step); + jobVo.setLastUpdated(DateUtil.currentGMTTime()); + update(workJobId, jobVo); + } + + @Override + public void expungeCompletedWorkJobs(Date cutDate) { + SearchCriteria sc = ExpungeWorkJobSearch.create(); + sc.setParameters("lastUpdated",cutDate); + sc.setParameters("jobStatus", JobInfo.Status.IN_PROGRESS); + + expunge(sc); + } +} diff --git a/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/VmWorkJobVO.java b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/VmWorkJobVO.java new file mode 100644 index 00000000000..860cc57a315 --- /dev/null +++ b/framework/jobs/src/org/apache/cloudstack/framework/jobs/impl/VmWorkJobVO.java @@ -0,0 +1,101 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.framework.jobs.impl; + +import javax.persistence.Column; +import javax.persistence.DiscriminatorValue; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.Table; + +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; + +import com.cloud.vm.VirtualMachine; + +@Entity +@Table(name="vm_work_job") +@DiscriminatorValue(value="VmWork") +@PrimaryKeyJoinColumn(name="id") +public class VmWorkJobVO extends AsyncJobVO { + + // These steps are rather arbitrary. What's recorded depends on the + // the operation being performed. + public enum Step { + Filed(false), + Prepare(false), + Starting(true), + Started(false), + Release(false), + Done(false), + Migrating(true), + Reconfiguring(false), + Error(false); + + boolean updateState; // Should the VM State be updated after this step? + private Step(boolean updateState) { + this.updateState = updateState; + } + + boolean updateState() { + return updateState; + } + } + + @Column(name="step") + Step step; + + @Column(name="vm_type") + @Enumerated(value=EnumType.STRING) + VirtualMachine.Type vmType; + + @Column(name="vm_instance_id") + long vmInstanceId; + + protected VmWorkJobVO() { + } + + public VmWorkJobVO(String related) { + step = Step.Filed; + setRelated(related); + } + + public Step getStep() { + return step; + } + + public void setStep(Step step) { + this.step = step; + } + + public VirtualMachine.Type getVmType() { + return vmType; + } + + public void setVmType(VirtualMachine.Type vmType) { + this.vmType = vmType; + } + + public long getVmInstanceId() { + return vmInstanceId; + } + + public void setVmInstanceId(long vmInstanceId) { + this.vmInstanceId = vmInstanceId; + } +} 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 2bd362eb8e3..38008bb76f2 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 @@ -34,7 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanNameAware; -import com.cloud.utils.component.Named; import com.cloud.utils.component.Registry; public class ExtensionRegistry implements Registry, Configurable, BeanNameAware { @@ -81,7 +80,7 @@ public class ExtensionRegistry implements Registry, Configurable, BeanNa } } - String name = getName(item); + String name = RegistryUtils.getName(item); if ( name != null && exclude.size() > 0 && exclude.contains(name) ) { return false; @@ -103,7 +102,7 @@ public class ExtensionRegistry implements Registry, Configurable, BeanNa break; } - if ( getName(registered.get(i)).equals(orderTest) ) { + if ( RegistryUtils.getName(registered.get(i)).equals(orderTest) ) { i++; } } @@ -116,16 +115,6 @@ public class ExtensionRegistry implements Registry, Configurable, BeanNa return true; } - - protected String getName(Object object) { - if ( object instanceof Named ) { - String name = ((Named)object).getName(); - if ( name != null ) - return name; - } - - return object == null ? null : object.getClass().getSimpleName(); - } @Override public void unregister(Object type) { diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java index bd7a0334a6f..4975c5a4d70 100644 --- a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryLifecycle.java @@ -20,6 +20,7 @@ package org.apache.cloudstack.spring.lifecycle.registry; import java.util.HashSet; import java.util.Iterator; +import java.util.Properties; import java.util.Set; import org.slf4j.Logger; @@ -29,6 +30,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.SmartLifecycle; +import org.springframework.util.StringUtils; import com.cloud.utils.component.Registry; @@ -36,6 +38,9 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App private static final Logger log = LoggerFactory.getLogger(RegistryLifecycle.class); + public static final String EXTENSION_EXCLUDE = "extensions.exclude"; + public static final String EXTENSION_INCLUDE_PREFIX = "extensions.include."; + Registry registry; /* The bean name works around circular dependency issues in Spring. This shouldn't be @@ -46,15 +51,52 @@ public class RegistryLifecycle implements BeanPostProcessor, SmartLifecycle, App Set beans = new HashSet(); Class typeClass; ApplicationContext applicationContext; + Set excludes = null; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if ( typeClass.isAssignableFrom(bean.getClass()) ) + if ( typeClass.isAssignableFrom(bean.getClass()) && ! isExcluded(bean) ) { beans.add(bean); - + } + return bean; } + protected synchronized boolean isExcluded(Object bean) { + String name = RegistryUtils.getName(bean); + + if ( excludes == null ) { + loadExcluded(); + } + + boolean result = excludes.contains(name); + if ( result ) { + log.info("Excluding extension [{}] based on configuration", name); + } + + return result; + } + + protected synchronized void loadExcluded() { + Properties props = applicationContext.getBean("DefaultConfigProperties", Properties.class); + excludes = new HashSet(); + for ( String exclude : props.getProperty(EXTENSION_EXCLUDE, "").trim().split("\\s*,\\s*") ) { + if ( StringUtils.hasText(exclude) ) { + excludes.add(exclude); + } + } + + for ( String key : props.stringPropertyNames() ) { + if ( key.startsWith(EXTENSION_INCLUDE_PREFIX) ) { + String module = key.substring(EXTENSION_INCLUDE_PREFIX.length()); + boolean include = props.getProperty(key).equalsIgnoreCase("true"); + if ( ! include ) { + excludes.add(module); + } + } + } + } + @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; diff --git a/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.java new file mode 100644 index 00000000000..8d7e9b68fb6 --- /dev/null +++ b/framework/spring/lifecycle/src/main/java/org/apache/cloudstack/spring/lifecycle/registry/RegistryUtils.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.spring.lifecycle.registry; + +import com.cloud.utils.component.Named; + +public class RegistryUtils { + + public static String getName(Object object) { + if ( object instanceof Named ) { + String name = ((Named)object).getName(); + if ( name != null ) + return name; + } + + return object == null ? null : object.getClass().getSimpleName(); + } + +} diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java index 60d0262ab36..3a8cb6fca96 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/context/ResourceApplicationContext.java @@ -18,6 +18,8 @@ */ package org.apache.cloudstack.spring.module.context; +import java.util.Arrays; + import org.springframework.context.support.AbstractXmlApplicationContext; import org.springframework.core.io.Resource; @@ -52,4 +54,10 @@ public class ResourceApplicationContext extends AbstractXmlApplicationContext { this.applicationName = applicationName; } + @Override + public String toString() { + return "ResourceApplicationContext [applicationName=" + applicationName + ", configResources=" + + Arrays.toString(configResources) + "]"; + } + } diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java index e624a5b04d4..09dbd6c9799 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/CloudStackSpringContext.java @@ -37,6 +37,7 @@ public class CloudStackSpringContext { private static final Logger log = LoggerFactory.getLogger(CloudStackSpringContext.class); + public static final String CLOUDSTACK_CONTEXT_SERVLET_KEY = CloudStackSpringContext.class.getSimpleName(); public static final String CLOUDSTACK_CONTEXT = "META-INF/cloudstack"; public static final String CLOUDSTACK_BASE = "bootstrap"; diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.java new file mode 100644 index 00000000000..74fc068fa74 --- /dev/null +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/factory/QuietLoaderFactory.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 org.apache.cloudstack.spring.module.factory; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.core.io.Resource; + +public class QuietLoaderFactory implements FactoryBean { + + Resource[] resources; + + @Override + public Resource[] getObject() throws Exception { + List existing = new ArrayList(); + + for ( Resource resource : resources ) { + if ( resource.exists() ) { + existing.add(resource); + } + } + + return existing.toArray(new Resource[existing.size()]); + } + + @Override + public Class getObjectType() { + return Resource[].class; + } + + @Override + public boolean isSingleton() { + return false; + } + + public Resource[] getResources() { + return resources; + } + + public void setResources(Resource[] resources) { + this.resources = resources; + } + +} diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java index 6c5180874b8..979d08e0eae 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinition.java @@ -114,7 +114,8 @@ public class DefaultModuleDefinition implements ModuleDefinition { if ( ! moduleUrl.equals(selfUrl) ) { throw new IOException("Resource [" + location() + "] and [" + self.getURL() + "] do not appear to be the same resource, " + - "please ensure the name property is correct"); + "please ensure the name property is correct or that the " + + "module is not defined twice"); } } diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java index 15df839cf1a..3106ee5234a 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/model/impl/DefaultModuleDefinitionSet.java @@ -19,19 +19,23 @@ package org.apache.cloudstack.spring.module.model.impl; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.Stack; import org.apache.cloudstack.spring.module.context.ResourceApplicationContext; import org.apache.cloudstack.spring.module.model.ModuleDefinition; import org.apache.cloudstack.spring.module.model.ModuleDefinitionSet; +import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; @@ -40,18 +44,25 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; +import org.springframework.util.StringUtils; public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { private static final Logger log = LoggerFactory.getLogger(DefaultModuleDefinitionSet.class); public static final String DEFAULT_CONFIG_RESOURCES = "DefaultConfigResources"; + public static final String DEFAULT_CONFIG_PROPERTIES = "DefaultConfigProperties"; + public static final String MODULES_EXCLUDE = "modules.exclude"; + public static final String MODULES_INCLUDE_PREFIX = "modules.include."; + public static final String MODULE_PROPERITES = "ModuleProperties"; public static final String DEFAULT_CONFIG_XML = "defaults-context.xml"; String root; Map modules; Map contexts = new HashMap(); ApplicationContext rootContext = null; + Set excludes = new HashSet(); + Properties configProperties = null; public DefaultModuleDefinitionSet(Map modules, String root) { super(); @@ -136,7 +147,7 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { } protected boolean shouldLoad(ModuleDefinition def) { - return true; + return ! excludes.contains(def.getName()); } protected ApplicationContext getDefaultsContext() { @@ -156,9 +167,52 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { } } }); - + + configProperties = (Properties) context.getBean(DEFAULT_CONFIG_PROPERTIES); + for ( Resource resource : resources ) { + load(resource, configProperties); + } + + for ( Resource resource : (Resource[])context.getBean(MODULE_PROPERITES) ) { + load(resource, configProperties); + } + + parseExcludes(); + return context; } + + protected void parseExcludes() { + for ( String exclude : configProperties.getProperty(MODULES_EXCLUDE, "").trim().split("\\s*,\\s*") ) { + if ( StringUtils.hasText(exclude) ) { + excludes.add(exclude); + } + } + + for ( String key : configProperties.stringPropertyNames() ) { + if ( key.startsWith(MODULES_INCLUDE_PREFIX) ) { + String module = key.substring(MODULES_INCLUDE_PREFIX.length()); + boolean include = configProperties.getProperty(key).equalsIgnoreCase("true"); + if ( ! include ) { + excludes.add(module); + } + } + } + } + + protected void load(Resource resource, Properties props) { + InputStream is = null; + try { + if ( resource.exists() ) { + is = resource.getInputStream(); + props.load(is); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to load resource [" + resource + "]", e); + } finally { + IOUtils.closeQuietly(is); + } + } protected void printHierarchy() { withModule(new WithModule() { @@ -178,6 +232,7 @@ public class DefaultModuleDefinitionSet implements ModuleDefinitionSet { return; if ( ! shouldLoad(def) ) { + log.info("Excluding context [{}] based on configuration", def.getName()); return; } diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/CloudStackContextLoaderListener.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/CloudStackContextLoaderListener.java index e704437774b..96e686c66df 100644 --- a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/CloudStackContextLoaderListener.java +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/CloudStackContextLoaderListener.java @@ -49,6 +49,7 @@ public class CloudStackContextLoaderListener extends ContextLoaderListener { public void contextInitialized(ServletContextEvent event) { try { cloudStackContext = new CloudStackSpringContext(); + event.getServletContext().setAttribute(CloudStackSpringContext.CLOUDSTACK_CONTEXT_SERVLET_KEY, cloudStackContext); } catch (IOException e) { log.error("Failed to start CloudStack", e); throw new RuntimeException("Failed to initialize CloudStack Spring modules", e); diff --git a/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/ModuleBasedFilter.java b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/ModuleBasedFilter.java new file mode 100644 index 00000000000..9dd1b82e902 --- /dev/null +++ b/framework/spring/module/src/main/java/org/apache/cloudstack/spring/module/web/ModuleBasedFilter.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 org.apache.cloudstack.spring.module.web; + +import javax.servlet.Filter; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; + +import org.apache.cloudstack.spring.module.factory.CloudStackSpringContext; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; + +public abstract class ModuleBasedFilter implements Filter { + + boolean enabled = false; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String module = filterConfig.getInitParameter("module"); + CloudStackSpringContext context = + (CloudStackSpringContext) filterConfig.getServletContext().getAttribute(CloudStackSpringContext.CLOUDSTACK_CONTEXT_SERVLET_KEY); + + if ( context == null ) + return; + + ApplicationContext applicationContext = context.getApplicationContextForWeb(module); + if ( applicationContext != null ) { + AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory(); + if ( factory != null ) { + factory.autowireBean(this); + enabled = true; + } + } + } + + @Override + public void destroy() { + enabled = false; + } + + public boolean isEnabled() { + return enabled; + } + +} diff --git a/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml b/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml index b19833a735b..8c133ec5a94 100644 --- a/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml +++ b/framework/spring/module/src/main/resources/org/apache/cloudstack/spring/module/model/impl/defaults-context.xml @@ -24,5 +24,14 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + + + + + classpath:modules.properties + + + diff --git a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java index 2947615d597..3cb00bcc714 100644 --- a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java +++ b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/factory/ModuleBasedContextFactoryTest.java @@ -66,6 +66,16 @@ public class ModuleBasedContextFactoryTest { assertEquals("a string", set.getApplicationContext("child1").getBean("override", String.class)); } + @Test + public void testExcluded() throws IOException { + ModuleBasedContextFactory factory = new ModuleBasedContextFactory(); + ModuleDefinitionSet set = factory.loadModules(defs, "base"); + + assertNull(set.getApplicationContext("excluded")); + assertNull(set.getApplicationContext("excluded2")); + assertNull(set.getApplicationContext("orphan-of-excluded")); + } + @Test public void testBeans() throws IOException { ModuleBasedContextFactory factory = new ModuleBasedContextFactory(); diff --git a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java index 5114187b9a5..989aa9fbcb9 100644 --- a/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java +++ b/framework/spring/module/src/test/java/org/apache/cloudstack/spring/module/locator/impl/ClasspathModuleDefinitionSetLocatorTest.java @@ -34,7 +34,7 @@ public class ClasspathModuleDefinitionSetLocatorTest { Collection modules = factory.locateModules("testhierarchy"); - assertEquals(5, modules.size()); + assertEquals(8, modules.size()); } } diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties b/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties new file mode 100644 index 00000000000..abd83684710 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded/module.properties @@ -0,0 +1,19 @@ +# 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=excluded +parent=base \ No newline at end of file diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml new file mode 100644 index 00000000000..57e68ddd595 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties b/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties new file mode 100644 index 00000000000..2d06f6918f1 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded2/module.properties @@ -0,0 +1,19 @@ +# 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=excluded2 +parent=base \ No newline at end of file diff --git a/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml new file mode 100644 index 00000000000..55fd9b4810d --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/excluded2/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties new file mode 100644 index 00000000000..b1f6ab97976 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/defaults.properties @@ -0,0 +1,20 @@ +# 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. + +modules.include.excluded=false +modules.include.base=True +modules.exclude=excluded2,excluded2 \ No newline at end of file diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties new file mode 100644 index 00000000000..7684deb6c26 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/module.properties @@ -0,0 +1,19 @@ +# 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=orphan-of-excluded +parent=excluded \ No newline at end of file diff --git a/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml new file mode 100644 index 00000000000..6495e1b48b8 --- /dev/null +++ b/framework/spring/module/src/test/resources/testhierarchy/orphan-of-excluded/test-context.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/packaging/centos63/cloud.spec b/packaging/centos63/cloud.spec index ebe45113eb1..cd6ff4bfbe2 100644 --- a/packaging/centos63/cloud.spec +++ b/packaging/centos63/cloud.spec @@ -252,7 +252,7 @@ rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/cl rm -rf ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/vms for name in db.properties log4j-cloud.xml tomcat6-nonssl.conf tomcat6-ssl.conf server-ssl.xml server-nonssl.xml \ - catalina.policy catalina.properties classpath.conf tomcat-users.xml web.xml environment.properties ; do + catalina.policy catalina.properties classpath.conf tomcat-users.xml web.xml environment.properties cloudmanagementserver.keystore ; do mv ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/webapps/client/WEB-INF/classes/$name \ ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/$name done @@ -450,6 +450,13 @@ else echo "Unable to determine ssl settings for tomcat.conf, please run cloudstack-setup-management manually" fi +if [ -f "%{_sysconfdir}/cloud.rpmsave/management/cloud.keystore" ]; then + mv %{_sysconfdir}/%{name}/management/cloudmanagementserver.keystore %{_sysconfdir}/%{name}/management/cloudmanagementserver.keystore.rpmnew + cp -p %{_sysconfdir}/cloud.rpmsave/management/cloud.keystore %{_sysconfdir}/%{name}/management/cloudmanagementserver.keystore + # make sure we only do this on the first install of this RPM, don't want to overwrite on a reinstall + mv %{_sysconfdir}/cloud.rpmsave/management/cloud.keystore %{_sysconfdir}/cloud.rpmsave/management/cloud.keystore.rpmsave +fi + %preun agent /sbin/service cloudstack-agent stop || true if [ "$1" == "0" ] ; then @@ -539,6 +546,7 @@ fi %config(noreplace) %{_sysconfdir}/%{name}/management/cloud-bridge.properties %config(noreplace) %{_sysconfdir}/%{name}/management/commons-logging.properties %config(noreplace) %{_sysconfdir}/%{name}/management/ec2-service.properties +%config(noreplace) %{_sysconfdir}/%{name}/management/cloudmanagementserver.keystore %attr(0755,root,root) %{_initrddir}/%{name}-management %attr(0755,root,root) %{_bindir}/%{name}-setup-management %attr(0755,root,root) %{_bindir}/%{name}-update-xenserver-licenses diff --git a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java index f1fa71c3c56..24368ee3d9c 100644 --- a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java +++ b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.implicitplanner; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -34,6 +35,7 @@ import java.util.UUID; import javax.inject.Inject; +import com.cloud.hypervisor.Hypervisor; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -163,7 +165,6 @@ public class ImplicitPlannerTest { acct.setId(accountId); UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); - CallContext.register(user, acct); } diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java index ef6ec312d13..9618e6bb47f 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalPxeManagerImpl.java @@ -187,7 +187,7 @@ public class BaremetalPxeManagerImpl extends ManagerBase implements BaremetalPxe UserVmVO vm = (UserVmVO) profile.getVirtualMachine(); _vmDao.loadDetails(vm); - String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText(); + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); String zoneName = _dcDao.findById(vm.getDataCenterId()).getName(); NicVO nvo = _nicDao.findById(nic.getId()); VmDataCommand cmd = new VmDataCommand(nvo.getIp4Address(), vm.getInstanceName(), _ntwkModel.getExecuteInSeqNtwkElmtCmd()); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore b/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore index 99afc0b89f2..a2f30c86ba7 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/.gitignore @@ -1,3 +1,20 @@ +# 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. + packages *.suo */obj/* diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index ae595fcc215..dfc3e206f06 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -279,6 +279,27 @@ namespace HypervResource } } + public class DiskTO + { + public string type; + public TemplateObjectTO templateObjectTO = null; + + public static DiskTO ParseJson(dynamic json) + { + DiskTO result = null; + if (json != null) + { + result = new DiskTO() + { + templateObjectTO = TemplateObjectTO.ParseJson(json.data), + type = (string)json.type, + }; + } + + return result; + } + } + enum VolumeType { UNKNOWN, @@ -375,6 +396,20 @@ namespace HypervResource } } + public class VmState + { + [JsonProperty("state")] + public String state; + [JsonProperty("host")] + String host; + public VmState() { } + public VmState(String vmState, String host) + { + this.state = vmState; + this.host = host; + } + } + public struct StoragePoolInfo { [JsonProperty("uuid")] @@ -589,6 +624,7 @@ namespace HypervResource public const string SwiftTO = "com.cloud.agent.api.to.SwiftTO"; public const string VirtualMachineTO = "com.cloud.agent.api.to.VirtualMachineTO"; public const string VolumeTO = "com.cloud.agent.api.to.VolumeTO"; + public const string DiskTO = "com.cloud.agent.api.to.DiskTO"; public const string InternalErrorException = "com.cloud.exception.InternalErrorException"; public const string HostType = "com.cloud.host.Host.Type"; public const string HypervisorType = "com.cloud.hypervisor.Hypervisor.HypervisorType"; diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index ed3736b072b..0d56fef0acd 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.Net.Http; using System.Security.Cryptography; using System.Security.Principal; using System.Web.Http; +using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2; namespace HypervResource { @@ -196,7 +197,19 @@ namespace HypervResource try { string vmName = (string)cmd.vmName; - result = true; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + TemplateObjectTO dataStore = disk.templateObjectTO; + + if (dataStore.nfsDataStoreTO != null) + { + NFSTO share = dataStore.nfsDataStoreTO; + Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); + + // The share is mapped, now attach the iso + string isoPath = Path.Combine(share.UncPath.Replace('/', Path.DirectorySeparatorChar), dataStore.path); + wmiCallsV2.AttachIso(vmName, isoPath); + result = true; + } } catch (Exception sysEx) { @@ -229,7 +242,18 @@ namespace HypervResource try { string vmName = (string)cmd.vmName; - result = true; + DiskTO disk = DiskTO.ParseJson(cmd.disk); + TemplateObjectTO dataStore = disk.templateObjectTO; + + if (dataStore.nfsDataStoreTO != null) + { + NFSTO share = dataStore.nfsDataStoreTO; + // The share is mapped, now attach the iso + string isoPath = Path.Combine(share.UncPath.Replace('/', Path.DirectorySeparatorChar), + dataStore.path.Replace('/', Path.DirectorySeparatorChar)); + wmiCallsV2.DetachDisk(vmName, isoPath); + result = true; + } } catch (Exception sysEx) { @@ -247,6 +271,49 @@ namespace HypervResource } } + // POST api/HypervResource/RebootCommand + [HttpPost] + [ActionName(CloudStackTypes.RebootCommand)] + public JContainer RebootCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.RebootCommand + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + string vmName = (string)cmd.vmName; + var sys = wmiCallsV2.GetComputerSystem(vmName); + if (sys == null) + { + details = CloudStackTypes.RebootCommand + " requested unknown VM " + vmName; + logger.Error(details); + } + else + { + wmiCallsV2.SetState(sys, RequiredState.Reset); + result = true; + } + } + catch (Exception sysEx) + { + details = CloudStackTypes.RebootCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.RebootAnswer); + } + } + // POST api/HypervResource/DestroyCommand [HttpPost] [ActionName(CloudStackTypes.DestroyCommand)] @@ -696,23 +763,40 @@ namespace HypervResource logger.Info(CloudStackTypes.ModifyStoragePoolCommand + cmd.ToString()); string details = null; string localPath; + StoragePoolType poolType; object ansContent; - bool result = ValidateStoragePoolCommand(cmd, out localPath, ref details); + bool result = ValidateStoragePoolCommand(cmd, out localPath, out poolType, ref details); if (!result) { ansContent = new - { - result = result, - details = details - }; + { + result = result, + details = details + }; return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.Answer); } var tInfo = new Dictionary(); - long capacityBytes; - long availableBytes; - GetCapacityForLocalPath(localPath, out capacityBytes, out availableBytes); + long capacityBytes = 0; + long availableBytes = 0; + if (poolType == StoragePoolType.Filesystem) + { + GetCapacityForLocalPath(localPath, out capacityBytes, out availableBytes); + } + 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); + // Check access to share. + Utils.ConnectToRemote(share.UncPath, share.Domain, share.User, share.Password); + Utils.GetShareDetails(share.UncPath, out capacityBytes, out availableBytes); + } + else + { + result = false; + } String uuid = null; var poolInfo = new @@ -727,34 +811,37 @@ namespace HypervResource }; ansContent = new - { - result = result, - details = details, - templateInfo = tInfo, - poolInfo = poolInfo - }; + { + result = result, + details = details, + templateInfo = tInfo, + poolInfo = poolInfo + }; + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.ModifyStoragePoolAnswer); } } - private bool ValidateStoragePoolCommand(dynamic cmd, out string localPath, ref string details) + private bool ValidateStoragePoolCommand(dynamic cmd, out string localPath, out StoragePoolType poolType, ref string details) { dynamic pool = cmd.pool; string poolTypeStr = pool.type; - StoragePoolType poolType; localPath = cmd.localPath; - if (!Enum.TryParse(poolTypeStr, out poolType) || poolType != StoragePoolType.Filesystem) + if (!Enum.TryParse(poolTypeStr, out poolType)) { details = "Request to create / modify unsupported pool type: " + (poolTypeStr == null ? "NULL" : poolTypeStr) + "in cmd " + JsonConvert.SerializeObject(cmd); logger.Error(details); return false; } - if (!Directory.Exists(localPath)) + + if (poolType != StoragePoolType.Filesystem && + poolType != StoragePoolType.NetworkFilesystem) { - details = "Request to create / modify unsupported StoragePoolType.Filesystem with non-existent path:" + (localPath == null ? "NULL" : localPath) + "in cmd " + JsonConvert.SerializeObject(cmd); + details = "Request to create / modify unsupported pool type: " + (poolTypeStr == null ? "NULL" : poolTypeStr) + "in cmd " + JsonConvert.SerializeObject(cmd); logger.Error(details); return false; } + return true; } @@ -1318,6 +1405,70 @@ namespace HypervResource } } + // POST api/HypervResource/PrepareForMigrationCommand + [HttpPost] + [ActionName(CloudStackTypes.PrepareForMigrationCommand)] + public JContainer PrepareForMigrationCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.PrepareForMigrationCommand + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + details = "NOP - failure"; + } + catch (Exception sysEx) + { + details = CloudStackTypes.PrepareForMigrationCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.PrepareForMigrationAnswer); + } + } + + // POST api/HypervResource/MigrateCommand + [HttpPost] + [ActionName(CloudStackTypes.MigrateCommand)] + public JContainer MigrateCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.MigrateCommand + cmd.ToString()); + + string details = null; + bool result = false; + + try + { + details = "NOP - failure"; + } + catch (Exception sysEx) + { + details = CloudStackTypes.MigrateCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateAnswer); + } + } + // POST api/HypervResource/StartupCommand [HttpPost] [ActionName(CloudStackTypes.StartupCommand)] @@ -1383,6 +1534,9 @@ namespace HypervResource logger.Debug(CloudStackTypes.StartupStorageCommand + " set available bytes to " + available); string ipAddr = strtRouteCmd.privateIpAddress; + var vmStates = wmiCallsV2.GetVmSync(config.PrivateIpAddress); + strtRouteCmd.vms = Utils.CreateCloudStackMapObject(vmStates); + StoragePoolInfo pi = new StoragePoolInfo( poolGuid.ToString(), ipAddr, diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 125c53ec384..223f7481071 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -63,5 +63,6 @@ namespace HypervResource VirtualSystemSettingData GetVmSettings(ComputerSystem vm); void patchSystemVmIso(string vmName, string systemVmIso); void SetState(ComputerSystem vm, ushort requiredState); + Dictionary GetVmSync(String privateIpAddress); } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs index e55f2ad1e99..0f7505d67d8 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/Utils.cs @@ -45,6 +45,16 @@ namespace HypervResource return new JObject(objTypeValuePairing); } + /// + /// serialize dictonary to map json type + /// + /// Object's data, can be an anonymous object, e.g. + /// + public static JToken CreateCloudStackMapObject(object objValue) + { + JToken objContent = JToken.FromObject(objValue); + return objContent; + } /// /// Copy file on network share to local volume. @@ -66,30 +76,30 @@ namespace HypervResource bool isSuccess = LogonUser(cifsShareDetails.User, cifsShareDetails.Domain, cifsShareDetails.Password, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref token); using (WindowsImpersonationContext remoteIdentity = new WindowsIdentity(token).Impersonate()) { - String dest = ""; - if (filePathRelativeToShare.EndsWith(".iso") || filePathRelativeToShare.EndsWith(".vhd") || filePathRelativeToShare.EndsWith(".vhdx")) - { - dest = Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare); - dest = dest.Replace('/', Path.DirectorySeparatorChar); - } - // if the filePathRelativeToShare string don't have filename and only a dir point then find the vhd files in that folder and use - // In the clean setup, first copy command wont be having the filename it contains onlyu dir path. - // we need to scan the folder point and then copy the file to destination. - else if (!filePathRelativeToShare.EndsWith(".vhd") || !filePathRelativeToShare.EndsWith(".vhdx")) - { - // scan the folder and get the vhd filename. - String uncPath = Path.Combine(cifsShareDetails.UncPath, Path.Combine(filePathRelativeToShare.Split('/'))); - //uncPath = uncPath.Replace("/", "\\"); - DirectoryInfo dir = new DirectoryInfo(uncPath); - FileInfo[] vhdFiles = dir.GetFiles("*.vhd*"); - if (vhdFiles.Length > 0) - { - FileInfo file = vhdFiles[0]; - dest = file.FullName; - } - } - s_logger.Info(CloudStackTypes.CopyCommand + ": copy " + Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare) + " to " + destFile); + String dest = ""; + if (filePathRelativeToShare.EndsWith(".iso") || filePathRelativeToShare.EndsWith(".vhd") || filePathRelativeToShare.EndsWith(".vhdx")) + { + dest = Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare); + dest = dest.Replace('/', Path.DirectorySeparatorChar); + } + // if the filePathRelativeToShare string don't have filename and only a dir point then find the vhd files in that folder and use + // In the clean setup, first copy command wont be having the filename it contains onlyu dir path. + // we need to scan the folder point and then copy the file to destination. + else if (!filePathRelativeToShare.EndsWith(".vhd") || !filePathRelativeToShare.EndsWith(".vhdx")) + { + // scan the folder and get the vhd filename. + String uncPath = Path.Combine(cifsShareDetails.UncPath, Path.Combine(filePathRelativeToShare.Split('/'))); + //uncPath = uncPath.Replace("/", "\\"); + DirectoryInfo dir = new DirectoryInfo(uncPath); + FileInfo[] vhdFiles = dir.GetFiles("*.vhd*"); + if (vhdFiles.Length > 0) + { + FileInfo file = vhdFiles[0]; + dest = file.FullName; + } + } + s_logger.Info(CloudStackTypes.CopyCommand + ": copy " + Path.Combine(cifsShareDetails.UncPath, filePathRelativeToShare) + " to " + destFile); File.Copy(dest, destFile, true); remoteIdentity.Undo(); } @@ -103,6 +113,47 @@ namespace HypervResource } } + public static void ConnectToRemote(string remoteUNC, string domain, string username, string password) + { + NETRESOURCE nr = new NETRESOURCE(); + nr.dwType = RESOURCETYPE_DISK; + nr.lpRemoteName = remoteUNC.Replace('/', Path.DirectorySeparatorChar); + if (domain != null) + { + username = domain + @"\" + username; + } + + int ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null); + if (ret != NO_ERROR) + { + throw new ArgumentException("net use of share " + remoteUNC + "failed with "+ getErrorForNumber(ret)); + } + } + + public static void DisconnectRemote(string remoteUNC) + { + int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false); + if (ret != NO_ERROR) + { + throw new ArgumentException("net disconnect of share " + remoteUNC + "failed with " + getErrorForNumber(ret)); + } + } + + public static void GetShareDetails(string remoteUNC, out long capacity, out long available) + { + ulong freeBytesAvailable; + ulong totalNumberOfBytes; + ulong totalNumberOfFreeBytes; + + if (!GetDiskFreeSpaceEx(remoteUNC, out freeBytesAvailable, out totalNumberOfBytes, out totalNumberOfFreeBytes)) + { + throw new ArgumentException("Not able to retrieve the capcity details of the share " + remoteUNC); + } + + available = freeBytesAvailable > 0 ? (long)freeBytesAvailable : 0; + capacity = totalNumberOfBytes > 0 ? (long)totalNumberOfBytes : 0; + } + // from http://stackoverflow.com/a/2541569/939250 #region imports [DllImport("advapi32.dll", SetLastError = true)] @@ -113,9 +164,22 @@ namespace HypervResource [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public extern static bool DuplicateToken(IntPtr existingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, ref IntPtr duplicateTokenHandle); + + [DllImport("Mpr.dll")] + private static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID, int dwFlags, + string lpAccessName, string lpBufferSize, string lpResult); + + [DllImport("Mpr.dll")] + private static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + private static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, + out ulong lpFreeBytesAvailable, + out ulong lpTotalNumberOfBytes, + out ulong lpTotalNumberOfFreeBytes); #endregion - #region logon consts + #region consts // logon types const int LOGON32_LOGON_INTERACTIVE = 2; const int LOGON32_LOGON_NETWORK = 3; @@ -126,6 +190,115 @@ namespace HypervResource const int LOGON32_PROVIDER_WINNT50 = 3; const int LOGON32_PROVIDER_WINNT40 = 2; const int LOGON32_PROVIDER_WINNT35 = 1; + + const int RESOURCE_CONNECTED = 0x00000001; + const int RESOURCE_GLOBALNET = 0x00000002; + const int RESOURCE_REMEMBERED = 0x00000003; + + const int RESOURCETYPE_ANY = 0x00000000; + const int RESOURCETYPE_DISK = 0x00000001; + const int RESOURCETYPE_PRINT = 0x00000002; + + const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000; + const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001; + const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002; + const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003; + const int RESOURCEDISPLAYTYPE_FILE = 0x00000004; + const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005; + + const int RESOURCEUSAGE_CONNECTABLE = 0x00000001; + const int RESOURCEUSAGE_CONTAINER = 0x00000002; + + + const int CONNECT_INTERACTIVE = 0x00000008; + const int CONNECT_PROMPT = 0x00000010; + const int CONNECT_REDIRECT = 0x00000080; + const int CONNECT_UPDATE_PROFILE = 0x00000001; + const int CONNECT_COMMANDLINE = 0x00000800; + const int CONNECT_CMD_SAVECRED = 0x00001000; + + const int CONNECT_LOCALDRIVE = 0x00000100; #endregion + + #region Errors + const int NO_ERROR = 0; + + const int ERROR_ACCESS_DENIED = 5; + const int ERROR_ALREADY_ASSIGNED = 85; + const int ERROR_BAD_DEVICE = 1200; + const int ERROR_BAD_NET_NAME = 67; + const int ERROR_BAD_PROVIDER = 1204; + const int ERROR_CANCELLED = 1223; + const int ERROR_EXTENDED_ERROR = 1208; + const int ERROR_INVALID_ADDRESS = 487; + const int ERROR_INVALID_PARAMETER = 87; + const int ERROR_INVALID_PASSWORD = 1216; + const int ERROR_MORE_DATA = 234; + const int ERROR_NO_MORE_ITEMS = 259; + const int ERROR_NO_NET_OR_BAD_PATH = 1203; + const int ERROR_NO_NETWORK = 1222; + + const int ERROR_BAD_PROFILE = 1206; + const int ERROR_CANNOT_OPEN_PROFILE = 1205; + const int ERROR_DEVICE_IN_USE = 2404; + const int ERROR_NOT_CONNECTED = 2250; + const int ERROR_OPEN_FILES = 2401; + + private struct ErrorClass + { + public int num; + public string message; + public ErrorClass(int num, string message) + { + this.num = num; + this.message = message; + } + } + + private static ErrorClass[] ERROR_LIST = new ErrorClass[] { + new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), + new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), + new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), + new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), + new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), + new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), + new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), + new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), + new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), + new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), + new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), + new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), + new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), + new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), + new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), + new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), + new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), + new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), + new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), + new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), + }; + + private static string getErrorForNumber(int errNum) + { + foreach (ErrorClass er in ERROR_LIST) + { + if (er.num == errNum) return er.message; + } + return "Error: Unknown, " + errNum; + } + #endregion + + [StructLayout(LayoutKind.Sequential)] + private class NETRESOURCE + { + public int dwScope = 0; + public int dwType = 0; + public int dwDisplayType = 0; + public int dwUsage = 0; + public string lpLocalName = ""; + public string lpRemoteName = ""; + public string lpComment = ""; + public string lpProvider = ""; + } } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index 18b96cc6ac1..0a5813128c2 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -447,6 +447,7 @@ namespace HypervResource pingReply = pingSender.Send(ipAddress, pingTimeout, buffer, pingOptions); if (pingReply.Status == IPStatus.Success) { + System.Threading.Thread.Sleep(30000); return true; } else @@ -591,11 +592,11 @@ namespace HypervResource } string hostResource = item.HostResource[0]; - if (!hostResource.Equals(diskFileName)) + if (Path.Equals(hostResource, diskFileName)) { - continue; + imageToRemove = item; + break; } - imageToRemove = item; } // assert @@ -739,7 +740,6 @@ namespace HypervResource // 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); } @@ -1549,6 +1549,19 @@ namespace HypervResource return null; } + public Dictionary GetVmSync(String privateIpAddress) + { + List vms = GetVmElementNames(); + Dictionary vmSyncStates = new Dictionary(); + String vmState; + foreach (String vm in vms) + { + vmState = EnabledState.ToCloudStackState(GetComputerSystem(vm).EnabledState); + vmSyncStates.Add(vm, new VmState(vmState, privateIpAddress)); + } + return vmSyncStates; + } + public List GetVmElementNames() { List result = new List(); 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 a8a09f6f98a..194ad77b05d 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,7 +19,6 @@ 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; @@ -27,16 +26,11 @@ import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.UUID; import javax.ejb.Local; -import javax.inject.Inject; import javax.naming.ConfigurationException; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.utils.identity.ManagementServerNode; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; @@ -47,6 +41,8 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; +import com.google.gson.Gson; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckRouterAnswer; import com.cloud.agent.api.CheckRouterCommand; @@ -60,7 +56,6 @@ import com.cloud.agent.api.NetworkUsageCommand; import com.cloud.agent.api.PingCommand; import com.cloud.agent.api.PingRoutingCommand; import com.cloud.agent.api.PingTestCommand; -import com.cloud.agent.api.StartCommand; import com.cloud.agent.api.StartupCommand; import com.cloud.agent.api.StartupRoutingCommand; import com.cloud.agent.api.StartupRoutingCommand.VmState; @@ -95,8 +90,6 @@ import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; -import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.configuration.Config; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.host.Host.Type; import com.cloud.hypervisor.Hypervisor; @@ -107,20 +100,11 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.resource.ServerResource; import com.cloud.resource.ServerResourceBase; import com.cloud.serializer.GsonHelper; -import com.cloud.storage.JavaStorageLayer; -import com.cloud.storage.StorageLayer; -import com.cloud.utils.FileUtil; -import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; -import com.cloud.utils.db.GlobalLock; -import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; -import com.cloud.utils.script.Script; import com.cloud.utils.ssh.SshHelper; -import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineName; -import com.google.gson.Gson; /** * Implementation of dummy resource to be returned from discoverer. @@ -164,8 +148,8 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S if (!_configureCalled) { String errMsg = this.getClass().getName() - + " requires configure() be called before" - + " initialize()"; + + " requires configure() be called before" + + " initialize()"; s_logger.error(errMsg); } @@ -220,7 +204,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S "Host %s (IP %s) changed zone/data center. Was " + defaultStartRoutCmd.getDataCenter() + " NOW its " + startCmd.getDataCenter(), - _name, _agentIp); + _name, _agentIp); s_logger.error(errMsg); // TODO: valid to return null, or should we throw? return null; @@ -267,8 +251,8 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S String errMsg = String.format( "Host %s (IP %s) name. Was " + startCmd.getName() - + " NOW its " - + defaultStartRoutCmd.getName(), _name, + + " NOW its " + + defaultStartRoutCmd.getName(), _name, _agentIp); s_logger.error(errMsg); // TODO: valid to return null, or should we throw? @@ -315,7 +299,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S s_logger.debug("Ping host " + _name + " (IP " + _agentIp + ")"); } - Answer pingAns = this.executeRequest(pingCmd); + Answer pingAns = executeRequest(pingCmd); if (pingAns == null || !pingAns.getResult()) { s_logger.info("Cannot ping host " + _name + " (IP " + _agentIp @@ -390,9 +374,9 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } else if (clazz == GetDomRVersionCmd.class) { answer = execute((GetDomRVersionCmd)cmd); } else if (cmd instanceof NetworkUsageCommand) { - answer = execute((NetworkUsageCommand) cmd); + answer = execute((NetworkUsageCommand) cmd); } else if (clazz == IpAssocCommand.class) { - answer = execute((IpAssocCommand) cmd); + answer = execute((IpAssocCommand) cmd); } else if (clazz == DnsMasqConfigCommand.class) { return execute((DnsMasqConfigCommand) cmd); } else if (clazz == CreateIpAliasCommand.class) { @@ -430,7 +414,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S // Else send the cmd to hyperv agent. String ansStr = postHttpRequest(s_gson.toJson(cmd), agentUri); if (ansStr == null) { - return Answer.createUnsupportedCommandAnswer(cmd); + return Answer.createUnsupportedCommandAnswer(cmd); } // Only Answer instances are returned by remote agents. // E.g. see Response.getAnswers() @@ -498,12 +482,13 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S Pair result; try { String controlIp = getRouterSshControlIp(cmd); - String cmdline = "/opt/cloud/bin/checkbatchs2svpn.sh "; + StringBuilder cmdline = new StringBuilder("/opt/cloud/bin/checkbatchs2svpn.sh "); for (String ip : cmd.getVpnIps()) { - cmdline += " " + ip; + cmdline.append(" "); + cmdline.append(ip); } - result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, cmdline); + result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", getSystemVMKeyFile(), null, cmdline.toString()); if (!result.first()) { s_logger.error("check site-to-site vpn connections command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + " failed, message: " @@ -833,13 +818,23 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S if (s_logger.isInfoEnabled()) { s_logger.info("Executing deleteIpAlias command: " + s_gson.toJson(cmd)); } - String args = ""; + StringBuilder args = new StringBuilder(); for (IpAliasTO ipAliasTO : revokedIpAliasTOs) { - args = args + ipAliasTO.getAlias_count() + ":" + ipAliasTO.getRouterip() + ":" + ipAliasTO.getNetmask() + "-"; + args.append(ipAliasTO.getAlias_count()); + args.append(":"); + args.append(ipAliasTO.getRouterip()); + args.append(":"); + args.append(ipAliasTO.getNetmask()); + args.append("-"); } - args = args + "- "; + args.append("- "); for (IpAliasTO ipAliasTO : activeIpAliasTOs) { - args = args + ipAliasTO.getAlias_count() + ":" + ipAliasTO.getRouterip() + ":" + ipAliasTO.getNetmask() + "-"; + args.append(ipAliasTO.getAlias_count()); + args.append(":"); + args.append(ipAliasTO.getRouterip()); + args.append(":"); + args.append(ipAliasTO.getNetmask()); + args.append("-"); } if (s_logger.isDebugEnabled()) { s_logger.debug("Run command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + ", /root/deleteIpAlias " + args); @@ -887,14 +882,14 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S String[][] rules = cfgtr.generateFwRules(cmd); String tmpCfgFilePath = "/tmp/" + routerIp.replace('.', '_') + ".cfg"; - String tmpCfgFileContents = ""; + StringBuilder tmpCfgFileContents = new StringBuilder(); for (int i = 0; i < config.length; i++) { - tmpCfgFileContents += config[i]; - tmpCfgFileContents += "\n"; + tmpCfgFileContents.append(config[i]); + tmpCfgFileContents.append("\n"); } try { - SshHelper.scpTo(controlIp, DEFAULT_DOMR_SSHPORT, "root", keyFile, null, "/tmp/", tmpCfgFileContents.getBytes(), routerIp.replace('.', '_') + ".cfg", null); + SshHelper.scpTo(controlIp, DEFAULT_DOMR_SSHPORT, "root", keyFile, null, "/tmp/", tmpCfgFileContents.toString().getBytes(), routerIp.replace('.', '_') + ".cfg", null); try { String[] addRules = rules[LoadBalancerConfigurator.ADD]; @@ -1076,7 +1071,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } catch (Throwable e) { s_logger.error("SetFirewallRulesCommand(args: " + args + ") failed on setting one rule due to " - ,e); + ,e); //FIXME - in the future we have to process each rule separately; now we temporarily set every rule to be false if single rule fails for (int i=0; i < results.length; i++) { results[i] = "Failed"; @@ -1229,9 +1224,14 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S } cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); List ipAliasTOs = cmd.getIpAliasList(); - String args=""; + StringBuilder args = new StringBuilder(); for (IpAliasTO ipaliasto : ipAliasTOs) { - args = args + ipaliasto.getAlias_count()+":"+ipaliasto.getRouterip()+":"+ipaliasto.getNetmask()+"-"; + args.append(ipaliasto.getAlias_count()); + args.append(":"); + args.append(ipaliasto.getRouterip()); + args.append(":"); + args.append(ipaliasto.getNetmask()); + args.append("-"); } if (s_logger.isDebugEnabled()) { s_logger.debug("Run command on domR " + cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP) + ", /root/createIpAlias " + args); @@ -1271,9 +1271,16 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S assert(controlIp != null); List dhcpTos = cmd.getIps(); - String args =""; + StringBuilder args = new StringBuilder(); for(DhcpTO dhcpTo : dhcpTos) { - args = args + dhcpTo.getRouterIp()+":"+dhcpTo.getGateway()+":"+dhcpTo.getNetmask()+":"+dhcpTo.getStartIpOfSubnet()+"-"; + args.append(dhcpTo.getRouterIp()); + args.append(":"); + args.append(dhcpTo.getGateway()); + args.append(":"); + args.append(dhcpTo.getNetmask()); + args.append(":"); + args.append(dhcpTo.getStartIpOfSubnet()); + args.append("-"); } try { @@ -1320,7 +1327,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S for (; i < cmd.getIpAddresses().length; i++) { results[i++] = IpAssocAnswer.errorResult; } - } catch (Throwable e) { + } catch (Throwable e) { s_logger.error("Unexpected exception: " + e.toString() + " will shortcut rest of IPAssoc commands", e); for (; i < cmd.getIpAddresses().length; i++) { @@ -1335,14 +1342,12 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S final boolean sourceNat, final String vlanId, final String vlanGateway, final String vlanNetmask, final String vifMacAddress) throws Exception { boolean addVif = false; - boolean removeVif = false; if (add) { if (s_logger.isDebugEnabled()) { s_logger.debug("Plug new NIC to associate" + privateIpAddress + " to " + publicIpAddress); } addVif = true; } else if (!add && firstIP) { - removeVif = true; } String args = null; @@ -1448,7 +1453,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S s_logger.info("Executing resource NetworkUsageCommand "+ s_gson.toJson(cmd)); } if(cmd.getOption()!=null && cmd.getOption().equals("create") ){ - String result = networkUsage(cmd.getPrivateIP(), "create", null); + networkUsage(cmd.getPrivateIP(), "create", null); NetworkUsageAnswer answer = new NetworkUsageAnswer(cmd, "true", 0L, 0L); return answer; } @@ -1594,7 +1599,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S // Unsupported commands will not route. if (response.getStatusLine().getStatusCode() - == HttpStatus.SC_NOT_FOUND) { + == HttpStatus.SC_NOT_FOUND) { String errMsg = "Failed to send : HTTP error code : " + response.getStatusLine().getStatusCode(); @@ -1608,11 +1613,11 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S s_logger.error(ans); result = s_gson.toJson(new Answer[] {ans}); } else if (response.getStatusLine().getStatusCode() - != HttpStatus.SC_OK) { + != HttpStatus.SC_OK) { String errMsg = "Failed send to " + agentUri.toString() - + " : HTTP error code : " - + response.getStatusLine().getStatusCode(); + + " : HTTP error code : " + + response.getStatusLine().getStatusCode(); s_logger.error(errMsg); return null; } else { @@ -1707,7 +1712,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S sch.configureBlocking(true); sch.socket().setSoTimeout(5000); // we need to connect to the public ip address to check the status of the VM -/* + /* InetSocketAddress addr = new InetSocketAddress(ipAddress, port); sch.connect(addr);*/ return null; 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 84743ec124e..b400fd25032 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 @@ -205,7 +205,11 @@ public class BridgeVifDriver extends VifDriverBase { private void deleteVnetBr(String brName) { synchronized (_vnetBridgeMonitor) { - String cmdout = Script.runSimpleBashScript("ls /sys/class/net/" + brName + "/brif | tr '\n' ' '"); + String cmdout = Script.runSimpleBashScript("ls /sys/class/net/" + brName); + if (cmdout == null) + // Bridge does not exist + return; + cmdout = Script.runSimpleBashScript("ls /sys/class/net/" + brName + "/brif | tr '\n' ' '"); if (cmdout != null && cmdout.contains("vnet")) { // Active VM remains on that bridge return; 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 e3f60f2357f..63bd5dc051a 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 @@ -51,11 +51,14 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.regex.Pattern; +import java.util.regex.Matcher; import javax.ejb.Local; import javax.naming.ConfigurationException; import com.cloud.agent.api.CheckOnHostCommand; +import com.cloud.agent.api.routing.*; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.libvirt.Connect; @@ -157,14 +160,6 @@ import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; -import com.cloud.agent.api.routing.IpAssocAnswer; -import com.cloud.agent.api.routing.IpAssocCommand; -import com.cloud.agent.api.routing.IpAssocVpcCommand; -import com.cloud.agent.api.routing.NetworkElementCommand; -import com.cloud.agent.api.routing.SetNetworkACLAnswer; -import com.cloud.agent.api.routing.SetNetworkACLCommand; -import com.cloud.agent.api.routing.SetSourceNatAnswer; -import com.cloud.agent.api.routing.SetSourceNatCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; @@ -204,6 +199,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GuestResourceDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InputDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef; +import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.guestNetType; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.VirtioSerialDef; @@ -1298,6 +1294,8 @@ ServerResource { return storageHandler.handleStorageCommands((StorageSubSystemCommand)cmd); } else if (cmd instanceof PvlanSetupCommand) { return execute((PvlanSetupCommand) cmd); + } else if (cmd instanceof SetMonitorServiceCommand) { + return execute((SetMonitorServiceCommand) cmd); } else if (cmd instanceof CheckOnHostCommand) { return execute((CheckOnHostCommand)cmd); } else { @@ -1661,14 +1659,19 @@ ServerResource { } } - private String getVlanIdFromBridge(String brName) { + private String getBroadcastUriFromBridge(String brName) { String pif= matchPifFileInDirectory(brName); - String[] pifparts = pif.split("\\."); - - if(pifparts.length == 2) { - return pifparts[1]; + Pattern pattern = Pattern.compile("(\\D+)(\\d+)"); + Matcher matcher = pattern.matcher(pif); + if(matcher.find()) { + if (brName.startsWith("brvx")){ + return BroadcastDomainType.Vxlan.toUri(matcher.group(2)).toString(); + } + else{ + return BroadcastDomainType.Vlan.toUri(matcher.group(2)).toString(); + } } else { - s_logger.debug("failed to get vlan id from bridge " + brName + s_logger.debug("failed to get vNet id from bridge " + brName + "attached to physical interface" + pif); return ""; } @@ -1875,6 +1878,19 @@ ServerResource { } } + private Answer execute(SetMonitorServiceCommand cmd) { + + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + String config = cmd.getConfiguration(); + + String result = _virtRouterResource.configureMonitor(routerIp, config); + + if (result != null) { + return new Answer(cmd, false, "SetMonitorServiceCommand failed"); + } + return new Answer(cmd); + + } private SetNetworkACLAnswer execute(SetNetworkACLCommand cmd) { String[] results = new String[cmd.getRules().length]; @@ -1926,7 +1942,7 @@ ServerResource { for (InterfaceDef pluggedNic : pluggedNics) { String pluggedVlanBr = pluggedNic.getBrName(); - String pluggedVlanId = getVlanIdFromBridge(pluggedVlanBr); + String pluggedVlanId = getBroadcastUriFromBridge(pluggedVlanBr); if (pubVlan.equalsIgnoreCase(Vlan.UNTAGGED) && pluggedVlanBr.equalsIgnoreCase(_publicBridgeName)) { break; @@ -1967,25 +1983,25 @@ ServerResource { conn = LibvirtConnection.getConnectionByVmName(routerName); IpAddressTO[] ips = cmd.getIpAddresses(); Integer devNum = 0; - Map vlanToNicNum = new HashMap(); + Map broadcastUriToNicNum = new HashMap(); List pluggedNics = getInterfaces(conn, routerName); for (InterfaceDef pluggedNic : pluggedNics) { String pluggedVlan = pluggedNic.getBrName(); if (pluggedVlan.equalsIgnoreCase(_linkLocalBridgeName)) { - vlanToNicNum.put("LinkLocal",devNum); + broadcastUriToNicNum.put("LinkLocal", devNum); } else if (pluggedVlan.equalsIgnoreCase(_publicBridgeName) || pluggedVlan.equalsIgnoreCase(_privBridgeName) || pluggedVlan.equalsIgnoreCase(_guestBridgeName)) { - vlanToNicNum.put(Vlan.UNTAGGED,devNum); + broadcastUriToNicNum.put(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED).toString(), devNum); } else { - vlanToNicNum.put(getVlanIdFromBridge(pluggedVlan),devNum); + broadcastUriToNicNum.put(getBroadcastUriFromBridge(pluggedVlan), devNum); } devNum++; } for (IpAddressTO ip : ips) { - String nicName = "eth" + vlanToNicNum.get(ip.getBroadcastUri()); + String nicName = "eth" + broadcastUriToNicNum.get(ip.getBroadcastUri()); String netmask = Long.toString(NetUtils.getCidrSize(ip.getVlanNetmask())); String subnet = NetUtils.getSubNet(ip.getPublicIp(), ip.getVlanNetmask()); _virtRouterResource.assignVpcIpToRouter(routerIP, ip.isAdd(), ip.getPublicIp(), @@ -2013,19 +2029,19 @@ ServerResource { try { conn = LibvirtConnection.getConnectionByVmName(routerName); List nics = getInterfaces(conn, routerName); - Map vlanAllocatedToVM = new HashMap(); + Map broadcastUriAllocatedToVM = new HashMap(); Integer nicPos = 0; for (InterfaceDef nic : nics) { if (nic.getBrName().equalsIgnoreCase(_linkLocalBridgeName)) { - vlanAllocatedToVM.put("LinkLocal", nicPos); + broadcastUriAllocatedToVM.put("LinkLocal", nicPos); } else { if (nic.getBrName().equalsIgnoreCase(_publicBridgeName) || nic.getBrName().equalsIgnoreCase(_privBridgeName) || nic.getBrName().equalsIgnoreCase(_guestBridgeName)) { - vlanAllocatedToVM.put(Vlan.UNTAGGED, nicPos); + broadcastUriAllocatedToVM.put(BroadcastDomainType.Vlan.toUri(Vlan.UNTAGGED).toString(), nicPos); } else { - String vlanId = getVlanIdFromBridge(nic.getBrName()); - vlanAllocatedToVM.put(vlanId, nicPos); + String broadcastUri = getBroadcastUriFromBridge(nic.getBrName()); + broadcastUriAllocatedToVM.put(broadcastUri, nicPos); } } nicPos++; @@ -2036,14 +2052,14 @@ ServerResource { int nicNum = 0; boolean newNic = false; for (IpAddressTO ip : ips) { - if (!vlanAllocatedToVM.containsKey(ip.getBroadcastUri())) { + if (!broadcastUriAllocatedToVM.containsKey(ip.getBroadcastUri())) { /* plug a vif into router */ VifHotPlug(conn, routerName, ip.getBroadcastUri(), ip.getVifMacAddress()); - vlanAllocatedToVM.put(ip.getBroadcastUri(), nicPos++); + broadcastUriAllocatedToVM.put(ip.getBroadcastUri(), nicPos++); newNic = true; } - nicNum = vlanAllocatedToVM.get(ip.getBroadcastUri()); + nicNum = broadcastUriAllocatedToVM.get(ip.getBroadcastUri()); networkUsage(routerIp, "addVif", "eth" + nicNum); result = _virtRouterResource.assignPublicIpAddress(routerName, routerIp, ip.getPublicIp(), ip.isAdd(), ip.isFirstIP(), @@ -2895,8 +2911,7 @@ ServerResource { try { conn = LibvirtConnection.getConnectionByVmName(cmd.getVmName()); ifaces = getInterfaces(conn, vmName); - dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName - .getBytes())); + dm = conn.domainLookupByName(vmName); /* We replace the private IP address with the address of the destination host. This is because the VNC listens on the private IP address of the hypervisor, @@ -3986,6 +4001,7 @@ ServerResource { (Long) info.get(4), (String) info.get(3), _hypervisorType, RouterPrivateIpStrategy.HostLocal); cmd.setStateChanges(changes); + cmd.setCpuSockets((Integer)info.get(5)); fillNetworkInformation(cmd); _privateIp = cmd.getPrivateIpAddress(); cmd.getHostDetails().putAll(getVersionStrings()); @@ -4311,6 +4327,7 @@ ServerResource { long speed = 0; long cpus = 0; long ram = 0; + int cpuSockets = 0; String cap = null; try { Connect conn = LibvirtConnection.getConnection(); @@ -4334,6 +4351,7 @@ ServerResource { speed = hosts.mhz; } + cpuSockets = hosts.sockets; cpus = hosts.cpus; ram = hosts.memory * 1024L; LibvirtCapXMLParser parser = new LibvirtCapXMLParser(); @@ -4366,8 +4384,9 @@ ServerResource { // 768M dom0ram = Math.max(dom0ram, _dom0MinMem); info.add(dom0ram); + info.add(cpuSockets); s_logger.debug("cpus=" + cpus + ", speed=" + speed + ", ram=" + ram - + ", dom0ram=" + dom0ram); + + ", dom0ram=" + dom0ram + ", cpu sockets=" + cpuSockets); return info; } @@ -4378,6 +4397,21 @@ ServerResource { try { dm = conn.domainLookupByName(vmName); String vmDef = dm.getXMLDesc(0); + LibvirtDomainXMLParser parser = new LibvirtDomainXMLParser(); + parser.parseDomainXML(vmDef); + for (InterfaceDef nic :parser.getInterfaces()) { + if ((nic.getNetType() == guestNetType.BRIDGE) && (nic.getBrName().startsWith("cloudVirBr"))) { + try { + int vnetId = Integer.parseInt(nic.getBrName().replaceFirst("cloudVirBr", "")); + String pifName = getPif(_guestBridgeName); + String newBrName = "br" + pifName + "-"+ vnetId; + vmDef = vmDef.replaceAll("'" + nic.getBrName() + "'", "'" + newBrName + "'"); + s_logger.debug("VM bridge name is changed from " + nic.getBrName() + " to " + newBrName); + } catch (NumberFormatException e) { + continue; + } + } + } s_logger.debug(vmDef); msg = stopVM(conn, vmName); msg = startVM(conn, vmName, vmDef); 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 0e2423e483b..4c34ebc365b 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 @@ -16,6 +16,23 @@ // under the License. package com.cloud.hypervisor.vmware.manager; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +import org.apache.cloudstack.storage.to.TemplateObjectTO; +import org.apache.cloudstack.storage.to.VolumeObjectTO; +import org.apache.log4j.Logger; + import com.cloud.agent.api.Answer; import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.agent.api.BackupSnapshotCommand; @@ -70,22 +87,6 @@ import com.vmware.vim25.HostDatastoreBrowserSearchSpec; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.TaskInfo; import com.vmware.vim25.VirtualDisk; -import org.apache.cloudstack.storage.to.TemplateObjectTO; -import org.apache.cloudstack.storage.to.VolumeObjectTO; -import org.apache.log4j.Logger; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.rmi.RemoteException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.UUID; public class VmwareStorageManagerImpl implements VmwareStorageManager { @Override @@ -332,7 +333,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { 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); } @@ -1056,7 +1057,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { // create a dummy worker vm for attaching the volume DatastoreMO dsMo = new DatastoreMO(hyperHost.getContext(), morDs); workerVm = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, workerVmName); - + if (workerVm == null) { String msg = "Unable to create worker VM to execute CopyVolumeCommand"; s_logger.error(msg); @@ -1401,7 +1402,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { baseName + "*.vmdk", store.getUuid(), newPath); if(volumeTo.getVolumeType()== Volume.Type.ROOT){ // add memory snapshot size - size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",volumeTo.getUuid(),null); + size = size + getVMSnapshotChainSize(context,hyperHost,cmd.getVmName()+"*.vmsn",store.getUuid(),null); } volumeTo.setSize(size); volumeTo.setPath(newPath); @@ -1449,6 +1450,9 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager { 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); 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 a205fb6ae12..03af0da51b1 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 @@ -45,6 +45,7 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.routing.*; import org.apache.log4j.Logger; import org.apache.log4j.NDC; @@ -113,7 +114,6 @@ import org.apache.cloudstack.engine.orchestration.VolumeOrchestrator; 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.PrimaryDataStoreTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -214,34 +214,6 @@ import com.cloud.agent.api.ValidateSnapshotCommand; import com.cloud.agent.api.VmStatsEntry; import com.cloud.agent.api.check.CheckSshAnswer; import com.cloud.agent.api.check.CheckSshCommand; -import com.cloud.agent.api.routing.CreateIpAliasCommand; -import com.cloud.agent.api.routing.DeleteIpAliasCommand; -import com.cloud.agent.api.routing.DhcpEntryCommand; -import com.cloud.agent.api.routing.DnsMasqConfigCommand; -import com.cloud.agent.api.routing.IpAliasTO; -import com.cloud.agent.api.routing.IpAssocAnswer; -import com.cloud.agent.api.routing.IpAssocCommand; -import com.cloud.agent.api.routing.IpAssocVpcCommand; -import com.cloud.agent.api.routing.LoadBalancerConfigCommand; -import com.cloud.agent.api.routing.NetworkElementCommand; -import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; -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.SetNetworkACLAnswer; -import com.cloud.agent.api.routing.SetNetworkACLCommand; -import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; -import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; -import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; -import com.cloud.agent.api.routing.SetSourceNatAnswer; -import com.cloud.agent.api.routing.SetSourceNatCommand; -import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; -import com.cloud.agent.api.routing.SetStaticNatRulesCommand; -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.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; @@ -572,6 +544,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa answer = execute((SetStaticRouteCommand) cmd); } else if (clz == UnregisterNicCommand.class) { answer = execute((UnregisterNicCommand) cmd); + } else if (clz == SetMonitorServiceCommand.class) { + answer = execute((SetMonitorServiceCommand) cmd); } else { answer = Answer.createUnsupportedCommandAnswer(cmd); } @@ -792,6 +766,36 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa return new NetworkUsageAnswer(cmd, "success", 0L, 0L); } + protected Answer execute(SetMonitorServiceCommand cmd) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Executing resource SetMonitorServiceCommand: " + _gson.toJson(cmd)); + } + + String controlIp = getRouterSshControlIp(cmd); + String config = cmd.getConfiguration(); + + String args = ""; + + args += " -c " + config; + + try { + VmwareManager mgr = getServiceContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); + Pair result = SshHelper.sshExecute(controlIp, DEFAULT_DOMR_SSHPORT, "root", mgr.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 " + VmwareHelper.getExceptionMessage(e)); + } + } + protected Answer execute(SetPortForwardingRulesCommand cmd) { if (s_logger.isInfoEnabled()) { s_logger.info("Executing resource SetPortForwardingRulesCommand: " + _gson.toJson(cmd)); @@ -2513,7 +2517,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa for (DiskTO vol : disks) { if (vol.getType() != Volume.Type.ISO) { VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + DataStoreTO primaryStore = volumeTO.getDataStore(); if (primaryStore.getUuid() != null && !primaryStore.getUuid().isEmpty()) { validatedDisks.add(vol); } @@ -2673,7 +2677,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa Pair rootDiskDataStoreDetails = null; for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ROOT) { - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getData().getDataStore(); + DataStoreTO primaryStore = vol.getData().getDataStore(); rootDiskDataStoreDetails = dataStoresDetails.get(primaryStore.getUuid()); } } @@ -2832,7 +2836,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + DataStoreTO primaryStore = volumeTO.getDataStore(); Pair volumeDsDetails = dataStoresDetails.get(primaryStore.getUuid()); assert (volumeDsDetails != null); @@ -2984,7 +2988,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa ) throws Exception { VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + DataStoreTO primaryStore = volumeTO.getDataStore(); Map details = vol.getDetails(); boolean isManaged = details != null && Boolean.parseBoolean(details.get(DiskTO.MANAGED)); @@ -3431,7 +3435,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa for (DiskTO vol : disks) { if (vol.getType() != Volume.Type.ISO) { VolumeObjectTO volumeTO = (VolumeObjectTO)vol.getData(); - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)volumeTO.getDataStore(); + DataStoreTO primaryStore = volumeTO.getDataStore(); String poolUuid = primaryStore.getUuid(); if(poolMors.get(poolUuid) == null) { boolean isManaged = false; @@ -3462,7 +3466,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa Pair rootDiskDataStoreDetails = null; for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ROOT) { - PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)vol.getData().getDataStore(); + DataStoreTO primaryStore = vol.getData().getDataStore(); rootDiskDataStoreDetails = dataStoresDetails.get(primaryStore.getUuid()); } } @@ -5456,11 +5460,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); VolumeObjectTO vol = (VolumeObjectTO)cmd.getData(); - PrimaryDataStoreTO store = (PrimaryDataStoreTO)vol.getDataStore(); + DataStoreTO store = vol.getDataStore(); ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, store.getUuid()); if (morDs == null) { - String msg = "Unable to find datastore based on volume mount point " + store.getPath(); + String msg = "Unable to find datastore based on volume mount point " + store.getUrl(); s_logger.error(msg); throw new Exception(msg); } @@ -6041,6 +6045,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa cmd.setCaps("hvm"); cmd.setDom0MinMemory(0); cmd.setSpeed(summary.getCpuSpeed()); + cmd.setCpuSockets(summary.getCpuSockets()); cmd.setCpus((int) summary.getCpuCount()); cmd.setMemory(summary.getMemoryBytes()); } diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java index 253d6fd3517..ac8b294b76e 100644 --- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java +++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareSecondaryStorageContextFactory.java @@ -18,6 +18,8 @@ package com.cloud.storage.resource; import org.apache.log4j.Logger; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; + import com.cloud.hypervisor.vmware.util.VmwareClient; import com.cloud.hypervisor.vmware.util.VmwareContext; import com.cloud.hypervisor.vmware.util.VmwareContextPool; 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 ccb27ea400b..69b7c9e07c7 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 @@ -118,34 +118,7 @@ import com.cloud.agent.api.check.CheckSshCommand; import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand; import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer; import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand; -import com.cloud.agent.api.routing.CreateIpAliasCommand; -import com.cloud.agent.api.routing.DeleteIpAliasCommand; -import com.cloud.agent.api.routing.DhcpEntryCommand; -import com.cloud.agent.api.routing.DnsMasqConfigCommand; -import com.cloud.agent.api.routing.IpAliasTO; -import com.cloud.agent.api.routing.IpAssocAnswer; -import com.cloud.agent.api.routing.IpAssocCommand; -import com.cloud.agent.api.routing.IpAssocVpcCommand; -import com.cloud.agent.api.routing.LoadBalancerConfigCommand; -import com.cloud.agent.api.routing.NetworkElementCommand; -import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; -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.SetNetworkACLAnswer; -import com.cloud.agent.api.routing.SetNetworkACLCommand; -import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; -import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; -import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; -import com.cloud.agent.api.routing.SetSourceNatAnswer; -import com.cloud.agent.api.routing.SetSourceNatCommand; -import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; -import com.cloud.agent.api.routing.SetStaticNatRulesCommand; -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.routing.*; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; @@ -633,6 +606,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((ScaleVmCommand) cmd); } else if (clazz == PvlanSetupCommand.class) { return execute((PvlanSetupCommand) cmd); + } else if (clazz == SetMonitorServiceCommand.class) { + return execute((SetMonitorServiceCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -4829,7 +4804,10 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe if (_host.cpus <= 0) { throw new CloudRuntimeException("Cannot get the numbers of cpu from XenServer host " + _host.ip); } - + Map cpuInfo = myself.getCpuInfo(conn); + if (cpuInfo.get("socket_count") != null) { + _host.cpuSockets = Integer.parseInt(cpuInfo.get("socket_count")); + } for (final HostCpu hc : hcs) { _host.speed = hc.getSpeed(conn).intValue(); break; @@ -5974,6 +5952,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe cmd.setCaps(caps.toString()); cmd.setSpeed(_host.speed); + cmd.setCpuSockets(_host.cpuSockets); cmd.setCpus(_host.cpus); HostMetrics hm = host.getMetrics(conn); @@ -8074,6 +8053,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe public String storagePif2; public String pool; public int speed; + public Integer cpuSockets; public int cpus; public String product_version; public String localSRuuid; @@ -8116,6 +8096,30 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return new Answer(cmd, success, ""); } + private Answer execute(SetMonitorServiceCommand cmd) { + boolean success = true; + + String config = cmd.getConfiguration(); + + Connection conn = getConnection(); + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + + if (routerIp == null) { + return new Answer(cmd); + } + + String args = "monitor_service.sh " + routerIp; + args += " -c " + config; + + String result = callHostPlugin(conn, "vmops", "routerProxy", "args", args); + if (result == null || result.isEmpty()) { + return new Answer(cmd, false, "SetMonitorServiceCommand failed to create cfg file."); + } + + return new Answer(cmd, success, ""); + + } + protected SetFirewallRulesAnswer execute(SetFirewallRulesCommand cmd) { String[] results = new String[cmd.getRules().length]; String callResult; diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java index 5c6f2e7dd63..e049cece058 100644 --- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java +++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java @@ -98,6 +98,7 @@ import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.lb.dao.ElasticLbVmMapDao; import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter.RedundantState; @@ -290,6 +291,7 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast private void createApplyLoadBalancingRulesCommands( List rules, DomainRouterVO elbVm, Commands cmds, long guestNetworkId) { + /* XXX: cert */ LoadBalancerTO[] lbs = new LoadBalancerTO[rules.size()]; int i = 0; for (LoadBalancingRule rule : rules) { @@ -302,7 +304,8 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast int srcPort = rule.getSourcePortStart(); String uuid = rule.getUuid(); List destinations = rule.getDestinations(); - LoadBalancerTO lb = new LoadBalancerTO(uuid, elbIp, srcPort, protocol, algorithm, revoked, false, false, destinations); + LoadBalancerTO lb = new LoadBalancerTO(uuid, elbIp, srcPort, protocol, algorithm, revoked, + false, false, destinations); lbs[i++] = lb; } @@ -377,8 +380,9 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast List policyList = _lbMgr.getStickinessPolicies(lb.getId()); List hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId()); Ip sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress(); + LbSslCert sslCert = _lbMgr.getLbSslCert(lb.getId()); LoadBalancingRule loadBalancing = new LoadBalancingRule( - lb, dstList, policyList, hcPolicyList, sourceIp); + lb, dstList, policyList, hcPolicyList, sourceIp, sslCert, lb.getLbProtocol()); lbRules.add(loadBalancing); } return applyLBRules(elbVm, lbRules, network.getId()); @@ -664,7 +668,7 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast result = _lbMgr.createPublicLoadBalancer(lb.getXid(), lb.getName(), lb.getDescription(), lb.getSourcePortStart(), lb.getDefaultPortStart(), ipId.longValue(), lb.getProtocol(), - lb.getAlgorithm(), false, CallContext.current()); + lb.getAlgorithm(), false, CallContext.current(), lb.getLbProtocol()); } catch (NetworkRuleConflictException e) { s_logger.warn("Failed to create LB rule, not continuing with ELB deployment"); if (newIp) { diff --git a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java index b6269ebae9d..b9482b5a490 100644 --- a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java +++ b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java @@ -77,6 +77,7 @@ import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; diff --git a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java index 82f90fb9dbf..90d6a183eb2 100644 --- a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java +++ b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java @@ -212,7 +212,7 @@ public class InternalLBVMManagerTest extends TestCase { List vms = new ArrayList(); List rules = new ArrayList(); LoadBalancingRule rule = new LoadBalancingRule(null, null, - null, null, null); + null, null, null, null, null); rules.add(rule); try { @@ -232,7 +232,7 @@ public class InternalLBVMManagerTest extends TestCase { List rules = new ArrayList(); LoadBalancingRule rule = new LoadBalancingRule(null, null, - null, null, null); + null, null, null, null, null); rules.add(rule); try { @@ -252,7 +252,7 @@ public class InternalLBVMManagerTest extends TestCase { List rules = new ArrayList(); LoadBalancingRule rule = new LoadBalancingRule(null, null, - null, null, null); + null, null, null, null, null); rules.add(rule); try { @@ -272,7 +272,7 @@ public class InternalLBVMManagerTest extends TestCase { List rules = new ArrayList(); LoadBalancingRule rule = new LoadBalancingRule(null, null, - null, null, null); + null, null, null, null, null); rules.add(rule); try { diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java index ae9bba9b7e5..4eaafca17cf 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java @@ -239,8 +239,7 @@ public class ContrailManagerImpl extends ManagerBase implements ContrailManager @Override public String getPhysicalNetworkName(PhysicalNetworkVO phys_net) { String physname = phys_net.getName(); - physname = physname.replaceAll("\\s", ""); - physname.replace("_", ""); + physname = physname.replaceAll("\\s", "").replace("_", ""); return physname; } diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerDBSyncImpl.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerDBSyncImpl.java index 8cb4e8d0b32..7a77040e0c9 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerDBSyncImpl.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerDBSyncImpl.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.network.contrail.management; import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -931,7 +932,7 @@ public class ServerDBSyncImpl implements ServerDBSync { s_logger.debug("equal " + siModel.getQualifiedName()); } - static class ServiceInstanceComparator implements Comparator { + static class ServiceInstanceComparator implements Comparator, Serializable { @Override public int compare(ServiceInstance obj1, ServiceInstance obj2) { String name1 = StringUtils.join(obj1.getQualifiedName(), ':'); diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerEventHandlerImpl.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerEventHandlerImpl.java index 455e6012e43..ec374409fc0 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerEventHandlerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ServerEventHandlerImpl.java @@ -63,10 +63,6 @@ public class ServerEventHandlerImpl implements ServerEventHandler { setClassMap(); } - protected void finalize () { - - } - private void setMethodMap() { _methodMap = new HashMap(); Method methods[] = this.getClass().getMethods(); diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObject.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObject.java index 71d28ac5867..e43c9db36cd 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObject.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObject.java @@ -18,6 +18,7 @@ package org.apache.cloudstack.network.contrail.model; import java.io.IOException; +import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.TreeSet; @@ -38,7 +39,7 @@ import com.cloud.exception.InternalErrorException; * The update method pushes updates to the contrail API server. */ public interface ModelObject { - public static class ModelReference implements Comparable { + public static class ModelReference implements Comparable, Serializable { WeakReference reference; ModelReference(ModelObject obj) { reference = new WeakReference(obj); @@ -57,8 +58,22 @@ public interface ModelObject { return lhs.compareTo(rhs); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((reference == null) ? 0 : reference.hashCode()); + return result; + } + @Override public boolean equals(Object other) { + if (this == other) + return true; + if (other == null) + return false; try { ModelReference rhs = (ModelReference) other; return compareTo(rhs) == 0; @@ -69,6 +84,8 @@ public interface ModelObject { public ModelObject get() { return reference.get(); } + + }; public void addSuccessor(ModelObject child); diff --git a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObjectBase.java b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObjectBase.java index f22c7c546a3..8d48fa10492 100644 --- a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObjectBase.java +++ b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/model/ModelObjectBase.java @@ -17,11 +17,12 @@ package org.apache.cloudstack.network.contrail.model; +import java.io.Serializable; import java.util.Comparator; import java.util.TreeSet; public abstract class ModelObjectBase implements ModelObject { - public static class UuidComparator implements Comparator { + public static class UuidComparator implements Comparator, Serializable { @Override public int compare(ModelObject lhs, ModelObject rhs) { if (lhs == null) { @@ -78,8 +79,23 @@ public abstract class ModelObjectBase implements ModelObject { _successors.clear(); } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((_ancestors == null) ? 0 : _ancestors.hashCode()); + result = prime * result + + ((_successors == null) ? 0 : _successors.hashCode()); + return result; + } + @Override public boolean equals(Object rhs) { + if (this == rhs) + return true; + if (rhs == null) + return false; ModelObject other; try { other = (ModelObject) rhs; diff --git a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java index 82893f1ed90..86750ca8d1b 100644 --- a/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java +++ b/plugins/network-elements/juniper-contrail/test/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java @@ -315,7 +315,7 @@ public class ManagementServerMock { _znet = _networkService.getPhysicalNetwork(id); List nets = _physicalNetworkDao.listByZoneAndTrafficType(_zone.getId(), TrafficType.Public); if (nets == null || nets.isEmpty()) { - _networkService.addTrafficTypeToPhysicalNetwork(_znet.getId(), TrafficType.Public.toString(), null, null, null, null, null); + _networkService.addTrafficTypeToPhysicalNetwork(_znet.getId(), TrafficType.Public.toString(), null, null, null, null, null, null); } } catch (InvalidParameterValueException e) { List isolationMethods = new ArrayList(); @@ -325,7 +325,7 @@ public class ManagementServerMock { null, "znet"); List nets = _physicalNetworkDao.listByZoneAndTrafficType(_zone.getId(), TrafficType.Public); if (nets == null || nets.isEmpty()) { - _networkService.addTrafficTypeToPhysicalNetwork(_znet.getId(), TrafficType.Public.toString(), null, null, null, null, null); + _networkService.addTrafficTypeToPhysicalNetwork(_znet.getId(), TrafficType.Public.toString(), null, null, null, null, null, null); } } if (_znet.getState() != PhysicalNetwork.State.Enabled) { @@ -344,7 +344,7 @@ public class ManagementServerMock { } if (!found) { _networkService.addTrafficTypeToPhysicalNetwork(_znet.getId(), TrafficType.Guest.toString(), - null, null, null, null, null); + null, null, null, null, null, null); } Pair, Integer> providers = diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java index 8101864840b..de3d7e86665 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java +++ b/plugins/network-elements/netscaler/src/com/cloud/network/element/NetscalerElement.java @@ -161,6 +161,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl @Inject ExternalLoadBalancerDeviceDao _externalLoadBalancerDeviceDao; + private boolean canHandle(Network config, Service service) { DataCenter zone = _dcDao.findById(config.getDataCenterId()); boolean handleInAdvanceZone = (zone.getNetworkType() == NetworkType.Advanced && @@ -277,7 +278,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl // Supports only Public load balancing lbCapabilities.put(Capability.LbSchemes, LoadBalancerContainer.Scheme.Public.toString()); - + // Specifies that load balancing rules can support autoscaling and the list of counters it supports AutoScaleCounter counter; List counterList = new ArrayList(); @@ -319,6 +320,10 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl staticNatCapabilities.put(Capability.ElasticIp, "true"); capabilities.put(Service.StaticNat, staticNatCapabilities); + // Supports SSL offloading + lbCapabilities.put(Capability.SslTermination, "true"); + + // TODO - Murali, please put correct capabilities here Map firewallCapabilities = new HashMap(); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); @@ -516,6 +521,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl cmdList.add(DeleteNetscalerLoadBalancerCmd.class); cmdList.add(ListNetscalerLoadBalancerNetworksCmd.class); cmdList.add(ListNetscalerLoadBalancersCmd.class); + return cmdList; } @@ -732,7 +738,8 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl List destinations = rule.getDestinations(); if ((destinations != null && !destinations.isEmpty()) || rule.isAutoScaleConfig()) { - LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, false, false, destinations, rule.getStickinessPolicies(), rule.getHealthCheckPolicies()); + LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, false, false, destinations, + rule.getStickinessPolicies(), rule.getHealthCheckPolicies(), rule.getLbSslCert(), rule.getLbProtocol()); if (rule.isAutoScaleConfig()) { loadBalancer.setAutoScaleVmGroup(rule.getAutoScaleVmGroup()); } @@ -894,7 +901,7 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl if ((destinations != null && !destinations.isEmpty()) || rule.isAutoScaleConfig()) { LoadBalancerTO loadBalancer = new LoadBalancerTO(lbUuid, srcIp, srcPort, protocol, algorithm, revoked, - false, false, destinations, null, rule.getHealthCheckPolicies()); + false, false, destinations, null, rule.getHealthCheckPolicies(), rule.getLbSslCert(), rule.getLbProtocol()); loadBalancersToApply.add(loadBalancer); } } @@ -1024,5 +1031,4 @@ public class NetscalerElement extends ExternalLoadBalancerDeviceManagerImpl impl } return true; } - } diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java index fe072e13718..e48d31d3f0a 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java +++ b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java @@ -25,6 +25,10 @@ import java.util.Map; import javax.naming.ConfigurationException; +import com.citrix.netscaler.nitro.resource.config.ssl.sslcertkey; +import com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; +import com.cloud.utils.ssh.SshHelper; import org.apache.log4j.Logger; import com.citrix.netscaler.nitro.exception.nitro_exception; @@ -235,6 +239,7 @@ public class NetscalerResource implements ServerResource { //enable load balancing feature enableLoadBalancingFeature(); + SSL.enableSslFeature(_netscalerService); //if the the device is cloud stack provisioned then make it part of the public network if (_cloudManaged) { @@ -550,6 +555,8 @@ public class NetscalerResource implements ServerResource { String lbAlgorithm = loadBalancer.getAlgorithm(); String nsVirtualServerName = generateNSVirtualServerName(srcIp, srcPort); String nsMonitorName = generateNSMonitorName(srcIp, srcPort); + LbSslCert sslCert = loadBalancer.getSslCert(); + if(loadBalancer.isAutoScaleVmGroupTO()) { applyAutoScaleConfig(loadBalancer); // Continue to process all the rules. @@ -558,6 +565,7 @@ public class NetscalerResource implements ServerResource { boolean hasMonitor = false; boolean deleteMonitor = false; boolean destinationsToAdd = false; + boolean deleteCert = false; for (DestinationTO destination : loadBalancer.getDestinations()) { if (!destination.isRevoked()) { destinationsToAdd = true; @@ -655,9 +663,35 @@ public class NetscalerResource implements ServerResource { } } + + + if(sslCert != null && lbProtocol.equals(NetUtils.SSL_PROTO)) { + if ( sslCert.isRevoked() ){ + deleteCert = true; + } else{ + + String certName = generateSslCertName(srcIp, srcPort); + String keyName = generateSslKeyName(srcIp, srcPort); + String certKeyName = generateSslCertKeyName(srcIp, srcPort); + + if ( SSL.isSslCertKeyPresent(_netscalerService, certKeyName)){ + SSL.deleteSslCertKey(_netscalerService, certKeyName); + } + + + SSL.uploadCert(_ip, _username, _password, certName, sslCert.getCert().getBytes()); + SSL.uploadKey(_ip, _username, _password, keyName, sslCert.getKey().getBytes()); + + SSL.createSslCertKey(_netscalerService, certName, keyName, certKeyName, sslCert.getPassword()); + SSL.bindCertKeyToVserver(_netscalerService, certKeyName, nsVirtualServerName); + } + + } + if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully added LB destination: " + destination.getDestIp() + ":" + destination.getDestPort() + " to load balancer " + srcIp + ":" + srcPort); } + } else { // remove a destination from the deployed load balancing rule com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding[] serviceBindings = com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding.get(_netscalerService, nsVirtualServerName); @@ -731,11 +765,27 @@ public class NetscalerResource implements ServerResource { } removeLBVirtualServer(nsVirtualServerName); deleteMonitor = true; + deleteCert = true; } } if(deleteMonitor) { removeLBMonitor(nsMonitorName); } + if ( sslCert != null && deleteCert){ + + String certName = generateSslCertName(srcIp, srcPort); + String keyName = generateSslKeyName(srcIp, srcPort); + String certKeyName = generateSslCertKeyName(srcIp, srcPort); + + // unbind before deleting + if ( nsVirtualServerExists(nsVirtualServerName) ){ + SSL.unbindCertKeyFromVserver(_netscalerService, certKeyName, nsVirtualServerName); + } + + SSL.deleteSslCertKey(_netscalerService, certKeyName); + SSL.deleteCertFile(_ip, _username, _password, certName); + SSL.deleteKeyFile(_ip, _username, _password, keyName); + } } @@ -1666,6 +1716,173 @@ public class NetscalerResource implements ServerResource { } } + /* SSL Termination */ + private static class SSL { + + private static final String SSL_CERT_PATH = "/nsconfig/ssl/"; + private static final int SSH_PORT = 22; + + private static boolean isSslCertKeyPresent(nitro_service ns, String certKeyName) throws ExecutionException { + + String filter = "certkey:" + certKeyName; + + try { + if (sslcertkey.count_filtered(ns, filter) > 0) return true; + } catch (nitro_exception e){ + throw new ExecutionException("Failed to get certkey " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to get certkey " + e.getMessage()); + } + + return false; + } + + private static void deleteSslCertKey(nitro_service ns, String certKeyName) throws ExecutionException { + try { + + sslcertkey certkey = new sslcertkey(); + certkey.set_certkey(certKeyName); + sslcertkey.delete(ns, certkey); + + } catch (nitro_exception e){ + throw new ExecutionException("Failed to delete certkey " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to delete certkey " + e.getMessage()); + } + + } + + private static void deleteCertFile(String nsIp, String username, String password, String certName) throws Exception { + SshHelper.sshExecute(nsIp,SSH_PORT,username,null,password,"shell rm " + SSL_CERT_PATH + certName); + } + + private static void deleteKeyFile(String nsIp, String username, String password, String keyName) throws Exception { + SshHelper.sshExecute(nsIp,SSH_PORT,username,null,password,"shell rm " + SSL_CERT_PATH + keyName); + } + + private static void createSslCertKey(nitro_service ns, String certName, String keyName, String certKeyName, String password) throws ExecutionException { + s_logger.debug("Adding cert to netscaler"); + try { + sslcertkey certkey = new sslcertkey(); + certkey.set_certkey(certKeyName); + certkey.set_cert(SSL_CERT_PATH + certName); + certkey.set_key(SSL_CERT_PATH + keyName); + + if( password != null ) { + certkey.set_passplain(password); + } + + certkey.perform_operation(ns); + + } catch (nitro_exception e){ + throw new ExecutionException("Failed to add certkey binding " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to add certkey binding " + e.getMessage()); + } + + } + + public static void updateCertKey(nitro_service ns, String certKeyName, String cert, String key, String password) throws ExecutionException { + try{ + sslcertkey certkey = sslcertkey.get(ns, certKeyName); + if ( cert != null ) + certkey.set_cert(cert); + if ( key != null ) + certkey.set_key(cert); + if ( password != null ) + certkey.set_passplain(cert); + + sslcertkey.change(ns,certkey); + + } catch (nitro_exception e){ + throw new ExecutionException("Failed to update ssl on load balancer due to " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to update ssl on load balancer due to " + e.getMessage()); + } + } + + private static void bindCertKeyToVserver(nitro_service ns, String certKeyName, String vserver) throws ExecutionException { + s_logger.debug("Adding cert to netscaler"); + + try { + sslvserver_sslcertkey_binding cert_binding = new sslvserver_sslcertkey_binding(); + cert_binding.set_certkeyname(certKeyName); + cert_binding.set_vservername(vserver); + cert_binding.perform_operation(ns); + } catch (nitro_exception e){ + throw new ExecutionException("Failed to bind certkey to vserver due to " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to bind certkey to vserver due to " + e.getMessage()); + } + } + + private static void unbindCertKeyFromVserver(nitro_service ns, String certKeyName, String vserver) throws ExecutionException { + try { + + sslvserver_sslcertkey_binding cert_binding = new sslvserver_sslcertkey_binding(); + cert_binding.set_certkeyname(certKeyName); + cert_binding.set_vservername(vserver); + sslvserver_sslcertkey_binding.delete(ns,cert_binding); + + } catch (nitro_exception e){ + throw new ExecutionException("Failed to unbind certkey to vserver due to " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to unbind certkey to vserver due to " + e.getMessage()); + } + + } + + + private static void uploadCert(String nsIp, String user, String password, String certName, byte[] certData) throws ExecutionException { + try { + SshHelper.scpTo(nsIp,SSH_PORT,user,null,password, SSL_CERT_PATH, certData, certName, null); + } catch (Exception e){ + throw new ExecutionException("Failed to copy private key to device " + e.getMessage()); + } + } + + private static void uploadKey(String nsIp, String user, String password, String keyName, byte[] keyData) throws ExecutionException { + try { + SshHelper.scpTo(nsIp, SSH_PORT, user, null, password, SSL_CERT_PATH, keyData, keyName, null); + } catch (Exception e){ + throw new ExecutionException("Failed to copy private key to device " + e.getMessage()); + } + } + + + private static void enableSslFeature(nitro_service ns) throws ExecutionException { + try { + base_response result = ns.enable_features(new String[]{"SSL"}); + if( result.errorcode != 0 ) + throw new ExecutionException("Unable to enable SSL on LB"); + } catch (nitro_exception e){ + throw new ExecutionException("Failed to enable ssl feature on load balancer due to " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to enable ssl feature on load balancer due to " + e.getMessage()); + } + } + + public static boolean checkSslFeature(nitro_service ns) throws ExecutionException { + try { + String[] features = ns.get_enabled_features(); + if (features != null) { + for (String feature : features) { + if (feature.equalsIgnoreCase("SSL")) { + return true; + } + } + } + return false; + } catch (nitro_exception e){ + throw new ExecutionException("Failed to check ssl feature on load balancer due to " + e.getMessage()); + } catch (Exception e){ + throw new ExecutionException("Failed to check ssl feature on load balancer due to " + e.getMessage()); + } + } + + + } + private void enableVPXInterfaces(String publicIf, String privateIf, ns ns_obj) { // enable VPX to use 10 gigabit Ethernet interfaces if public/private interface @@ -2110,6 +2327,25 @@ public class NetscalerResource implements ServerResource { } } + private boolean nsVirtualServerExists(String vserverName) throws ExecutionException { + try { + if (com.citrix.netscaler.nitro.resource.config.lb.lbvserver.get(_netscalerService, vserverName) != null) { + return true; + } else { + return false; + } + } catch (nitro_exception e) { + if (e.getErrorCode() == NitroError.NS_RESOURCE_NOT_EXISTS) { + return false; + } else { + throw new ExecutionException("Failed to verify VServer " + vserverName + " exists on the NetScaler device due to " + e.getMessage()); + } + } catch (Exception e) { + throw new ExecutionException("Failed to verify VServer " + vserverName + " exists on the NetScaler device due to " + e.getMessage()); + } + } + + private boolean nsVlanNsipBindingExists(long vlanTag, String vlanSelfIp) throws ExecutionException { try { vlan_nsip_binding[] vlanNsipBindings = vlan_nsip_binding.get(_netscalerService, vlanTag); @@ -2308,11 +2544,14 @@ public class NetscalerResource implements ServerResource { private String getNetScalerProtocol(LoadBalancerTO loadBalancer) throws ExecutionException { String port = Integer.toString(loadBalancer.getSrcPort()); - String lbProtocol = loadBalancer.getProtocol(); + String lbProtocol = loadBalancer.getLbProtocol(); StickinessPolicyTO[] stickyPolicies = loadBalancer.getStickinessPolicies(); String nsProtocol = "TCP"; - if ((stickyPolicies != null) && (stickyPolicies.length > 0) && (stickyPolicies[0] != null)){ + if(lbProtocol == null) + lbProtocol = loadBalancer.getProtocol(); + + if ((stickyPolicies != null) && (stickyPolicies.length > 0) && (stickyPolicies[0] != null)){ StickinessPolicyTO stickinessPolicy = stickyPolicies[0]; if (StickinessMethodType.LBCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()) || (StickinessMethodType.AppCookieBased.getName().equalsIgnoreCase(stickinessPolicy.getMethodName()))) { @@ -2321,6 +2560,10 @@ public class NetscalerResource implements ServerResource { } } + + if( lbProtocol.equalsIgnoreCase(NetUtils.SSL_PROTO) || lbProtocol.equalsIgnoreCase(NetUtils.HTTP_PROTO)) + return lbProtocol.toUpperCase(); + if (port.equals(NetUtils.HTTP_PORT)) { nsProtocol = "HTTP"; } else if (NetUtils.TCP_PROTO.equalsIgnoreCase(lbProtocol)) { @@ -3381,6 +3624,18 @@ public class NetscalerResource implements ServerResource { return counterName.replace(' ', '_'); } + private String generateSslCertName(String srcIp, long srcPort) { + // maximum length supported by NS is 31 + return genObjectName("Cloud-Cert", srcIp, srcPort); + } + + private String generateSslKeyName(String srcIp, long srcPort) { + return genObjectName("Cloud-Key", srcIp, srcPort); + } + private String generateSslCertKeyName(String srcIp, long srcPort) { + return genObjectName("Cloud-CertKey", srcIp, srcPort); + } + private String genObjectName(Object... args) { String objectName = ""; for (int i = 0; i < args.length; i++) { diff --git a/plugins/network-elements/palo-alto/pom.xml b/plugins/network-elements/palo-alto/pom.xml new file mode 100644 index 00000000000..50b4c134f60 --- /dev/null +++ b/plugins/network-elements/palo-alto/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + cloud-plugin-network-palo-alto + Apache CloudStack Plugin - Palo Alto + + org.apache.cloudstack + cloudstack-plugins + 4.3.0-SNAPSHOT + ../../pom.xml + + diff --git a/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/module.properties b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/module.properties new file mode 100644 index 00000000000..960fdba8352 --- /dev/null +++ b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/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=paloalto +parent=network \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml new file mode 100644 index 00000000000..251f444b8a0 --- /dev/null +++ b/plugins/network-elements/palo-alto/resources/META-INF/cloudstack/paloalto/spring-paloalto-context.xml @@ -0,0 +1,33 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.java new file mode 100644 index 00000000000..84ee869866a --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddExternalFirewallCmd.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.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.Host; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "addExternalFirewall", description="Adds an external firewall appliance", responseObject = ExternalFirewallResponse.class) +public class AddExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(AddExternalFirewallCmd.class.getName()); + private static final String s_name = "addexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + required = true, description="Zone in which to add the external firewall appliance.") + private Long zoneId; + + @Parameter(name=ApiConstants.URL, type=CommandType.STRING, required = true, description="URL of the external firewall appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Username of the external firewall appliance.") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Password of the external firewall appliance.") + private String password; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getZoneId() { + return zoneId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + try { + Host externalFirewall = _paElementService.addExternalFirewall(this); + ExternalFirewallResponse response = _paElementService.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException ipve) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, ipve.getMessage()); + } catch (CloudRuntimeException cre) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, cre.getMessage()); + } + } +} + diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.java new file mode 100644 index 00000000000..faf28e2cf26 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/AddPaloAltoFirewallCmd.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.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.log4j.Logger; + +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.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +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.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "addPaloAltoFirewall", responseObject=PaloAltoFirewallResponse.class, description="Adds a Palo Alto firewall device") +public class AddPaloAltoFirewallCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(AddPaloAltoFirewallCmd.class.getName()); + private static final String s_name = "addpaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// 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="URL of the Palo Alto appliance.") + private String url; + + @Parameter(name=ApiConstants.USERNAME, type=CommandType.STRING, required = true, description="Credentials to reach Palo Alto firewall device") + private String username; + + @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required = true, description="Credentials to reach Palo Alto firewall device") + private String password; + + @Parameter(name = ApiConstants.NETWORK_DEVICE_TYPE, type = CommandType.STRING, required = true, description = "supports only PaloAltoFirewall") + private String deviceType; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + public String getUrl() { + return url; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getDeviceType() { + return deviceType; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + ExternalFirewallDeviceVO fwDeviceVO = _paFwService.addPaloAltoFirewall(this); + if (fwDeviceVO != null) { + PaloAltoFirewallResponse response = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + response.setObjectName("pafirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add Palo Alto firewall due to internal error."); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Adding a Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_ADD; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java new file mode 100644 index 00000000000..199bb832d7d --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ConfigurePaloAltoFirewallCmd.java @@ -0,0 +1,114 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +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.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +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.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "configurePaloAltoFirewall", responseObject=PaloAltoFirewallResponse.class, description="Configures a Palo Alto firewall device") +public class ConfigurePaloAltoFirewallCmd extends BaseAsyncCmd { + + public static final Logger s_logger = Logger.getLogger(ConfigurePaloAltoFirewallCmd.class.getName()); + private static final String s_name = "configurepaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required=true, description="Palo Alto firewall device ID") + private Long fwDeviceId; + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_CAPACITY, type=CommandType.LONG, required=false, description="capacity of the firewall device, Capacity will be interpreted as number of networks device can handle") + private Long capacity; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + public Long getFirewallCapacity() { + return capacity; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + ExternalFirewallDeviceVO fwDeviceVO = _paFwService.configurePaloAltoFirewall(this); + if (fwDeviceVO != null) { + PaloAltoFirewallResponse response = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + response.setObjectName("pafirewall"); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to configure Palo Alto firewall device due to internal error."); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Configuring a Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_CONFIGURE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.java new file mode 100644 index 00000000000..93f752ac47c --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeleteExternalFirewallCmd.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 com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.HostResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.user.Account; + +@APICommand(name = "deleteExternalFirewall", description="Deletes an external firewall appliance.", responseObject = SuccessResponse.class) +public class DeleteExternalFirewallCmd extends BaseCmd { + public static final Logger s_logger = Logger.getLogger(DeleteExternalFirewallCmd.class.getName()); + private static final String s_name = "deleteexternalfirewallresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType = HostResponse.class, + required = true, description="Id of the external firewall appliance.") + private Long id; + + /////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getId() { + return id; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return Account.ACCOUNT_ID_SYSTEM; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + try { + boolean result = _paElementService.deleteExternalFirewall(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete external firewall."); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, "Failed to delete external firewall."); + } + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java new file mode 100644 index 00000000000..86149816af5 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/DeletePaloAltoFirewallCmd.java @@ -0,0 +1,105 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +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.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.event.EventTypes; +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.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.context.CallContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "deletePaloAltoFirewall", responseObject=SuccessResponse.class, description=" delete a Palo Alto firewall device") +public class DeletePaloAltoFirewallCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(DeletePaloAltoFirewallCmd.class.getName()); + private static final String s_name = "deletepaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paElementService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required=true, description="Palo Alto firewall device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + boolean result = _paElementService.deletePaloAltoFirewall(this); + if (result) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete Palo Alto firewall device"); + } + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getEventDescription() { + return "Deleting Palo Alto firewall device"; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_EXTERNAL_FIREWALL_DEVICE_DELETE; + } + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.java new file mode 100644 index 00000000000..ebced7e2039 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListExternalFirewallsCmd.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 com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.command.user.offering.ListServiceOfferingsCmd; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.host.Host; +import com.cloud.network.element.PaloAltoFirewallElementService; +import org.apache.cloudstack.api.response.ExternalFirewallResponse; + +@APICommand(name = "listExternalFirewalls", description="List external firewall appliances.", responseObject = ExternalFirewallResponse.class) +public class ListExternalFirewallsCmd extends BaseListCmd { + public static final Logger s_logger = Logger.getLogger(ListServiceOfferingsCmd.class.getName()); + private static final String s_name = "listexternalfirewallsresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.UUID, entityType = ZoneResponse.class, + required = true, description="zone Id") + private long zoneId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public long getZoneId() { + return zoneId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Inject PaloAltoFirewallElementService _paElementService; + + @Override + public String getCommandName() { + return s_name; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(){ + + List externalFirewalls = _paElementService.listExternalFirewalls(this); + + ListResponse listResponse = new ListResponse(); + List responses = new ArrayList(); + for (Host externalFirewall : externalFirewalls) { + ExternalFirewallResponse response = _paElementService.createExternalFirewallResponse(externalFirewall); + response.setObjectName("externalfirewall"); + response.setResponseName(getCommandName()); + responses.add(response); + } + + listResponse.setResponses(responses); + listResponse.setResponseName(getCommandName()); + this.setResponseObject(listResponse); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java new file mode 100644 index 00000000000..15c5bfcdbed --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallNetworksCmd.java @@ -0,0 +1,95 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.*; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NetworkResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +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.network.Network; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "listPaloAltoFirewallNetworks", responseObject=NetworkResponse.class, description="lists network that are using Palo Alto firewall device") +public class ListPaloAltoFirewallNetworksCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(ListPaloAltoFirewallNetworksCmd.class.getName()); + private static final String s_name = "listpaloaltofirewallnetworksresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.LOAD_BALANCER_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + required = true, description="palo alto balancer device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + List networks = _paFwService.listNetworks(this); + ListResponse response = new ListResponse(); + List networkResponses = new ArrayList(); + + if (networks != null && !networks.isEmpty()) { + for (Network network : networks) { + NetworkResponse networkResponse = _responseGenerator.createNetworkResponse(network); + networkResponses.add(networkResponse); + } + } + + response.setResponses(networkResponses); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java new file mode 100644 index 00000000000..b788aca8c35 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/commands/ListPaloAltoFirewallsCmd.java @@ -0,0 +1,103 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.api.*; +import org.apache.cloudstack.api.response.PhysicalNetworkResponse; +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.response.ListResponse; +import com.cloud.api.response.PaloAltoFirewallResponse; +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.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.element.PaloAltoFirewallElementService; +import com.cloud.utils.exception.CloudRuntimeException; + +@APICommand(name = "listPaloAltoFirewalls", responseObject=PaloAltoFirewallResponse.class, description="lists Palo Alto firewall devices in a physical network") +public class ListPaloAltoFirewallsCmd extends BaseListCmd { + + public static final Logger s_logger = Logger.getLogger(ListPaloAltoFirewallsCmd.class.getName()); + private static final String s_name = "listpaloaltofirewallresponse"; + @Inject PaloAltoFirewallElementService _paFwService; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name=ApiConstants.PHYSICAL_NETWORK_ID, type=CommandType.UUID, entityType = PhysicalNetworkResponse.class, + description="the Physical Network ID") + private Long physicalNetworkId; + + @Parameter(name=ApiConstants.FIREWALL_DEVICE_ID, type=CommandType.UUID, entityType = PaloAltoFirewallResponse.class, + description="Palo Alto firewall device ID") + private Long fwDeviceId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getFirewallDeviceId() { + return fwDeviceId; + } + + public Long getPhysicalNetworkId() { + return physicalNetworkId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { + try { + List fwDevices = _paFwService.listPaloAltoFirewalls(this); + ListResponse response = new ListResponse(); + List fwDevicesResponse = new ArrayList(); + + if (fwDevices != null && !fwDevices.isEmpty()) { + for (ExternalFirewallDeviceVO fwDeviceVO : fwDevices) { + PaloAltoFirewallResponse deviceResponse = _paFwService.createPaloAltoFirewallResponse(fwDeviceVO); + fwDevicesResponse.add(deviceResponse); + } + } + + response.setResponses(fwDevicesResponse); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } catch (InvalidParameterValueException invalidParamExcp) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, invalidParamExcp.getMessage()); + } catch (CloudRuntimeException runtimeExcp) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, runtimeExcp.getMessage()); + } + } + + @Override + public String getCommandName() { + return s_name; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.java b/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.java new file mode 100644 index 00000000000..cda018d7b4c --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/api/response/PaloAltoFirewallResponse.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 com.cloud.api.response; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.EntityReference; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.BaseResponse; + +import com.cloud.network.dao.ExternalFirewallDeviceVO; + +@EntityReference(value=ExternalFirewallDeviceVO.class) +@SuppressWarnings("unused") +public class PaloAltoFirewallResponse extends BaseResponse { + + @SerializedName(ApiConstants.FIREWALL_DEVICE_ID) @Param(description="device id of the Palo Alto firewall") + private String id; + + @SerializedName(ApiConstants.PHYSICAL_NETWORK_ID) @Param(description="the physical network to which this Palo Alto firewall belongs to") + private String physicalNetworkId; + + @SerializedName(ApiConstants.PROVIDER) @Param(description="name of the provider") + private String providerName; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_NAME) @Param(description="device name") + private String deviceName; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_STATE) @Param(description="device state") + private String deviceState; + + @SerializedName(ApiConstants.FIREWALL_DEVICE_CAPACITY) @Param(description="device capacity") + private Long deviceCapacity; + + @SerializedName(ApiConstants.ZONE_ID) @Param(description="the zone ID of the external firewall") + private String zoneId; + + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the management IP address of the external firewall") + private String ipAddress; + + @SerializedName(ApiConstants.USERNAME) @Param(description="the username that's used to log in to the external firewall") + private String username; + + @SerializedName(ApiConstants.PUBLIC_INTERFACE) @Param(description="the public interface of the external firewall") + private String publicInterface; + + @SerializedName(ApiConstants.USAGE_INTERFACE) @Param(description="the usage interface of the external firewall") + private String usageInterface; + + @SerializedName(ApiConstants.PRIVATE_INTERFACE) @Param(description="the private interface of the external firewall") + private String privateInterface; + + @SerializedName(ApiConstants.PUBLIC_ZONE) @Param(description="the public security zone of the external firewall") + private String publicZone; + + @SerializedName(ApiConstants.PRIVATE_ZONE) @Param(description="the private security zone of the external firewall") + private String privateZone; + + @SerializedName(ApiConstants.NUM_RETRIES) @Param(description="the number of times to retry requests to the external firewall") + private String numRetries; + + @SerializedName(ApiConstants.TIMEOUT) @Param(description="the timeout (in seconds) for requests to the external firewall") + private String timeout; + + public void setId(String lbDeviceId) { + this.id = lbDeviceId; + } + + public void setPhysicalNetworkId(String physicalNetworkId) { + this.physicalNetworkId = physicalNetworkId; + } + + public void setProvider(String provider) { + this.providerName = provider; + } + + public void setDeviceName(String deviceName) { + this.deviceName = deviceName; + } + + public void setDeviceCapacity(long deviceCapacity) { + this.deviceCapacity = deviceCapacity; + } + + public void setDeviceState(String deviceState) { + this.deviceState = deviceState; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setPublicInterface(String publicInterface) { + this.publicInterface = publicInterface; + } + + public void setUsageInterface(String usageInterface) { + this.usageInterface = usageInterface; + } + + public void setPrivateInterface(String privateInterface) { + this.privateInterface = privateInterface; + } + + public void setPublicZone(String publicZone) { + this.publicZone = publicZone; + } + + public void setPrivateZone(String privateZone) { + this.privateZone = privateZone; + } + + public String getNumRetries() { + return numRetries; + } + + public void setNumRetries(String numRetries) { + this.numRetries = numRetries; + } + + public String getTimeout() { + return timeout; + } + + public void setTimeout(String timeout) { + this.timeout = timeout; + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java new file mode 100644 index 00000000000..3eb802ed8f6 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoExternalFirewallElement.java @@ -0,0 +1,538 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.network.element; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import org.apache.cloudstack.network.ExternalNetworkDeviceManager.NetworkDevice; +import org.apache.log4j.Logger; + +import com.cloud.api.ApiDBUtils; +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddPaloAltoFirewallCmd; +import com.cloud.api.commands.ConfigurePaloAltoFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeletePaloAltoFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListPaloAltoFirewallNetworksCmd; +import com.cloud.api.commands.ListPaloAltoFirewallsCmd; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.configuration.Config; +import com.cloud.configuration.ConfigurationManager; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InsufficientNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.ExternalFirewallDeviceManagerImpl; +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.NetworkModel; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.dao.ExternalFirewallDeviceDao; +import com.cloud.network.dao.ExternalFirewallDeviceVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkExternalFirewallDao; +import com.cloud.network.dao.NetworkExternalFirewallVO; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.ExternalFirewallDeviceVO.FirewallDeviceState; +import com.cloud.network.resource.PaloAltoResource; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +@Local(value = {NetworkElement.class, FirewallServiceProvider.class, + PortForwardingServiceProvider.class, IpDeployer.class, + SourceNatServiceProvider.class}) +public class PaloAltoExternalFirewallElement extends ExternalFirewallDeviceManagerImpl implements SourceNatServiceProvider, FirewallServiceProvider, +PortForwardingServiceProvider, IpDeployer, PaloAltoFirewallElementService, StaticNatServiceProvider { + + private static final Logger s_logger = Logger.getLogger(PaloAltoExternalFirewallElement.class); + + private static final Map> capabilities = setCapabilities(); + + @Inject + NetworkModel _networkManager; + @Inject + HostDao _hostDao; + @Inject + ConfigurationManager _configMgr; + @Inject + NetworkOfferingDao _networkOfferingDao; + @Inject + NetworkDao _networksDao; + @Inject + DataCenterDao _dcDao; + @Inject + PhysicalNetworkDao _physicalNetworkDao; + @Inject + ExternalFirewallDeviceDao _fwDevicesDao; + @Inject + NetworkExternalFirewallDao _networkFirewallDao; + @Inject + NetworkDao _networkDao; + @Inject + NetworkServiceMapDao _ntwkSrvcDao; + @Inject + HostDetailsDao _hostDetailDao; + @Inject + ConfigurationDao _configDao; + @Inject + EntityManager _entityMgr; + + private boolean canHandle(Network network, Service service) { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + if (zone.getNetworkType() == NetworkType.Advanced && network.getGuestType() != Network.GuestType.Isolated) { + s_logger.trace("Element " + getProvider().getName() + "is not handling network type = " + network.getGuestType()); + return false; + } + + if (service == null) { + if (!_networkManager.isProviderForNetwork(getProvider(), network.getId())) { + s_logger.trace("Element " + getProvider().getName() + " is not a provider for the network " + network); + return false; + } + } else { + if (!_networkManager.isProviderSupportServiceInNetwork(network.getId(), service, getProvider())) { + s_logger.trace("Element " + getProvider().getName() + " doesn't support service " + service.getName() + " in the network " + network); + return false; + } + } + + return true; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, + InsufficientNetworkCapacityException { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + + // don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network implement in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network, null)) { + return false; + } + + try { + return manageGuestNetworkWithExternalFirewall(true, network); + } catch (InsufficientCapacityException capacityException) { + // TODO: handle out of capacity exception in more gracefule manner when multiple providers are present for + // the network + s_logger.error("Fail to implement the Palo Alto for network " + network, capacityException); + return false; + } + } + + @Override + public boolean prepare(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, + InsufficientNetworkCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean release(Network config, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { + return true; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ResourceUnavailableException, ConcurrentOperationException { + DataCenter zone = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + + // don't have to implement network is Basic zone + if (zone.getNetworkType() == NetworkType.Basic) { + s_logger.debug("Not handling network shutdown in zone of type " + NetworkType.Basic); + return false; + } + + if (!canHandle(network, null)) { + return false; + } + try { + return manageGuestNetworkWithExternalFirewall(false, network); + } catch (InsufficientCapacityException capacityException) { + // TODO: handle out of capacity exception + return false; + } + } + + @Override + public boolean destroy(Network config, ReservationContext context) { + return true; + } + + @Override + public boolean applyFWRules(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config, Service.Firewall)) { + return false; + } + + return applyFirewallRules(config, rules); + } + + @Override + public Provider getProvider() { + return Provider.PaloAlto; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + private static Map> setCapabilities() { + Map> capabilities = new HashMap>(); + + // Set capabilities for Firewall service + Map firewallCapabilities = new HashMap(); + firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Capability.SupportedEgressProtocols, "tcp,udp,icmp,all"); + firewallCapabilities.put(Capability.MultipleIps, "true"); + firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); + firewallCapabilities.put(Capability.SupportedTrafficDirection, "ingress, egress"); + capabilities.put(Service.Firewall, firewallCapabilities); + + capabilities.put(Service.Gateway, null); + + Map sourceNatCapabilities = new HashMap(); + // Specifies that this element supports either one source NAT rule per account; + sourceNatCapabilities.put(Capability.SupportedSourceNatTypes, "peraccount"); + capabilities.put(Service.SourceNat, sourceNatCapabilities); + + // Specifies that port forwarding rules are supported by this element + capabilities.put(Service.PortForwarding, null); + + // Specifies that static NAT rules are supported by this element + capabilities.put(Service.StaticNat, null); + + return capabilities; + } + + @Override + public boolean applyPFRules(Network network, List rules) throws ResourceUnavailableException { + if (!canHandle(network, Service.PortForwarding)) { + return false; + } + + return applyPortForwardingRules(network, rules); + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + + List fwDevices = _fwDevicesDao.listByPhysicalNetworkAndProvider(provider.getPhysicalNetworkId(), Provider.PaloAlto.getName()); + // true if at-least one Palo Alto device is added in to physical network and is in configured (in enabled state) state + if (fwDevices != null && !fwDevices.isEmpty()) { + for (ExternalFirewallDeviceVO fwDevice : fwDevices) { + if (fwDevice.getDeviceState() == FirewallDeviceState.Enabled) { + return true; + } + } + } + return false; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, + ResourceUnavailableException { + // TODO Auto-generated method stub + return true; + } + + @Override + public boolean canEnableIndividualServices() { + return true; + } + + @Override + @Deprecated + // should use more generic addNetworkDevice command to add firewall + public Host addExternalFirewall(AddExternalFirewallCmd cmd) { + Long zoneId = cmd.getZoneId(); + DataCenterVO zone = null; + PhysicalNetworkVO pNetwork = null; + HostVO fwHost = null; + + zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + List physicalNetworks = _physicalNetworkDao.listByZone(zoneId); + if ((physicalNetworks == null) || (physicalNetworks.size() > 1)) { + throw new InvalidParameterValueException("There are no physical networks or multiple physical networks configured in zone with ID: " + + zoneId + " to add this device."); + } + pNetwork = physicalNetworks.get(0); + + String deviceType = NetworkDevice.PaloAltoFirewall.getName(); + ExternalFirewallDeviceVO fwDeviceVO = addExternalFirewall(pNetwork.getId(), cmd.getUrl(), cmd.getUsername(), cmd.getPassword(), deviceType, new PaloAltoResource()); + if (fwDeviceVO != null) { + fwHost = _hostDao.findById(fwDeviceVO.getHostId()); + } + + return fwHost; + } + + @Override + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd) { + return deleteExternalFirewall(cmd.getId()); + } + + @Override + @Deprecated + // should use more generic listNetworkDevice command + public List listExternalFirewalls(ListExternalFirewallsCmd cmd) { + List firewallHosts = new ArrayList(); + Long zoneId = cmd.getZoneId(); + DataCenterVO zone = null; + PhysicalNetworkVO pNetwork = null; + + if (zoneId != null) { + zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Could not find zone with ID: " + zoneId); + } + + List physicalNetworks = _physicalNetworkDao.listByZone(zoneId); + if ((physicalNetworks == null) || (physicalNetworks.size() > 1)) { + throw new InvalidParameterValueException("There are no physical networks or multiple physical networks configured in zone with ID: " + + zoneId + " to add this device."); + } + pNetwork = physicalNetworks.get(0); + } + + firewallHosts.addAll(listExternalFirewalls(pNetwork.getId(), NetworkDevice.PaloAltoFirewall.getName())); + return firewallHosts; + } + + @Override + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall) { + return super.createExternalFirewallResponse(externalFirewall); + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(AddExternalFirewallCmd.class); + cmdList.add(AddPaloAltoFirewallCmd.class); + cmdList.add(ConfigurePaloAltoFirewallCmd.class); + cmdList.add(DeleteExternalFirewallCmd.class); + cmdList.add(DeletePaloAltoFirewallCmd.class); + cmdList.add(ListExternalFirewallsCmd.class); + cmdList.add(ListPaloAltoFirewallNetworksCmd.class); + cmdList.add(ListPaloAltoFirewallsCmd.class); + return cmdList; + } + + @Override + public ExternalFirewallDeviceVO addPaloAltoFirewall(AddPaloAltoFirewallCmd cmd) { + String deviceName = cmd.getDeviceType(); + if (!deviceName.equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Invalid Palo Alto firewall device type"); + } + return addExternalFirewall(cmd.getPhysicalNetworkId(), cmd.getUrl(), cmd.getUsername(), cmd.getPassword(), deviceName, + new PaloAltoResource()); + } + + @Override + public boolean deletePaloAltoFirewall(DeletePaloAltoFirewallCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + + ExternalFirewallDeviceVO fwDeviceVO = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVO == null || !fwDeviceVO.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("No Palo Alto firewall device found with ID: " + fwDeviceId); + } + return deleteExternalFirewall(fwDeviceVO.getHostId()); + } + + @Override + public ExternalFirewallDeviceVO configurePaloAltoFirewall(ConfigurePaloAltoFirewallCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + Long deviceCapacity = cmd.getFirewallCapacity(); + + ExternalFirewallDeviceVO fwDeviceVO = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVO == null || !fwDeviceVO.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("No Palo Alto firewall device found with ID: " + fwDeviceId); + } + + if (deviceCapacity != null) { + // check if any networks are using this Palo Alto device + List networks = _networkFirewallDao.listByFirewallDeviceId(fwDeviceId); + if ((networks != null) && !networks.isEmpty()) { + if (deviceCapacity < networks.size()) { + throw new CloudRuntimeException("There are more number of networks already using this Palo Alto firewall device than configured capacity"); + } + } + if (deviceCapacity != null) { + fwDeviceVO.setCapacity(deviceCapacity); + } + } + + fwDeviceVO.setDeviceState(FirewallDeviceState.Enabled); + _fwDevicesDao.update(fwDeviceId, fwDeviceVO); + return fwDeviceVO; + } + + @Override + public List listPaloAltoFirewalls(ListPaloAltoFirewallsCmd cmd) { + Long physcialNetworkId = cmd.getPhysicalNetworkId(); + Long fwDeviceId = cmd.getFirewallDeviceId(); + PhysicalNetworkVO pNetwork = null; + List fwDevices = new ArrayList(); + + if (physcialNetworkId == null && fwDeviceId == null) { + throw new InvalidParameterValueException("Either physical network Id or load balancer device Id must be specified"); + } + + if (fwDeviceId != null) { + ExternalFirewallDeviceVO fwDeviceVo = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVo == null || !fwDeviceVo.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Could not find Palo Alto firewall device with ID: " + fwDeviceId); + } + fwDevices.add(fwDeviceVo); + } + + if (physcialNetworkId != null) { + pNetwork = _physicalNetworkDao.findById(physcialNetworkId); + if (pNetwork == null) { + throw new InvalidParameterValueException("Could not find phyical network with ID: " + physcialNetworkId); + } + fwDevices = _fwDevicesDao.listByPhysicalNetworkAndProvider(physcialNetworkId, Provider.PaloAlto.getName()); + } + + return fwDevices; + } + + @Override + public List listNetworks(ListPaloAltoFirewallNetworksCmd cmd) { + Long fwDeviceId = cmd.getFirewallDeviceId(); + List networks = new ArrayList(); + + ExternalFirewallDeviceVO fwDeviceVo = _fwDevicesDao.findById(fwDeviceId); + if (fwDeviceVo == null || !fwDeviceVo.getDeviceName().equalsIgnoreCase(NetworkDevice.PaloAltoFirewall.getName())) { + throw new InvalidParameterValueException("Could not find Palo Alto firewall device with ID " + fwDeviceId); + } + + List networkFirewallMaps = _networkFirewallDao.listByFirewallDeviceId(fwDeviceId); + if (networkFirewallMaps != null && !networkFirewallMaps.isEmpty()) { + for (NetworkExternalFirewallVO networkFirewallMap : networkFirewallMaps) { + NetworkVO network = _networkDao.findById(networkFirewallMap.getNetworkId()); + networks.add(network); + } + } + + return networks; + } + + @Override + public PaloAltoFirewallResponse createPaloAltoFirewallResponse(ExternalFirewallDeviceVO fwDeviceVO) { + PaloAltoFirewallResponse response = new PaloAltoFirewallResponse(); + Map fwDetails = _hostDetailDao.findDetails(fwDeviceVO.getHostId()); + Host fwHost = _hostDao.findById(fwDeviceVO.getHostId()); + + response.setId(fwDeviceVO.getUuid()); + PhysicalNetwork pnw = ApiDBUtils.findPhysicalNetworkById(fwDeviceVO.getPhysicalNetworkId()); + if (pnw != null) { + response.setPhysicalNetworkId(pnw.getUuid()); + } + response.setDeviceName(fwDeviceVO.getDeviceName()); + if (fwDeviceVO.getCapacity() == 0) { + long defaultFwCapacity = NumbersUtil.parseLong(_configDao.getValue(Config.DefaultExternalFirewallCapacity.key()), 50); + response.setDeviceCapacity(defaultFwCapacity); + } else { + response.setDeviceCapacity(fwDeviceVO.getCapacity()); + } + response.setProvider(fwDeviceVO.getProviderName()); + response.setDeviceState(fwDeviceVO.getDeviceState().name()); + response.setIpAddress(fwHost.getPrivateIpAddress()); + response.setPublicInterface(fwDetails.get("publicInterface")); + response.setUsageInterface(fwDetails.get("usageInterface")); + response.setPrivateInterface(fwDetails.get("privateInterface")); + response.setPublicZone(fwDetails.get("publicZone")); + response.setPrivateZone(fwDetails.get("privateZone")); + response.setNumRetries(fwDetails.get("numRetries")); + response.setTimeout(fwDetails.get("timeout")); + response.setObjectName("paloaltofirewall"); + return response; + } + + @Override + public boolean verifyServicesCombination(Set services) { + if (!services.contains(Service.Firewall)) { + s_logger.warn("Palo Alto must be used as Firewall Service Provider in the network"); + return false; + } + return true; + } + + @Override + public IpDeployer getIpDeployer(Network network) { + return this; + } + + @Override + public boolean applyIps(Network network, List ipAddress, Set service) throws ResourceUnavailableException { + // return true, as IP will be associated as part of static NAT/port forwarding rule configuration + return true; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + if (!canHandle(config, Service.StaticNat)) { + return false; + } + return applyStaticNatRules(config, rules); + } +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.java b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.java new file mode 100644 index 00000000000..d2842ab101d --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/element/PaloAltoFirewallElementService.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 com.cloud.network.element; + +import java.util.List; + +import com.cloud.api.commands.AddExternalFirewallCmd; +import com.cloud.api.commands.AddPaloAltoFirewallCmd; +import com.cloud.api.commands.ConfigurePaloAltoFirewallCmd; +import com.cloud.api.commands.DeleteExternalFirewallCmd; +import com.cloud.api.commands.DeletePaloAltoFirewallCmd; +import com.cloud.api.commands.ListExternalFirewallsCmd; +import com.cloud.api.commands.ListPaloAltoFirewallNetworksCmd; +import com.cloud.api.commands.ListPaloAltoFirewallsCmd; +import com.cloud.api.response.PaloAltoFirewallResponse; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.network.dao.ExternalFirewallDeviceVO; + +import org.apache.cloudstack.api.response.ExternalFirewallResponse; +import com.cloud.utils.component.PluggableService; + +public interface PaloAltoFirewallElementService extends PluggableService { + + /** + * adds a Palo Alto firewall device in to a physical network + * @param AddPaloAltoFirewallCmd + * @return ExternalFirewallDeviceVO object for the firewall added + */ + public ExternalFirewallDeviceVO addPaloAltoFirewall(AddPaloAltoFirewallCmd cmd); + + /** + * removes Palo Alto firewall device from a physical network + * @param DeletePaloAltoFirewallCmd + * @return true if firewall device successfully deleted + */ + public boolean deletePaloAltoFirewall(DeletePaloAltoFirewallCmd cmd); + + /** + * configures a Palo Alto firewal device added in a physical network + * @param ConfigurePaloAltoFirewallCmd + * @return ExternalFirewallDeviceVO for the device configured + */ + public ExternalFirewallDeviceVO configurePaloAltoFirewall(ConfigurePaloAltoFirewallCmd cmd); + + /** + * lists all the Palo Alto firewall devices added in to a physical network + * @param ListPaloAltoFirewallsCmd + * @return list of ExternalFirewallDeviceVO for the devices in the physical network. + */ + public List listPaloAltoFirewalls(ListPaloAltoFirewallsCmd cmd); + + /** + * lists all the guest networks using a PaloAlto firewall device + * @param ListPaloAltoFirewallNetworksCmd + * @return list of the guest networks that are using this F5 load balancer + */ + public List listNetworks(ListPaloAltoFirewallNetworksCmd cmd); + + public PaloAltoFirewallResponse createPaloAltoFirewallResponse(ExternalFirewallDeviceVO fwDeviceVO); + + + @Deprecated // API helper function supported for backward compatibility + public Host addExternalFirewall(AddExternalFirewallCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public boolean deleteExternalFirewall(DeleteExternalFirewallCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public List listExternalFirewalls(ListExternalFirewallsCmd cmd); + + @Deprecated // API helper function supported for backward compatibility + public ExternalFirewallResponse createExternalFirewallResponse(Host externalFirewall); +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java new file mode 100644 index 00000000000..2251ce06851 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java @@ -0,0 +1,2030 @@ +// 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.resource; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +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.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + +public class PaloAltoResource implements ServerResource { + + private String _name; + private String _zoneId; + private String _ip; + private String _username; + private String _password; + private String _guid; + private String _key; + private Integer _numRetries; + private Integer _timeoutInSeconds; + private String _publicZone; + private String _privateZone; + private String _publicInterface; + private String _privateInterface; + private String _publicInterfaceType; + private String _privateInterfaceType; + private String _virtualRouter; + private String _threatProfile; + private String _logProfile; + private String _pingManagementProfile; + private final Logger s_logger = Logger.getLogger(PaloAltoResource.class); + + private static String _apiUri = "/api"; + private static HttpClient _httpclient; + + protected enum PaloAltoMethod { + GET, POST; + } + + private enum PaloAltoPrimative { + CHECK_IF_EXISTS, ADD, DELETE; + } + + private enum InterfaceType { + AGGREGATE("aggregate-ethernet"), + ETHERNET("ethernet"); + + private String type; + + private InterfaceType(String type) { + this.type = type; + } + public String toString() { + return type; + } + } + + private enum Protocol { + TCP("tcp"), + UDP("udp"), + ICMP("icmp"), + ALL("all"); + + private String protocol; + + private Protocol(String protocol) { + this.protocol = protocol; + } + public String toString() { + return protocol; + } + } + + private enum GuestNetworkType { + SOURCE_NAT, + INTERFACE_NAT; + } + + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return execute((ReadyCommand) cmd); + } else if (cmd instanceof MaintainCommand) { + return execute((MaintainCommand) cmd); + } else if (cmd instanceof IpAssocCommand) { + return execute((IpAssocCommand) cmd); + } else if (cmd instanceof SetStaticNatRulesCommand) { + return execute((SetStaticNatRulesCommand) cmd); + } else if (cmd instanceof SetPortForwardingRulesCommand) { + return execute((SetPortForwardingRulesCommand) cmd); + } else if (cmd instanceof SetFirewallRulesCommand) { + return execute((SetFirewallRulesCommand) cmd); + } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { + return execute((ExternalNetworkResourceUsageCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + public boolean configure(String name, Map params) throws ConfigurationException { + try { + _name = (String) params.get("name"); + if (_name == null) { + throw new ConfigurationException("Unable to find name"); + } + + _zoneId = (String) params.get("zoneId"); + if (_zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + _ip = (String) params.get("ip"); + if (_ip == null) { + throw new ConfigurationException("Unable to find IP"); + } + + _username = (String) params.get("username"); + if (_username == null) { + throw new ConfigurationException("Unable to find username"); + } + + _password = (String) params.get("password"); + if (_password == null) { + throw new ConfigurationException("Unable to find password"); + } + + _publicInterface = (String) params.get("publicinterface"); + if (_publicInterface == null) { + throw new ConfigurationException("Unable to find public interface."); + } + + _privateInterface = (String) params.get("privateinterface"); + if (_privateInterface == null) { + throw new ConfigurationException("Unable to find private interface."); + } + + _publicZone = (String) params.get("publicnetwork"); + if (_publicZone == null) { + throw new ConfigurationException("Unable to find public zone"); + } + + _privateZone = (String) params.get("privatenetwork"); + if (_privateZone == null) { + throw new ConfigurationException("Unable to find private zone"); + } + + _virtualRouter = (String) params.get("pavr"); + if (_virtualRouter == null) { + throw new ConfigurationException("Unable to find virtual router"); + } + + _threatProfile = (String) params.get("patp"); + _logProfile = (String) params.get("palp"); + + _guid = (String) params.get("guid"); + if (_guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + _numRetries = NumbersUtil.parseInt((String) params.get("numretries"), 1); + _timeoutInSeconds = NumbersUtil.parseInt((String) params.get("timeout"), 300); + + // Open a socket and login + if (!refreshPaloAltoConnection()) { + throw new ConfigurationException("Unable to open a connection to the Palo Alto."); + } + + // check that the threat profile exists if one was specified + if (_threatProfile != null) { + try { + boolean has_profile = getThreatProfile(_threatProfile); + if (!has_profile) { + throw new ConfigurationException("The specified threat profile group does not exist."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + } + + // check that the log profile exists if one was specified + if (_logProfile != null) { + try { + boolean has_profile = getLogProfile(_logProfile); + if (!has_profile) { + throw new ConfigurationException("The specified log profile does not exist."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + } + + // get public interface type + try { + _publicInterfaceType = getInterfaceType(_publicInterface); + if (_publicInterfaceType.equals("")) { + throw new ConfigurationException("The specified public interface is not configured on the Palo Alto."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + // get private interface type + try { + _privateInterfaceType = getInterfaceType(_privateInterface); + if (_privateInterfaceType.equals("")) { + throw new ConfigurationException("The specified private interface is not configured on the Palo Alto."); + } + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + _pingManagementProfile = "Ping"; + try { + ArrayList cmdList = new ArrayList(); + managePingProfile(cmdList, PaloAltoPrimative.ADD); + boolean status = requestWithCommit(cmdList); + } catch (ExecutionException e) { + throw new ConfigurationException(e.getMessage()); + } + + return true; + } catch (Exception e) { + throw new ConfigurationException(e.getMessage()); + } + + } + + public StartupCommand[] initialize() { + StartupExternalFirewallCommand cmd = new StartupExternalFirewallCommand(); + cmd.setName(_name); + cmd.setDataCenter(_zoneId); + cmd.setPod(""); + cmd.setPrivateIpAddress(_ip); + cmd.setStorageIpAddress(""); + cmd.setVersion(PaloAltoResource.class.getPackage().getImplementationVersion()); + cmd.setGuid(_guid); + return new StartupCommand[]{cmd}; + } + + public Host.Type getType() { + return Host.Type.ExternalFirewall; + } + + @Override + public String getName() { + return _name; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public PingCommand getCurrentStatus(final long id) { + return new PingCommand(Host.Type.ExternalFirewall, id); + } + + @Override + public void disconnected() { + // nothing for now... + } + + public IAgentControl getAgentControl() { + return null; + } + + public void setAgentControl(IAgentControl agentControl) { + return; + } + + /* + * Login + */ + private void openHttpConnection(){ + _httpclient = new DefaultHttpClient(); + + // Allows you to connect via SSL using unverified certs + _httpclient = HttpClientWrapper.wrapClient(_httpclient); + } + + private boolean refreshPaloAltoConnection() { + if (_httpclient == null) { + openHttpConnection(); + } + + try { + return login(_username, _password); + } catch (ExecutionException e) { + s_logger.error("Failed to login due to " + e.getMessage()); + return false; + } + } + + private boolean login(String username, String password) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "keygen"); + params.put("user", username); + params.put("password", password); + + String keygenBody; + try { + keygenBody = request(PaloAltoMethod.GET, params); + } catch (ExecutionException e) { + return false; + } + Document keygen_doc = getDocument(keygenBody); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/key/text()"); + _key = (String) expr.evaluate(keygen_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (_key != null) { + return true; + } + return false; + } + + + // ENTRY POINTS... + + + private Answer execute(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer execute(MaintainCommand cmd) { + return new MaintainAnswer(cmd); + } + + private ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) { + return new ExternalNetworkResourceUsageAnswer(cmd); + } + + + /* + * Guest networks + */ + + private synchronized Answer execute(IpAssocCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(IpAssocCommand cmd, int numRetries) { + String[] results = new String[cmd.getIpAddresses().length]; + int i = 0; + try { + IpAddressTO ip; + if (cmd.getIpAddresses().length != 1) { + throw new ExecutionException("Received an invalid number of guest IPs to associate."); + } else { + ip = cmd.getIpAddresses()[0]; + } + + String sourceNatIpAddress = null; + GuestNetworkType type = GuestNetworkType.INTERFACE_NAT; + + if (ip.isSourceNat()) { + type = GuestNetworkType.SOURCE_NAT; + + if (ip.getPublicIp() == null) { + throw new ExecutionException("Source NAT IP address must not be null."); + } else { + sourceNatIpAddress = ip.getPublicIp(); + } + } + + long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG)); + String guestVlanGateway = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY); + String cidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR); + long cidrSize = NetUtils.cidrToLong(cidr)[1]; + String guestVlanSubnet = NetUtils.getCidrSubNet(guestVlanGateway, cidrSize); + + Long publicVlanTag = null; + if (ip.getBroadcastUri() != null && !ip.getBroadcastUri().equals("untagged")) { + try { + publicVlanTag = Long.parseLong(ip.getBroadcastUri()); + } catch (Exception e) { + throw new ExecutionException("Could not parse public VLAN tag: " + ip.getBroadcastUri()); + } + } + + ArrayList commandList = new ArrayList(); + + if (ip.isAdd()) { + // Implement the guest network for this VLAN + implementGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + } else { + // Remove the guest network: + shutdownGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize); + } + + boolean status = requestWithCommit(commandList); + + results[i++] = ip.getPublicIp() + " - success"; + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying IPAssocCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + results[i++] = IpAssocAnswer.errorResult; + } + } + + return new IpAssocAnswer(cmd, results); + } + + private void implementGuestNetwork(ArrayList cmdList, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrNumber) throws ExecutionException { + privateSubnet = privateSubnet+"/"+privateCidrNumber; + + managePrivateInterface(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateGateway+"/"+privateCidrNumber); + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + managePublicInterface(cmdList, PaloAltoPrimative.ADD, publicVlanTag, publicIp+"/32", privateVlanTag); + manageSrcNatRule(cmdList, PaloAltoPrimative.ADD, type, publicVlanTag, publicIp+"/32", privateVlanTag, privateGateway+"/"+privateCidrNumber); + manageNetworkIsolation(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateSubnet, privateGateway); + } + + String msg = "Implemented guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrNumber; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + publicIp : ""; + s_logger.debug(msg); + } + + private void shutdownGuestNetwork(ArrayList cmdList, GuestNetworkType type, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException { + privateSubnet = privateSubnet+"/"+privateCidrSize; + + if (type.equals(GuestNetworkType.SOURCE_NAT)) { + manageNetworkIsolation(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateSubnet, privateGateway); + manageSrcNatRule(cmdList, PaloAltoPrimative.DELETE, type, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag, privateGateway+"/"+privateCidrSize); + managePublicInterface(cmdList, PaloAltoPrimative.DELETE, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag); + } + + managePrivateInterface(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateGateway+"/"+privateCidrSize); + + String msg = "Shut down guest network with type " + type +". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrSize; + msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + sourceNatIpAddress : ""; + s_logger.debug(msg); + } + + + + /* + * Firewall rule entry point + */ + private synchronized Answer execute(SetFirewallRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetFirewallRulesCommand cmd, int numRetries) { + FirewallRuleTO[] rules = cmd.getRules(); + try { + ArrayList commandList = new ArrayList(); + + for (FirewallRuleTO rule : rules) { + if (!rule.revoked()) { + manageFirewallRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageFirewallRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetFirewallRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + /* + * Static NAT rule entry point + */ + + private synchronized Answer execute(SetStaticNatRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetStaticNatRulesCommand cmd, int numRetries) { + StaticNatRuleTO[] rules = cmd.getRules(); + + try { + ArrayList commandList = new ArrayList(); + + for (StaticNatRuleTO rule : rules) { + if (!rule.revoked()) { + manageStcNatRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageStcNatRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetStaticNatRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + /* + * Destination NAT (Port Forwarding) entry point + */ + private synchronized Answer execute (SetPortForwardingRulesCommand cmd) { + refreshPaloAltoConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetPortForwardingRulesCommand cmd, int numRetries) { + PortForwardingRuleTO[] rules = cmd.getRules(); + + try { + ArrayList commandList = new ArrayList(); + + for (PortForwardingRuleTO rule : rules) { + if (!rule.revoked()) { + manageDstNatRule(commandList, PaloAltoPrimative.ADD, rule); + } else { + manageDstNatRule(commandList, PaloAltoPrimative.DELETE, rule); + } + } + + boolean status = requestWithCommit(commandList); + + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + + if (numRetries > 0 && refreshPaloAltoConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + + + // IMPLEMENTATIONS... + + + /* + * Private interface implementation + */ + + private String genPrivateInterfaceName(long vlanTag) { + return _privateInterface+"."+Long.toString(vlanTag); + } + + public boolean managePrivateInterface(ArrayList cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateGateway) throws ExecutionException { + String interfaceName = genPrivateInterfaceName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Private sub-interface exists: "+interfaceName+", "+result); + return result; + + case ADD: + if (managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { + return true; + } + + // add cmds + // add sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + a_sub_params.put("element", ""+privateVlanTag+""+_pingManagementProfile+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add sub-interface to VR... + Map a_vr_params = new HashMap(); + a_vr_params.put("type", "config"); + a_vr_params.put("action", "set"); + a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface"); + a_vr_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); + + // add sub-interface to vsys... + Map a_vsys_params = new HashMap(); + a_vsys_params.put("type", "config"); + a_vsys_params.put("action", "set"); + a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); + a_vsys_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); + + // add sub-interface to zone... + Map a_zone_params = new HashMap(); + a_zone_params.put("type", "config"); + a_zone_params.put("action", "set"); + a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3"); + a_zone_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); + + return true; + + case DELETE: + if (!managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) { + return true; + } + + // add cmds to the list + // delete sub-interface from zone... + Map d_zone_params = new HashMap(); + d_zone_params.put("type", "config"); + d_zone_params.put("action", "delete"); + d_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_zone_params)); + + // delete sub-interface from vsys... + Map d_vsys_params = new HashMap(); + d_vsys_params.put("type", "config"); + d_vsys_params.put("action", "delete"); + d_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vsys_params)); + + // delete sub-interface from VR... + Map d_vr_params = new HashMap(); + d_vr_params.put("type", "config"); + d_vr_params.put("action", "delete"); + d_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface/member[text()='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vr_params)); + + // delete sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Public Interface implementation + */ + + private String genPublicInterfaceName(Long id) { + return _publicInterface+"."+Long.toString(id); + } + + public boolean managePublicInterface(ArrayList cmdList, PaloAltoPrimative prim, Long publicVlanTag, String publicIp, long privateVlanTag) throws ExecutionException { + String interfaceName; + if (publicVlanTag == null) { + interfaceName = genPublicInterfaceName(new Long("9999")); + } else { + interfaceName = genPublicInterfaceName(publicVlanTag); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Public sub-interface & IP exists: "+interfaceName+" : "+publicIp+", "+result); + return result; + + case ADD: + if (managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { + return true; + } + + // add IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add sub-interface to VR (does nothing if already done)... + Map a_vr_params = new HashMap(); + a_vr_params.put("type", "config"); + a_vr_params.put("action", "set"); + a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface"); + a_vr_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params)); + + // add sub-interface to vsys (does nothing if already done)... + Map a_vsys_params = new HashMap(); + a_vsys_params.put("type", "config"); + a_vsys_params.put("action", "set"); + a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface"); + a_vsys_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params)); + + // add sub-interface to zone (does nothing if already done)... + Map a_zone_params = new HashMap(); + a_zone_params.put("type", "config"); + a_zone_params.put("action", "set"); + a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_publicZone+"']/network/layer3"); + a_zone_params.put("element", ""+interfaceName+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params)); + + return true; + + case DELETE: + if (!managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) { + return true; + } + + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Source NAT rule implementation + */ + + private String genSrcNatRuleName(Long privateVlanTag) { + return "src_nat."+Long.toString(privateVlanTag); + } + + public boolean manageSrcNatRule(ArrayList cmdList, PaloAltoPrimative prim, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway) throws ExecutionException { + String publicInterfaceName; + if (publicVlanTag == null) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(publicVlanTag); + } + String srcNatName = genSrcNatRuleName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Source NAT exists: "+srcNatName+", "+result); + return result; + + case ADD: + if (manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { + return true; + } + + String xml = ""; + xml += ""+_privateZone+""; + xml += ""+_publicZone+""; + xml += ""+privateGateway+""; + xml += "any"; + xml += "any"; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""; + xml += ""+publicIp+""; + xml += ""+publicInterfaceName+""; + xml += ""; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Destination NAT rules (Port Forwarding) implementation + */ + private String genDstNatRuleName(String publicIp, long id) { + return "dst_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id); + } + + public boolean manageDstNatRule(ArrayList cmdList, PaloAltoPrimative prim, PortForwardingRuleTO rule) throws ExecutionException { + String publicIp = rule.getSrcIp(); + String dstNatName = genDstNatRuleName(publicIp, rule.getId()); + + String publicInterfaceName; + String publicVlanTag = rule.getSrcVlanTag(); + if (publicVlanTag == null || publicVlanTag.equals("untagged")) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Destination NAT exists: "+dstNatName+", "+result); + return result; + + case ADD: + if (manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // build source service xml + String srcService; + String protocol = rule.getProtocol(); + int[] srcPortRange = rule.getSrcPortRange(); + if (srcPortRange != null) { + String portRange; + if (srcPortRange.length == 1 || srcPortRange[0] == srcPortRange[1]) { + portRange = String.valueOf(srcPortRange[0]); + } else { + portRange = String.valueOf(srcPortRange[0])+"-"+String.valueOf(srcPortRange[1]); + } + manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); + srcService = genServiceName(protocol, portRange, null); + } else { + // no equivalent config in PA, so allow all traffic... + srcService = "any"; + } + + // build destination port xml (single port limit in PA) + String dstPortXML = ""; + int[] dstPortRange = rule.getDstPortRange(); + if (dstPortRange != null) { + dstPortXML = ""+dstPortRange[0]+""; + } + + // add public IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add the destination nat rule for the public IP + String xml = ""; + xml += ""+_publicZone+""; + xml += ""+_publicZone+""; + xml += "any"; + xml += ""+publicIp+""; + xml += ""+srcService+""; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""+rule.getDstIp()+""+dstPortXML+""; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // determine if we need to delete the ip from the interface as well... + Map c_params = new HashMap(); + c_params.put("type", "config"); + c_params.put("action", "get"); + c_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='"+publicIp+"']]"); + String c_response = request(PaloAltoMethod.GET, c_params); + + String count = ""; + NodeList response_body; + Document doc = getDocument(c_response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (response_body.getLength() > 0 && response_body.item(0).getAttributes().getLength() > 0) { + count = response_body.item(0).getAttributes().getNamedItem("count").getTextContent(); + } + + // delete the dst nat rule + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + if (!count.equals("") && Integer.parseInt(count) == 1) { // this dst nat rule is the last, so remove the ip... + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + } + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + + /* + * Static NAT rule implementation + */ + private String genStcNatRuleName(String publicIp, long id) { + return "stc_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id); + } + + public boolean manageStcNatRule(ArrayList cmdList, PaloAltoPrimative prim, StaticNatRuleTO rule) throws ExecutionException { + String publicIp = rule.getSrcIp(); + String stcNatName = genStcNatRuleName(publicIp, rule.getId()); + + String publicInterfaceName; + String publicVlanTag = rule.getSrcVlanTag(); + if (publicVlanTag == null || publicVlanTag.equals("untagged")) { + publicInterfaceName = genPublicInterfaceName(new Long("9999")); + } else { + publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag)); + } + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Static NAT exists: "+stcNatName+", "+result); + return result; + + case ADD: + if (manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // add public IP to the sub-interface + Map a_sub_params = new HashMap(); + a_sub_params.put("type", "config"); + a_sub_params.put("action", "set"); + a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip"); + a_sub_params.put("element", ""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params)); + + // add the static nat rule for the public IP + String xml = ""; + xml += ""+_publicZone+""; + xml += ""+_publicZone+""; + xml += "any"; + xml += ""+publicIp+""; + xml += "any"; + xml += "ipv4"; + xml += ""+publicInterfaceName+""; + xml += ""+rule.getDstIp()+""; + + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + // delete the static nat rule + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + // delete IP from sub-interface... + Map d_sub_params = new HashMap(); + d_sub_params.put("type", "config"); + d_sub_params.put("action", "delete"); + d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + /* + * Firewall rule implementation + */ + + private String genFirewallRuleName(long id) { + return "policy_"+Long.toString(id); + } + + public boolean manageFirewallRule(ArrayList cmdList, PaloAltoPrimative prim, FirewallRuleTO rule) throws ExecutionException { + String ruleName = genFirewallRuleName(rule.getId()); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Firewall policy exists: "+ruleName+", "+result); + return result; + + case ADD: + if (manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + String srcZone; + String dstZone; + String dstAddressXML; + String appXML; + String serviceXML; + + String protocol = rule.getProtocol(); + + // Only ICMP will use an Application, so others will be any. + if (protocol.equals(Protocol.ICMP.toString())) { + appXML = "icmppingtraceroute"; // use the default icmp applications... + } else { + appXML = "any"; + } + + // Only TCP and UDP will use a Service, others will use any. + if (protocol.equals(Protocol.TCP.toString()) || protocol.equals(Protocol.UDP.toString())) { + String portRange; + if (rule.getSrcPortRange() != null) { + int startPort = rule.getSrcPortRange()[0]; + int endPort = rule.getSrcPortRange()[1]; + if (startPort == endPort) { + portRange = String.valueOf(startPort); + } else { + portRange = String.valueOf(startPort)+"-"+String.valueOf(endPort); + } + manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null); + serviceXML = ""+genServiceName(protocol, portRange, null)+""; + } else { + // no equivalent config in PA, so allow all traffic... + serviceXML = "any"; + } + } else { + serviceXML = "any"; + } + + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { // Network egress rule + srcZone = _privateZone; + dstZone = _publicZone; + dstAddressXML = "any"; + } else { + srcZone = _publicZone; + dstZone = _privateZone; + dstAddressXML = ""+rule.getSrcIp()+""; + } + + // build the source cidr xml + String srcCidrXML = ""; + List ruleSrcCidrList = rule.getSourceCidrList(); + if (ruleSrcCidrList.size() > 0) { // a cidr was entered, modify as needed... + for (int i = 0; i < ruleSrcCidrList.size(); i++) { + if (ruleSrcCidrList.get(i).trim().equals("0.0.0.0/0")) { // allow any + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { + srcCidrXML += ""+getPrivateSubnet(rule.getSrcVlanTag())+""; + } else { + srcCidrXML += "any"; + } + } else { + srcCidrXML += ""+ruleSrcCidrList.get(i).trim()+""; + } + } + } else { // no cidr was entered, so allow ALL according to firewall rule type + if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { + srcCidrXML = ""+getPrivateSubnet(rule.getSrcVlanTag())+""; + } else { + srcCidrXML = "any"; + } + } + + String xml = ""; + xml += ""+srcZone+""; + xml += ""+dstZone+""; + xml += ""+srcCidrXML+""; + xml += ""+dstAddressXML+""; + xml += ""+appXML+""; + xml += ""+serviceXML+""; + xml += "allow"; + xml += "no"; + xml += "no"; + if (_threatProfile != null) { // add the threat profile if it exists + xml += ""+_threatProfile+""; + } + if (_logProfile != null) { // add the log profile if it exists + xml += ""+_logProfile+""; + } + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + + /* + * Usage + */ + + + + /* + * Helper config functions + */ + + // ensure guest network isolation + private String genNetworkIsolationName(long privateVlanTag) { + return "isolate_"+Long.toString(privateVlanTag); + } + + public boolean manageNetworkIsolation(ArrayList cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateSubnet, String privateGateway) throws ExecutionException { + String ruleName = genNetworkIsolationName(privateVlanTag); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Firewall policy exists: "+ruleName+", "+result); + return result; + + case ADD: + if (manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { + return true; + } + + String xml = ""; + xml += ""+_privateZone+""; + xml += ""+_privateZone+""; + xml += ""+privateSubnet+""; + xml += ""+privateGateway+""; + xml += "any"; + xml += "any"; + xml += "deny"; + xml += "no"; + xml += "yes"; + + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + a_params.put("element", xml); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params)); + + return true; + + case DELETE: + if (!manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) { + return true; + } + + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + // make the interfaces pingable for basic network troubleshooting + public boolean managePingProfile(ArrayList cmdList, PaloAltoPrimative prim) throws ExecutionException { + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Management profile exists: "+_pingManagementProfile+", "+result); + return result; + + case ADD: + if (managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { + return true; + } + + // add ping profile... + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + a_params.put("element", "yes"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); + + return true; + + case DELETE: + if (!managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) { + return true; + } + + // delete ping profile... + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + + private String genServiceName(String protocol, String dstPorts, String srcPorts) { + String name; + if (srcPorts == null) { + name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.'); + } else { + name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.')+"_"+srcPorts.replace(',', '.'); + } + return name; + } + + public boolean manageService(ArrayList cmdList, PaloAltoPrimative prim, String protocol, String dstPorts, String srcPorts) throws ExecutionException { + String serviceName = genServiceName(protocol, dstPorts, srcPorts); + + switch (prim) { + + case CHECK_IF_EXISTS: + // check if one exists already + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + String response = request(PaloAltoMethod.GET, params); + boolean result = (validResponse(response) && responseNotEmpty(response)); + s_logger.debug("Service exists: "+serviceName+", "+result); + return result; + + case ADD: + if (manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { + return true; + } + + String dstPortXML = ""+dstPorts+""; + String srcPortXML = ""; + if (srcPorts != null) { + srcPortXML = ""+srcPorts+""; + } + + // add ping profile... + Map a_params = new HashMap(); + a_params.put("type", "config"); + a_params.put("action", "set"); + a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + a_params.put("element", "<"+protocol.toLowerCase()+">"+dstPortXML+srcPortXML+""); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params)); + + return true; + + case DELETE: + if (!manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) { + return true; + } + + // delete ping profile... + Map d_params = new HashMap(); + d_params.put("type", "config"); + d_params.put("action", "delete"); + d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']"); + cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params)); + + return true; + + default: + s_logger.debug("Unrecognized command."); + return false; + } + } + + private String getPrivateSubnet(String vlan) throws ExecutionException { + String _interfaceName = genPrivateInterfaceName(Long.valueOf(vlan).longValue()); + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+_interfaceName+"']/ip/entry"); + String response = request(PaloAltoMethod.GET, params); + if (validResponse(response) && responseNotEmpty(response)) { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/entry"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (response_body.getLength() > 0) { + return response_body.item(0).getAttributes().getNamedItem("name").getTextContent(); + } + } + return null; + } + + + /* + * XML API commands + */ + + /* Function to make calls to the Palo Alto API. */ + /* All API calls will end up going through this function. */ + protected String request(PaloAltoMethod method, Map params) throws ExecutionException { + if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) { + throw new ExecutionException("Invalid http method used to access the Palo Alto API."); + } + + String responseBody = ""; + String debug_msg = "Palo Alto Request\n"; + + // a GET method... + if (method == PaloAltoMethod.GET) { + String queryString = "?"; + for (String key : params.keySet()) { + if (!queryString.equals("?")) { + queryString = queryString + "&"; + } + try { + queryString = queryString + key+"="+URLEncoder.encode(params.get(key), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new ExecutionException(e.getMessage()); + } + } + if (_key != null) { + queryString = queryString + "&key="+_key; + } + + try { + debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + URLDecoder.decode(queryString, "UTF-8") + "\n"; + } catch (UnsupportedEncodingException e) { + debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + queryString + "\n"; + } + + + HttpGet get_request = new HttpGet("https://" + _ip + _apiUri + queryString); + ResponseHandler responseHandler = new BasicResponseHandler(); + try { + responseBody = _httpclient.execute(get_request, responseHandler); + } catch (IOException e) { + throw new ExecutionException(e.getMessage()); + } + } + + // a POST method... + if (method == PaloAltoMethod.POST) { + List nvps = new ArrayList (); + for (String key : params.keySet()) { + nvps.add(new BasicNameValuePair(key, params.get(key))); + } + if (_key != null) { + nvps.add(new BasicNameValuePair("key", _key)); + } + + debug_msg = debug_msg + "POST request: https://" + _ip + _apiUri + "\n"; + for (NameValuePair nvp : nvps) { + debug_msg = debug_msg + "param: "+nvp.getName()+", "+nvp.getValue() + "\n"; + } + + HttpPost post_request = new HttpPost("https://" + _ip + _apiUri); + try { + post_request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8)); + } catch (UnsupportedEncodingException e) { + throw new ExecutionException(e.getMessage()); + } + ResponseHandler responseHandler = new BasicResponseHandler(); + try { + responseBody = _httpclient.execute(post_request, responseHandler); + } catch (IOException e) { + throw new ExecutionException(e.getMessage()); + } + } + + debug_msg = debug_msg + prettyFormat(responseBody); + debug_msg = debug_msg + "\n" + responseBody.replace("\"", "\\\"") + "\n\n"; // test cases + //s_logger.debug(debug_msg); // this can be commented if we don't want to show each request in the log. + + return responseBody; + } + + /* Used for requests that require polling to get a result (eg: commit) */ + private String requestWithPolling(PaloAltoMethod method, Map params) throws ExecutionException { + String job_id; + String job_response = request(method, params); + Document doc = getDocument(job_response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']/result/job/text()"); + job_id = (String) expr.evaluate(doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (job_id.length() > 0) { + boolean finished = false; + Map job_params = new HashMap(); + job_params.put("type", "op"); + job_params.put("cmd", ""+job_id+""); + + while (!finished) { + String job_status; + String response = request(PaloAltoMethod.GET, job_params); + Document job_doc = getDocument(response); + XPath job_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/status/text()"); + job_status = (String) expr.evaluate(job_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (job_status.equals("FIN")) { + finished = true; + String job_result; + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/result/text()"); + job_result = (String) expr.evaluate(job_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (!job_result.equals("OK")) { + NodeList job_details; + try { + XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/details/line"); + job_details = (NodeList) expr.evaluate(job_doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + String error = ""; + for (int i = 0; i < job_details.getLength(); i++) { + error = error + job_details.item(i).getTextContent() + "\n"; + } + throw new ExecutionException(error); + } + return response; + } else { + try { + Thread.sleep(2000); // poll periodically for the status of the async job... + } catch (InterruptedException e) { /* do nothing */ } + } + } + } else { + return job_response; + } + return null; + } + + /* Runs a sequence of commands and attempts to commit at the end. */ + /* Uses the Command pattern to enable overriding of the response handling if needed. */ + private synchronized boolean requestWithCommit(ArrayList commandList) throws ExecutionException { + boolean result = true; + + if (commandList.size() > 0) { + // CHECK IF THERE IS PENDING CHANGES THAT HAVE NOT BEEN COMMITTED... + String pending_changes; + Map check_params = new HashMap(); + check_params.put("type", "op"); + check_params.put("cmd", ""); + String check_response = request(PaloAltoMethod.GET, check_params); + Document check_doc = getDocument(check_response); + XPath check_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = check_xpath.compile("/response[@status='success']/result/text()"); + pending_changes = (String) expr.evaluate(check_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (pending_changes.equals("yes")) { + throw new ExecutionException("The Palo Alto has uncommited changes, so no changes can be made. Try again later or contact your administrator."); + } else { + // ADD A CONFIG LOCK TO CAPTURE THE PALO ALTO RESOURCE + String add_lock_status; + Map add_lock_params = new HashMap(); + add_lock_params.put("type", "op"); + add_lock_params.put("cmd", ""); + String add_lock_response = request(PaloAltoMethod.GET, add_lock_params); + Document add_lock_doc = getDocument(add_lock_response); + XPath add_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = add_lock_xpath.compile("/response[@status='success']/result/text()"); + add_lock_status = (String) expr.evaluate(add_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (add_lock_status.length() == 0) { + throw new ExecutionException("The Palo Alto is locked, no changes can be made at this time."); + } + + try { + // RUN THE SEQUENCE OF COMMANDS + for (IPaloAltoCommand command : commandList) { + result = (result && command.execute()); // run commands and modify result boolean + } + + // COMMIT THE CHANGES (ALSO REMOVES CONFIG LOCK) + String commit_job_id; + Map commit_params = new HashMap(); + commit_params.put("type", "commit"); + commit_params.put("cmd", ""); + String commit_response = requestWithPolling(PaloAltoMethod.GET, commit_params); + Document commit_doc = getDocument(commit_response); + XPath commit_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = commit_xpath.compile("/response[@status='success']/result/job/id/text()"); + commit_job_id = (String) expr.evaluate(commit_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (commit_job_id.length() == 0) { // no commit was done, so release the lock... + // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE + String remove_lock_status; + Map remove_lock_params = new HashMap(); + remove_lock_params.put("type", "op"); + remove_lock_params.put("cmd", ""); + String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); + Document remove_lock_doc = getDocument(remove_lock_response); + XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); + remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (remove_lock_status.length() == 0) { + throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); + } + } + + } catch (ExecutionException ex) { + // REVERT TO RUNNING + String revert_job_id; + Map revert_params = new HashMap(); + revert_params.put("type", "op"); + revert_params.put("cmd", "running-config.xml"); + requestWithPolling(PaloAltoMethod.GET, revert_params); + + // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE + String remove_lock_status; + Map remove_lock_params = new HashMap(); + remove_lock_params.put("type", "op"); + remove_lock_params.put("cmd", ""); + String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params); + Document remove_lock_doc = getDocument(remove_lock_response); + XPath remove_lock_xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()"); + remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (remove_lock_status.length() == 0) { + throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!"); + } + + throw ex; // Bubble up the reason we reverted... + } + + return result; + } + } else { + return true; // nothing to do + } + } + + /* A default response handler to validate that the request was successful. */ + public boolean validResponse(String response) throws ExecutionException { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (response_body.getLength() > 0) { + return true; + } else { + NodeList error_details; + try { + XPathExpression expr = xpath.compile("/response/msg/line/line"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + if (error_details.getLength() == 0) { + try { + XPathExpression expr = xpath.compile("/response/msg/line"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (error_details.getLength() == 0) { + try { + XPathExpression expr = xpath.compile("/response/result/msg"); + error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + } + } + String error = ""; + for (int i = 0; i < error_details.getLength(); i++) { + error = error + error_details.item(i).getTextContent() + "\n"; + } + throw new ExecutionException(error); + } + } + + /* Validate that the response is not empty. */ + public boolean responseNotEmpty(String response) throws ExecutionException { + NodeList response_body; + Document doc = getDocument(response); + XPath xpath = XPathFactory.newInstance().newXPath(); + try { + XPathExpression expr = xpath.compile("/response[@status='success']"); + response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new ExecutionException(e.getCause().getMessage()); + } + + if (response_body.getLength() > 0 && + (!response_body.item(0).getTextContent().equals("") || + (response_body.item(0).hasChildNodes() && response_body.item(0).getFirstChild().hasChildNodes()))) { + return true; + } else { + return false; + } + } + + /* Get the type of interface from the PA device. */ + private String getInterfaceType(String interface_name) throws ExecutionException { + String[] types = { InterfaceType.ETHERNET.toString(), InterfaceType.AGGREGATE.toString() }; + for (String type : types) { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/network/interface/"+type+"/entry[@name='"+interface_name+"']"); + String ethernet_response = request(PaloAltoMethod.GET, params); + if (validResponse(ethernet_response) && responseNotEmpty(ethernet_response)) { + return type; + } + } + return ""; + } + + /* Get the threat profile from the server if it exists. */ + private boolean getThreatProfile(String profile) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/profile-group/entry[@name='"+profile+"']"); + String response = request(PaloAltoMethod.GET, params); + return (validResponse(response) && responseNotEmpty(response)); + } + + /* Get the log profile from the server if it exists. */ + private boolean getLogProfile(String profile) throws ExecutionException { + Map params = new HashMap(); + params.put("type", "config"); + params.put("action", "get"); + params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/log-settings/profiles/entry[@name='"+profile+"']"); + String response = request(PaloAltoMethod.GET, params); + return (validResponse(response) && responseNotEmpty(response)); + } + + /* Command Interface */ + public interface IPaloAltoCommand { + public boolean execute() throws ExecutionException; + } + + /* Command Abstract */ + private abstract class AbstractPaloAltoCommand implements IPaloAltoCommand { + PaloAltoMethod method; + Map params; + + public AbstractPaloAltoCommand() {} + + public AbstractPaloAltoCommand(PaloAltoMethod method, Map params) { + this.method = method; + this.params = params; + } + + public boolean execute() throws ExecutionException { + String response = request(method, params); + return validResponse(response); + } + } + + /* Implement the default functionality */ + private class DefaultPaloAltoCommand extends AbstractPaloAltoCommand { + public DefaultPaloAltoCommand(PaloAltoMethod method, Map params) { + super(method, params); + } + } + + + /* + * Misc + */ + + private String genIpIdentifier(String ip) { + return ip.replace('.', '-').replace('/', '-'); + } + + private Protocol getProtocol(String protocolName) throws ExecutionException { + protocolName = protocolName.toLowerCase(); + + try { + return Protocol.valueOf(protocolName); + } catch (Exception e) { + throw new ExecutionException("Invalid protocol: " + protocolName); + } + } + + private Document getDocument(String xml) throws ExecutionException { + StringReader xmlReader = new StringReader(xml); + InputSource xmlSource = new InputSource(xmlReader); + Document doc = null; + + try { + doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlSource); + } catch (Exception e) { + s_logger.error(e); + throw new ExecutionException(e.getMessage()); + } + + if (doc == null) { + throw new ExecutionException("Failed to parse xml " + xml); + } else { + return doc; + } + } + + private String prettyFormat(String input) { + int indent = 4; + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", indent); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(xmlInput, xmlOutput); + return xmlOutput.getWriter().toString(); + } catch (Throwable e) { + try { + Source xmlInput = new StreamSource(new StringReader(input)); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent)); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.transform(xmlInput, xmlOutput); + return xmlOutput.getWriter().toString(); + } catch(Throwable t) { + return input; + } + } + } + + //@Override + public void setName(String name) { + // TODO Auto-generated method stub + + } + + //@Override + public void setConfigParams(Map params) { + // TODO Auto-generated method stub + + } + + //@Override + public Map getConfigParams() { + // TODO Auto-generated method stub + return null; + } + + //@Override + public int getRunLevel() { + // TODO Auto-generated method stub + return 0; + } + + //@Override + public void setRunLevel(int level) { + // TODO Auto-generated method stub + + } + +} diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.java b/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.java new file mode 100644 index 00000000000..7dfec92cbe6 --- /dev/null +++ b/plugins/network-elements/palo-alto/src/com/cloud/network/utils/HttpClientWrapper.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 com.cloud.network.utils; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.apache.http.client.HttpClient; +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.impl.client.DefaultHttpClient; + +import java.io.*; + +public class HttpClientWrapper { + + public static HttpClient wrapClient(HttpClient base) { + try { + SSLContext ctx = SSLContext.getInstance("TLS"); + X509TrustManager tm = new X509TrustManager() { + + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + X509HostnameVerifier verifier = new X509HostnameVerifier() { + + @Override + public void verify(String string, SSLSocket ssls) throws IOException { + } + + @Override + public void verify(String string, X509Certificate xc) throws SSLException { + } + + @Override + public void verify(String string, String[] strings, String[] strings1) throws SSLException { + } + + @Override + public boolean verify(String string, SSLSession ssls) { + return true; + } + }; + ctx.init(null, new TrustManager[]{tm}, null); + SSLSocketFactory ssf = new SSLSocketFactory(ctx); + ssf.setHostnameVerifier(verifier); + ClientConnectionManager ccm = base.getConnectionManager(); + SchemeRegistry sr = ccm.getSchemeRegistry(); + sr.register(new Scheme("https", ssf, 443)); + return new DefaultHttpClient(ccm, base.getParams()); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } +} diff --git a/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java new file mode 100755 index 00000000000..9a9eb6e0c2e --- /dev/null +++ b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/MockablePaloAltoResource.java @@ -0,0 +1,460 @@ +// 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.resource; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +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.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + + +public class MockablePaloAltoResource extends PaloAltoResource { + private HashMap context; + public void setMockContext(HashMap context) { + this.context = context; + } + + /* Fake the calls to the Palo Alto API */ + protected String request(PaloAltoMethod method, Map params) throws ExecutionException { + if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) { + throw new ExecutionException("Invalid http method used to access the Palo Alto API."); + } + + String response = ""; + + // 'keygen' request + if (params.containsKey("type") && params.get("type").equals("keygen")) { + response = "LUFRPT14MW5xOEo1R09KVlBZNnpnemh0VHRBOWl6TGM9bXcwM3JHUGVhRlNiY0dCR0srNERUQT09"; + } + + // 'config' requests + if (params.containsKey("type") && params.get("type").equals("config") && params.containsKey("action")) { + // action = 'get' + if (params.get("action").equals("get")) { + // get interface for type + // | public_using_ethernet + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']")) { + if (context.containsKey("public_using_ethernet") && context.get("public_using_ethernet").equals("true")) { + context.put("public_interface_type", "ethernet"); + response = "no20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64nono20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64no3033autoautoauto"; + } else { + response = ""; + } + } // | private_using_ethernet + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']")) { + if (context.containsKey("private_using_ethernet") && context.get("private_using_ethernet").equals("true")) { + context.put("private_interface_type", "ethernet"); + response = "no20060064unspecifiedunspecified1800nononounspecifiedno3011noEUI-64noautoautoauto"; + } else { + response = ""; + } + } + + // get management profile | has_management_profile + if (params.get("xpath").equals("/config/devices/entry/network/profiles/interface-management-profile/entry[@name='Ping']")) { + if (context.containsKey("has_management_profile") && context.get("has_management_profile").equals("true")) { + response = "yes"; + } else { + response = ""; + } + } + + // get public interface IP | has_public_interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.102/32']")) { + if (context.containsKey("has_public_interface") && context.get("has_public_interface").equals("true")) { + response = ""; + } else { + response = ""; + } + } + + // get private interface | has_private_interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + if (context.containsKey("has_private_interface") && context.get("has_private_interface").equals("true")) { + response = "3954Ping"; + } else { + response = ""; + } + } + + // get private interface ip + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']/ip/entry")) { + response = ""; + } + + // get source nat | has_src_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + if (context.containsKey("has_src_nat_rule") && context.get("has_src_nat_rule").equals("true")) { + response = "untrusttrust10.5.80.1/20anyanyipv4ethernet1/1.9999192.168.80.102/32ethernet1/1.9999"; + } else { + response = ""; + } + } + + // get isolation firewall rule | has_isolation_fw_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + if (context.containsKey("has_isolation_fw_rule") && context.get("has_isolation_fw_rule").equals("true")) { + response = "trusttrust10.5.80.0/2010.5.80.1anyanydenynoyes"; + } else { + response = ""; + } + } + + // get service | has_service + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='cs_tcp_80']")) { + if (context.containsKey("has_service_tcp_80") && context.get("has_service_tcp_80").equals("true")) { + response = "80"; + } else { + response = ""; + } + } + + // get egress firewall rule | has_egress_fw_rule | policy_0 + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + if (context.containsKey("has_egress_fw_rule") && context.get("has_egress_fw_rule").equals("true")) { + response = "trustuntrust10.3.96.1/20anyanycs_tcp_80allownono"; + } else { + response = ""; + } + } + + // get ingress firewall rule | has_ingress_fw_rule | policy_8 + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + if (context.containsKey("has_ingress_fw_rule") && context.get("has_ingress_fw_rule").equals("true")) { + response = "untrusttrustany192.168.80.103anycs_tcp_80allownono"; + } else { + response = ""; + } + } + + // get destination nat rule (port forwarding) | has_dst_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + if (context.containsKey("has_dst_nat_rule") && context.get("has_dst_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103cs_tcp_80ipv4ethernet1/1.999910.3.97.1588080"; + } else { + response = ""; + } + } + + // get destination nat rules (returns all dst nat rules per ip) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='192.168.80.103']]")) { + if (context.containsKey("has_dst_nat_rule") && context.get("has_dst_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103cs_tcp_80ipv4ethernet1/1.999910.3.97.1588080"; + } else { + response = ""; + } + } + + // get static nat rule | has_stc_nat_rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + if (context.containsKey("has_stc_nat_rule") && context.get("has_stc_nat_rule").equals("true")) { + response = "untrustuntrustany192.168.80.103anyipv4ethernet1/1.999910.3.97.158"; + } else { + response = ""; + } + } + + } + + // action = 'set' + if (params.get("action").equals("set")) { + // set management profile + if (params.get("xpath").equals("/config/devices/entry/network/profiles/interface-management-profile/entry[@name='Ping']")) { + response = "command succeeded"; + context.put("has_management_profile", "true"); + } + + // add private interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + response = "command succeeded"; + context.put("has_private_interface", "true"); + } + + // add public ip to public interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip")) { + response = "command succeeded"; + context.put("has_public_interface", "true"); + } + + // add private interface to zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='trust']/network/layer3")) { + response = "command succeeded"; + } + + // add public interface to zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='untrust']/network/layer3")) { + response = "command succeeded"; + } + + // set virtual router (public | private) + if (params.get("xpath").equals("/config/devices/entry/network/virtual-router/entry[@name='default']/interface")) { + response = "command succeeded"; + } + + // add interface to network (public | private) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface")) { + response = "command succeeded"; + } + + // add src nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + response = "command succeeded"; + context.put("has_src_nat_rule", "true"); + } + + // add isolation firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + response = "command succeeded"; + context.put("has_isolation_fw_rule", "true"); + } + + // add egress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + response = "command succeeded"; + context.put("has_egress_fw_rule", "true"); + } + + // add ingress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + response = "command succeeded"; + context.put("has_ingress_fw_rule", "true"); + } + + // add destination nat rule (port forwarding) + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + response = "command succeeded"; + context.put("has_dst_nat_rule", "true"); + } + + // add static nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + response = "command succeeded"; + context.put("has_stc_nat_rule", "true"); + } + + // add tcp 80 service + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='cs_tcp_80']")) { + response = "command succeeded"; + context.put("has_service_tcp_80", "true"); + } + } + + // action = 'delete' + if (params.get("action").equals("delete")) { + // remove egress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_0']")) { + response = "command succeeded"; + context.remove("has_egress_fw_rule"); + } + + // remove ingress firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='policy_8']")) { + response = "command succeeded"; + context.remove("has_ingress_fw_rule"); + } + + // remove destination nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='dst_nat.192-168-80-103_9']")) { + response = "command succeeded"; + context.remove("has_dst_nat_rule"); + } + + // remove static nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='stc_nat.192-168-80-103_0']")) { + response = "command succeeded"; + context.remove("has_dst_nat_rule"); + } + + // remove public ip from interface (dst_nat | stc_nat) + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.103/32']")) { + response = "command succeeded"; + } + + // remove isolation firewall rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='isolate_3954']")) { + response = "command succeeded"; + context.remove("has_isolation_fw_rule"); + } + + // remove source nat rule + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='src_nat.3954']")) { + response = "command succeeded"; + context.remove("has_src_nat_rule"); + } + + // remove public source nat ip + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/1']/layer3/units/entry[@name='ethernet1/1.9999']/ip/entry[@name='192.168.80.102/32']")) { + response = "command succeeded"; + context.remove("has_public_interface"); + } + + // remove private interface from the zone + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='trust']/network/layer3/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface from the virtual router + if (params.get("xpath").equals("/config/devices/entry/network/virtual-router/entry[@name='default']/interface/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface from network + if (params.get("xpath").equals("/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='ethernet1/2.3954']")) { + response = "command succeeded"; + } + + // remove private interface + if (params.get("xpath").equals("/config/devices/entry/network/interface/ethernet/entry[@name='ethernet1/2']/layer3/units/entry[@name='ethernet1/2.3954']")) { + response = "command succeeded"; + context.remove("has_private_interface"); + } + + } + } // end 'config' + + // 'op' requests + if (params.containsKey("type") && params.get("type").equals("op")) { + // check if there are pending changes + if (params.get("cmd").equals("")) { + if (context.containsKey("firewall_has_pending_changes") && context.get("firewall_has_pending_changes").equals("true")) { + response = "yes"; + } else { + response = "no"; + } + } + + // add a config lock + if (params.get("cmd").equals("")) { + response = "Successfully acquired lock. Other administrators will not be able to modify configuration for scope shared until lock is released"; + } + + // check job status + if (params.get("cmd").equals("1")) { + if (context.containsKey("simulate_commit_failure") && context.get("simulate_commit_failure").equals("true")) { + response = "2013/07/10 11:11:491adminCommitFINnoFAIL11:11:5411:11:54
Bad configCommit failed
"; + } else { + response = "2013/07/02 14:49:491adminCommitFINnoOK14:50:0214:50:02
Configuration committed successfully
"; + } + } + + // load from running config + if (params.get("cmd").equals("running-config.xml")) { + response = "Config loaded from running-config.xml"; + } + + // remove config lock + if (params.get("cmd").equals("")) { + response = "Config lock released for scope shared"; + } + } // end 'op' + + // 'commit' requests + if (params.containsKey("type") && params.get("type").equals("commit")) { + // cmd = '' + if (params.get("cmd").equals("")) { + response = "Commit job enqueued with jobid 11"; + } + } // end 'commit' + + + // print out the details into the console + if (context.containsKey("enable_console_output") && context.get("enable_console_output") == "true") { + if (params.containsKey("xpath")) { + System.out.println("XPATH("+params.get("action")+"): "+params.get("xpath")); + } + if (params.containsKey("type") && params.get("type").equals("op")) { + System.out.println("OP CMD: "+params.get("cmd")); + } + System.out.println(response+"\n"); + } + + return response; + } +} \ No newline at end of file diff --git a/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java new file mode 100755 index 00000000000..c2704734c6c --- /dev/null +++ b/plugins/network-elements/palo-alto/test/com/cloud/network/resource/PaloAltoResourceTest.java @@ -0,0 +1,507 @@ +// 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.resource; + +// test imports +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; + +// basic imports +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import javax.naming.ConfigurationException; +import javax.xml.parsers.DocumentBuilderFactory; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer; +import com.cloud.agent.api.ExternalNetworkResourceUsageCommand; +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.agent.api.StartupExternalFirewallCommand; +import com.cloud.agent.api.routing.IpAssocAnswer; +import com.cloud.agent.api.routing.IpAssocCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetStaticNatRulesCommand; +import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.PortForwardingRuleTO; +import com.cloud.agent.api.to.StaticNatRuleTO; +import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRule.State; +import com.cloud.resource.ServerResource; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.ExecutionException; +import com.cloud.utils.net.NetUtils; +import com.cloud.utils.script.Script; + +// http client handling +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.BasicResponseHandler; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.protocol.HTTP; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.net.URLDecoder; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import com.cloud.network.utils.HttpClientWrapper; + +// for prettyFormat() +import javax.xml.transform.stream.StreamSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import java.io.StringWriter; + +public class PaloAltoResourceTest { + // configuration data + private String _test_name = "PaloAltoTestDevice"; + private String _test_zoneId = "TestZone"; + private String _test_ip = "192.168.80.2"; + private String _test_username = "admin"; + private String _test_password = "admin"; + private String _test_publicInterface = "ethernet1/1"; + private String _test_privateInterface = "ethernet1/2"; + private String _test_publicZone = "untrust"; + private String _test_privateZone = "trust"; + private String _test_virtualRouter = "default"; + + MockablePaloAltoResource _resource; + Map _resource_params; + HashMap _context; + + @Before + public void setUp() { + _resource = new MockablePaloAltoResource(); + _resource_params = new HashMap(); // params to be passed to configure() + _resource_params.put("name", _test_name); + _resource_params.put("zoneId", _test_zoneId); + _resource_params.put("ip", _test_ip); + _resource_params.put("username", _test_username); + _resource_params.put("password", _test_password); + _resource_params.put("publicinterface", _test_publicInterface); + _resource_params.put("privateinterface", _test_privateInterface); + _resource_params.put("publicnetwork", _test_publicZone); + _resource_params.put("privatenetwork", _test_privateZone); + _resource_params.put("pavr", _test_virtualRouter); + _resource_params.put("guid", "aaaaa-bbbbb-ccccc"); + + _context = new HashMap(); // global context + _context.put("name", _test_name); + _context.put("zone_id", _test_zoneId); + _context.put("ip", _test_ip); + _context.put("username", _test_username); + _context.put("password", _test_password); + _context.put("public_interface", _test_publicInterface); + _context.put("private_interface", _test_privateInterface); + _context.put("public_zone", _test_publicZone); + _context.put("private_zone", _test_privateZone); + _context.put("pa_vr", _test_virtualRouter); + // -- + _context.put("public_using_ethernet", "true"); + _context.put("private_using_ethernet", "true"); + _context.put("has_management_profile", "true"); + _context.put("enable_console_output", "false"); // CHANGE TO "true" TO ENABLE CONSOLE LOGGING OF TESTS + _resource.setMockContext(_context); + } + + @Test (expected=ConfigurationException.class) + public void resourceConfigureFailure() throws ConfigurationException { + _resource.configure("PaloAltoResource", new HashMap()); + } + + @Test + public void resourceConfigureWithoutManagementProfile() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: resourceConfigureWithoutManagementProfile"); + System.out.println("---------------------------------------------------"); + } + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test + public void resourceConfigureWithManagementProfile() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: resourceConfigureWithManagementProfile"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test (expected=ConfigurationException.class) + public void simulateFirewallNotConfigurable() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: simulateFirewallNotConfigurable"); + System.out.println("---------------------------------------------------"); + } + _context.put("firewall_has_pending_changes", "true"); + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test (expected=ConfigurationException.class) + public void simulateFirewallCommitFailure() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: simulateFirewallCommitFailure"); + System.out.println("---------------------------------------------------"); + } + _context.put("simulate_commit_failure", "true"); + _context.remove("has_management_profile"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + } + + @Test + public void testInitialize() throws ConfigurationException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: testInitialization"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + + StartupCommand[] sc = _resource.initialize(); + assertTrue(sc.length == 1); + assertTrue("aaaaa-bbbbb-ccccc".equals(sc[0].getGuid())); + assertTrue("PaloAltoTestDevice".equals(sc[0].getName())); + assertTrue("TestZone".equals(sc[0].getDataCenter())); + } + + @Test // implement public & private interfaces, source nat, guest network + public void implementGuestNetwork() throws ConfigurationException, ExecutionException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: implementGuestNetwork"); + System.out.println("---------------------------------------------------"); + } + _resource.configure("PaloAltoResource", _resource_params); + + IpAddressTO ip = new IpAddressTO(Long.valueOf("1"), "192.168.80.102", true, false, true, "untagged", null, null, null, 100, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, "10.3.96.1"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, "3954"); + + IpAssocAnswer answer = (IpAssocAnswer) _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test // remove public & private interface details, source nat, guest network + public void shutdownGuestNetwork() throws ConfigurationException, ExecutionException { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: shutdownGuestNetwork"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + IpAddressTO ip = new IpAddressTO(Long.valueOf("1"), "192.168.80.102", false, false, true, "untagged", null, null, null, 100, false); + IpAddressTO[] ips = new IpAddressTO[1]; + ips[0] = ip; + IpAssocCommand cmd = new IpAssocCommand(ips); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY, "10.3.96.1"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, "3954"); + + IpAssocAnswer answer = (IpAssocAnswer) _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addIngressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addIngressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + List cidrList = new ArrayList(); + cidrList.add("0.0.0.0/0"); + FirewallRuleTO active = new FirewallRuleTO(8, + null, "192.168.80.103", "tcp", 80, 80, false, false, + FirewallRule.Purpose.Firewall, cidrList, null, null); + rules.add(active); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeIngressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeIngressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_ingress_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + FirewallRuleTO revoked = new FirewallRuleTO(8, + null, "192.168.80.103", "tcp", 80, 80, true, false, + FirewallRule.Purpose.Firewall, null, null, null); + rules.add(revoked); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addEgressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addEgressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + List cidrList = new ArrayList(); + cidrList.add("0.0.0.0/0"); + FirewallRuleVO activeVO = new FirewallRuleVO(null, null, 80, 80, "tcp", + 1, 1, 1, Purpose.Firewall, cidrList, null, + null, null, FirewallRule.TrafficType.Egress); + FirewallRuleTO active = new FirewallRuleTO(activeVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress); + rules.add(active); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeEgressFirewallRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeEgressFirewallRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_egress_fw_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + FirewallRuleVO revokedVO = new FirewallRuleVO(null, null, 80, 80, "tcp", + 1, 1, 1, Purpose.Firewall, null, null, null, null, FirewallRule.TrafficType.Egress); + revokedVO.setState(State.Revoke); + FirewallRuleTO revoked = new FirewallRuleTO(revokedVO, Long.toString(vlanId), null, Purpose.Firewall, FirewallRule.TrafficType.Egress); + rules.add(revoked); + + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addStaticNatRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addStaticNatRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + StaticNatRuleTO active = new StaticNatRuleTO(0, "192.168.80.103", null, + null, "10.3.97.158", null, null, null, false, false); + rules.add(active); + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rules, null); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removeStaticNatRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removeStaticNatRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_stc_nat_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + StaticNatRuleTO revoked = new StaticNatRuleTO(0, "192.168.80.103", null, + null, "10.3.97.158", null, null, null, true, false); + rules.add(revoked); + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rules, null); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void addPortForwardingRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: addPortForwardingRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + PortForwardingRuleTO active = new PortForwardingRuleTO(9, "192.168.80.103", 80, + 80, "10.3.97.158", 8080, 8080, "tcp", false, false); + rules.add(active); + + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void removePortForwardingRule() throws ConfigurationException, Exception { + if (_context.containsKey("enable_console_output") && _context.get("enable_console_output").equals("true")) { + System.out.println("\nTEST: removePortForwardingRule"); + System.out.println("---------------------------------------------------"); + } + _context.put("has_public_interface", "true"); + _context.put("has_private_interface", "true"); + _context.put("has_src_nat_rule", "true"); + _context.put("has_isolation_fw_rule", "true"); + _context.put("has_service_tcp_80", "true"); + _context.put("has_dst_nat_rule", "true"); + _resource.setMockContext(_context); + _resource.configure("PaloAltoResource", _resource_params); + + long vlanId = 3954; + List rules = new ArrayList(); + PortForwardingRuleTO revoked = new PortForwardingRuleTO(9, "192.168.80.103", 80, + 80, "10.3.97.158", 8080, 8080, "tcp", true, false); + rules.add(revoked); + + SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rules); + cmd.setContextParam(NetworkElementCommand.GUEST_VLAN_TAG, Long.toString(vlanId)); + cmd.setContextParam(NetworkElementCommand.GUEST_NETWORK_CIDR, "10.3.96.1/20"); + + Answer answer = _resource.executeRequest(cmd); + assertTrue(answer.getResult()); + } +} + diff --git a/plugins/network-elements/stratosphere-ssp/src/org/apache/cloudstack/network/dao/SspCredentialVO.java b/plugins/network-elements/stratosphere-ssp/src/org/apache/cloudstack/network/dao/SspCredentialVO.java index 1780296841c..d828f64e645 100644 --- a/plugins/network-elements/stratosphere-ssp/src/org/apache/cloudstack/network/dao/SspCredentialVO.java +++ b/plugins/network-elements/stratosphere-ssp/src/org/apache/cloudstack/network/dao/SspCredentialVO.java @@ -23,6 +23,8 @@ import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.utils.db.Encrypt; + @Entity @Table(name="external_stratosphere_ssp_credentials") public class SspCredentialVO { @@ -38,6 +40,7 @@ public class SspCredentialVO { @Column(name="username") private String username; + @Encrypt @Column(name="password") private String password; diff --git a/plugins/pom.xml b/plugins/pom.xml index 4f193bcde0c..d0817a2ddc8 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -44,6 +44,7 @@ network-elements/elastic-loadbalancer network-elements/ovs network-elements/juniper-contrail + network-elements/palo-alto network-elements/nicira-nvp network-elements/bigswitch-vns network-elements/midonet 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 2db0a0879f1..327d87c6722 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 @@ -22,6 +22,8 @@ import java.util.UUID; import javax.inject.Inject; +import com.cloud.storage.*; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.log4j.Logger; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; @@ -58,11 +60,6 @@ import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.configuration.Config; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.dao.HostDao; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.ResizeVolumePayload; -import com.cloud.storage.Storage; -import com.cloud.storage.StorageManager; -import com.cloud.storage.StoragePool; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; @@ -249,8 +246,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { CreateCmdResult result = null; try { - DataTO snapshotTO = snapshot.getTO(); - + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)snapshot.getTO(); + Object payload = snapshot.getPayload(); + if (payload != null && payload instanceof CreateSnapshotPayload) { + CreateSnapshotPayload snapshotPayload = (CreateSnapshotPayload)payload; + snapshotTO.setQuiescevm(snapshotPayload.getQuiescevm()); + } CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO); EndPoint ep = epSelector.select(snapshot); 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 a02474d7371..1c726cd110b 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 @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.storage.datastore.driver; +import java.text.NumberFormat; import java.util.List; import javax.inject.Inject; @@ -278,8 +279,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { Long minIops = volumeInfo.getMinIops(); Long maxIops = volumeInfo.getMaxIops(); - if (minIops == null || minIops <= 0 || - maxIops == null || maxIops <= 0) { + if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) { long defaultMaxIops = getDefaultMaxIops(storagePoolId); iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops)); @@ -288,10 +288,20 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops())); } - long volumeSize = volumeInfo.getSize() * 2; // in reality, use a multiplier that's at cluster-level scope + long volumeSize = volumeInfo.getSize(); + Integer hypervisorSnapshotReserve = volumeInfo.getHypervisorSnapshotReserve(); + + if (hypervisorSnapshotReserve != null) { + if (hypervisorSnapshotReserve < 25) { + hypervisorSnapshotReserve = 25; + } + + volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f); + } long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, - getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, volumeInfo.getSize().toString(), + 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); @@ -410,8 +420,7 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId); if (!sfAccountExists(sfAccountName, sfConnection)) { - SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, - sfConnection); + SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName, sfConnection); updateCsDbWithAccountInfo(account.getId(), sfAccount); } diff --git a/pom.xml b/pom.xml index 6ea62c28e0f..2cee0840696 100644 --- a/pom.xml +++ b/pom.xml @@ -535,7 +535,7 @@ CHANGES INSTALL.md .idea/ - *.log + **/*.log **/*.patch **/.classpath **/.project 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 5e3790825cb..f7a8d6795cd 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 @@ -223,4 +223,6 @@ + + diff --git a/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java index 0da2c925d3b..3f1994e0d8e 100755 --- a/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/UserConcentratedAllocator.java @@ -260,7 +260,7 @@ public class UserConcentratedAllocator extends AdapterBase implements PodAllocat } } - so = _offeringDao.findById(vm.getServiceOfferingId()); + so = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); if (capacityType == CapacityVO.CAPACITY_TYPE_MEMORY) { usedCapacity += so.getRamSize() * 1024L * 1024L; diff --git a/server/src/com/cloud/api/ApiDispatcher.java b/server/src/com/cloud/api/ApiDispatcher.java index 9672ef1e837..44fb6765a34 100755 --- a/server/src/com/cloud/api/ApiDispatcher.java +++ b/server/src/com/cloud/api/ApiDispatcher.java @@ -33,6 +33,8 @@ import java.util.regex.Matcher; import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.InfrastructureEntity; import org.apache.cloudstack.acl.RoleType; @@ -49,7 +51,6 @@ import org.apache.cloudstack.api.EntityReference; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; -import org.apache.cloudstack.api.Validate; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; @@ -58,7 +59,6 @@ import org.apache.cloudstack.api.command.user.event.ListEventsCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.framework.jobs.AsyncJob; import org.apache.cloudstack.framework.jobs.AsyncJobManager; -import org.apache.log4j.Logger; import com.cloud.exception.InvalidParameterValueException; import com.cloud.user.Account; @@ -369,7 +369,7 @@ public class ApiDispatcher { for (Class entity : entities) { // For backward compatibility, we search within removed entities and let service layer deal // with removed ones, return empty response or error - Object objVO = s_instance._entityMgr.findByUuid(entity, uuid); + Object objVO = s_instance._entityMgr.findByUuidIncludingRemoved(entity, uuid); if (objVO == null) { continue; } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 769d34562c3..f70af5f9a05 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -79,6 +79,7 @@ import org.apache.cloudstack.api.response.LBHealthCheckPolicyResponse; import org.apache.cloudstack.api.response.LBHealthCheckResponse; import org.apache.cloudstack.api.response.LBStickinessPolicyResponse; import org.apache.cloudstack.api.response.LBStickinessResponse; +import org.apache.cloudstack.api.response.ListResponse; import org.apache.cloudstack.api.response.LoadBalancerResponse; import org.apache.cloudstack.api.response.NetworkACLItemResponse; import org.apache.cloudstack.api.response.NetworkACLResponse; @@ -119,6 +120,7 @@ import org.apache.cloudstack.api.response.TemplatePermissionsResponse; import org.apache.cloudstack.api.response.TemplateResponse; import org.apache.cloudstack.api.response.TrafficMonitorResponse; import org.apache.cloudstack.api.response.TrafficTypeResponse; +import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; @@ -2631,7 +2633,7 @@ public class ApiResponseHelper implements ResponseGenerator { List serviceProvidersResponses = new ArrayList(); for (Network.Provider serviceProvider : serviceProviders) { // return only Virtual Router/JuniperSRX/CiscoVnmc as a provider for the firewall - if (service == Service.Firewall && !(serviceProvider == Provider.VirtualRouter || serviceProvider == Provider.JuniperSRX || serviceProvider == Provider.CiscoVnmc)) { + if (service == Service.Firewall && !(serviceProvider == Provider.VirtualRouter || serviceProvider == Provider.JuniperSRX || serviceProvider == Provider.CiscoVnmc || serviceProvider == Provider.PaloAlto)) { continue; } @@ -2694,6 +2696,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setXenLabel(result.getXenNetworkLabel()); response.setKvmLabel(result.getKvmNetworkLabel()); response.setVmwareLabel(result.getVmwareNetworkLabel()); + response.setHypervLabel(result.getHypervNetworkLabel()); response.setObjectName("traffictype"); return response; @@ -3793,4 +3796,19 @@ public class ApiResponseHelper implements ResponseGenerator { response.setObjectName("networkacllist"); return response; } + + @Override + public ListResponse createUpgradeRouterTemplateResponse(List jobIds){ + ListResponse response = new ListResponse(); + List responses = new ArrayList(); + for(Long jobId : jobIds){ + UpgradeRouterTemplateResponse routerResponse = new UpgradeRouterTemplateResponse(); + AsyncJob job = _entityMgr.findById(AsyncJob.class, jobId); + routerResponse.setAsyncJobId((job.getUuid())); + routerResponse.setObjectName("asyncjobs"); + responses.add(routerResponse); + } + response.setResponses(responses); + return response; + } } diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java index 735bbc92295..2b5b81d3eb5 100644 --- a/server/src/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/com/cloud/api/query/QueryManagerImpl.java @@ -26,6 +26,7 @@ import java.util.Set; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.hypervisor.Hypervisor; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -1089,7 +1090,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { public ListResponse searchForRouters(ListRoutersCmd cmd) { Pair, Integer> result = searchForRoutersInternal(cmd, cmd.getId(), cmd.getRouterName(), cmd.getState(), cmd.getZoneId(), cmd.getPodId(), cmd.getHostId(), cmd.getKeyword(), cmd.getNetworkId(), - cmd.getVpcId(), cmd.getForVpc(), cmd.getRole()); + cmd.getVpcId(), cmd.getForVpc(), cmd.getRole(), cmd.getVersion()); ListResponse response = new ListResponse(); List routerResponses = ViewResponseHelper.createDomainRouterResponse(result.first() @@ -1102,7 +1103,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { public ListResponse searchForInternalLbVms(ListInternalLBVMsCmd cmd) { Pair, Integer> result = searchForRoutersInternal(cmd, cmd.getId(), cmd.getRouterName(), cmd.getState(), cmd.getZoneId(), cmd.getPodId(), cmd.getHostId(), cmd.getKeyword(), cmd.getNetworkId(), - cmd.getVpcId(), cmd.getForVpc(), cmd.getRole()); + cmd.getVpcId(), cmd.getForVpc(), cmd.getRole(), null); ListResponse response = new ListResponse(); List routerResponses = ViewResponseHelper.createDomainRouterResponse(result.first() @@ -1112,7 +1113,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { } private Pair, Integer> searchForRoutersInternal(BaseListProjectAndAccountResourcesCmd cmd, Long id, - String name, String state, Long zoneId, Long podId, Long hostId, String keyword, Long networkId, Long vpcId, Boolean forVpc, String role) { + String name, String state, Long zoneId, Long podId, Long hostId, String keyword, Long networkId, Long vpcId, Boolean forVpc, + String role, String version) { Account caller = CallContext.current().getCallingAccount(); List permittedAccounts = new ArrayList(); @@ -1146,6 +1148,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sb.and("hostId", sb.entity().getHostId(), SearchCriteria.Op.EQ); sb.and("vpcId", sb.entity().getVpcId(), SearchCriteria.Op.EQ); sb.and("role", sb.entity().getRole(), SearchCriteria.Op.EQ); + sb.and("version", sb.entity().getTemplateVersion(), SearchCriteria.Op.EQ); if (forVpc != null) { if (forVpc) { @@ -1209,6 +1212,10 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.setParameters("role", role); } + if(version != null){ + sc.setParameters("version", "Cloudstack Release "+version + "%"); + } + // search VR details by ids Pair, Integer> uniqueVrPair = _routerJoinDao.searchAndCount(sc, searchFilter); Integer count = uniqueVrPair.second(); @@ -1530,6 +1537,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { Object haHosts = cmd.getHaHost(); Long startIndex = cmd.getStartIndex(); Long pageSize = cmd.getPageSizeVal(); + Hypervisor.HypervisorType hypervisorType = cmd.getHypervisor(); Filter searchFilter = new Filter(HostJoinVO.class, "id", Boolean.TRUE, startIndex, pageSize); @@ -1544,6 +1552,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ); sb.and("clusterId", sb.entity().getClusterId(), SearchCriteria.Op.EQ); sb.and("resourceState", sb.entity().getResourceState(), SearchCriteria.Op.EQ); + sb.and("hypervisor_type", sb.entity().getHypervisorType(), SearchCriteria.Op.EQ); String haTag = _haMgr.getHaTag(); if (haHosts != null && haTag != null && !haTag.isEmpty()) { @@ -1599,6 +1608,9 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { sc.setParameters("tag", haTag); } + if (hypervisorType != HypervisorType.None && hypervisorType != HypervisorType.Any) { + sc.setParameters("hypervisor_type", hypervisorType); + } // search host details by ids Pair, Integer> uniqueHostPair = _hostJoinDao.searchAndCount(sc, searchFilter); Integer count = uniqueHostPair.second(); @@ -2425,7 +2437,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService { _accountMgr.checkAccess(caller, null, true, vmInstance); - ServiceOfferingVO offering = _srvOfferingDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + ServiceOfferingVO offering = _srvOfferingDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); sc.addAnd("id", SearchCriteria.Op.NEQ, offering.getId()); // Only return offerings with the same Guest IP type and storage diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java b/server/src/com/cloud/api/query/ViewResponseHelper.java index 4051f09c627..e4be3110dd4 100644 --- a/server/src/com/cloud/api/query/ViewResponseHelper.java +++ b/server/src/com/cloud/api/query/ViewResponseHelper.java @@ -58,6 +58,7 @@ import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.TemplateResponse; +import org.apache.cloudstack.api.response.UpgradeRouterTemplateResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; import org.apache.cloudstack.api.response.VolumeResponse; @@ -440,4 +441,5 @@ public class ViewResponseHelper { } return new ArrayList(vrDataList.values()); } + } diff --git a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java index 42965bca3e7..f1930317fb1 100644 --- a/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/DomainRouterJoinDaoImpl.java @@ -22,6 +22,8 @@ import java.util.List; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.maint.Version; +import com.cloud.network.VirtualNetworkApplianceService; import org.apache.cloudstack.api.response.DomainRouterResponse; import org.apache.cloudstack.api.response.NicResponse; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -76,6 +78,11 @@ public class DomainRouterJoinDaoImpl extends GenericDaoBase implements hostResponse.setId(host.getUuid()); hostResponse.setCapabilities(host.getCapabilities()); hostResponse.setClusterId(host.getClusterUuid()); + hostResponse.setCpuSockets(host.getCpuSockets()); hostResponse.setCpuNumber(host.getCpus()); hostResponse.setZoneId(host.getZoneUuid()); hostResponse.setDisconnectedOn(host.getDisconnectedOn()); diff --git a/server/src/com/cloud/api/query/vo/HostJoinVO.java b/server/src/com/cloud/api/query/vo/HostJoinVO.java index fbc59cd5884..2f10a67223d 100644 --- a/server/src/com/cloud/api/query/vo/HostJoinVO.java +++ b/server/src/com/cloud/api/query/vo/HostJoinVO.java @@ -98,6 +98,9 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity @Column(name="mgmt_server_id") private Long managementServerId; + @Column(name="cpu_sockets") + private Integer cpuSockets; + @Column(name="cpus") private Integer cpus; @@ -249,6 +252,10 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity return managementServerId; } + public Integer getCpuSockets() { + return cpuSockets; + } + public Integer getCpus() { return cpus; } diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 72905a7c0c4..a9b9ae410d6 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -60,7 +60,6 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; import com.cloud.offering.ServiceOffering; -import com.cloud.org.Cluster; import com.cloud.org.Grouping.AllocationState; import com.cloud.resource.ResourceListener; import com.cloud.resource.ResourceManager; @@ -81,7 +80,6 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; -import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; import com.cloud.utils.fsm.StateListener; @@ -174,7 +172,7 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, @DB @Override public boolean releaseVmCapacity(VirtualMachine vm, final boolean moveFromReserved, final boolean moveToReservered, final Long hostId) { - final ServiceOfferingVO svo = _offeringsDao.findById(vm.getServiceOfferingId()); + final ServiceOfferingVO svo = _offeringsDao.findById(vm.getId(), vm.getServiceOfferingId()); CapacityVO capacityCpu = _capacityDao.findByHostIdType(hostId, CapacityVO.CAPACITY_TYPE_CPU); CapacityVO capacityMemory = _capacityDao.findByHostIdType(hostId, CapacityVO.CAPACITY_TYPE_MEMORY); Long clusterId = null; @@ -270,7 +268,7 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, final float cpuOvercommitRatio = Float.parseFloat(_clusterDetailsDao.findDetail(clusterId, "cpuOvercommitRatio").getValue()); final float memoryOvercommitRatio = Float.parseFloat(_clusterDetailsDao.findDetail(clusterId, "memoryOvercommitRatio").getValue()); - final ServiceOfferingVO svo = _offeringsDao.findById(vm.getServiceOfferingId()); + final ServiceOfferingVO svo = _offeringsDao.findById(vm.getId(), vm.getServiceOfferingId()); CapacityVO capacityCpu = _capacityDao.findByHostIdType(hostId, CapacityVO.CAPACITY_TYPE_CPU); CapacityVO capacityMem = _capacityDao.findByHostIdType(hostId, CapacityVO.CAPACITY_TYPE_MEMORY); @@ -549,16 +547,23 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, Float cpuOvercommitRatio = 1f; Float ramOvercommitRatio = 1f; for (VMInstanceVO vm : vms) { - UserVmDetailVO vmDetailCpu = _userVmDetailsDao.findDetail(vm.getId(), "cpuOvercommitRatio"); - UserVmDetailVO vmDetailRam = _userVmDetailsDao.findDetail(vm.getId(),"memoryOvercommitRatio"); + Map vmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + String vmDetailCpu = vmDetails.get("cpuOvercommitRatio"); + String vmDetailRam = vmDetails.get("memoryOvercommitRatio"); if (vmDetailCpu != null ) { //if vmDetail_cpu is not null it means it is running in a overcommited cluster. - cpuOvercommitRatio = Float.parseFloat(vmDetailCpu.getValue()); - ramOvercommitRatio = Float.parseFloat(vmDetailRam.getValue()); + cpuOvercommitRatio = Float.parseFloat(vmDetailCpu); + ramOvercommitRatio = Float.parseFloat(vmDetailRam); } ServiceOffering so = offeringsMap.get(vm.getServiceOfferingId()); - usedMemory += ((so.getRamSize() * 1024L * 1024L)/ramOvercommitRatio)*clusterRamOvercommitRatio; - usedCpu += ((so.getCpu() * so.getSpeed())/cpuOvercommitRatio)*clusterCpuOvercommitRatio; + if (so.isDynamic()) { + usedMemory += ((Integer.parseInt(vmDetails.get(ServiceOfferingVO.DynamicParameters.memory.name())) * 1024L * 1024L)/ramOvercommitRatio)*clusterRamOvercommitRatio; + usedCpu += ((Integer.parseInt(vmDetails.get(ServiceOfferingVO.DynamicParameters.cpuNumber.name())) * Integer.parseInt(vmDetails.get(ServiceOfferingVO.DynamicParameters.cpuSpeed.name())))/cpuOvercommitRatio)*clusterCpuOvercommitRatio; + } + else { + usedMemory += ((so.getRamSize() * 1024L * 1024L)/ramOvercommitRatio)*clusterRamOvercommitRatio; + usedCpu += ((so.getCpu() * so.getSpeed())/cpuOvercommitRatio)*clusterCpuOvercommitRatio; + } } List vmsByLastHostId = _vmDao.listByLastHostId(host.getId()); @@ -599,34 +604,58 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, CapacityVO memCap = _capacityDao.findByHostIdType(host.getId(), CapacityVO.CAPACITY_TYPE_MEMORY); if (cpuCap != null && memCap != null){ + + long hostTotalCpu = host.getCpus().longValue() * host.getSpeed().longValue(); + + if (cpuCap.getTotalCapacity() != hostTotalCpu) { + s_logger.debug("Calibrate total cpu for host: " + host.getId() + " old total CPU:" + + cpuCap.getTotalCapacity() + " new total CPU:" + hostTotalCpu); + cpuCap.setTotalCapacity(hostTotalCpu); + + } + if (cpuCap.getUsedCapacity() == usedCpu && cpuCap.getReservedCapacity() == reservedCpu) { s_logger.debug("No need to calibrate cpu capacity, host:" + host.getId() + " usedCpu: " + cpuCap.getUsedCapacity() + " reservedCpu: " + cpuCap.getReservedCapacity()); - } else if (cpuCap.getReservedCapacity() != reservedCpu) { - s_logger.debug("Calibrate reserved cpu for host: " + host.getId() + " old reservedCpu:" + cpuCap.getReservedCapacity() - + " new reservedCpu:" + reservedCpu); - cpuCap.setReservedCapacity(reservedCpu); - } else if (cpuCap.getUsedCapacity() != usedCpu) { - s_logger.debug("Calibrate used cpu for host: " + host.getId() + " old usedCpu:" + cpuCap.getUsedCapacity() + " new usedCpu:" - + usedCpu); - cpuCap.setUsedCapacity(usedCpu); + } else { + if (cpuCap.getReservedCapacity() != reservedCpu) { + s_logger.debug("Calibrate reserved cpu for host: " + host.getId() + " old reservedCpu:" + + cpuCap.getReservedCapacity() + " new reservedCpu:" + reservedCpu); + cpuCap.setReservedCapacity(reservedCpu); + } + if (cpuCap.getUsedCapacity() != usedCpu) { + s_logger.debug("Calibrate used cpu for host: " + host.getId() + " old usedCpu:" + + cpuCap.getUsedCapacity() + " new usedCpu:" + usedCpu); + cpuCap.setUsedCapacity(usedCpu); + } } + if (memCap.getTotalCapacity() != host.getTotalMemory()) { + s_logger.debug("Calibrate total memory for host: " + host.getId() + " old total memory:" + + memCap.getTotalCapacity() + " new total memory:" + host.getTotalMemory()); + memCap.setTotalCapacity(host.getTotalMemory()); + + } + if (memCap.getUsedCapacity() == usedMemory && memCap.getReservedCapacity() == reservedMemory) { s_logger.debug("No need to calibrate memory capacity, host:" + host.getId() + " usedMem: " + memCap.getUsedCapacity() + " reservedMem: " + memCap.getReservedCapacity()); - } else if (memCap.getReservedCapacity() != reservedMemory) { - s_logger.debug("Calibrate reserved memory for host: " + host.getId() + " old reservedMem:" + memCap.getReservedCapacity() - + " new reservedMem:" + reservedMemory); - memCap.setReservedCapacity(reservedMemory); - } else if (memCap.getUsedCapacity() != usedMemory) { - /* - * Didn't calibrate for used memory, because VMs can be in state(starting/migrating) that I don't know on which host they are - * allocated - */ - s_logger.debug("Calibrate used memory for host: " + host.getId() + " old usedMem: " + memCap.getUsedCapacity() - + " new usedMem: " + usedMemory); - memCap.setUsedCapacity(usedMemory); + } else { + if (memCap.getReservedCapacity() != reservedMemory) { + s_logger.debug("Calibrate reserved memory for host: " + host.getId() + " old reservedMem:" + + memCap.getReservedCapacity() + " new reservedMem:" + reservedMemory); + memCap.setReservedCapacity(reservedMemory); + } + if (memCap.getUsedCapacity() != usedMemory) { + /* + * Didn't calibrate for used memory, because VMs can be in + * state(starting/migrating) that I don't know on which host + * they are allocated + */ + s_logger.debug("Calibrate used memory for host: " + host.getId() + " old usedMem: " + + memCap.getUsedCapacity() + " new usedMem: " + usedMemory); + memCap.setUsedCapacity(usedMemory); + } } try { diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index e78757639ee..bc805b7043e 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -275,6 +275,11 @@ public enum Config { KvmPrivateNetwork("Hidden", ManagementServer.class, String.class, "kvm.private.network.device", null, "Specify the private bridge on host for private network", null), KvmGuestNetwork("Hidden", ManagementServer.class, String.class, "kvm.guest.network.device", null, "Specify the private bridge on host for private network", null), KvmSshToAgentEnabled("Advanced", ManagementServer.class, Boolean.class, "kvm.ssh.to.agent", "true", "Specify whether or not the management server is allowed to SSH into KVM Agents", null), + + // Hyperv + HypervPublicNetwork("Hidden", ManagementServer.class, String.class, "hyperv.public.network.device", null, "Specify the public virtual switch on host for public network", null), + HypervPrivateNetwork("Hidden", ManagementServer.class, String.class, "hyperv.private.network.device", null, "Specify the virtual switch on host for private network", null), + HypervGuestNetwork("Hidden", ManagementServer.class, String.class, "hyperv.guest.network.device", null, "Specify the virtual switch on host for private network", null), // Usage UsageExecutionTimezone("Usage", ManagementServer.class, String.class, "usage.execution.timezone", null, "The timezone to use for usage job execution time", null), diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index e3aa4fa7617..82256ca952e 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -28,6 +28,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; @@ -35,6 +36,8 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.log4j.Logger; + import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; import org.apache.cloudstack.affinity.AffinityGroupService; @@ -79,10 +82,8 @@ import org.apache.cloudstack.region.Region; import org.apache.cloudstack.region.RegionVO; import org.apache.cloudstack.region.dao.RegionDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; 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; @@ -95,7 +96,6 @@ import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; -import com.cloud.dc.DataCenterDetailVO; import com.cloud.dc.DataCenterIpAddressVO; import com.cloud.dc.DataCenterLinkLocalIpAddressVO; import com.cloud.dc.DataCenterVO; @@ -206,7 +206,8 @@ import com.cloud.vm.dao.NicIpAliasVO; import com.cloud.vm.dao.NicSecondaryIpDao; @Local(value = { ConfigurationManager.class, ConfigurationService.class }) -public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService { +public class +ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService { public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class); @Inject @@ -406,16 +407,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _alertMgr.sendAlert(AlertManager.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]); + .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.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", - ""); + .sendAlert( + AlertManager.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", + ""); } } @@ -474,7 +475,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati throw new InvalidParameterValueException("unable to find storage pool by id " + resourceId); } _storagePoolDetailsDao.addDetail(resourceId, name, value); - + break; case Account: @@ -1199,7 +1200,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _capacityDao.updateCapacityState(null, pod.getId(), null, null, allocationStateStrFinal); pod.setAllocationState(allocationState); } - + _podDao.update(id, pod); } }); @@ -1678,8 +1679,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati boolean checkForDuplicates = !zoneName.equals(oldZoneName); checkZoneParameters(zoneName, dns1, dns2, internalDns1, internalDns2, checkForDuplicates, null, allocationStateStr, ip6Dns1, ip6Dns2);// not allowing updating - // domain associated with - // a zone, once created + // domain associated with + // a zone, once created zone.setName(zoneName); zone.setDns1(dns1); @@ -1711,10 +1712,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } updatedDetails.putAll(newDetails); zone.setDetails(updatedDetails); - + if (allocationStateStr != null && !allocationStateStr.isEmpty()) { Grouping.AllocationState allocationState = Grouping.AllocationState.valueOf(allocationStateStr); - + if (allocationState == Grouping.AllocationState.Enabled) { // check if zone has necessary trafficTypes before enabling try { @@ -1728,7 +1729,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // network with public Thpe _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Public); } - + try { _networkModel.getDefaultPhysicalNetworkByZoneAndTrafficType(zoneId, TrafficType.Storage); } catch (InvalidParameterValueException noStorage) { @@ -1737,7 +1738,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati _networkSvc.addTrafficTypeToPhysicalNetwork(mgmtPhyNetwork.getId(), TrafficType.Storage.toString(), mgmtTraffic.getXenNetworkLabel(), mgmtTraffic.getKvmNetworkLabel(), mgmtTraffic.getVmwareNetworkLabel(), - mgmtTraffic.getSimulatorNetworkLabel(), mgmtTraffic.getVlan()); + 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"); } @@ -1975,7 +1976,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati return createZone(userId, zoneName, dns1, dns2, internalDns1, internalDns2, guestCidr, domainVO != null ? domainVO.getName() : null, domainId, zoneType, allocationState, networkDomain, - isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); + isSecurityGroupEnabled, isLocalStorageEnabled, ip6Dns1, ip6Dns2); } @Override @@ -1995,20 +1996,19 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati + ": specify the display text that has non-zero length"); } - Long cpuNumber = cmd.getCpuNumber(); - if ((cpuNumber == null) || (cpuNumber.intValue() <= 0) || (cpuNumber.intValue() > 2147483647)) { + Integer cpuNumber = cmd.getCpuNumber(); + Integer cpuSpeed = cmd.getCpuSpeed(); + Integer memory = cmd.getMemory(); + + if ((cpuNumber != null) && ((cpuNumber.intValue() <=0) || (cpuNumber.intValue() > 2147483647))) { throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu number value between 1 and 2147483647"); } - - Long cpuSpeed = cmd.getCpuSpeed(); - if ((cpuSpeed == null) || (cpuSpeed.intValue() <= 0) || (cpuSpeed.intValue() > 2147483647)) { + if ((cpuSpeed != null) && ((cpuSpeed.intValue() < 0) || (cpuSpeed.intValue() > 2147483647))) { throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu speed value between 1 and 2147483647"); } - - Long memory = cmd.getMemory(); - if ((memory == null) || (memory.intValue() < 32) || (memory.intValue() > 2147483647)) { + if (( memory != null ) && ((memory.intValue() < 32) || (memory.intValue() > 2147483647))) { throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the memory value between 32 and 2147483647 MB"); } @@ -2074,21 +2074,20 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), - cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(), + 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 vm_type, - String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, + 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, vm_type, domainId, hostTag, deploymentPlanner); - if ((bytesReadRate != null) && (bytesReadRate > 0)) offering.setBytesReadRate(bytesReadRate); if ((bytesWriteRate != null) && (bytesWriteRate > 0)) @@ -2101,10 +2100,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if ((offering = _serviceOfferingDao.persist(offering)) != null) { if (details != null) { List detailsVO = new ArrayList(); - for (String key : details.keySet()) { - detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), key, details.get(key))); + for (Entry detailEntry : details.entrySet()) { + detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), detailEntry.getKey(), detailEntry.getValue())); } - + _serviceOfferingDetailsDao.saveDetails(detailsVO); } CallContext.current().setEventDetails("Service offering id=" + offering.getId()); @@ -2187,8 +2186,8 @@ 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) { + 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."); @@ -2211,8 +2210,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati iopsWriteRate = null; if (isCustomizedIops) { - minIops = null; - maxIops = null; + minIops = null; + maxIops = null; } else { if (minIops == null && maxIops == null) { @@ -2220,17 +2219,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati maxIops = 0L; } else { - if (minIops == null || minIops <= 0) { - throw new InvalidParameterValueException("The min IOPS must be greater than 0."); - } + if (minIops == null || minIops <= 0) { + throw new InvalidParameterValueException("The min IOPS must be greater than 0."); + } - if (maxIops == null) { - maxIops = 0L; - } + if (maxIops == null) { + maxIops = 0L; + } - if (minIops > maxIops) { - throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); - } + if (minIops > maxIops) { + throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); + } } } } @@ -2241,7 +2240,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati tags = StringUtils.cleanupTags(tags); DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized, - isCustomizedIops, minIops, maxIops); + isCustomizedIops, minIops, maxIops); newDiskOffering.setUseLocalStorage(localStorageRequired); newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled); @@ -2254,6 +2253,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if (iopsWriteRate != null && (iopsWriteRate > 0)) newDiskOffering.setIopsWriteRate(iopsWriteRate); + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { + throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); + } + + newDiskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId()); DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering); if (offering != null) { @@ -2272,8 +2277,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long numGibibytes = cmd.getDiskSize(); boolean isDisplayOfferingEnabled = cmd.getDisplayOffering() != null ? cmd.getDisplayOffering() : true; boolean isCustomized = cmd.isCustomized() != null ? cmd.isCustomized() : false; // false - // by - // default + // by + // default String tags = cmd.getTags(); // Long domainId = cmd.getDomainId() != null ? cmd.getDomainId() : // Long.valueOf(DomainVO.ROOT_DOMAIN); // disk offering @@ -2303,10 +2308,11 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long bytesWriteRate = cmd.getBytesWriteRate(); Long iopsReadRate = cmd.getIopsReadRate(); 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); + localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops, + bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve); } @Override @@ -2434,9 +2440,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_CREATE, eventDescription = "creating vlan ip range", - async = false) + async = false) public Vlan createVlanAndPublicIpRange(CreateVlanIpRangeCmd cmd) throws InsufficientCapacityException, - ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException { + ConcurrentOperationException, ResourceUnavailableException, ResourceAllocationException { Long zoneId = cmd.getZoneId(); Long podId = cmd.getPodId(); String startIP = cmd.getStartIp(); @@ -2446,8 +2452,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati String vlanId = cmd.getVlan(); // TODO decide if we should be forgiving or demand a valid and complete URI if (!((vlanId == null) - || ("".equals(vlanId)) - || vlanId.startsWith(BroadcastDomainType.Vlan.scheme()))) + || ("".equals(vlanId)) + || vlanId.startsWith(BroadcastDomainType.Vlan.scheme()))) vlanId = BroadcastDomainType.Vlan.toUri(vlanId).toString(); Boolean forVirtualNetwork = cmd.isForVirtualNetwork(); Long networkId = cmd.getNetworkID(); @@ -2683,12 +2689,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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); 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"); + throw new InvalidParameterValueException("The Dhcp serivice provider for this network dose not support the dhcp across multiple subnets"); } s_logger.info("adding a new subnet to the network " + network.getId()); } else if (sameSubnet != null) { @@ -2769,7 +2775,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati sameSubnet = true; //check if the gateway provided by the user is same as that of the subnet. if (newVlanGateway != null && !newVlanGateway.equals(vlanGateway)) { - throw new InvalidParameterValueException("The gateway of the subnet should be unique. The subnet alreaddy has a gateway "+ vlanGateway); + throw new InvalidParameterValueException("The gateway of the subnet should be unique. The subnet alreaddy has a gateway "+ vlanGateway); } break; } @@ -2975,7 +2981,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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("\\-"); String otherVlanStartIP = otherVlanIpRange[0]; @@ -2992,7 +2998,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } 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."); + " Please specify a different start IP/end IP."); } } } @@ -3195,7 +3201,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DEDICATE, eventDescription = "dedicating vlan ip range", - async = false) + async = false) public Vlan dedicatePublicIpRange(DedicatePublicIpRangeCmd cmd) throws ResourceAllocationException { Long vlanDbId = cmd.getId(); String accountName = cmd.getAccountName(); @@ -3284,7 +3290,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_RELEASE, eventDescription = "releasing a public ip range", - async = false) + async = false) public boolean releasePublicIpRange(ReleasePublicIpRangeCmd cmd) { Long vlanDbId = cmd.getId(); @@ -3418,7 +3424,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } private void checkConflictsWithPortableIpRange(long zoneId, String vlanId, String vlanGateway, String vlanNetmask, - String startIP, String endIP) { + 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 @@ -3542,7 +3548,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati 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."); + + " in zone " + zoneName + ". Please specify a different CIDR."); } else { throw new InvalidParameterValueException("Warning: The pods " + podName + " and " + otherPodName + " in zone " + zoneName @@ -3603,7 +3609,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @ActionEvent(eventType = EventTypes.EVENT_VLAN_IP_RANGE_DELETE, eventDescription = "deleting vlan ip range", - async = false) + async = false) public boolean deleteVlanIpRange(DeleteVlanIpRangeCmd cmd) { Long vlanDbId = cmd.getId(); @@ -3792,6 +3798,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati firewallProvider = provider; } + if (provider == Provider.PaloAlto) { + firewallProvider = Provider.PaloAlto; + } + if ((service == Service.PortForwarding || service == Service.StaticNat) && provider == Provider.VirtualRouter) { firewallProvider = Provider.VirtualRouter; @@ -3829,7 +3839,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati "Capabilities for LB service can be specifed only when LB service is enabled for network offering."); } validateLoadBalancerServiceCapabilities(lbServiceCapabilityMap); - + if (lbServiceCapabilityMap != null && !lbServiceCapabilityMap.isEmpty()) { maxconn = cmd.getMaxconnections(); if (maxconn == null) { @@ -3839,7 +3849,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati if(cmd.getKeepAliveEnabled() != null && cmd.getKeepAliveEnabled()) { enableKeepAlive = true; } - + // validate the Source NAT service capabilities specified in the network // offering Map sourceNatServiceCapabilityMap = cmd.getServiceCapabilities(Service.SourceNat); @@ -4153,7 +4163,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } } - + if (serviceProviderMap != null && serviceProviderMap.containsKey(Service.Lb) && !internalLb && !publicLb) { //if not specified, default public lb to true publicLb = true; @@ -4615,7 +4625,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } if (_ntwkOffServiceMapDao.areServicesSupportedByNetworkOffering(offering.getId(), Service.Lb)){ if (maxconn != null) { - offering.setConcurrentConnections(maxconn); + offering.setConcurrentConnections(maxconn); } } @@ -4834,7 +4844,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RANGE_CREATE, - eventDescription = "creating portable ip range", async = false) + eventDescription = "creating portable ip range", async = false) public PortableIpRange createPortableIpRange(CreatePortableIpRangeCmd cmd) throws ConcurrentOperationException { final Integer regionId = cmd.getRegionId(); final String startIP = cmd.getStartIp(); @@ -4909,7 +4919,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati // implicitly enable portable IP service for the region region.setPortableipEnabled(true); _regionDao.update(region.getId(), region); - + return portableIpRange; } }); @@ -4921,7 +4931,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Override @DB @ActionEvent(eventType = EventTypes.EVENT_PORTABLE_IP_RANGE_DELETE, - eventDescription = "deleting portable ip range", async = false) + eventDescription = "deleting portable ip range", async = false) public boolean deletePortableIpRange(DeletePortableIpRangeCmd cmd) { long rangeId = cmd.getId(); @@ -5021,7 +5031,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati @Inject public void setSecChecker(List secChecker) { - this._secChecker = secChecker; + _secChecker = secChecker; } diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index e79e8e3883f..0886840e435 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -864,6 +864,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy DataCenterDeployment potentialPlan = new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext()); + // find suitable hosts under this cluster, need as many hosts as we // get. List suitableHosts = findSuitableHosts(vmProfile, potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL); @@ -906,7 +907,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy s_logger.debug("No suitable hosts found under this Cluster: " + clusterId); } - if (canAvoidCluster(clusterVO, avoid, PlannerAvoidOutput)) { + if (canAvoidCluster(clusterVO, avoid, PlannerAvoidOutput, vmProfile)) { avoid.addCluster(clusterVO.getId()); } } @@ -914,7 +915,8 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return null; } - private boolean canAvoidCluster(Cluster clusterVO, ExcludeList avoids, ExcludeList plannerAvoidOutput) { + private boolean canAvoidCluster(Cluster clusterVO, ExcludeList avoids, ExcludeList plannerAvoidOutput, + VirtualMachineProfile vmProfile) { ExcludeList allocatorAvoidOutput = new ExcludeList(avoids.getDataCentersToAvoid(), avoids.getPodsToAvoid(), avoids.getClustersToAvoid(), avoids.getHostsToAvoid(), avoids.getPoolsToAvoid()); @@ -938,26 +940,27 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } } + // all hosts in avoid set, avoid the cluster. Otherwise check the pools + if (avoidAllHosts) { + return true; + } + // Cluster can be put in avoid set in following scenarios: // 1. If storage allocators haven't put any pools in avoid set means either no pools in cluster - // or pools not suitable for the allocators to handle. + // or pools not suitable for the allocators to handle or there is no + // linkage of any suitable host to any of the pools in cluster // 2. If all 'shared' or 'local' pools are in avoid set if (allocatorAvoidOutput.getPoolsToAvoid() != null && !allocatorAvoidOutput.getPoolsToAvoid().isEmpty()) { - // check shared pools - List allPoolsInCluster = _storagePoolDao.findPoolsByTags(clusterVO.getDataCenterId(), - clusterVO.getPodId(), clusterVO.getId(), null); - for (StoragePoolVO pool : allPoolsInCluster) { - if (!allocatorAvoidOutput.shouldAvoid(pool)) { - // there's some pool in the cluster that is not yet in avoid set - avoidAllPools = false; - break; - } - } - if (avoidAllPools) { - // check local pools - List allLocalPoolsInCluster = _storagePoolDao.findLocalStoragePoolsByTags(clusterVO.getDataCenterId(), + + Pair storageRequirements = findVMStorageRequirements(vmProfile); + boolean vmRequiresSharedStorage = storageRequirements.first(); + boolean vmRequiresLocalStorege = storageRequirements.second(); + + if (vmRequiresSharedStorage) { + // check shared pools + List allPoolsInCluster = _storagePoolDao.findPoolsByTags(clusterVO.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null); - for (StoragePoolVO pool : allLocalPoolsInCluster) { + for (StoragePoolVO pool : allPoolsInCluster) { if (!allocatorAvoidOutput.shouldAvoid(pool)) { // there's some pool in the cluster that is not yet in avoid set avoidAllPools = false; @@ -965,6 +968,20 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } } } + + if (vmRequiresLocalStorege) { + // check local pools + List allLocalPoolsInCluster = _storagePoolDao.findLocalStoragePoolsByTags( + clusterVO.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null); + for (StoragePoolVO pool : allLocalPoolsInCluster) { + if (!allocatorAvoidOutput.shouldAvoid(pool)) { + // there's some pool in the cluster that is not yet + // in avoid set + avoidAllPools = false; + break; + } + } + } } if (avoidAllHosts || avoidAllPools) { @@ -973,6 +990,27 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return false; } + private Pair findVMStorageRequirements(VirtualMachineProfile vmProfile) { + + boolean requiresShared = false, requiresLocal = false; + + List volumesTobeCreated = _volsDao.findUsableVolumesForInstance(vmProfile.getId()); + + // for each volume find whether shared or local pool is required + for (VolumeVO toBeCreated : volumesTobeCreated) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(toBeCreated.getDiskOfferingId()); + + if (diskOffering != null) { + if (diskOffering.getUseLocalStorage()) { + requiresLocal = true; + } else { + requiresShared = true; + } + } + } + + return new Pair(requiresShared, requiresLocal); + } protected Pair> findPotentialDeploymentResources(List suitableHosts, Map> suitableVolumeStoragePools, ExcludeList avoid, DeploymentPlanner.PlannerResourceUsage resourceUsageRequired) { diff --git a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java index 6ca40c01e98..8d54541dd62 100644 --- a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java @@ -899,7 +899,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase if ((destinations != null && !destinations.isEmpty()) || rule.isAutoScaleConfig()) { boolean inline = _networkMgr.isNetworkInlineMode(network); LoadBalancerTO loadBalancer = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, false, inline, destinations, rule.getStickinessPolicies(), - rule.getHealthCheckPolicies()); + rule.getHealthCheckPolicies(), rule.getLbSslCert(), rule.getLbProtocol()); if (rule.isAutoScaleConfig()) { loadBalancer.setAutoScaleVmGroup(rule.getAutoScaleVmGroup()); } @@ -1184,7 +1184,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase if ((destinations != null && !destinations.isEmpty()) || !rule.isAutoScaleConfig()) { boolean inline = _networkMgr.isNetworkInlineMode(network); LoadBalancerTO loadBalancer = new LoadBalancerTO(uuid, srcIp, srcPort, protocol, algorithm, revoked, - false, inline, destinations, rule.getStickinessPolicies(), rule.getHealthCheckPolicies()); + false, inline, destinations, rule.getStickinessPolicies(), rule.getHealthCheckPolicies(), rule.getLbSslCert(), rule.getLbProtocol()); loadBalancersToApply.add(loadBalancer); } } @@ -1218,5 +1218,5 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase } } return null; - } + } } diff --git a/server/src/com/cloud/network/IpAddressManagerImpl.java b/server/src/com/cloud/network/IpAddressManagerImpl.java index 8619ae178cd..2c54137adcd 100644 --- a/server/src/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/com/cloud/network/IpAddressManagerImpl.java @@ -1892,7 +1892,10 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage //nic.setBroadcastType(BroadcastDomainType.Vlan); //nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag())); nic.setBroadcastType(network.getBroadcastDomainType()); - nic.setBroadcastUri(network.getBroadcastUri()); + if (network.getBroadcastUri() != null) + nic.setBroadcastUri(network.getBroadcastUri()); + else + nic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(ip.getVlanTag())); nic.setFormat(AddressFormat.Ip4); nic.setReservationId(String.valueOf(ip.getVlanTag())); nic.setMacAddress(ip.getMacAddress()); diff --git a/server/src/com/cloud/network/NetworkModelImpl.java b/server/src/com/cloud/network/NetworkModelImpl.java index df9f651b8f2..4a298cbe258 100755 --- a/server/src/com/cloud/network/NetworkModelImpl.java +++ b/server/src/com/cloud/network/NetworkModelImpl.java @@ -941,16 +941,21 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { // from the network offering boolean isUserVmsDefaultNetwork = false; boolean isDomRGuestOrPublicNetwork = false; + boolean isSystemVmNetwork = false; if (vm != null) { Nic nic = _nicDao.findByNtwkIdAndInstanceId(networkId, vmId); if (vm.getType() == Type.User && nic != null && nic.isDefaultNic()) { isUserVmsDefaultNetwork = true; } else if (vm.getType() == Type.DomainRouter && ntwkOff != null && (ntwkOff.getTrafficType() == TrafficType.Public || ntwkOff.getTrafficType() == TrafficType.Guest)) { isDomRGuestOrPublicNetwork = true; + } else if (vm.getType() == Type.ConsoleProxy || vm.getType() == Type.SecondaryStorageVm) { + isSystemVmNetwork = true; } } if (isUserVmsDefaultNetwork || isDomRGuestOrPublicNetwork) { return _configMgr.getServiceOfferingNetworkRate(vm.getServiceOfferingId(), network.getDataCenterId()); + } else if (isSystemVmNetwork) { + return -1; } else { return _configMgr.getNetworkOfferingNetworkRate(ntwkOff.getId(), network.getDataCenterId()); } @@ -1174,6 +1179,9 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { break; case VMware : label = mgmtTraffic.getVmwareNetworkLabel(); break; + case Hyperv: + label = mgmtTraffic.getHypervNetworkLabel(); + break; } return label; } @@ -1199,6 +1207,9 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { break; case VMware : label = storageTraffic.getVmwareNetworkLabel(); break; + case Hyperv: + label = storageTraffic.getHypervNetworkLabel(); + break; } return label; } @@ -1544,6 +1555,9 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { case VMware: label = publicTraffic.getVmwareNetworkLabel(); break; + case Hyperv: + label = publicTraffic.getHypervNetworkLabel(); + break; } return label; } @@ -1573,6 +1587,10 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel { case VMware: label = guestTraffic.getVmwareNetworkLabel(); break; + case Hyperv: + label = guestTraffic.getHypervNetworkLabel(); + break; + } return label; } diff --git a/server/src/com/cloud/network/NetworkServiceImpl.java b/server/src/com/cloud/network/NetworkServiceImpl.java index 7e971bd0e25..c6c78f50fc3 100755 --- a/server/src/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/com/cloud/network/NetworkServiceImpl.java @@ -3542,7 +3542,7 @@ 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) { + 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); @@ -3592,7 +3592,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { if (xenLabel == null) { xenLabel = getDefaultXenNetworkLabel(trafficType); } - PhysicalNetworkTrafficTypeVO pNetworktrafficType = new PhysicalNetworkTrafficTypeVO(physicalNetworkId, trafficType, xenLabel, kvmLabel, vmwareLabel, simulatorLabel, vlan); + PhysicalNetworkTrafficTypeVO pNetworktrafficType = new PhysicalNetworkTrafficTypeVO(physicalNetworkId, trafficType, xenLabel, kvmLabel, vmwareLabel, simulatorLabel, vlan, hypervLabel); pNetworktrafficType = _pNTrafficTypeDao.persist(pNetworktrafficType); return pNetworktrafficType; @@ -3636,7 +3636,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { @Override @ActionEvent(eventType = EventTypes.EVENT_TRAFFIC_TYPE_UPDATE, eventDescription = "Updating physical network TrafficType", async = true) - public PhysicalNetworkTrafficType updatePhysicalNetworkTrafficType(Long id, String xenLabel, String kvmLabel, String vmwareLabel) { + public PhysicalNetworkTrafficType updatePhysicalNetworkTrafficType(Long id, String xenLabel, String kvmLabel, String vmwareLabel, String hypervLabel) { PhysicalNetworkTrafficTypeVO trafficType = _pNTrafficTypeDao.findById(id); @@ -3662,6 +3662,13 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService { } trafficType.setVmwareNetworkLabel(vmwareLabel); } + + if (hypervLabel != null) { + if("".equals(hypervLabel)){ + hypervLabel = null; + } + trafficType.setHypervNetworkLabel(hypervLabel); + } _pNTrafficTypeDao.update(id, trafficType); return trafficType; diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 0d434784505..a1650e07109 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -107,6 +107,9 @@ import com.cloud.network.dao.LoadBalancerVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.LoadBalancerCertMapDao; +import com.cloud.network.dao.LoadBalancerCertMapVO; +import com.cloud.network.dao.SslCertVO; import com.cloud.network.element.LoadBalancingServiceProvider; import com.cloud.network.lb.LoadBalancingRule.LbAutoScalePolicy; import com.cloud.network.lb.LoadBalancingRule.LbAutoScaleVmGroup; @@ -115,6 +118,7 @@ import com.cloud.network.lb.LoadBalancingRule.LbCondition; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.FirewallRuleType; @@ -253,6 +257,9 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements IpAddressManager _ipAddrMgr; @Inject EntityManager _entityMgr; + @Inject + LoadBalancerCertMapDao _lbCertMapDao; + // Will return a string. For LB Stickiness this will be a json, for // autoscale this will be "," separated values @@ -350,7 +357,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements */ List policyList = getStickinessPolicies(lb.getId()); Ip sourceIp = getSourceIp(lb); - LoadBalancingRule rule = new LoadBalancingRule(lb, null, policyList, null, sourceIp); + LoadBalancingRule rule = new LoadBalancingRule(lb, null, policyList, null, sourceIp, null, lb.getLbProtocol()); rule.setAutoScaleVmGroup(lbAutoScaleVmGroup); if (!isRollBackAllowedForProvider(lb)) { @@ -558,7 +565,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements policyList.add(new LbStickinessPolicy(cmd.getStickinessMethodName(), lbpolicy.getParams())); Ip sourceIp = getSourceIp(loadBalancer); LoadBalancingRule lbRule = new LoadBalancingRule(loadBalancer, getExistingDestinations(lbpolicy.getId()), - policyList, null, sourceIp); + policyList, null, sourceIp,null, loadBalancer.getLbProtocol()); if (!validateLbRule(lbRule)) { throw new InvalidParameterValueException("Failed to create Stickiness policy: Validation Failed " + cmd.getLbRuleId()); @@ -905,7 +912,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements // hashealtChecks if (hcPolicyList != null && hcPolicyList.size() > 0) { Ip sourceIp = getSourceIp(lb); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, null, hcPolicyList, sourceIp); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, null, hcPolicyList, sourceIp, null, lb.getLbProtocol()); lbrules.add(loadBalancing); } } @@ -1087,12 +1094,161 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements return success; } + @Override + public boolean assignSSLCertToLoadBalancerRule(Long lbId, String certName, String publicCert, String privateKey) { + s_logger.error("Calling the manager for LB"); + LoadBalancerVO loadBalancer = _lbDao.findById(lbId); + + return false; //TODO + } + @Override @ActionEvent(eventType = EventTypes.EVENT_REMOVE_FROM_LOAD_BALANCER_RULE, eventDescription = "removing from load balancer", async = true) public boolean removeFromLoadBalancer(long loadBalancerId, List instanceIds) { return removeFromLoadBalancerInternal(loadBalancerId, instanceIds, true); } + @Override + public LbSslCert getLbSslCert(long lbRuleId) { + LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lbRuleId); + + if ( lbCertMap == null) + return null; + + SslCertVO certVO = _entityMgr.findById(SslCertVO.class, lbCertMap.getCertId()); + if ( certVO == null) { + s_logger.warn("Cert rule with cert ID " + lbCertMap.getCertId() + " but Cert is not found"); + return null; + } + + return new LbSslCert(certVO.getCertificate(), certVO.getKey(), + certVO.getChain(), certVO.getPassword(), lbCertMap.isRevoke()); + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_ASSIGN, eventDescription = "assigning certificate to load balancer", async = true) + public boolean assignCertToLoadBalancer(long lbRuleId, Long certId) { + CallContext caller = CallContext.current(); + + LoadBalancerVO loadBalancer = _lbDao.findById(Long.valueOf(lbRuleId)); + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid load balancer id: " + lbRuleId); + } + + SslCertVO certVO = _entityMgr.findById(SslCertVO.class,certId); + if (certVO == null) { + throw new InvalidParameterException("Invalid certificate id: " + certId); + } + + _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer); + + // check if LB and Cert belong to the same account + if( loadBalancer.getAccountId() != certVO.getAccountId() ){ + throw new InvalidParameterValueException("Access denied for account " + certVO.getAccountId()); + } + + + String capability = getLBCapability(loadBalancer.getNetworkId(), Capability.SslTermination.getName()); + if ( capability == null){ + throw new InvalidParameterValueException("Ssl termination not supported by the loadbalancer"); + } + + //check if the lb is already bound + LoadBalancerCertMapVO certMapRule = _lbCertMapDao.findByLbRuleId(loadBalancer.getId()); + if (certMapRule != null) + throw new InvalidParameterValueException("Another certificate is already bound to the LB"); + + //check for correct port + if ( loadBalancer.getLbProtocol() == null || !(loadBalancer.getLbProtocol().equals(NetUtils.SSL_PROTO))) + throw new InvalidParameterValueException("Bad LB protocol: Expected ssl got " + loadBalancer.getLbProtocol()); + + + boolean success = false; + FirewallRule.State backupState = loadBalancer.getState(); + + try { + + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + LoadBalancerCertMapVO certMap = new LoadBalancerCertMapVO(lbRuleId,certId,false); + _lbCertMapDao.persist(certMap); + applyLoadBalancerConfig(loadBalancer.getId()); + /*s_logger.warn("Failed to apply Ssl Cert to LB " + loadBalancer.getId()); + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to apply Ssl Cert to LB " + loadBalancer.getId()); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); + throw ex;*/ + success = true; + } catch (ResourceUnavailableException e) { + if (isRollBackAllowedForProvider(loadBalancer)) { + + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + LoadBalancerCertMapVO certMap = _lbCertMapDao.findByLbRuleId(lbRuleId); + _lbCertMapDao.remove(certMap.getId()); + s_logger.debug("LB Rollback rule id: " + loadBalancer.getId() + " while adding cert"); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + } + return success; + } + + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_REMOVE, eventDescription = "removing certificate from load balancer", async = true) + public boolean removeCertFromLoadBalancer(long lbRuleId) { + CallContext caller = CallContext.current(); + + LoadBalancerVO loadBalancer = _lbDao.findById(lbRuleId); + LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lbRuleId); + + if (loadBalancer == null) { + throw new InvalidParameterException("Invalid load balancer value: " + lbRuleId); + } + + if( lbCertMap == null ) { + throw new InvalidParameterException("No certificate is bound to lb with id: " + lbRuleId); + } + + _accountMgr.checkAccess(caller.getCallingAccount(), null, true, loadBalancer); + + boolean success = false; + FirewallRule.State backupState = loadBalancer.getState(); + try { + + loadBalancer.setState(FirewallRule.State.Add); + _lbDao.persist(loadBalancer); + lbCertMap.setRevoke(true); + _lbCertMapDao.persist(lbCertMap); + + if (!applyLoadBalancerConfig(lbRuleId)) { + s_logger.warn("Failed to remove cert from load balancer rule id " + lbRuleId); + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to remove certificate load balancer rule id " + lbRuleId); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); + throw ex; + } + success = true; + } catch (ResourceUnavailableException e) { + if (isRollBackAllowedForProvider(loadBalancer)) { + lbCertMap.setRevoke(false); + _lbCertMapDao.persist(lbCertMap); + loadBalancer.setState(backupState); + _lbDao.persist(loadBalancer); + s_logger.debug("Rolled back certificate removal lb id " + lbRuleId); + } + s_logger.warn("Unable to apply the load balancer config because resource is unavaliable.", e); + if (!success) { + CloudRuntimeException ex = new CloudRuntimeException( + "Failed to remove certificate from load balancer rule id " + lbRuleId); + ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); + throw ex; + } + } + return success; + } + private boolean removeFromLoadBalancerInternal(long loadBalancerId, List instanceIds, boolean rollBack) { CallContext caller = CallContext.current(); @@ -1317,7 +1473,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements @Override @ActionEvent(eventType = EventTypes.EVENT_LOAD_BALANCER_CREATE, eventDescription = "creating load balancer") public LoadBalancer createPublicLoadBalancerRule(String xId, String name, String description, - int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd, Long ipAddrId, String protocol, String algorithm, long networkId, long lbOwnerId, boolean openFirewall) + int srcPortStart, int srcPortEnd, int defPortStart, int defPortEnd, Long ipAddrId, String protocol, String algorithm, + long networkId, long lbOwnerId, boolean openFirewall, String lbProtocol) throws NetworkRuleConflictException, InsufficientAddressCapacityException { Account lbOwner = _accountMgr.getAccount(lbOwnerId); @@ -1377,7 +1534,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements + network); } - result = createPublicLoadBalancer(xId, name, description, srcPortStart, defPortStart, ipVO.getId(), protocol, algorithm, openFirewall, CallContext.current()); + result = createPublicLoadBalancer(xId, name, description, srcPortStart, defPortStart, ipVO.getId(), protocol, + algorithm, openFirewall, CallContext.current(), lbProtocol); } catch (Exception ex) { s_logger.warn("Failed to create load balancer due to ", ex); if (ex instanceof NetworkRuleConflictException) { @@ -1407,13 +1565,14 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements @DB @Override public LoadBalancer createPublicLoadBalancer(final String xId, final String name, final String description, - final int srcPort, final int destPort, final long sourceIpId, final String protocol, final String algorithm, final boolean openFirewall, final CallContext caller) + final int srcPort, final int destPort, final long sourceIpId, final String protocol, + final String algorithm, final boolean openFirewall, final CallContext caller, final String lbProtocol) throws NetworkRuleConflictException { if (!NetUtils.isValidPort(destPort)) { throw new InvalidParameterValueException("privatePort is an invalid value: " + destPort); } - + if ((algorithm == null) || !NetUtils.isValidAlgorithm(algorithm)) { throw new InvalidParameterValueException("Invalid algorithm: " + algorithm); } @@ -1454,18 +1613,29 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements _firewallMgr.validateFirewallRule(caller.getCallingAccount(), ipAddr, srcPort, srcPort, protocol, Purpose.LoadBalancing, FirewallRuleType.User, networkId, null); + LoadBalancerVO newRule = new LoadBalancerVO(xId, name, description, + sourceIpId, srcPort, destPort, algorithm, + networkId, ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId(), lbProtocol); + + // verify rule is supported by Lb provider of the network + Ip sourceIp = getSourceIp(newRule); + LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), + new ArrayList(), new ArrayList(), sourceIp, null, lbProtocol); + if (!validateLbRule(loadBalancing)) { + throw new InvalidParameterValueException("LB service provider cannot support this rule"); + } return Transaction.execute(new TransactionCallbackWithException() { @Override public LoadBalancerVO doInTransaction(TransactionStatus status) throws NetworkRuleConflictException { LoadBalancerVO newRule = new LoadBalancerVO(xId, name, description, sourceIpId, srcPort, destPort, algorithm, - networkId, ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId()); + networkId, ipAddr.getAllocatedToAccountId(), ipAddr.getAllocatedInDomainId(), lbProtocol); // verify rule is supported by Lb provider of the network Ip sourceIp = getSourceIp(newRule); LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), - new ArrayList(), new ArrayList(), sourceIp); + new ArrayList(), new ArrayList(), sourceIp, null, lbProtocol); if (!validateLbRule(loadBalancing)) { throw new InvalidParameterValueException("LB service provider cannot support this rule"); } @@ -1510,7 +1680,6 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements } - @Override public boolean applyLoadBalancerConfig(long lbRuleId) throws ResourceUnavailableException { LoadBalancerVO lb = _lbDao.findById(lbRuleId); List lbs; @@ -1576,7 +1745,8 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements List policyList = getStickinessPolicies(lb.getId()); Ip sourceIp = getSourceIp(lb); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, null, policyList, null, sourceIp); + LbSslCert sslCert = getLbSslCert(lb.getId()); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, null, policyList, null, sourceIp, sslCert, lb.getLbProtocol()); if (_autoScaleVmGroupDao.isAutoScaleLoadBalancer(lb.getId())) { // Get the associated VmGroup @@ -1659,7 +1829,13 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements _lb2healthcheckDao.remove(lb.getId(), true); s_logger.debug("Load balancer rule id " + lb.getId() + " is removed health check monitors policies"); } - + + LoadBalancerCertMapVO lbCertMap = _lbCertMapDao.findByLbRuleId(lb.getId()); + if (lbCertMap != null && lbCertMap.isRevoke()) { + _lbCertMapDao.remove(lbCertMap.getId()); + s_logger.debug("Load balancer rule id " + lb.getId() + " removed certificate mapping"); + } + return checkForReleaseElasticIp; } }); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index a93480be56d..b8d0babe3f9 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; +import java.util.UUID; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -41,14 +42,24 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.agent.api.to.*; +import com.cloud.api.ApiAsyncJobDispatcher; +import com.cloud.api.ApiDispatcher; +import com.cloud.api.ApiGsonHelper; +import com.cloud.maint.Version; +import com.cloud.utils.component.ComponentContext; +import org.apache.cloudstack.api.command.admin.router.RebootRouterCmd; import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.framework.jobs.AsyncJobManager; +import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.utils.identity.ManagementServerNode; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -82,17 +93,12 @@ 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.SetFirewallRulesCommand; +import com.cloud.agent.api.routing.SetMonitorServiceCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; 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; -import com.cloud.agent.api.to.LoadBalancerTO; -import com.cloud.agent.api.to.PortForwardingRuleTO; -import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.agent.manager.Commands; import com.cloud.alert.AlertManager; import com.cloud.cluster.ClusterManager; @@ -164,6 +170,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVMMapDao; import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.MonitoringServiceVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; @@ -175,14 +182,17 @@ import com.cloud.network.dao.Site2SiteVpnGatewayDao; import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.network.dao.VirtualRouterProviderDao; import com.cloud.network.dao.VpnUserDao; +import com.cloud.network.dao.MonitoringServiceDao; import com.cloud.network.lb.LoadBalancingRule; import com.cloud.network.lb.LoadBalancingRule.LbDestination; import com.cloud.network.lb.LoadBalancingRule.LbHealthCheckPolicy; import com.cloud.network.lb.LoadBalancingRule.LbStickinessPolicy; +import com.cloud.network.lb.LoadBalancingRule.LbSslCert; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.router.VirtualRouter.RedundantState; import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.MonitoringService; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.network.rules.PortForwardingRule; @@ -369,8 +379,13 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V IpAddressManager _ipAddrMgr; @Inject ConfigDepot _configDepot; + @Inject + MonitoringServiceDao _monitorServiceDao; + @Inject + AsyncJobManager _asyncMgr; + @Inject + protected ApiAsyncJobDispatcher _asyncDispatcher; - int _routerRamSize; int _routerCpuMHz; int _retry = 2; @@ -2332,10 +2347,56 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V finalizeUserDataAndDhcpOnStart(cmds, router, provider, guestNetworkId); } + finalizeMonitorServiceOnStrat(cmds, router, provider, routerGuestNtwkIds.get(0)); return true; } + private void finalizeMonitorServiceOnStrat(Commands cmds, DomainRouterVO router, Provider provider, long networkId) { + + NetworkVO network = _networkDao.findById(networkId); + + s_logger.debug("Creating monitoring services on "+ router +" start..."); + + + // get the list of sevices for this network to monitor + List services = new ArrayList(); + if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, Provider.VirtualRouter) || + _networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dns, Provider.VirtualRouter)) { + MonitoringServiceVO dhcpService = _monitorServiceDao.getServiceByName(MonitoringService.Service.Dhcp.toString()); + if (dhcpService != null) { + services.add(dhcpService); + } + } + + if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Lb, Provider.VirtualRouter)) { + MonitoringServiceVO lbService = _monitorServiceDao.getServiceByName(MonitoringService.Service.LoadBalancing.toString()); + if (lbService != null) { + services.add(lbService); + } + } + List defaultServices = _monitorServiceDao.listDefaultServices(true); + services.addAll(defaultServices); + + List servicesTO = new ArrayList(); + for (MonitoringServiceVO service: services) { + MonitorServiceTO serviceTO = new MonitorServiceTO( service.getService(), service.getProcessname(), service.getServiceName(), service.getServicePath(), + service.getPidFile(), service.isDefaultService()); + servicesTO.add(serviceTO); + } + + SetMonitorServiceCommand command = new SetMonitorServiceCommand(servicesTO); + command.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); + command.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, getRouterIpInNetwork(networkId, router.getId())); + command.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + + cmds.addCommand("monitor", command); + } + + + + + protected NicProfile getControlNic(VirtualMachineProfile profile) { DomainRouterVO router = _routerDao.findById(profile.getId()); DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); @@ -2484,7 +2545,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List policyList = _lbMgr.getStickinessPolicies(lb.getId()); List hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId()); Ip sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress(); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp); + LbSslCert sslCert = _lbMgr.getLbSslCert(lb.getId()); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp, sslCert, lb.getLbProtocol()); lbRules.add(loadBalancing); } } @@ -3385,7 +3447,7 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V } private void createVmDataCommand(VirtualRouter router, UserVm vm, NicVO nic, String publicKey, Commands cmds) { - String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()).getDisplayText(); + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); String zoneName = _dcDao.findById(router.getDataCenterId()).getName(); cmds.addCommand("vmdata", generateVmDataCommand(router, nic.getIp4Address(), vm.getUserData(), serviceOffering, zoneName, nic.getIp4Address(), @@ -3503,6 +3565,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()); + } Answer[] answers = null; try { answers = _agentMgr.send(router.getHostId(), cmds); @@ -3617,8 +3682,11 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List dstList = _lbMgr.getExistingDestinations(lb.getId()); List policyList = _lbMgr.getStickinessPolicies(lb.getId()); List hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId()); + LbSslCert sslCert = _lbMgr.getLbSslCert(lb.getId()); Ip sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress(); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp, + sslCert, lb.getLbProtocol()); + lbRules.add(loadBalancing); } return sendLBRules(router, lbRules, network.getId()); @@ -3653,8 +3721,9 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V List dstList = _lbMgr.getExistingDestinations(lb.getId()); List policyList = _lbMgr.getStickinessPolicies(lb.getId()); List hcPolicyList = _lbMgr.getHealthCheckPolicies(lb.getId()); + LbSslCert sslCert = _lbMgr.getLbSslCert(lb.getId()); Ip sourceIp = _networkModel.getPublicIpAddress(lb.getSourceIpAddressId()).getAddress(); - LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp); + LoadBalancingRule loadBalancing = new LoadBalancingRule(lb, dstList, policyList, hcPolicyList, sourceIp, sslCert, lb.getLbProtocol()); lbRules.add(loadBalancing); } return sendLBRules(router, lbRules, network.getId()); @@ -4071,4 +4140,94 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V public VirtualRouter findRouter(long routerId) { return _routerDao.findById(routerId); } + + @Override + public List upgradeRouterTemplate(UpgradeRouterTemplateCmd cmd){ + + List routers = new ArrayList(); + int params = 0; + + Long routerId = cmd.getId(); + if(routerId != null) { + params++; + DomainRouterVO router = _routerDao.findById(routerId); + if(router != null){ + routers.add(router); + } + } + + Long accountId = cmd.getAccountId(); + if(accountId != null){ + params++; + routers = _routerDao.listBy(accountId); + } + + Long domainId = cmd.getDomainId(); + if(domainId != null){ + params++; + routers = _routerDao.listByDomain(domainId); + } + + Long clusterId = cmd.getClusterId(); + if(clusterId != null){ + params++; + routers = _routerDao.listByClusterId(clusterId); + } + + Long podId = cmd.getPodId(); + if(podId != null){ + params++; + routers = _routerDao.listByPodId(podId); + } + + Long zoneId = cmd.getZoneId(); + if(zoneId != null){ + params++; + routers = _routerDao.listByDataCenter(zoneId); + } + + if(params > 1){ + throw new InvalidParameterValueException("Multiple parameters not supported. Specify only one among routerId/zoneId/podId/clusterId/accountId/domainId"); + } + + if(routers != null){ + return rebootRouters(routers); + } + + return null; + } + + //Checks if the router is at the required version + // Compares MS version and router version + private boolean checkRouterVersion(VirtualRouter router){ + String trimmedVersion = Version.trimRouterVersion(router.getTemplateVersion()); + return (Version.compare(trimmedVersion, _minVRVersion) >= 0); + } + + private List rebootRouters(List routers){ + List jobIds = new ArrayList(); + for(DomainRouterVO router: routers){ + if(!checkRouterVersion(router)){ + s_logger.debug("Upgrading template for router: "+router.getId()); + ApiDispatcher.getInstance(); + Map params = new HashMap(); + params.put("ctxUserId", "1"); + params.put("ctxAccountId", "" + router.getAccountId()); + + RebootRouterCmd cmd = new RebootRouterCmd(); + ComponentContext.inject(cmd); + params.put("id", ""+router.getId()); + params.put("ctxStartEventId", "1"); + AsyncJobVO job = new AsyncJobVO(UUID.randomUUID().toString(), User.UID_SYSTEM, router.getAccountId(), RebootRouterCmd.class.getName(), + ApiGsonHelper.getBuilder().create().toJson(params), router.getId(), + cmd.getInstanceType() != null ? cmd.getInstanceType().toString() : null); + job.setDispatcher(_asyncDispatcher.getName()); + long jobId = _asyncMgr.submitAsyncJob(job); + jobIds.add(jobId); + } else { + s_logger.debug("Router: "+router.getId()+" is already at the latest version. No upgrade required" ); + } + } + return jobIds; + } } diff --git a/server/src/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/com/cloud/network/vpc/VpcManagerImpl.java index 6e0b54d9b9a..017d38df3bb 100644 --- a/server/src/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/com/cloud/network/vpc/VpcManagerImpl.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -31,6 +32,8 @@ 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.api.command.user.vpc.ListPrivateGatewaysCmd; import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; @@ -39,7 +42,6 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; -import org.apache.log4j.Logger; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; @@ -118,13 +120,13 @@ import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallback; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionCallbackWithException; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.db.SearchCriteria.Op; -import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.ExceptionUtil; import com.cloud.utils.net.NetUtils; @@ -203,7 +205,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis private List vpcElements = null; private final List nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall); private final List supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler); - + int _cleanupInterval; int _maxNetworks; SearchBuilder IpAddressSearch; @@ -217,7 +219,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis public void doInTransactionWithoutResult(TransactionStatus status) { if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCOfferingName) == null) { s_logger.debug("Creating default VPC offering " + VpcOffering.defaultVPCOfferingName); - + Map> svcProviderMap = new HashMap>(); Set defaultProviders = new HashSet(); defaultProviders.add(Provider.VPCVirtualRouter); @@ -232,9 +234,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, - true, State.Enabled); + true, + State.Enabled, null); } - + //configure default vpc offering with Netscaler as LB Provider if (_vpcOffDao.findByUniqueName(VpcOffering.defaultVPCNSOfferingName ) == null) { s_logger.debug("Creating default VPC offering with Netscaler as LB Provider" + VpcOffering.defaultVPCNSOfferingName); @@ -252,11 +255,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, - svcProviderMap, false, State.Enabled); + svcProviderMap, + false, State.Enabled, null); } } }); - + Map configs = _configDao.getConfiguration(params); String value = configs.get(Config.VpcCleanupInterval.key()); _cleanupInterval = NumbersUtil.parseInt(value, 60 * 60); // 1 hour @@ -273,7 +277,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis virtualNetworkVlanSB.and("vlanType", virtualNetworkVlanSB.entity().getVlanType(), Op.EQ); IpAddressSearch.join("virtualNetworkVlanSB", virtualNetworkVlanSB, IpAddressSearch.entity().getVlanId(), virtualNetworkVlanSB.entity().getId(), JoinBuilder.JoinType.INNER); IpAddressSearch.done(); - + return true; } @@ -300,7 +304,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create=true) - public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders) { + public VpcOffering createVpcOffering(String name, String displayText, List supportedServices, + Map> serviceProviders, Long serviceOfferingId) { Map> svcProviderMap = new HashMap>(); Set defaultProviders = new HashSet(); defaultProviders.add(Provider.VPCVirtualRouter); @@ -328,35 +333,35 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (service == Service.NetworkACL) { firewallSvs = true; } - + if (service == Service.SourceNat) { sourceNatSvc = true; } } - + if (!sourceNatSvc) { s_logger.debug("Automatically adding source nat service to the list of VPC services"); svcProviderMap.put(Service.SourceNat, defaultProviders); } - + if (!firewallSvs) { s_logger.debug("Automatically adding network ACL service to the list of VPC services"); svcProviderMap.put(Service.NetworkACL, defaultProviders); } - + svcProviderMap.put(Service.Gateway, defaultProviders); if (serviceProviders != null) { - for (String serviceStr : serviceProviders.keySet()) { - Network.Service service = Network.Service.getService(serviceStr); + for (Entry> serviceEntry : serviceProviders.entrySet()) { + Network.Service service = Network.Service.getService(serviceEntry.getKey()); if (svcProviderMap.containsKey(service)) { Set providers = new HashSet(); // don't allow to specify more than 1 provider per service - if (serviceProviders.get(serviceStr) != null && serviceProviders.get(serviceStr).size() > 1) { + if (serviceEntry.getValue() != null && serviceEntry.getValue().size() > 1) { throw new InvalidParameterValueException("In the current release only one provider can be " + "specified for the service"); } - for (String prvNameStr : serviceProviders.get(serviceStr)) { + for (String prvNameStr : serviceEntry.getValue()) { // check if provider is supported Network.Provider provider = Network.Provider.getProvider(prvNameStr); if (provider == null) { @@ -367,27 +372,28 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } svcProviderMap.put(service, providers); } else { - throw new InvalidParameterValueException("Service " + serviceStr + " is not enabled for the network " + + throw new InvalidParameterValueException("Service " + serviceEntry.getKey() + " is not enabled for the network " + "offering, can't add a provider to it"); } } } - VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null); + VpcOffering offering = createVpcOffering(name, displayText, svcProviderMap, false, null, serviceOfferingId); CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); - + return offering; } - + @DB protected VpcOffering createVpcOffering(final String name, final String displayText, final Map> svcProviderMap, final boolean isDefault, final State state) { + Set> svcProviderMap, final boolean isDefault, final State state, + final Long serviceOfferingId) { return Transaction.execute(new TransactionCallback() { @Override public VpcOffering doInTransaction(TransactionStatus status) { // create vpc offering object - VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, null); + VpcOfferingVO offering = new VpcOfferingVO(name, displayText, isDefault, serviceOfferingId); if (state != null) { offering.setState(state); @@ -414,7 +420,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } }); } - + @Override public Vpc getActiveVpc(long vpcId) { return _vpcDao.getActiveVpcById(vpcId); @@ -438,8 +444,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return serviceProviderMap; } - - + + @Override public List listVpcOfferings(Long id, String name, String displayText, List supportedServicesStr, Boolean isDefault, String keyword, String state, Long startIndex, Long pageSizeVal) { @@ -479,7 +485,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // filter by supported services boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !offerings.isEmpty()); - + if (listBySupportedServices) { List supportedOfferings = new ArrayList(); Service[] supportedServices = null; @@ -514,8 +520,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis protected boolean areServicesSupportedByVpcOffering(long vpcOffId, Service... services) { return (_vpcOffSvcMapDao.areServicesSupportedByNetworkOffering(vpcOffId, services)); } - - + + @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_DELETE, eventDescription = "deleting vpc offering") public boolean deleteVpcOffering(long offId) { @@ -594,10 +600,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis String networkDomain) throws ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); Account owner = _accountMgr.getAccount(vpcOwnerId); - + //Verify that caller can perform actions in behalf of vpc owner _accountMgr.checkAccess(caller, null, false, owner); - + //check resource limit _resourceLimitMgr.checkResourceLimit(owner, ResourceType.vpc); @@ -613,20 +619,20 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } throw ex; } - + //Validate zone DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); if (zone == null) { throw new InvalidParameterValueException("Can't find zone by id specified"); } - + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { // See DataCenterVO.java PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled"); ex.addProxyObject(zone.getUuid(), "zoneId"); throw ex; } - + if (networkDomain == null) { // 1) Get networkDomain from the corresponding account networkDomain = _ntwkModel.getAccountNetworkDomain(owner.getId(), zoneId); @@ -637,11 +643,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis networkDomain = "cs" + Long.toHexString(owner.getId()) + NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId); } } - + return createVpc(zoneId, vpcOffId, owner, vpcName, displayText, cidr, networkDomain); } - + @DB protected Vpc createVpc(final long zoneId, final long vpcOffId, final Account vpcOwner, final String vpcName, final String displayText, final String cidr, final String networkDomain) { @@ -660,8 +666,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis 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', " + "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 \"-\""); } @@ -698,7 +704,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw new InvalidParameterValueException("Provider " + provider + " should be enabled in at least one physical network of the zone specified"); } - + List providers = null; if (svcProviders.get(service) == null) { providers = new ArrayList(); @@ -723,10 +729,10 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (vpc == null) { throw new InvalidParameterValueException("unable to find VPC id=" + vpcId); } - + //verify permissions _accountMgr.checkAccess(ctx.getCallingAccount(), null, false, vpc); - + return destroyVpc(vpc, ctx.getCallingAccount(), ctx.getCallingUserId()); } @@ -734,7 +740,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @DB public boolean destroyVpc(final Vpc vpc, Account caller, Long callerUserId) throws ConcurrentOperationException, ResourceUnavailableException { s_logger.debug("Destroying vpc " + vpc); - + //don't allow to delete vpc if it's in use by existing non system networks (system networks are networks of a private gateway of the VPC, //and they will get removed as a part of VPC cleanup int networksCount = _ntwkDao.getNonSystemNetworkCountByVpcId(vpc.getId()); @@ -747,7 +753,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis s_logger.debug("Updating VPC " + vpc + " with state " + Vpc.State.Inactive + " as a part of vpc delete"); final VpcVO vpcVO = _vpcDao.findById(vpc.getId()); vpcVO.setState(Vpc.State.Inactive); - + Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -758,13 +764,13 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } }); } - + //shutdown VPC if (!shutdownVpc(vpc.getId())) { s_logger.warn("Failed to shutdown vpc " + vpc + " as a part of vpc destroy process"); return false; } - + //cleanup vpc resources if (!cleanupVpcResources(vpc.getId(), caller, callerUserId)) { s_logger.warn("Failed to cleanup resources for vpc " + vpc); @@ -792,7 +798,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (vpcToUpdate == null) { throw new InvalidParameterValueException("Unable to find vpc offering " + vpcId); } - + _accountMgr.checkAccess(caller, null, false, vpcToUpdate); VpcVO vpc = _vpcDao.createForUpdate(vpcId); @@ -820,7 +826,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Long startIndex, Long pageSizeVal, Long zoneId, Boolean isRecursive, Boolean listAll, Boolean restartRequired, Map tags, Long projectId) { Account caller = CallContext.current().getCallingAccount(); List permittedAccounts = new ArrayList(); - + Ternary domainIdRecursiveListProject = new Ternary(domainId, isRecursive, null); _accountMgr.buildACLSearchParameters(caller, id, accountName, projectId, permittedAccounts, domainIdRecursiveListProject, @@ -841,7 +847,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("restartRequired", sb.entity().isRestartRequired(), SearchCriteria.Op.EQ); sb.and("cidr", sb.entity().getCidr(), SearchCriteria.Op.EQ); - + if (tags != null && !tags.isEmpty()) { SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); for (int count=0; count < tags.size(); count++) { @@ -853,7 +859,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis sb.groupBy(sb.entity().getId()); sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); } - + // now set the SC criteria... SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); @@ -872,7 +878,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (displayText != null) { sc.addAnd("displayText", SearchCriteria.Op.LIKE, "%" + displayText + "%"); } - + if (tags != null && !tags.isEmpty()) { int count = 0; sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.Vpc.toString()); @@ -881,28 +887,28 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); count++; } - } + } if (id != null) { sc.addAnd("id", SearchCriteria.Op.EQ, id); } - + if (vpcOffId != null) { sc.addAnd("vpcOfferingId", SearchCriteria.Op.EQ, vpcOffId); } - + if (zoneId != null) { sc.addAnd("zoneId", SearchCriteria.Op.EQ, zoneId); } - + if (state != null) { sc.addAnd("state", SearchCriteria.Op.EQ, state); } - + if (cidr != null) { sc.addAnd("cidr", SearchCriteria.Op.EQ, cidr); } - + if (restartRequired != null) { sc.addAnd("restartRequired", SearchCriteria.Op.EQ, restartRequired); } @@ -911,7 +917,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // filter by supported services boolean listBySupportedServices = (supportedServicesStr != null && !supportedServicesStr.isEmpty() && !vpcs.isEmpty()); - + if (listBySupportedServices) { List supportedVpcs = new ArrayList(); Service[] supportedServices = null; @@ -942,7 +948,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } - + protected List getSupportedServices() { List services = new ArrayList(); services.add(Network.Service.Dhcp); @@ -957,14 +963,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis services.add(Network.Service.Vpn); return services; } - + @Override public boolean startVpc(long vpcId, boolean destroyOnFailure) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { CallContext ctx = CallContext.current(); Account caller = ctx.getCallingAccount(); User callerUser = _accountMgr.getActiveUser(ctx.getCallingUserId()); - + //check if vpc exists Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { @@ -972,16 +978,16 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } - + //permission check _accountMgr.checkAccess(caller, null, false, vpc); - + DataCenter dc = _entityMgr.findById(DataCenter.class, vpc.getZoneId()); - + DeployDestination dest = new DeployDestination(dc, null, null, null); ReservationContext context = new ReservationContextImpl(null, null, callerUser, _accountMgr.getAccount(vpc.getAccountId())); - + boolean result = true; try { if (!startVpc(vpc, dest, context)) { @@ -1022,18 +1028,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } return success; } - + @Override public boolean shutdownVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException { CallContext ctx = CallContext.current(); Account caller = ctx.getCallingAccount(); - + //check if vpc exists Vpc vpc = _vpcDao.findById(vpcId); if (vpc == null) { throw new InvalidParameterValueException("Unable to find vpc by id " + vpcId); } - + //permission check _accountMgr.checkAccess(caller, null, false, vpc); @@ -1057,18 +1063,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return success; } - + @DB @Override public void validateNtwkOffForNtwkInVpc(Long networkId, long newNtwkOffId, String newCidr, String newNetworkDomain, Vpc vpc, String gateway, Account networkOwner, Long aclId) { - + NetworkOffering guestNtwkOff = _entityMgr.findById(NetworkOffering.class, newNtwkOffId); - + if (guestNtwkOff == null) { throw new InvalidParameterValueException("Can't find network offering by id specified"); } - + if (networkId == null) { //1) Validate attributes that has to be passed in when create new guest network validateNewVpcGuestNetwork(newCidr, gateway, networkOwner, vpc, newNetworkDomain); @@ -1080,7 +1086,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis //3) Check services/providers against VPC providers List networkProviders = _ntwkOffServiceDao.listByNetworkOfferingId(guestNtwkOff.getId()); - + for (NetworkOfferingServiceMapVO nSvcVO : networkProviders) { String pr = nSvcVO.getProvider(); String service = nSvcVO.getService(); @@ -1101,7 +1107,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis NetworkOffering otherOff = _entityMgr.findById(NetworkOffering.class, network.getNetworkOfferingId()); if (_ntwkModel.areServicesSupportedInNetwork(network.getId(), Service.Lb) && otherOff.getPublicLb()) { throw new InvalidParameterValueException("Public LB service is already supported " + - "by network " + network + " in VPC " + vpc); + "by network " + network + " in VPC " + vpc); } } } @@ -1124,7 +1130,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis + " is not supported for network offerings that can be used in VPC"); } } - + //2) Only Isolated networks with Source nat service enabled can be added to vpc if (!(guestNtwkOff.getGuestType() == GuestType.Isolated && supportedSvcs.contains(Service.SourceNat))) { @@ -1144,7 +1150,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw new InvalidParameterValueException("Only networks with conserve mode Off can belong to VPC"); } - //5) If Netscaler is LB provider make sure it is in dedicated mode + //5) If Netscaler is LB provider make sure it is in dedicated mode if ( providers.contains(Provider.Netscaler) && !guestNtwkOff.getDedicatedLB() ) { throw new InvalidParameterValueException("Netscaler only with Dedicated LB can belong to VPC"); } @@ -1153,7 +1159,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @DB protected void validateNewVpcGuestNetwork(final String cidr, final String gateway, final Account networkOwner, final Vpc vpc, final String networkDomain) { - + Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -1161,49 +1167,49 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (locked == null) { throw new CloudRuntimeException("Unable to acquire lock on " + vpc); } - + try { //check number of active networks in vpc if (_ntwkDao.countVpcNetworks(vpc.getId()) >= _maxNetworks) { throw new CloudRuntimeException("Number of networks per VPC can't extend " + _maxNetworks + "; increase it using global config " + Config.VpcMaxNetworks); } - - + + //1) CIDR is required if (cidr == null) { throw new InvalidParameterValueException("Gateway/netmask are required when create network for VPC"); } - + //2) Network cidr should be within vpcCidr if (!NetUtils.isNetworkAWithinNetworkB(cidr, vpc.getCidr())) { throw new InvalidParameterValueException("Network cidr " + cidr + " is not within vpc " + vpc + " cidr"); } - + //3) Network cidr shouldn't cross the cidr of other vpc network cidrs List ntwks = _ntwkDao.listByVpc(vpc.getId()); for (Network ntwk : ntwks) { assert (cidr != null) : "Why the network cidr is null when it belongs to vpc?"; - + if (NetUtils.isNetworkAWithinNetworkB(ntwk.getCidr(), cidr) || NetUtils.isNetworkAWithinNetworkB(cidr, ntwk.getCidr())) { throw new InvalidParameterValueException("Network cidr " + cidr + " crosses other network cidr " + ntwk + " belonging to the same vpc " + vpc); } } - + //4) vpc and network should belong to the same owner if (vpc.getAccountId() != networkOwner.getId()) { throw new InvalidParameterValueException("Vpc " + vpc + " owner is different from the network owner " + networkOwner); } - + //5) network domain should be the same as VPC's if (!networkDomain.equalsIgnoreCase(vpc.getNetworkDomain())) { throw new InvalidParameterValueException("Network domain of the new network should match network" + - " domain of vpc " + vpc); + " domain of vpc " + vpc); } - + //6) gateway should never be equal to the cidr subnet if (NetUtils.getCidrSubNet(cidr).equalsIgnoreCase(gateway)) { throw new InvalidParameterValueException("Invalid gateway specified. It should never be equal to the cidr subnet value"); @@ -1229,12 +1235,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return vpcElements; } - + @Override public List getVpcsForAccount(long accountId) { return _vpcDao.listByAccountId(accountId); } - + public boolean cleanupVpcResources(long vpcId, Account caller, long callerUserId) throws ResourceUnavailableException, ConcurrentOperationException { s_logger.debug("Cleaning up resources for vpc id=" + vpcId); @@ -1245,7 +1251,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _s2sVpnMgr.cleanupVpnConnectionByVpc(vpcId); s_logger.debug("Cleaning up existed site to site VPN gateways"); _s2sVpnMgr.cleanupVpnGatewayByVpc(vpcId); - + //2) release all ip addresses List ipsToRelease = _ipAddressDao.listByAssociatedVpc(vpcId, null); s_logger.debug("Releasing ips for vpc id=" + vpcId + " as a part of vpc cleanup"); @@ -1264,7 +1270,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } } - + if (success) { s_logger.debug("Released ip addresses for vpc id=" + vpcId + " as a part of cleanup vpc process"); } else { @@ -1293,7 +1299,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } } - + return success; } @@ -1301,7 +1307,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_RESTART, eventDescription = "restarting vpc") public boolean restartVpc(long vpcId) throws ConcurrentOperationException, ResourceUnavailableException, - InsufficientCapacityException { + InsufficientCapacityException { Account caller = CallContext.current().getCallingAccount(); // Verify input parameters @@ -1311,9 +1317,9 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis ex.addProxyObject(String.valueOf(vpcId), "VPC"); throw ex; } - + _accountMgr.checkAccess(caller, null, false, vpc); - + s_logger.debug("Restarting VPC " + vpc); boolean restartRequired = false; try { @@ -1323,7 +1329,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis restartRequired = true; return false; } - + s_logger.debug("Starting VPC " + vpc + " as a part of VPC restart process"); if (!startVpc(vpcId, false)) { s_logger.warn("Failed to start vpc as a part of VPC " + vpc + " restart process"); @@ -1339,7 +1345,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _vpcDao.update(vpc.getId(), vo); } } - + @Override public List getVpcPrivateGateways(long vpcId) { @@ -1365,7 +1371,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } return getPrivateGatewayProfile(gateway); } - + protected PrivateGateway getPrivateGatewayProfile(VpcGateway gateway) { Network network = _ntwkModel.getNetwork(gateway.getNetworkId()); return new PrivateGatewayProfile(gateway, network.getPhysicalNetworkId()); @@ -1377,7 +1383,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis public PrivateGateway createVpcPrivateGateway(final long vpcId, Long physicalNetworkId, final String broadcastUri, final String ipAddress, final String gateway, final String netmask, final long gatewayOwnerId, final Long networkOfferingId, final Boolean isSourceNat, final Long aclId) throws ResourceAllocationException, ConcurrentOperationException, InsufficientCapacityException { - + //Validate parameters final Vpc vpc = getActiveVpc(vpcId); if (vpc == null) { @@ -1396,12 +1402,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis physNet = pNtwks.get(0); physicalNetworkId = physNet.getId(); } - + if (physNet == null) { physNet = _entityMgr.findById(PhysicalNetwork.class,physicalNetworkId); } final Long dcId = physNet.getDataCenterId(); - + final Long physicalNetworkIdFinal = physicalNetworkId; final PhysicalNetwork physNetFinal = physNet; VpcGatewayVO gatewayVO = null; @@ -1414,7 +1420,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis Network privateNtwk = null; if (BroadcastDomainType.getSchemeValue(BroadcastDomainType.fromString(broadcastUri)) == BroadcastDomainType.Lswitch) { String cidr = NetUtils.ipAndNetMaskToCidr(gateway, netmask); - + privateNtwk = _ntwkDao.getPrivateNetwork(broadcastUri, cidr, gatewayOwnerId, dcId, networkOfferingId); s_logger.info("found and using existing network for vpc " + vpc + ": " + broadcastUri); @@ -1422,28 +1428,28 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if (privateNtwk == null) { s_logger.info("creating new network for vpc " + vpc + " using broadcast uri: " + broadcastUri); String networkName = "vpc-" + vpc.getName() + "-privateNetwork"; - privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkIdFinal, - broadcastUri, ipAddress, null, gateway, netmask, gatewayOwnerId, vpcId, isSourceNat, networkOfferingId); + privateNtwk = _ntwkSvc.createPrivateNetwork(networkName, networkName, physicalNetworkIdFinal, + broadcastUri, ipAddress, null, gateway, netmask, gatewayOwnerId, vpcId, isSourceNat, networkOfferingId); } else { // create the nic/ip as createPrivateNetwork doesn''t do that work for us now DataCenterVO dc = _dcDao.lockRow(physNetFinal.getDataCenterId(), true); - + //add entry to private_ip_address table PrivateIpVO privateIp = _privateIpDao.findByIpAndSourceNetworkId(privateNtwk.getId(), ipAddress); if (privateIp != null) { throw new InvalidParameterValueException("Private ip address " + ipAddress + " already used for private gateway" + " in zone " + _entityMgr.findById(DataCenter.class,dcId).getName()); } - + Long mac = dc.getMacAddress(); Long nextMac = mac + 1; dc.setMacAddress(nextMac); - + privateIp = new PrivateIpVO(ipAddress, privateNtwk.getId(), nextMac, vpcId, true); _privateIpDao.persist(privateIp); - + _dcDao.update(dc.getId(), dc); } - + long networkAclId = NetworkACL.DEFAULT_DENY; if (aclId != null) { NetworkACLVO aclVO = _networkAclDao.findById(aclId); @@ -1453,17 +1459,17 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis if ((aclVO.getVpcId() != vpcId) && !(aclId == NetworkACL.DEFAULT_DENY || aclId == NetworkACL.DEFAULT_ALLOW)) { throw new InvalidParameterValueException("Private gateway and network acl are not in the same vpc"); } - + networkAclId = aclId; } - + //2) create gateway entry VpcGatewayVO gatewayVO = new VpcGatewayVO(ipAddress, VpcGateway.Type.Private, vpcId, privateNtwk.getDataCenterId(), privateNtwk.getId(), broadcastUri, gateway, netmask, vpc.getAccountId(), vpc.getDomainId(), isSourceNat, networkAclId); _vpcGatewayDao.persist(gatewayVO); - + s_logger.debug("Created vpc gateway entry " + gatewayVO); - + return gatewayVO; } }); @@ -1473,7 +1479,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis ExceptionUtil.rethrow(e, ResourceAllocationException.class); throw new IllegalStateException(e); } - + return getVpcPrivateGateway(gatewayVO.getId()); } @@ -1521,7 +1527,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @ActionEvent(eventType = EventTypes.EVENT_PRIVATE_GATEWAY_DELETE, eventDescription = "deleting private gateway") @DB public boolean deleteVpcPrivateGateway(long gatewayId) throws ConcurrentOperationException, ResourceUnavailableException { - + final VpcGatewayVO gatewayVO = _vpcGatewayDao.acquireInLockTable(gatewayId); if (gatewayVO == null || gatewayVO.getType() != VpcGateway.Type.Private) { throw new ConcurrentOperationException("Unable to lock gateway " + gatewayId); @@ -1555,29 +1561,29 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return false; } } - + //2) Delete private gateway from the DB return deletePrivateGatewayFromTheDB(gateway); - + } finally { if (gatewayVO != null) { _vpcGatewayDao.releaseFromLockTable(gatewayId); } } } - + @DB protected boolean deletePrivateGatewayFromTheDB(final PrivateGateway gateway) { //check if there are ips allocted in the network final long networkId = gateway.getNetworkId(); - + boolean deleteNetwork = true; List privateIps = _privateIpDao.listByNetworkId(networkId); if (privateIps.size() > 1 || !privateIps.get(0).getIpAddress().equalsIgnoreCase(gateway.getIp4Address())) { s_logger.debug("Not removing network id=" + gateway.getNetworkId() + " as it has private ip addresses for other gateways"); deleteNetwork = false; } - + //TODO: Clean this up, its bad. There is a DB transaction wrapping calls to NetworkElements (destroyNetwork will // call network elements). final boolean deleteNetworkFinal = deleteNetwork; @@ -1589,7 +1595,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _privateIpDao.remove(ip.getId()); s_logger.debug("Deleted private ip " + ip); } - + if (deleteNetworkFinal) { User callerUser = _accountMgr.getActiveUser(CallContext.current().getCallingUserId()); Account owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); @@ -1597,12 +1603,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis _ntwkMgr.destroyNetwork(networkId, context); s_logger.debug("Deleted private network id=" + networkId); } - + _vpcGatewayDao.remove(gateway.getId()); s_logger.debug("Deleted private gateway " + gateway); } }); - + return true; } @@ -1632,45 +1638,45 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis SearchBuilder sb = _vpcGatewayDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - + if (vlan != null) { SearchBuilder ntwkSearch = _ntwkDao.createSearchBuilder(); ntwkSearch.and("vlan", ntwkSearch.entity().getBroadcastUri(), SearchCriteria.Op.EQ); sb.join("networkSearch", ntwkSearch, sb.entity().getNetworkId(), ntwkSearch.entity().getId(), JoinBuilder.JoinType.INNER); } - + SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - + if (id != null) { sc.addAnd("id", Op.EQ, id); } - + if (ipAddress != null) { sc.addAnd("ip4Address", Op.EQ, ipAddress); } - + if (state != null) { sc.addAnd("state", Op.EQ, state); } - + if (vpcId != null) { sc.addAnd("vpcId", Op.EQ, vpcId); } - + if (vlan != null) { sc.setJoinParameters("networkSearch", "vlan", BroadcastDomainType.Vlan.toUri(vlan)); } - + Pair, Integer> vos = _vpcGatewayDao.searchAndCount(sc, searchFilter); List privateGtws = new ArrayList(vos.first().size()); for (VpcGateway vo : vos.first()) { privateGtws.add(getPrivateGatewayProfile(vo)); } - + return new Pair, Integer>(privateGtws, vos.second()); } - + @Override public StaticRoute getStaticRoute(long routeId) { return _staticRouteDao.findById(routeId); @@ -1716,14 +1722,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return success; } - + protected boolean applyStaticRoutes(List routes) throws ResourceUnavailableException{ if (routes.isEmpty()) { s_logger.debug("No static routes to apply"); return true; } Vpc vpc = _vpcDao.findById(routes.get(0).getVpcId()); - + s_logger.debug("Applying static routes for vpc " + vpc); String staticNatProvider = _vpcSrvcDao.getProviderForServiceInVpc(vpc.getId(), Service.StaticNat); @@ -1739,7 +1745,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return false; } } - + return true; } @@ -1747,19 +1753,19 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_DELETE, eventDescription = "deleting static route") public boolean revokeStaticRoute(long routeId) throws ResourceUnavailableException { Account caller = CallContext.current().getCallingAccount(); - + StaticRouteVO route = _staticRouteDao.findById(routeId); if (route == null) { throw new InvalidParameterValueException("Unable to find static route by id"); } - + _accountMgr.checkAccess(caller, null, false, route); markStaticRouteForRevoke(route, caller); return applyStaticRoutes(route.getVpcId()); } - + @DB protected boolean revokeStaticRoutesForVpc(long vpcId, final Account caller) throws ResourceUnavailableException { //get all static routes for the vpc @@ -1777,7 +1783,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis }); return applyStaticRoutes(vpcId); } - + return true; } @@ -1786,38 +1792,38 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @ActionEvent(eventType = EventTypes.EVENT_STATIC_ROUTE_CREATE, eventDescription = "creating static route", create=true) public StaticRoute createStaticRoute(long gatewayId, final String cidr) throws NetworkRuleConflictException { Account caller = CallContext.current().getCallingAccount(); - + //parameters validation final VpcGateway gateway = _vpcGatewayDao.findById(gatewayId); if (gateway == null) { throw new InvalidParameterValueException("Invalid gateway id is given"); } - + if (gateway.getState() != VpcGateway.State.Ready) { throw new InvalidParameterValueException("Gateway is not in the " + VpcGateway.State.Ready + " state: " + gateway.getState()); } - + final Vpc vpc = getActiveVpc(gateway.getVpcId()); if (vpc == null) { throw new InvalidParameterValueException("Can't add static route to VPC that is being deleted"); } _accountMgr.checkAccess(caller, null, false, vpc); - + if (!NetUtils.isValidCIDR(cidr)){ throw new InvalidParameterValueException("Invalid format for cidr " + cidr); } - + //validate the cidr //1) CIDR should be outside of VPC cidr for guest networks if (NetUtils.isNetworksOverlap(vpc.getCidr(), cidr)) { throw new InvalidParameterValueException("CIDR should be outside of VPC cidr " + vpc.getCidr()); } - + //2) CIDR should be outside of link-local cidr if (NetUtils.isNetworksOverlap(vpc.getCidr(), NetUtils.getLinkLocalCIDR())) { throw new InvalidParameterValueException("CIDR should be outside of link local cidr " + NetUtils.getLinkLocalCIDR()); } - + //3) Verify against blacklisted routes if (isCidrBlacklisted(cidr, vpc.getZoneId())) { throw new InvalidParameterValueException("The static gateway cidr overlaps with one of the blacklisted routes of the zone the VPC belongs to"); @@ -1829,14 +1835,14 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis StaticRouteVO newRoute = new StaticRouteVO(gateway.getId(), cidr, vpc.getId(), vpc.getAccountId(), vpc.getDomainId()); s_logger.debug("Adding static route " + newRoute); newRoute = _staticRouteDao.persist(newRoute); - + detectRoutesConflict(newRoute); - + if (!_staticRouteDao.setStateToAdd(newRoute)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRoute); } CallContext.current().setEventDetails("Static route Id: " + newRoute.getId()); - + return newRoute; } }); @@ -1846,7 +1852,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis String routesStr = NetworkOrchestrationService.GuestDomainSuffix.valueIn(zoneId); if (routesStr != null && !routesStr.isEmpty()) { String[] cidrBlackList = routesStr.split(","); - + if (cidrBlackList != null && cidrBlackList.length > 0) { for (String blackListedRoute : cidrBlackList) { if (NetUtils.isNetworksOverlap(blackListedRoute, cidr)) { @@ -1855,7 +1861,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } } - + return false; } @@ -1888,7 +1894,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("vpcId", sb.entity().getVpcId(), SearchCriteria.Op.EQ); sb.and("vpcGatewayId", sb.entity().getVpcGatewayId(), SearchCriteria.Op.EQ); - + if (tags != null && !tags.isEmpty()) { SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); for (int count=0; count < tags.size(); count++) { @@ -1900,22 +1906,22 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis sb.groupBy(sb.entity().getId()); sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER); } - + SearchCriteria sc = sb.create(); _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); - + if (id != null) { sc.addAnd("id", Op.EQ, id); } - + if (vpcId != null) { sc.addAnd("vpcId", Op.EQ, vpcId); } - + if (gatewayId != null) { sc.addAnd("vpcGatewayId", Op.EQ, gatewayId); } - + if (tags != null && !tags.isEmpty()) { int count = 0; sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.StaticRoute.toString()); @@ -1925,29 +1931,29 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis count++; } } - + Pair, Integer> result = _staticRouteDao.searchAndCount(sc, searchFilter); return new Pair, Integer>(result.first(), result.second()); } - + protected void detectRoutesConflict(StaticRoute newRoute) throws NetworkRuleConflictException { //Multiple private gateways can exist within Vpc. Check for conflicts for all static routes in Vpc //and not just the gateway List routes = _staticRouteDao.listByVpcIdAndNotRevoked(newRoute.getVpcId()); assert (routes.size() >= 1) : "For static routes, we now always first persist the route and then check for " + - "network conflicts so we should at least have one rule at this point."; - + "network conflicts so we should at least have one rule at this point."; + for (StaticRoute route : routes) { if (route.getId() == newRoute.getId()) { continue; // Skips my own route. } - + if (NetUtils.isNetworksOverlap(route.getCidr(), newRoute.getCidr())) { throw new NetworkRuleConflictException("New static route cidr conflicts with existing route " + route); } } } - + protected void markStaticRouteForRevoke(StaticRouteVO route, Account caller) { s_logger.debug("Revoking static route " + route); if (caller != null) { @@ -1965,7 +1971,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis s_logger.debug("Marked static route " + route + " with state " + StaticRoute.State.Revoke); } } - + protected class VpcCleanupTask extends ManagedContextRunnable { @Override protected void runInContext() { @@ -2005,7 +2011,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } - + @DB @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_ASSIGN, eventDescription = "associating Ip", async = true) @@ -2038,7 +2044,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis s_logger.debug("Associating ip " + ipToAssoc + " to vpc " + vpc); - final boolean isSourceNatFinal = isSourceNat; + final boolean isSourceNatFinal = isSourceNat; Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) { @@ -2057,8 +2063,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return _ipAddressDao.findById(ipId); } - - + + @Override public void unassignIPFromVpcNetwork(long ipId, long networkId) { IPAddressVO ip = _ipAddressDao.findById(ipId); @@ -2092,18 +2098,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } s_logger.debug("Successfully released VPC ip address " + ip + " back to VPC pool "); } - + @Override public boolean isIpAllocatedToVpc(IpAddress ip) { return (ip != null && ip.getVpcId() != null && (ip.isOneToOneNat() || !_firewallDao.listByIp(ip.getId()).isEmpty())); } - + @DB @Override public Network createVpcGuestNetwork(long ntwkOffId, String name, String displayText, String gateway, - String cidr, String vlanId, String networkDomain, Account owner, Long domainId, - PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, long vpcId, Long aclId, Account caller, Boolean isDisplayNetworkEnabled) + String cidr, String vlanId, String networkDomain, Account owner, Long domainId, + PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, long vpcId, Long aclId, Account caller, Boolean isDisplayNetworkEnabled) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { Vpc vpc = getActiveVpc(vpcId); @@ -2114,15 +2120,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw ex; } _accountMgr.checkAccess(caller, null, false, vpc); - + if (networkDomain == null) { networkDomain = vpc.getNetworkDomain(); } - + if (vpc.getZoneId() != zoneId) { throw new InvalidParameterValueException("New network doesn't belong to vpc zone"); } - + //1) Validate if network can be created for VPC validateNtwkOffForNtwkInVpc(null, ntwkOffId, cidr, networkDomain, vpc, gateway, owner, aclId); @@ -2136,12 +2142,12 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } return guestNetwork; } - - + + protected IPAddressVO getExistingSourceNatInVpc(long ownerId, long vpcId) { List addrs = listPublicIpsAssignedToVpc(ownerId, true, vpcId); - + IPAddressVO sourceNatIp = null; if (addrs.isEmpty()) { return null; @@ -2160,7 +2166,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return sourceNatIp; } - + protected List listPublicIpsAssignedToVpc(long accountId, Boolean sourceNat, long vpcId) { SearchCriteria sc = IpAddressSearch.create(); sc.setParameters("accountId", accountId); @@ -2173,8 +2179,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return _ipAddressDao.search(sc, null); } - - + + @Override public PublicIp assignSourceNatIpAddressToVpc(Account owner, Vpc vpc) throws InsufficientAddressCapacityException, ConcurrentOperationException { long dcId = vpc.getZoneId(); @@ -2191,7 +2197,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis return ipToReturn; } - + @Override public List getSupportedVpcHypervisors() { diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java index e9d51936878..428a57b8af3 100755 --- a/server/src/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/com/cloud/resource/ResourceManagerImpl.java @@ -1926,6 +1926,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, host.setType(com.cloud.host.Host.Type.Routing); host.setDetails(details); host.setCaps(ssCmd.getCapabilities()); + host.setCpuSockets(ssCmd.getCpuSockets()); host.setCpus(ssCmd.getCpus()); host.setTotalMemory(ssCmd.getMemory()); host.setSpeed(ssCmd.getSpeed()); diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 0676db8ca18..8459adafb13 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -584,7 +584,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio File confFile = PropertiesUtil.findConfigFile("db.properties"); /* This line may throw a NPE, but that's due to fail to find db.properities, meant some bugs in the other places */ String confPath = confFile.getParent(); - String keystorePath = confPath + "/cloud.keystore"; + String keystorePath = confPath + "/cloudmanagementserver.keystore"; File keystoreFile = new File(keystorePath); boolean dbExisted = (dbString != null && !dbString.isEmpty()); diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 79b20d008b6..d083c11eceb 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -42,6 +42,7 @@ import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; import javax.naming.ConfigurationException; +import org.apache.cloudstack.api.command.admin.router.UpgradeRouterTemplateCmd; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; @@ -286,6 +287,11 @@ import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRuleI import org.apache.cloudstack.api.command.user.loadbalancer.ListLoadBalancerRulesCmd; import org.apache.cloudstack.api.command.user.loadbalancer.RemoveFromLoadBalancerRuleCmd; import org.apache.cloudstack.api.command.user.loadbalancer.UpdateLoadBalancerRuleCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.AssignCertToLoadBalancerCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.RemoveCertFromLoadBalancerCmd; import org.apache.cloudstack.api.command.user.nat.CreateIpForwardingRuleCmd; import org.apache.cloudstack.api.command.user.nat.DeleteIpForwardingRuleCmd; import org.apache.cloudstack.api.command.user.nat.DisableStaticNatCmd; @@ -2874,6 +2880,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ReplaceNetworkACLListCmd.class); cmdList.add(UpdateNetworkACLItemCmd.class); cmdList.add(CleanVMReservationsCmd.class); + cmdList.add(UpgradeRouterTemplateCmd.class); + cmdList.add(UploadSslCertCmd.class); + cmdList.add(DeleteSslCertCmd.class); + cmdList.add(ListSslCertsCmd.class); + cmdList.add(AssignCertToLoadBalancerCmd.class); + cmdList.add(RemoveCertFromLoadBalancerCmd.class); return cmdList; } diff --git a/server/src/com/cloud/storage/CreateSnapshotPayload.java b/server/src/com/cloud/storage/CreateSnapshotPayload.java index 5adc7462ffe..3bc571ef742 100644 --- a/server/src/com/cloud/storage/CreateSnapshotPayload.java +++ b/server/src/com/cloud/storage/CreateSnapshotPayload.java @@ -22,6 +22,7 @@ public class CreateSnapshotPayload { private Long snapshotPolicyId; private Long snapshotId; private Account account; + private boolean quiescevm; public Long getSnapshotPolicyId() { return snapshotPolicyId; @@ -47,4 +48,12 @@ public class CreateSnapshotPayload { this.account = account; } + public void setQuiescevm(boolean quiescevm) { + this.quiescevm = quiescevm; + } + + public boolean getQuiescevm() { + return this.quiescevm; + } + } diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index a3016396394..e42d87100a6 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -875,19 +875,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C List capacities = _capacityDao.search(capacitySC, null); long totalOverProvCapacity; - if (storagePool.getPoolType() == StoragePoolType.NetworkFilesystem) { + if (storagePool.getPoolType() == StoragePoolType.NetworkFilesystem || storagePool.getPoolType() == StoragePoolType.VMFS) { BigDecimal overProvFactor = getStorageOverProvisioningFactor(storagePool.getDataCenterId()); - totalOverProvCapacity = overProvFactor.multiply(new BigDecimal(storagePool.getCapacityBytes())).longValue();// All - // this - // for - // the - // inaccuracy - // of - // floats - // for - // big - // number - // multiplication. + totalOverProvCapacity = overProvFactor.multiply(new BigDecimal(storagePool.getCapacityBytes())).longValue(); + // All this is for the inaccuracy of floats for big number multiplication. } else { totalOverProvCapacity = storagePool.getCapacityBytes(); } @@ -1596,7 +1587,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } long totalOverProvCapacity; - if (pool.getPoolType() == StoragePoolType.NetworkFilesystem) { + if (pool.getPoolType() == StoragePoolType.NetworkFilesystem || pool.getPoolType() == StoragePoolType.VMFS) { totalOverProvCapacity = getStorageOverProvisioningFactor(pool.getDataCenterId()).multiply(new BigDecimal(pool.getCapacityBytes())) .longValue(); } else { diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 28df21935f8..c830e3d64f6 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -1383,7 +1383,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } @Override - public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) throws ResourceAllocationException { + 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"); @@ -1397,6 +1397,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic payload.setSnapshotId(snapshotId); payload.setSnapshotPolicyId(policyId); payload.setAccount(account); + payload.setQuiescevm(quiescevm); volume.addPayload(payload); return volService.takeSnapshot(volume); } diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 9888c7697dc..4f6a4558cd1 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -940,7 +940,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, Long snapshotId = payload.getSnapshotId(); Account snapshotOwner = payload.getAccount(); SnapshotInfo snapshot = snapshotFactory.getSnapshot(snapshotId, volume.getDataStore()); - + snapshot.addPayload(payload); try { SnapshotStrategy snapshotStrategy = _storageStrategyFactory.getSnapshotStrategy(snapshot, SnapshotOperation.TAKE); diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 80a40362d00..ca10b06d768 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.Executors; @@ -35,6 +36,7 @@ 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; @@ -68,20 +70,18 @@ import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationSer import org.apache.cloudstack.engine.service.api.OrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +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.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.managed.context.ManagedContextRunnable; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; -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.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; @@ -220,7 +220,6 @@ import com.cloud.template.VirtualMachineTemplate.BootloaderType; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountService; -import com.cloud.user.AccountVO; import com.cloud.user.ResourceLimitService; import com.cloud.user.SSHKeyPair; import com.cloud.user.SSHKeyPairVO; @@ -246,10 +245,9 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.TransactionCallback; -import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.SearchCriteria.Func; import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionCallbackWithException; import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn; import com.cloud.utils.db.TransactionStatus; @@ -445,6 +443,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VolumeService _volService; @Inject VolumeDataFactory volFactory; + @Inject + UserVmDetailsDao _uservmDetailsDao; protected ScheduledExecutorService _executor = null; protected int _expungeInterval; @@ -728,7 +728,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return true; } - User user = _userDao.findById(userId); + _userDao.findById(userId); try { VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); status = vmEntity.stop(Long.toString(userId)); @@ -791,7 +791,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Check resource limits for CPU and Memory. ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId); - ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); int newCpu = newServiceOffering.getCpu(); int newMemory = newServiceOffering.getRamSize(); @@ -859,7 +859,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // Check resource limits for CPU and Memory. ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId); - ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); int newCpu = newServiceOffering.getCpu(); int newMemory = newServiceOffering.getRamSize(); @@ -1150,11 +1150,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir UsageEventUtils.publishUsageEvent(EventTypes.EVENT_NETWORK_OFFERING_REMOVE, vmInstance.getAccountId(), vmInstance.getDataCenterId(), vmInstance.getId(), 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()); + 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()); + vmInstance.getId(), oldNicIdString, oldNetworkOfferingId, null, 0L, VirtualMachine.class.getName(), vmInstance.getUuid()); return _vmDao.findById(vmInstance.getId()); } @@ -1256,7 +1256,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //Check if its a scale "up" ServiceOffering newServiceOffering = _entityMgr.findById(ServiceOffering.class, newServiceOfferingId); - ServiceOffering currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getServiceOfferingId()); + ServiceOffering currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); int newCpu = newServiceOffering.getCpu(); int newMemory = newServiceOffering.getRamSize(); int newSpeed = newServiceOffering.getSpeed(); @@ -1288,7 +1288,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ExcludeList excludes = new ExcludeList(); boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId()); if(!enableDynamicallyScaleVm){ - throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin"); + throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin"); } if (!vmInstance.isDynamicallyScalable()) { throw new CloudRuntimeException("Unable to Scale the vm: " + vmInstance.getUuid() + " as vm does not have tools to support dynamic scaling"); @@ -1432,24 +1432,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Transaction.execute(new TransactionCallbackWithExceptionNoReturn() { @Override public void doInTransactionWithoutResult(TransactionStatus status) throws ResourceAllocationException { - + Account account = _accountDao.lockRow(vm.getAccountId(), true); - + // if the account is deleted, throw error if (account.getRemoved() != null) { throw new CloudRuntimeException( "Unable to recover VM as the account is deleted"); } - + // Get serviceOffering for Virtual Machine - ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); - + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); + // 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())); - + _haMgr.cancelDestroy(vm, vm.getHostId()); - + try { if (!_itMgr.stateTransitTo(vm, VirtualMachine.Event.RecoveryRequested, null)) { @@ -1464,7 +1464,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir "Unable to recover the vm because it is not in the correct state: " + vmId); } - + // Recover the VM's disks List volumes = _volsDao.findByInstance(vmId); for (VolumeVO volume : volumes) { @@ -1486,7 +1486,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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())); @@ -1546,7 +1546,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir String value = _configDao.getValue(Config.SetVmInternalNameUsingDisplayName.key()); _instanceNameFlag = (value == null)?false:Boolean.parseBoolean(value); - _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2); + _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2); s_logger.info("User VM Manager is configured."); @@ -1721,7 +1721,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _alertMgr.sendAlert(AlertManager.ALERT_TYPE_USERVM, vm.getDataCenterId(), vm.getPodIdToDeployIn(), msg, msg); // Get serviceOffering for Virtual Machine - ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); // Update Resource Count for the given account resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), @@ -1797,7 +1797,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, vmInstance); - + if (isDisplayVmEnabled != null) { if(!_accountMgr.isRootAdmin(caller.getType())){ throw new PermissionDeniedException( "Cannot update parameter displayvm, only admin permitted "); @@ -1810,7 +1810,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) - throws ResourceUnavailableException, InsufficientCapacityException { + throws ResourceUnavailableException, InsufficientCapacityException { UserVmVO vm = _vmDao.findById(id); if (vm == null) { throw new CloudRuntimeException( @@ -1822,7 +1822,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Vm with id " + id + " is not in the right state"); } - + if (displayName == null) { displayName = vm.getDisplayName(); } @@ -1831,12 +1831,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir ha = vm.isHaEnabled(); } - ServiceOffering offering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + ServiceOffering offering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); if (!offering.getOfferHA() && ha) { throw new InvalidParameterValueException( "Can't enable ha for the vm as it's created from the Service offering having HA disabled"); } - + if (isDisplayVmEnabled == null) { isDisplayVmEnabled = vm.isDisplayVm(); } @@ -1855,7 +1855,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (isDynamicallyScalable == null) { isDynamicallyScalable = vm.isDynamicallyScalable(); } - + if (osTypeId == null) { osTypeId = vm.getGuestOSId(); } @@ -1888,26 +1888,26 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List nics = _nicDao.listByVmId(vm.getId()); if (nics == null || nics.isEmpty()) { - s_logger.error("unable to find any nics for vm " + vm.getUuid()); - return false; + s_logger.error("unable to find any nics for vm " + vm.getUuid()); + return false; } for (Nic nic : nics) { - Network network = _networkDao.findById(nic.getNetworkId()); - NicProfile nicProfile = new NicProfile(nic, network, null, null, null, - _networkModel.isSecurityGroupSupportedInNetwork(network), - _networkModel.getNetworkTag(template.getHypervisorType(), network)); + Network network = _networkDao.findById(nic.getNetworkId()); + NicProfile nicProfile = new NicProfile(nic, network, null, null, null, + _networkModel.isSecurityGroupSupportedInNetwork(network), + _networkModel.getNetworkTag(template.getHypervisorType(), network)); - VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); + VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(vm); - UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network); - if (element == null) { - throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); - } - boolean result = element.saveUserData(network, nicProfile, vmProfile); - if (!result) { - s_logger.error("Failed to update userdata for vm " + vm + " and nic " + nic); - } + UserDataServiceProvider element = _networkModel.getUserDataUpdateProvider(network); + if (element == null) { + throw new CloudRuntimeException("Can't find network element for " + Service.UserData.getName() + " provider needed for UserData update"); + } + boolean result = element.saveUserData(network, nicProfile, vmProfile); + if (!result) { + s_logger.error("Failed to update userdata for vm " + vm + " and nic " + nic); + } } return true; @@ -1939,7 +1939,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // If the VM is Volatile in nature, on reboot discard the VM's root disk and create a new root disk for it: by calling restoreVM long serviceOfferingId = vmInstance.getServiceOfferingId(); - ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOfferingId); + ServiceOfferingVO offering = _serviceOfferingDao.findById(vmInstance.getId(), serviceOfferingId); if(offering != null && offering.getRemoved() == null) { if(offering.getVolatileVm()){ return restoreVMInternal(caller, vmInstance, null); @@ -1959,11 +1959,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir CallContext ctx = CallContext.current(); long vmId = cmd.getId(); boolean expunge = cmd.getExpunge(); - + if (!_accountMgr.isAdmin(ctx.getCallingAccount().getType()) && expunge) { throw new PermissionDeniedException("Parameter " + ApiConstants.EXPUNGE + " can be passed by Admin only"); } - + UserVm destroyedVm = destroyVm(vmId); if (expunge) { UserVmVO vm = _vmDao.findById(vmId); @@ -1971,7 +1971,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new CloudRuntimeException("Failed to expunge vm " + destroyedVm); } } - + return destroyedVm; } @@ -2105,7 +2105,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new CloudRuntimeException("Failed to acquire lock on vm group id=" + groupFinal.getId() + " name=" + groupFinal.getName()); } - + // Currently don't allow to assign a vm to more than one group if (_groupVMMapDao.listByInstanceId(userVmId) != null) { // Delete all mappings from group_vm_map table @@ -2190,10 +2190,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @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) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, + Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, + List affinityGroupIdList, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootDiskSize) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2243,7 +2243,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, - caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList); + caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, cpuSpeed , memory, cpuNumber, rootDiskSize); } @@ -2251,10 +2251,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @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, + Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, - List affinityGroupIdList) throws InsufficientCapacityException, ConcurrentOperationException, - ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + List affinityGroupIdList, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootDiskSize) throws InsufficientCapacityException, ConcurrentOperationException, + ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2283,7 +2283,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); } - NetworkVO network = _networkDao.findById(networkIdList.get(0).longValue()); + NetworkVO network = _networkDao.findById(networkIdList.get(0)); if (network == null) { throw new InvalidParameterValueException( @@ -2361,16 +2361,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, - caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList); + caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, cpuSpeed, memory, cpuNumber, rootDiskSize); } @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) - throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + 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, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootDiskSize) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); List networkList = new ArrayList(); @@ -2456,7 +2456,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir throw new InvalidParameterValueException( "Can't create vm of hypervisor type " + hypervisor + " in vpc network"); - + } } @@ -2475,8 +2475,8 @@ 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); + diskSize, networkList, null, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, + defaultIps, displayvm, keyboard, affinityGroupIdList, cpuSpeed, memory, cpuNumber, rootDiskSize); } @@ -2489,9 +2489,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @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) + 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, Integer cpuSpeed, Integer memory, Integer cpuNumber, Long rootDiskSize) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, true, owner); @@ -2504,7 +2504,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (template != null) { _templateDao.loadDetails(template); } - + long accountId = owner.getId(); assert !(requestedIps != null && (defaultIps.getIp4Address() != null || defaultIps.getIp6Address() != null)) : "requestedIp list and defaultNetworkIp should never be specified together"; @@ -2531,22 +2531,26 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); - // check if account/domain is with in resource limits to create a new vm boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); // For baremetal, size can be null Long tmp = _templateDao.findById(template.getId()).getSize(); long size = 0; if (tmp != null) { - size = tmp; + size = tmp; } if (diskOfferingId != null) { size += _diskOfferingDao.findById(diskOfferingId).getDiskSize(); } - resourceLimitCheck(owner, new Long(offering.getCpu()), new Long(offering.getRamSize())); + if (!offering.isDynamic()) { + resourceLimitCheck(owner, new Long(offering.getCpu()), new Long(offering.getRamSize())); + } + else { + resourceLimitCheck(owner, new Long(cpuSpeed), new Long(memory)); + } _resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2)); - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long (size)); + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, size); // verify security group ids if (securityGroupIdList != null) { @@ -2732,11 +2736,38 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir long id = _vmDao.getNextInSequence(Long.class, "id"); + List details = new ArrayList(); + if (offering.isDynamic()) { + //insert the custom value of dynamic parameters + if (offering.getCpu() == null) { + if ((cpuNumber != null) && (cpuNumber <= 0)) { + throw new InvalidParameterValueException("Invalid CPU number value, specify a value between 1 and 2147483647"); + } + } + + if (offering.getSpeed() == null) { + if ((cpuSpeed != null) && (cpuSpeed <= 0)) { + throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and 2147483647"); + } + } + + if (offering.getRamSize() == null) { + if ((memory != null) && (memory < 32)) { + throw new InvalidParameterValueException("Invalid memory value, specify a value between 32 and 2147483647 MB"); + } + } + + details.add(new UserVmDetailVO(id, ServiceOfferingVO.DynamicParameters.cpuNumber.toString(), cpuNumber.toString())); + details.add(new UserVmDetailVO(id, ServiceOfferingVO.DynamicParameters.cpuSpeed.toString(), cpuSpeed.toString())); + details.add(new UserVmDetailVO(id, ServiceOfferingVO.DynamicParameters.memory.toString(), memory.toString())); + offering = _serviceOfferingDao.getcomputeOffering(serviceOffering.getId(), cpuNumber, cpuSpeed, memory); + } if (hostName != null) { // Check is hostName is RFC compliant checkNameForRFCCompliance(hostName); } + String instanceName = null; String uuidName = UUID.randomUUID().toString(); if (_instanceNameFlag && hypervisor.equals(HypervisorType.VMware)) { @@ -2744,15 +2775,20 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (displayName != null) { hostName = displayName; } else { - hostName = uuidName; + hostName = generateHostName(uuidName); } } } else { if (hostName == null) { - hostName = uuidName; + //Generate name using uuid and instance.name global config + hostName = generateHostName(uuidName); } } + if (hostName != null) { + // Check is hostName is RFC compliant + checkNameForRFCCompliance(hostName); + } instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); // Check if VM with instanceName already exists. @@ -2776,21 +2812,20 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - for (String ntwkDomain : ntwkDomains.keySet()) { - for (Long ntwkId : ntwkDomains.get(ntwkDomain)) { + for (Entry> ntwkDomain : ntwkDomains.entrySet()) { + for (Long ntwkId : ntwkDomain.getValue()) { // * get all vms hostNames in the network 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 + "; network=" - + _networkModel.getNetwork(ntwkId)); + throw new InvalidParameterValueException("The vm with hostName " + hostName + " already exists in the network domain: " + ntwkDomain.getKey() + "; network=" + + _networkModel.getNetwork(ntwkId)); } } } HypervisorType hypervisorType = null; - if (template == null || template.getHypervisorType() == null + if (template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { hypervisorType = hypervisor; } else { @@ -2799,7 +2834,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, hypervisor, caller, isDisplayVmEnabled, keyboard, accountId, offering, isIso, sshPublicKey, - networkNicMap, id, instanceName, uuidName, hypervisorType); + networkNicMap, id, instanceName, uuidName, hypervisorType, rootDiskSize, details); // Assign instance to the group try { @@ -2824,12 +2859,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return vm; } + private String generateHostName(String uuidName) { + return _instance + "-" + uuidName; + } + 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) throws InsufficientCapacityException { + final HypervisorType hypervisorType, final Long rootDiskSize,final List vmdetails) throws InsufficientCapacityException { return Transaction.execute(new TransactionCallbackWithException() { @Override public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException { @@ -2843,14 +2882,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (sshPublicKey != null) { vm.setDetail("SSH.PublicKey", sshPublicKey); } - + if (keyboard != null && !keyboard.isEmpty()) vm.setDetail(VmDetailConstants.KEYBOARD, keyboard); - + if (isIso) { vm.setIsoId(template.getId()); } - + if(isDisplayVmEnabled != null){ if(!_accountMgr.isRootAdmin(caller.getType())){ throw new PermissionDeniedException( "Cannot update parameter displayvm, only admin permitted "); @@ -2859,7 +2898,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir }else { vm.setDisplayVm(true); } - + // If hypervisor is vSphere, check for clone type setting. if (hypervisorType.equals(HypervisorType.VMware)) { // retrieve clone flag. @@ -2872,13 +2911,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir UserVmCloneSettingVO vmCloneSettingVO = new UserVmCloneSettingVO(id, cloneType.toString()); _vmCloneSettingDao.persist(vmCloneSettingVO); } - + long guestOSId = template.getGuestOSId(); GuestOSVO guestOS = _guestOSDao.findById(guestOSId); long guestOSCategoryId = guestOS.getCategoryId(); GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId); - - + + // If hypervisor is vSphere and OS is OS X, set special settings. if (hypervisorType.equals(HypervisorType.VMware)) { if (guestOS.getDisplayName().toLowerCase().contains("apple mac os")){ @@ -2887,42 +2926,58 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir vm.setDetail("firmware", "efi"); s_logger.info("guestOS is OSX : overwrite root disk controller to scsi, use smc and efi"); } - } - + } + Map details = template.getDetails(); if ( details != null && !details.isEmpty() ) { vm.details.putAll(details); } _vmDao.persist(vm); + if (vmdetails != null && vmdetails.size() >0) { + for (UserVmDetailVO detail : vmdetails) { + vm.setDetail(detail.getName(), detail.getValue()); + } + } _vmDao.saveDetails(vm); - + s_logger.debug("Allocating in the DB for vm"); DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); - + List computeTags = new ArrayList(); computeTags.add(offering.getHostTag()); - + List rootDiskTags = new ArrayList(); rootDiskTags.add(offering.getTags()); - + if(isIso){ - VirtualMachineEntity vmEntity = _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); + _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); }else { - VirtualMachineEntity vmEntity = _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); + _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()) { s_logger.debug("Successfully allocated DB entry for " + vm); } CallContext.current().setEventDetails("Vm Id: " + vm.getId()); - - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), - vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString(), - VirtualMachine.class.getName(), vm.getUuid()); - + + 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 { + HashMap vmdetailsMap = new HashMap(); + for (UserVmDetailVO vmdetail :vmdetails) { + vmdetailsMap.put(vmdetail.getName(), vmdetail.getValue()); + } + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), + vm.getHostName(), offering.getId(), template.getId(), hypervisorType.toString(), + VirtualMachine.class.getName(), vm.getUuid(), vmdetailsMap); + } + //Update Resource Count for the given account resourceCountIncrement(accountId, new Long(offering.getCpu()), new Long(offering.getRamSize())); @@ -2947,7 +3002,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir decodedUserData = Base64.decodeBase64(userData.getBytes()); if (decodedUserData.length > MAX_HTTP_GET_LENGTH) { throw new InvalidParameterValueException( - "User data is too long for GET request"); + "User data is too long for GET request"); } } else if (httpmethod.equals(HTTPMethod.POST)) { if (userData.length() >= MAX_HTTP_POST_LENGTH) { @@ -2957,7 +3012,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir decodedUserData = Base64.decodeBase64(userData.getBytes()); if (decodedUserData.length > MAX_HTTP_POST_LENGTH) { throw new InvalidParameterValueException( - "User data is too long for POST request"); + "User data is too long for POST request"); } } @@ -3045,17 +3100,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Override public boolean setupVmForPvlan(boolean add, Long hostId, NicProfile nic) { if (!nic.getBroadCastUri().getScheme().equals("pvlan")) { - return false; - } + return false; + } String op = "add"; if (!add) { - // "delete" would remove all the rules(if using ovs) related to this vm - op = "delete"; + // "delete" would remove all the rules(if using ovs) related to this vm + op = "delete"; } Network network = _networkDao.findById(nic.getNetworkId()); Host host = _hostDao.findById(hostId); String networkTag = _networkModel.getNetworkTag(host.getHypervisorType(), network); - PvlanSetupCommand cmd = PvlanSetupCommand.createVmSetup(op, nic.getBroadCastUri(), networkTag, nic.getMacAddress()); + PvlanSetupCommand cmd = PvlanSetupCommand.createVmSetup(op, nic.getBroadCastUri(), networkTag, nic.getMacAddress()); Answer answer = null; try { answer = _agentMgr.send(hostId, cmd); @@ -3069,7 +3124,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir boolean result = true; if (answer == null || !answer.getResult()) { - result = false; + result = false; } return result; } @@ -3092,10 +3147,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List volumes = _volsDao.findByInstance(userVm.getId()); VmDiskStatisticsVO diskstats = null; for (VolumeVO volume : volumes) { - diskstats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId()); + diskstats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId()); if (diskstats == null) { - diskstats = new VmDiskStatisticsVO(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId()); - _vmDiskStatsDao.persist(diskstats); + diskstats = new VmDiskStatisticsVO(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId()); + _vmDiskStatsDao.persist(diskstats); } } @@ -3148,15 +3203,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir guestNetwork = network; // In vmware, we will be effecting pvlan settings in portgroups in StartCommand. if (profile.getHypervisorType() != HypervisorType.VMware) { - if (nic.getBroadcastUri().getScheme().equals("pvlan")) { - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), 0, false, "pvlan-nic"); - if (!setupVmForPvlan(true, hostId, nicProfile)) { - return false; - } + if (nic.getBroadcastUri().getScheme().equals("pvlan")) { + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), 0, false, "pvlan-nic"); + if (!setupVmForPvlan(true, hostId, nicProfile)) { + return false; + } + } } } } - } boolean ipChanged = false; if (originalIp != null && !originalIp.equalsIgnoreCase(returnedIp)) { if (returnedIp != null && guestNic != null) { @@ -3171,7 +3226,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } if (ipChanged) { - DataCenterVO dc = _dcDao.findById(vm.getDataCenterId()); + _dcDao.findById(vm.getDataCenterId()); UserVmVO userVm = _vmDao.findById(profile.getId()); // dc.getDhcpProvider().equalsIgnoreCase(Provider.ExternalDhcpServer.getName()) if (_ntwkSrvcDao.canProviderSupportServiceInNetwork( @@ -3225,15 +3280,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir "unable to find a virtual machine with id " + vmId); } - UserVO user = _userDao.findById(userId); + _userDao.findById(userId); boolean status = false; try { VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); status = vmEntity.stop(Long.toString(userId)); if (status) { - return _vmDao.findById(vmId); + return _vmDao.findById(vmId); } else { - return null; + return null; } } catch (ResourceUnavailableException e) { throw new CloudRuntimeException( @@ -3273,8 +3328,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network.getTrafficType() == TrafficType.Guest) { if (nic.getBroadcastUri() != null && nic.getBroadcastUri().getScheme().equals("pvlan")) { - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), 0, false, "pvlan-nic"); - setupVmForPvlan(false, vm.getHostId(), nicProfile); + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), 0, false, "pvlan-nic"); + setupVmForPvlan(false, vm.getHostId(), nicProfile); } } } @@ -3395,7 +3450,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); // Get serviceOffering for Virtual Machine - ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); String plannerName = offering.getDeploymentPlanner(); if (plannerName == null) { if (vm.getHypervisorType() == HypervisorType.BareMetal) { @@ -3443,7 +3498,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } _accountMgr.checkAccess(caller, null, true, vm); - User userCaller = _userDao.findById(userId); + _userDao.findById(userId); boolean status; State vmState = vm.getState(); @@ -3471,7 +3526,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vmState != State.Error) { // Get serviceOffering for Virtual Machine - ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); //Update Resource Count for the given account resourceCountDecrement(vm.getAccountId(), new Long(offering.getCpu()), @@ -3492,16 +3547,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // support KVM only util 2013.06.25 if (!userVm.getHypervisorType().equals(HypervisorType.KVM)) return; - s_logger.debug("Collect vm disk statistics from host before stopping Vm"); - long hostId = userVm.getHostId(); - List vmNames = new ArrayList(); - vmNames.add(userVm.getInstanceName()); - final HostVO host = _hostDao.findById(hostId); + s_logger.debug("Collect vm disk statistics from host before stopping Vm"); + long hostId = userVm.getHostId(); + List vmNames = new ArrayList(); + vmNames.add(userVm.getInstanceName()); + final HostVO host = _hostDao.findById(hostId); - GetVmDiskStatsAnswer diskStatsAnswer = null; - try { - diskStatsAnswer = (GetVmDiskStatsAnswer) _agentMgr.easySend(hostId, new GetVmDiskStatsCommand(vmNames, host.getGuid(), host.getName())); - } catch (Exception e) { + GetVmDiskStatsAnswer diskStatsAnswer = null; + try { + diskStatsAnswer = (GetVmDiskStatsAnswer) _agentMgr.easySend(hostId, new GetVmDiskStatsCommand(vmNames, host.getGuid(), host.getName())); + } catch (Exception e) { s_logger.warn("Error while collecting disk stats for vm: " + userVm.getHostName() + " from host: " + host.getName(), e); return; } @@ -3521,83 +3576,83 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir List vmDiskStats = vmDiskStatsByName.get(userVm.getInstanceName()); if (vmDiskStats == null) return; - - for (VmDiskStatsEntry vmDiskStat:vmDiskStats) { - SearchCriteria sc_volume = _volsDao.createSearchCriteria(); - sc_volume.addAnd("path", SearchCriteria.Op.EQ, vmDiskStat.getPath()); - VolumeVO volume = _volsDao.search(sc_volume, null).get(0); - 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)) { - s_logger.debug("Read/Write of IO and Bytes are both 0. Not updating vm_disk_statistics"); - continue; - } - - 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()); - 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()); - 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()); - } - 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()); - } - 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()); - } - 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()); - } - vmDiskStat_lock.setNetBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite()); - } - vmDiskStat_lock.setCurrentBytesWrite(vmDiskStat.getBytesWrite()); - - if (! _dailyOrHourly) { - //update agg bytes - vmDiskStat_lock.setAggIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead()); - vmDiskStat_lock.setAggIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite()); - vmDiskStat_lock.setAggBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead()); - vmDiskStat_lock.setAggBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite()); - } - - _vmDiskStatsDao.update(vmDiskStat_lock.getId(), vmDiskStat_lock); - } + + for (VmDiskStatsEntry vmDiskStat:vmDiskStats) { + SearchCriteria sc_volume = _volsDao.createSearchCriteria(); + sc_volume.addAnd("path", SearchCriteria.Op.EQ, vmDiskStat.getPath()); + VolumeVO volume = _volsDao.search(sc_volume, null).get(0); + 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)) { + s_logger.debug("Read/Write of IO and Bytes are both 0. Not updating vm_disk_statistics"); + continue; + } + + 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()); + 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()); + 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()); + } + 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()); + } + 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()); + } + 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()); + } + vmDiskStat_lock.setNetBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite()); + } + vmDiskStat_lock.setCurrentBytesWrite(vmDiskStat.getBytesWrite()); + + if (! _dailyOrHourly) { + //update agg bytes + vmDiskStat_lock.setAggIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead()); + vmDiskStat_lock.setAggIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite()); + vmDiskStat_lock.setAggBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead()); + vmDiskStat_lock.setAggBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite()); + } + + _vmDiskStatsDao.update(vmDiskStat_lock.getId(), vmDiskStat_lock); + } } }); } catch (Exception e) { @@ -3637,7 +3692,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir boolean status; - status = expunge(vm, userId, caller); + status = expunge(vm, userId, caller); if (status) { return _vmDao.findByIdIncludingRemoved(vmId); } else { @@ -3907,7 +3962,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private boolean isVMUsingLocalStorage(VMInstanceVO vm) { boolean usesLocalStorage = false; - ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm + ServiceOfferingVO svcOffering = _serviceOfferingDao.findById(vm.getId(), vm .getServiceOfferingId()); if (svcOffering.getUseLocalStorage()) { usesLocalStorage = true; @@ -4009,7 +4064,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir checkHostsDedication(vm, srcHostId, destinationHost.getId()); - // call to core process + // call to core process DataCenterVO dcVO = _dcDao.findById(destinationHost.getDataCenterId()); HostPodVO pod = _podDao.findById(destinationHost.getPodId()); Cluster cluster = _clusterDao.findById(destinationHost.getClusterId()); @@ -4123,7 +4178,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } // Checks for implicitly dedicated hosts - ServiceOfferingVO deployPlanner = _offeringDao.findById(vm.getServiceOfferingId()); + ServiceOfferingVO deployPlanner = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); if(deployPlanner.getDeploymentPlanner() != null && deployPlanner.getDeploymentPlanner().equals("ImplicitDedicationPlanner")) { //VM is deployed using implicit planner long accountOfVm = vm.getAccountId(); @@ -4147,11 +4202,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir //If vm is deployed using preferred implicit planner, check if all vms on destination host must be //using implicit planner and must belong to same account for (VMInstanceVO vmsDest : vmsOnDest) { - ServiceOfferingVO destPlanner = _offeringDao.findById(vmsDest.getServiceOfferingId()); + 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; + " not having all vms implicitly dedicated to account " + accountOfVm; } } } @@ -4304,11 +4359,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // 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()))) { + 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()); + "Source hypervisor type and version: " + srcHost.getHypervisorType().toString() + " " + + srcHost.getHypervisorVersion() + ", Destination hypervisor type and version: " + + destinationHost.getHypervisorType().toString() + " " + destinationHost.getHypervisorVersion()); } HypervisorCapabilitiesVO capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion( @@ -4378,7 +4433,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _itMgr.migrateWithStorage(vm.getUuid(), srcHostId, destinationHost.getId(), volToPoolObjectMap); return _vmDao.findById(vm.getId()); -} + } @DB @Override @@ -4488,7 +4543,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir DataCenterVO zone = _dcDao.findById(vm.getDataCenterId()); // Get serviceOffering and Volumes for Virtual Machine - final ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + final ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); final List volumes = _volsDao.findByInstance(cmd.getVmId()); //Remove vm from instance group @@ -4526,16 +4581,16 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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())); - + // OWNERSHIP STEP 1: update the vm owner vm.setAccountId(newAccount.getAccountId()); vm.setDomainId(cmd.getDomainId()); _vmDao.persist(vm); - + // OS 2: update volume for (VolumeVO volume : volumes) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), @@ -4559,10 +4614,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir _snapshotDao.remove(snapshot.getId()); } } - + //update resource count of new account resourceCountIncrement(newAccount.getAccountId(), 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(), @@ -4813,7 +4868,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Long userId = caller.getId(); Account owner = _accountDao.findById(vm.getAccountId()); - UserVO user = _userDao.findById(userId); + _userDao.findById(userId); long vmId = vm.getId(); boolean needRestart = false; @@ -4854,7 +4909,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Long templateId = root.getTemplateId(); boolean isISO = false; if(templateId == null) { - // Assuming that for a vm deployed using ISO, template ID is set to NULL + // Assuming that for a vm deployed using ISO, template ID is set to NULL isISO = true; templateId = vm.getIsoId(); } @@ -4909,10 +4964,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir vm.setTemplateId(newTemplateId); _vmDao.update(vmId, vm); } else { - newVol = volumeMgr.allocateDuplicateVolume(root, newTemplateId); - vm.setGuestOSId(template.getGuestOSId()); - vm.setTemplateId(newTemplateId); - _vmDao.update(vmId, vm); + newVol = volumeMgr.allocateDuplicateVolume(root, newTemplateId); + vm.setGuestOSId(template.getGuestOSId()); + vm.setTemplateId(newTemplateId); + _vmDao.update(vmId, vm); } } else { newVol = volumeMgr.allocateDuplicateVolume(root, null); @@ -4978,7 +5033,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vm != null && vm.getState() == State.Stopping) collectVmDiskStatistics(vm); } - + private void encryptAndStorePassword(UserVmVO vm, String password) { String sshPublicKey = vm.getDetail("SSH.PublicKey"); if (sshPublicKey != null && !sshPublicKey.equals("") diff --git a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java index b7b51c9c29f..1ac9dcc2a03 100644 --- a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java +++ b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java @@ -1,563 +1,545 @@ -// 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.affinity; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import javax.ejb.Local; -import javax.inject.Inject; -import javax.naming.ConfigurationException; - -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.acl.ControlledEntity.ACLType; -import org.apache.cloudstack.acl.SecurityChecker.AccessType; -import org.apache.cloudstack.affinity.dao.AffinityGroupDao; -import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; -import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; -import org.apache.cloudstack.context.CallContext; -import org.apache.log4j.Logger; -import org.springframework.context.annotation.Primary; - - - - - - - - -import com.cloud.deploy.DeploymentPlanner; -import com.cloud.domain.DomainVO; -import com.cloud.domain.dao.DomainDao; -import com.cloud.event.ActionEvent; -import com.cloud.event.EventTypes; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.PermissionDeniedException; -import com.cloud.exception.ResourceInUseException; -import com.cloud.network.Network; -import com.cloud.network.dao.NetworkDomainVO; -import com.cloud.network.security.SecurityGroup; -import com.cloud.user.Account; -import com.cloud.user.AccountManager; -import com.cloud.user.DomainManager; -import com.cloud.uservm.UserVm; -import com.cloud.utils.Pair; -import com.cloud.utils.component.ComponentContext; -import com.cloud.utils.component.Manager; -import com.cloud.utils.component.ManagerBase; -import com.cloud.utils.db.DB; -import com.cloud.utils.db.Filter; -import com.cloud.utils.db.JoinBuilder; -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.TransactionCallbackNoReturn; -import com.cloud.utils.db.TransactionStatus; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.StateListener; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachine.Event; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.dao.UserVmDao; - -@Local(value = { AffinityGroupService.class }) -public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGroupService, Manager, - StateListener { - - public static final Logger s_logger = Logger.getLogger(AffinityGroupServiceImpl.class); - private String _name; - - @Inject - AccountManager _accountMgr; - - @Inject - AffinityGroupDao _affinityGroupDao; - - @Inject - AffinityGroupVMMapDao _affinityGroupVMMapDao; - - @Inject - AffinityGroupDomainMapDao _affinityGroupDomainMapDao; - - @Inject - private UserVmDao _userVmDao; - - @Inject - DomainDao _domainDao; - - @Inject - DomainManager _domainMgr; - - protected List _affinityProcessors; - - public List getAffinityGroupProcessors() { - return _affinityProcessors; - } - - public void setAffinityGroupProcessors(List affinityProcessors) { - this._affinityProcessors = affinityProcessors; - } - - @DB - @Override - @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_CREATE, eventDescription = "Creating Affinity Group", create = true) - public AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, - String affinityGroupType, String description) { - - Account caller = CallContext.current().getCallingAccount(); - - //validate the affinityGroupType - Map typeProcessorMap = getAffinityTypeToProcessorMap(); - if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { - if (!typeProcessorMap.containsKey(affinityGroupType)) { - throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" - + affinityGroupType); - } - } else { - throw new InvalidParameterValueException( - "Unable to create affinity group, no Affinity Group Types configured"); - } - - AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); - - if (processor.isAdminControlledGroup()) { - throw new PermissionDeniedException("Cannot create the affinity group"); - } - - return createAffinityGroupInternal(account, domainId, affinityGroupName, affinityGroupType, description); - } - - @DB - @Override - public AffinityGroup createAffinityGroupInternal(String account, final Long domainId, final String affinityGroupName, - final String affinityGroupType, final String description) { - - Account caller = CallContext.current().getCallingAccount(); - - // validate the affinityGroupType - Map typeProcessorMap = getAffinityTypeToProcessorMap(); - if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { - if (!typeProcessorMap.containsKey(affinityGroupType)) { - throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" - + affinityGroupType); - } - } else { - throw new InvalidParameterValueException( - "Unable to create affinity group, no Affinity Group Types configured"); - } - - final AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); - - if (processor.isAdminControlledGroup() && !_accountMgr.isRootAdmin(caller.getType())) { - throw new PermissionDeniedException("Cannot create the affinity group"); - } - - ControlledEntity.ACLType aclType = null; - Account owner = null; - boolean domainLevel = false; - - if (account != null && domainId != null) { - - owner = _accountMgr.finalizeOwner(caller, account, domainId, null); - aclType = ControlledEntity.ACLType.Account; - - } else if (domainId != null && account == null) { - - if (!_accountMgr.isRootAdmin(caller.getType())) { - // non root admin need to pass both account and domain - throw new InvalidParameterValueException( - "Unable to create affinity group, account name must be passed with the domainId"); - } else if (!processor.canBeSharedDomainWide()) { - // cannot be domain level - throw new InvalidParameterValueException("Unable to create affinity group, account name is needed"); - } - - DomainVO domain = _domainDao.findById(domainId); - if (domain == null) { - throw new InvalidParameterValueException("Unable to find domain by specified id"); - } - _accountMgr.checkAccess(caller, domain); - - // domain level group, owner is SYSTEM. - owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); - aclType = ControlledEntity.ACLType.Domain; - domainLevel = true; - - } else { - owner = caller; - aclType = ControlledEntity.ACLType.Account; - } - - if (_affinityGroupDao.isNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName)) { - throw new InvalidParameterValueException("Unable to create affinity group, a group with name " - + affinityGroupName + " already exisits."); - } - if (domainLevel && _affinityGroupDao.findDomainLevelGroupByName(domainId, affinityGroupName) != null) { - throw new InvalidParameterValueException("Unable to create affinity group, a group with name " - + affinityGroupName + " already exisits under the domain."); - } - - final Account ownerFinal = owner; - final ControlledEntity.ACLType aclTypeFinal = aclType; - AffinityGroupVO group = Transaction.execute(new TransactionCallback() { - @Override - public AffinityGroupVO doInTransaction(TransactionStatus status) { - AffinityGroupVO group = new AffinityGroupVO(affinityGroupName, affinityGroupType, description, ownerFinal.getDomainId(), - ownerFinal.getId(), aclTypeFinal); - _affinityGroupDao.persist(group); - - if (domainId != null && aclTypeFinal == ACLType.Domain) { - boolean subDomainAccess = false; - subDomainAccess = processor.subDomainAccess(); - AffinityGroupDomainMapVO domainMap = new AffinityGroupDomainMapVO(group.getId(), domainId, subDomainAccess); - _affinityGroupDomainMapDao.persist(domainMap); - } - - return group; - } - }); - - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Created affinity group =" + affinityGroupName); - } - - return group; - } - - - @DB - @Override - @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") - public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) { - - Account caller = CallContext.current().getCallingAccount(); - Account owner = _accountMgr.finalizeOwner(caller, account, domainId, null); - - AffinityGroupVO group = null; - if (affinityGroupId != null) { - group = _affinityGroupDao.findById(affinityGroupId); - if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupId - + "; failed to delete group."); - } - } else if (affinityGroupName != null) { - group = _affinityGroupDao.findByAccountAndName(owner.getAccountId(), affinityGroupName); - if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupName - + "; failed to delete group."); - } - } else { - throw new InvalidParameterValueException( - "Either the affinity group Id or group name must be specified to delete the group"); - } - if (affinityGroupId == null) { - affinityGroupId = group.getId(); - } - // check permissions - _accountMgr.checkAccess(caller, AccessType.ModifyEntry, true, group); - - final Long affinityGroupIdFinal = affinityGroupId; - Transaction.execute(new TransactionCallbackNoReturn() { - @Override - public void doInTransactionWithoutResult(TransactionStatus status) { - - AffinityGroupVO group = _affinityGroupDao.lockRow(affinityGroupIdFinal, true); - if (group == null) { - throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupIdFinal); - } - - List affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupIdFinal); - if (!affinityGroupVmMap.isEmpty()) { - SearchBuilder listByAffinityGroup = _affinityGroupVMMapDao.createSearchBuilder(); - listByAffinityGroup.and("affinityGroupId", listByAffinityGroup.entity().getAffinityGroupId(), - SearchCriteria.Op.EQ); - listByAffinityGroup.done(); - SearchCriteria sc = listByAffinityGroup.create(); - sc.setParameters("affinityGroupId", affinityGroupIdFinal); - - _affinityGroupVMMapDao.lockRows(sc, null, true); - _affinityGroupVMMapDao.remove(sc); - } - - // call processor to handle the group delete - AffinityGroupProcessor processor = getAffinityGroupProcessorForType(group.getType()); - if (processor != null) { - processor.handleDeleteGroup(group); - } - - _affinityGroupDao.expunge(affinityGroupIdFinal); - } - }); - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Deleted affinity group id=" + affinityGroupId); - } - return true; - } - - @Override - public Pair, Integer> listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, Long startIndex, Long pageSize) { - Filter searchFilter = new Filter(AffinityGroupVO.class, "id", Boolean.TRUE, startIndex, pageSize); - - Account caller = CallContext.current().getCallingAccount(); - - Long accountId = caller.getAccountId(); - Long domainId = caller.getDomainId(); - - SearchBuilder vmInstanceSearch = _affinityGroupVMMapDao.createSearchBuilder(); - vmInstanceSearch.and("instanceId", vmInstanceSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); - - SearchBuilder groupSearch = _affinityGroupDao.createSearchBuilder(); - - SearchCriteria sc = groupSearch.create(); - - if (accountId != null) { - sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); - } - - if (domainId != null) { - sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); - } - - if (affinityGroupId != null) { - sc.addAnd("id", SearchCriteria.Op.EQ, affinityGroupId); - } - - if (affinityGroupName != null) { - sc.addAnd("name", SearchCriteria.Op.EQ, affinityGroupName); - } - - if (affinityGroupType != null) { - sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); - } - - if (vmId != null) { - UserVmVO userVM = _userVmDao.findById(vmId); - if (userVM == null) { - throw new InvalidParameterValueException("Unable to list affinity groups for virtual machine instance " - + vmId + "; instance not found."); - } - _accountMgr.checkAccess(caller, null, true, userVM); - // add join to affinity_groups_vm_map - groupSearch.join("vmInstanceSearch", vmInstanceSearch, groupSearch.entity().getId(), vmInstanceSearch - .entity().getAffinityGroupId(), JoinBuilder.JoinType.INNER); - sc.setJoinParameters("vmInstanceSearch", "instanceId", vmId); - } - - Pair, Integer> result = _affinityGroupDao.searchAndCount(sc, searchFilter); - return new Pair, Integer>(result.first(), result.second()); - } - - - @Override - public List listAffinityGroupTypes() { - Account caller = CallContext.current().getCallingAccount(); - - List types = new ArrayList(); - Map componentMap = ComponentContext.getComponentsOfType(AffinityGroupProcessor.class); - - if (componentMap.size() > 0) { - for (Entry entry : componentMap.entrySet()) { - AffinityGroupProcessor processor = entry.getValue(); - if (processor.isAdminControlledGroup()) { - continue; // we dont list the type if this group can be - // created only as an admin/system operation. - } - types.add(processor.getType()); - } - - } - return types; - } - - protected Map getAffinityTypeToProcessorMap() { - Map typeProcessorMap = new HashMap(); - Map componentMap = ComponentContext - .getComponentsOfType(AffinityGroupProcessor.class); - - if (componentMap.size() > 0) { - for (Entry entry : componentMap.entrySet()) { - typeProcessorMap.put(entry.getValue().getType(), entry.getValue()); - } - } - return typeProcessorMap; - } - - @Override - public boolean isAdminControlledGroup(AffinityGroup group) { - - if (group != null) { - String affinityGroupType = group.getType(); - Map typeProcessorMap = getAffinityTypeToProcessorMap(); - if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { - AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); - if (processor != null) { - return processor.isAdminControlledGroup(); - } - } - } - return false; - - } - - @Override - public boolean configure(final String name, final Map params) throws ConfigurationException { - _name = name; - VirtualMachine.State.getStateMachine().registerListener(this); - return true; - } - - @Override - public boolean start() { - return true; - } - - @Override - public boolean stop() { - return true; - } - - @Override - public String getName() { - return _name; - } - - @Override - public AffinityGroup getAffinityGroup(Long groupId) { - return _affinityGroupDao.findById(groupId); - } - - @Override - public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, - boolean status, Object opaque) { - return true; - } - - @Override - public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, - boolean status, Object opaque) { - if (!status) { - return false; - } - if ((newState == State.Expunging) || (newState == State.Error)) { - // cleanup all affinity groups associations of the Expunged VM - SearchCriteria sc = _affinityGroupVMMapDao.createSearchCriteria(); - sc.addAnd("instanceId", SearchCriteria.Op.EQ, vo.getId()); - _affinityGroupVMMapDao.expunge(sc); - } - return true; - } - - @Override - public UserVm updateVMAffinityGroups(Long vmId, List affinityGroupIds) { - // Verify input parameters - UserVmVO vmInstance = _userVmDao.findById(vmId); - if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - // Check that the VM is stopped - if (!vmInstance.getState().equals(State.Stopped)) { - s_logger.warn("Unable to update affinity groups of the virtual machine " + vmInstance.toString() - + " in state " + vmInstance.getState()); - throw new InvalidParameterValueException("Unable update affinity groups of the virtual machine " - + vmInstance.toString() + " " + "in state " + vmInstance.getState() - + "; make sure the virtual machine is stopped and not in an error state before updating."); - } - - Account caller = CallContext.current().getCallingAccount(); - Account owner = _accountMgr.getAccount(vmInstance.getAccountId()); - - // check that the affinity groups exist - for (Long affinityGroupId : affinityGroupIds) { - AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); - if (ag == null) { - throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupId); - } else { - // verify permissions - _accountMgr.checkAccess(caller, null, true, owner, ag); - // Root admin has access to both VM and AG by default, but make sure the - // owner of these entities is same - if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getType())) { - if (ag.getAccountId() != owner.getAccountId()) { - throw new PermissionDeniedException("Affinity Group " + ag - + " does not belong to the VM's account"); - } - } - } - } - _affinityGroupVMMapDao.updateMap(vmId, affinityGroupIds); - if (s_logger.isDebugEnabled()) { - s_logger.debug("Updated VM :" + vmId + " affinity groups to =" + affinityGroupIds); - } - // APIResponseHelper will pull out the updated affinitygroups. - return vmInstance; - - } - - @Override - public boolean isAffinityGroupProcessorAvailable(String affinityGroupType) { - for (AffinityGroupProcessor processor : _affinityProcessors) { - if (affinityGroupType != null && affinityGroupType.equals(processor.getType())) { - return true; - } - } - return false; - } - - private AffinityGroupProcessor getAffinityGroupProcessorForType(String affinityGroupType) { - for (AffinityGroupProcessor processor : _affinityProcessors) { - if (affinityGroupType != null && affinityGroupType.equals(processor.getType())) { - return processor; - } - } - return null; - } - - @Override - public boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId) { - Long groupDomainId = null; - - AffinityGroupDomainMapVO domainMap = _affinityGroupDomainMapDao.findByAffinityGroup(affinityGroupId); - if (domainMap == null) { - return false; - } else { - groupDomainId = domainMap.getDomainId(); - } - - if (domainId == groupDomainId.longValue()) { - return true; - } - - if (domainMap.subdomainAccess) { - Set parentDomains = _domainMgr.getDomainParentIds(domainId); - if (parentDomains.contains(groupDomainId)) { - return true; - } - } - - return false; - } - -} +// 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.affinity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import javax.ejb.Local; +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.springframework.context.annotation.Primary; +import com.cloud.deploy.DeploymentPlanner; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceInUseException; +import com.cloud.network.Network; +import com.cloud.network.dao.NetworkDomainVO; +import com.cloud.network.security.SecurityGroup; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.DomainManager; +import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.ManagerBase; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.JoinBuilder; +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.TransactionCallbackNoReturn; +import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateListener; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.UserVmDao; + +@Local(value = { AffinityGroupService.class }) +public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGroupService, Manager, + StateListener { + + public static final Logger s_logger = Logger.getLogger(AffinityGroupServiceImpl.class); + private String _name; + + @Inject + AccountManager _accountMgr; + + @Inject + AffinityGroupDao _affinityGroupDao; + + @Inject + AffinityGroupVMMapDao _affinityGroupVMMapDao; + + @Inject + AffinityGroupDomainMapDao _affinityGroupDomainMapDao; + + @Inject + private UserVmDao _userVmDao; + + @Inject + DomainDao _domainDao; + + @Inject + DomainManager _domainMgr; + + protected List _affinityProcessors; + + public List getAffinityGroupProcessors() { + return _affinityProcessors; + } + + public void setAffinityGroupProcessors(List affinityProcessors) { + this._affinityProcessors = affinityProcessors; + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_CREATE, eventDescription = "Creating Affinity Group", create = true) + public AffinityGroup createAffinityGroup(String account, Long domainId, String affinityGroupName, + String affinityGroupType, String description) { + + Account caller = CallContext.current().getCallingAccount(); + + //validate the affinityGroupType + Map typeProcessorMap = getAffinityTypeToProcessorMap(); + if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { + if (!typeProcessorMap.containsKey(affinityGroupType)) { + throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" + + affinityGroupType); + } + } else { + throw new InvalidParameterValueException( + "Unable to create affinity group, no Affinity Group Types configured"); + } + + AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); + + if (processor.isAdminControlledGroup()) { + throw new PermissionDeniedException("Cannot create the affinity group"); + } + + return createAffinityGroupInternal(account, domainId, affinityGroupName, affinityGroupType, description); + } + + @DB + @Override + public AffinityGroup createAffinityGroupInternal(String account, final Long domainId, final String affinityGroupName, + final String affinityGroupType, final String description) { + + Account caller = CallContext.current().getCallingAccount(); + + // validate the affinityGroupType + Map typeProcessorMap = getAffinityTypeToProcessorMap(); + if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { + if (!typeProcessorMap.containsKey(affinityGroupType)) { + throw new InvalidParameterValueException("Unable to create affinity group, invalid affinity group type" + + affinityGroupType); + } + } else { + throw new InvalidParameterValueException( + "Unable to create affinity group, no Affinity Group Types configured"); + } + + final AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); + + if (processor.isAdminControlledGroup() && !_accountMgr.isRootAdmin(caller.getType())) { + throw new PermissionDeniedException("Cannot create the affinity group"); + } + + ControlledEntity.ACLType aclType = null; + Account owner = null; + boolean domainLevel = false; + + if (account != null && domainId != null) { + + owner = _accountMgr.finalizeOwner(caller, account, domainId, null); + aclType = ControlledEntity.ACLType.Account; + + } else if (domainId != null && account == null) { + + if (!_accountMgr.isRootAdmin(caller.getType())) { + // non root admin need to pass both account and domain + throw new InvalidParameterValueException( + "Unable to create affinity group, account name must be passed with the domainId"); + } else if (!processor.canBeSharedDomainWide()) { + // cannot be domain level + throw new InvalidParameterValueException("Unable to create affinity group, account name is needed"); + } + + DomainVO domain = _domainDao.findById(domainId); + if (domain == null) { + throw new InvalidParameterValueException("Unable to find domain by specified id"); + } + _accountMgr.checkAccess(caller, domain); + + // domain level group, owner is SYSTEM. + owner = _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM); + aclType = ControlledEntity.ACLType.Domain; + domainLevel = true; + + } else { + owner = caller; + aclType = ControlledEntity.ACLType.Account; + } + + if (_affinityGroupDao.isNameInUse(owner.getAccountId(), owner.getDomainId(), affinityGroupName)) { + throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + + affinityGroupName + " already exisits."); + } + if (domainLevel && _affinityGroupDao.findDomainLevelGroupByName(domainId, affinityGroupName) != null) { + throw new InvalidParameterValueException("Unable to create affinity group, a group with name " + + affinityGroupName + " already exisits under the domain."); + } + + final Account ownerFinal = owner; + final ControlledEntity.ACLType aclTypeFinal = aclType; + AffinityGroupVO group = Transaction.execute(new TransactionCallback() { + @Override + public AffinityGroupVO doInTransaction(TransactionStatus status) { + AffinityGroupVO group = new AffinityGroupVO(affinityGroupName, affinityGroupType, description, ownerFinal.getDomainId(), + ownerFinal.getId(), aclTypeFinal); + _affinityGroupDao.persist(group); + + if (domainId != null && aclTypeFinal == ACLType.Domain) { + boolean subDomainAccess = false; + subDomainAccess = processor.subDomainAccess(); + AffinityGroupDomainMapVO domainMap = new AffinityGroupDomainMapVO(group.getId(), domainId, subDomainAccess); + _affinityGroupDomainMapDao.persist(domainMap); + } + + return group; + } + }); + + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Created affinity group =" + affinityGroupName); + } + + return group; + } + + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_AFFINITY_GROUP_DELETE, eventDescription = "Deleting affinity group") + public boolean deleteAffinityGroup(Long affinityGroupId, String account, Long domainId, String affinityGroupName) { + + Account caller = CallContext.current().getCallingAccount(); + Account owner = _accountMgr.finalizeOwner(caller, account, domainId, null); + + AffinityGroupVO group = null; + if (affinityGroupId != null) { + group = _affinityGroupDao.findById(affinityGroupId); + if (group == null) { + throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupId + + "; failed to delete group."); + } + } else if (affinityGroupName != null) { + group = _affinityGroupDao.findByAccountAndName(owner.getAccountId(), affinityGroupName); + if (group == null) { + throw new InvalidParameterValueException("Unable to find affinity group: " + affinityGroupName + + "; failed to delete group."); + } + } else { + throw new InvalidParameterValueException( + "Either the affinity group Id or group name must be specified to delete the group"); + } + if (affinityGroupId == null) { + affinityGroupId = group.getId(); + } + // check permissions + _accountMgr.checkAccess(caller, AccessType.ModifyEntry, true, group); + + final Long affinityGroupIdFinal = affinityGroupId; + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + + AffinityGroupVO group = _affinityGroupDao.lockRow(affinityGroupIdFinal, true); + if (group == null) { + throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupIdFinal); + } + + List affinityGroupVmMap = _affinityGroupVMMapDao.listByAffinityGroup(affinityGroupIdFinal); + if (!affinityGroupVmMap.isEmpty()) { + SearchBuilder listByAffinityGroup = _affinityGroupVMMapDao.createSearchBuilder(); + listByAffinityGroup.and("affinityGroupId", listByAffinityGroup.entity().getAffinityGroupId(), + SearchCriteria.Op.EQ); + listByAffinityGroup.done(); + SearchCriteria sc = listByAffinityGroup.create(); + sc.setParameters("affinityGroupId", affinityGroupIdFinal); + + _affinityGroupVMMapDao.lockRows(sc, null, true); + _affinityGroupVMMapDao.remove(sc); + } + + // call processor to handle the group delete + AffinityGroupProcessor processor = getAffinityGroupProcessorForType(group.getType()); + if (processor != null) { + processor.handleDeleteGroup(group); + } + + _affinityGroupDao.expunge(affinityGroupIdFinal); + } + }); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deleted affinity group id=" + affinityGroupId); + } + return true; + } + + @Override + public Pair, Integer> listAffinityGroups(Long affinityGroupId, String affinityGroupName, String affinityGroupType, Long vmId, Long startIndex, Long pageSize) { + Filter searchFilter = new Filter(AffinityGroupVO.class, "id", Boolean.TRUE, startIndex, pageSize); + + Account caller = CallContext.current().getCallingAccount(); + + Long accountId = caller.getAccountId(); + Long domainId = caller.getDomainId(); + + SearchBuilder vmInstanceSearch = _affinityGroupVMMapDao.createSearchBuilder(); + vmInstanceSearch.and("instanceId", vmInstanceSearch.entity().getInstanceId(), SearchCriteria.Op.EQ); + + SearchBuilder groupSearch = _affinityGroupDao.createSearchBuilder(); + + SearchCriteria sc = groupSearch.create(); + + if (accountId != null) { + sc.addAnd("accountId", SearchCriteria.Op.EQ, accountId); + } + + if (domainId != null) { + sc.addAnd("domainId", SearchCriteria.Op.EQ, domainId); + } + + if (affinityGroupId != null) { + sc.addAnd("id", SearchCriteria.Op.EQ, affinityGroupId); + } + + if (affinityGroupName != null) { + sc.addAnd("name", SearchCriteria.Op.EQ, affinityGroupName); + } + + if (affinityGroupType != null) { + sc.addAnd("type", SearchCriteria.Op.EQ, affinityGroupType); + } + + if (vmId != null) { + UserVmVO userVM = _userVmDao.findById(vmId); + if (userVM == null) { + throw new InvalidParameterValueException("Unable to list affinity groups for virtual machine instance " + + vmId + "; instance not found."); + } + _accountMgr.checkAccess(caller, null, true, userVM); + // add join to affinity_groups_vm_map + groupSearch.join("vmInstanceSearch", vmInstanceSearch, groupSearch.entity().getId(), vmInstanceSearch + .entity().getAffinityGroupId(), JoinBuilder.JoinType.INNER); + sc.setJoinParameters("vmInstanceSearch", "instanceId", vmId); + } + + Pair, Integer> result = _affinityGroupDao.searchAndCount(sc, searchFilter); + return new Pair, Integer>(result.first(), result.second()); + } + + + @Override + public List listAffinityGroupTypes() { + List types = new ArrayList(); + + for (AffinityGroupProcessor processor : _affinityProcessors) { + if (processor.isAdminControlledGroup()) { + continue; // we dont list the type if this group can be + // created only as an admin/system operation. + } + types.add(processor.getType()); + } + + return types; + } + + protected Map getAffinityTypeToProcessorMap() { + Map typeProcessorMap = new HashMap(); + + for (AffinityGroupProcessor processor : _affinityProcessors) { + typeProcessorMap.put(processor.getType(), processor); + } + + return typeProcessorMap; + } + + @Override + public boolean isAdminControlledGroup(AffinityGroup group) { + + if (group != null) { + String affinityGroupType = group.getType(); + Map typeProcessorMap = getAffinityTypeToProcessorMap(); + if (typeProcessorMap != null && !typeProcessorMap.isEmpty()) { + AffinityGroupProcessor processor = typeProcessorMap.get(affinityGroupType); + if (processor != null) { + return processor.isAdminControlledGroup(); + } + } + } + return false; + + } + + @Override + public boolean configure(final String name, final Map params) throws ConfigurationException { + _name = name; + VirtualMachine.State.getStateMachine().registerListener(this); + return true; + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } + + @Override + public String getName() { + return _name; + } + + @Override + public AffinityGroup getAffinityGroup(Long groupId) { + return _affinityGroupDao.findById(groupId); + } + + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + return true; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + if (!status) { + return false; + } + if ((newState == State.Expunging) || (newState == State.Error)) { + // cleanup all affinity groups associations of the Expunged VM + SearchCriteria sc = _affinityGroupVMMapDao.createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, vo.getId()); + _affinityGroupVMMapDao.expunge(sc); + } + return true; + } + + @Override + public UserVm updateVMAffinityGroups(Long vmId, List affinityGroupIds) { + // Verify input parameters + UserVmVO vmInstance = _userVmDao.findById(vmId); + if (vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + + // Check that the VM is stopped + if (!vmInstance.getState().equals(State.Stopped)) { + s_logger.warn("Unable to update affinity groups of the virtual machine " + vmInstance.toString() + + " in state " + vmInstance.getState()); + throw new InvalidParameterValueException("Unable update affinity groups of the virtual machine " + + vmInstance.toString() + " " + "in state " + vmInstance.getState() + + "; make sure the virtual machine is stopped and not in an error state before updating."); + } + + Account caller = CallContext.current().getCallingAccount(); + Account owner = _accountMgr.getAccount(vmInstance.getAccountId()); + + // check that the affinity groups exist + for (Long affinityGroupId : affinityGroupIds) { + AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); + if (ag == null) { + throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupId); + } else { + // verify permissions + _accountMgr.checkAccess(caller, null, true, owner, ag); + // Root admin has access to both VM and AG by default, but make sure the + // owner of these entities is same + if (caller.getId() == Account.ACCOUNT_ID_SYSTEM || _accountMgr.isRootAdmin(caller.getType())) { + if (ag.getAccountId() != owner.getAccountId()) { + throw new PermissionDeniedException("Affinity Group " + ag + + " does not belong to the VM's account"); + } + } + } + } + _affinityGroupVMMapDao.updateMap(vmId, affinityGroupIds); + if (s_logger.isDebugEnabled()) { + s_logger.debug("Updated VM :" + vmId + " affinity groups to =" + affinityGroupIds); + } + // APIResponseHelper will pull out the updated affinitygroups. + return vmInstance; + + } + + @Override + public boolean isAffinityGroupProcessorAvailable(String affinityGroupType) { + for (AffinityGroupProcessor processor : _affinityProcessors) { + if (affinityGroupType != null && affinityGroupType.equals(processor.getType())) { + return true; + } + } + return false; + } + + private AffinityGroupProcessor getAffinityGroupProcessorForType(String affinityGroupType) { + for (AffinityGroupProcessor processor : _affinityProcessors) { + if (affinityGroupType != null && affinityGroupType.equals(processor.getType())) { + return processor; + } + } + return null; + } + + @Override + public boolean isAffinityGroupAvailableInDomain(long affinityGroupId, long domainId) { + Long groupDomainId = null; + + AffinityGroupDomainMapVO domainMap = _affinityGroupDomainMapDao.findByAffinityGroup(affinityGroupId); + if (domainMap == null) { + return false; + } else { + groupDomainId = domainMap.getDomainId(); + } + + if (domainId == groupDomainId.longValue()) { + return true; + } + + if (domainMap.subdomainAccess) { + Set parentDomains = _domainMgr.getDomainParentIds(domainId); + if (parentDomains.contains(groupDomainId)) { + return true; + } + } + + return false; + } + +} diff --git a/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java b/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java index b7cd231831c..f0f0a6efbf2 100644 --- a/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java +++ b/server/src/org/apache/cloudstack/network/lb/ApplicationLoadBalancerManagerImpl.java @@ -146,7 +146,7 @@ public class ApplicationLoadBalancerManagerImpl extends ManagerBase implements A //4) Validate Load Balancing rule on the providers LoadBalancingRule loadBalancing = new LoadBalancingRule(newRule, new ArrayList(), - new ArrayList(), new ArrayList(), sourceIpAddr); + new ArrayList(), new ArrayList(), sourceIpAddr, null, null); if (!_lbMgr.validateLbRule(loadBalancing)) { throw new InvalidParameterValueException("LB service provider cannot support this rule"); } diff --git a/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java b/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java new file mode 100644 index 00000000000..53dae507bbb --- /dev/null +++ b/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java @@ -0,0 +1,477 @@ +// 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.lb; + +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.network.dao.*; +import com.cloud.network.lb.CertService; +import com.cloud.network.rules.LoadBalancer; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd; +import org.apache.cloudstack.api.response.SslCertResponse; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.db.DB; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMReader; +import org.bouncycastle.openssl.PasswordFinder; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.ejb.Local; +import javax.inject.Inject; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Local(value = {CertService.class}) +public class CertServiceImpl implements CertService { + + private static final Logger s_logger = Logger.getLogger(CertServiceImpl.class); + + @Inject AccountManager _accountMgr; + @Inject AccountDao _accountDao; + @Inject SslCertDao _sslCertDao; + @Inject LoadBalancerCertMapDao _lbCertDao; + @Inject EntityManager _entityMgr; + + + + public CertServiceImpl() { + Security.addProvider(new BouncyCastleProvider()); + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_UPLOAD, eventDescription = "Uploading a certificate to cloudstack", async = false) + public SslCertResponse uploadSslCert(UploadSslCertCmd certCmd) { + try { + + String cert = URLDecoder.decode(certCmd.getCert(), "UTF-8"); + String key = URLDecoder.decode(certCmd.getKey(), "UTF-8"); + String password = certCmd.getPassword(); + String chain = certCmd.getChain() == null ? null: URLDecoder.decode(certCmd.getChain(), "UTF-8"); + + validate(cert, key, password, chain); + s_logger.debug("Certificate Validation succeeded"); + String fingerPrint = generateFingerPrint(parseCertificate(cert)); + + Long accountId = CallContext.current().getCallingAccount().getId(); + Long domainId = CallContext.current().getCallingAccount().getDomainId(); + + + SslCertVO certVO = new SslCertVO(cert, key, password, chain, accountId, domainId, fingerPrint); + _sslCertDao.persist(certVO); + + + return createCertResponse(certVO, null); + + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Error decoding certificate data"); + } + + } + + @DB + @Override + @ActionEvent(eventType = EventTypes.EVENT_LB_CERT_DELETE, eventDescription = "Deleting a certificate to cloudstack", async = false) + public void deleteSslCert(DeleteSslCertCmd deleteSslCertCmd) { + + CallContext ctx = CallContext.current(); + Account caller = ctx.getCallingAccount(); + + Long certId = deleteSslCertCmd.getId(); + SslCertVO certVO = _sslCertDao.findById(certId); + + if (certVO == null) { + throw new InvalidParameterValueException("Invalid certificate id: " + certId); + } + _accountMgr.checkAccess(caller, SecurityChecker.AccessType.ModifyEntry, true, certVO); + + List lbCertRule = _lbCertDao.listByCertId(certId); + + if ( (lbCertRule != null) && (!lbCertRule.isEmpty()) ){ + String lbUuids = ""; + + for( LoadBalancerCertMapVO rule : lbCertRule ){ + LoadBalancerVO lb = _entityMgr.findById(LoadBalancerVO.class, rule.getLbId()); + lbUuids += " " + lb.getUuid(); + } + + throw new CloudRuntimeException("Certificate in use by a loadbalancer(s)" + lbUuids); + } + + _sslCertDao.remove(certId); + } + + @Override + public List listSslCerts(ListSslCertsCmd listSslCertCmd) { + CallContext ctx = CallContext.current(); + Account caller = ctx.getCallingAccount(); + + Long certId= listSslCertCmd.getCertId(); + Long accountId = listSslCertCmd.getAccountId(); + Long lbRuleId = listSslCertCmd.getLbId(); + + List certResponseList = new ArrayList(); + + if (certId == null && accountId == null && lbRuleId == null ) { + throw new InvalidParameterValueException("Invalid parameters either certificate ID or Account ID or Loadbalancer ID required"); + } + + List certLbMap = null; + SslCertVO certVO = null; + + + if(certId != null) { + + certVO = _sslCertDao.findById(certId); + + if (certVO == null) { + throw new InvalidParameterValueException("Invalid certificate id: " + certId); + } + + + _accountMgr.checkAccess(caller, SecurityChecker.AccessType.ListEntry, true, certVO); + + certLbMap = _lbCertDao.listByCertId(certId); + + certResponseList.add(createCertResponse(certVO, certLbMap)); + return certResponseList; + } + + if ( lbRuleId != null) { + LoadBalancer lb = _entityMgr.findById(LoadBalancerVO.class, lbRuleId); + + if ( lb == null ){ + throw new InvalidParameterValueException("found no loadbalancer wth id: " + lbRuleId); + } + + _accountMgr.checkAccess(caller, SecurityChecker.AccessType.ListEntry, true, lb); + + // get the cert id + LoadBalancerCertMapVO lbCertMapRule; + lbCertMapRule = _lbCertDao.findByLbRuleId(lbRuleId); + + if (lbCertMapRule == null) { + throw new InvalidParameterValueException("No certificate bound to loadbalancer id: " + lbRuleId); + } + + certVO = _sslCertDao.findById(lbCertMapRule.getCertId()); + certLbMap = _lbCertDao.listByCertId(lbCertMapRule.getCertId()); + + certResponseList.add(createCertResponse(certVO, certLbMap)); + return certResponseList; + + } + + //reached here look by accountId + List certVOList = _sslCertDao.listByAccountId(accountId); + if ( certVOList == null || certVOList.isEmpty()) + return certResponseList; + _accountMgr.checkAccess(caller, SecurityChecker.AccessType.ListEntry, true, certVOList.get(0)); + + for( SslCertVO cert : certVOList ){ + certLbMap = _lbCertDao.listByCertId(cert.getId()); + certResponseList.add(createCertResponse(cert, certLbMap)); + } + + return certResponseList; + } + + + private void validate(String _cert, String _key, String _password, String _chain) { + Certificate cert; + PrivateKey key; + List chain=null; + + + try { + cert = parseCertificate(_cert); + key = parsePrivateKey(_key, _password); + + if ( _chain != null) { + chain = parseChain(_chain); + } + + } catch (IOException e) { + throw new IllegalArgumentException("Parsing certificate/key failed: " + e.getMessage()); + } + + validateCert(cert, _chain != null? true: false); + validateKeys(cert.getPublicKey(), key); + + if ( _chain != null ) + validateChain(chain, cert); + } + + public SslCertResponse createCertResponse(SslCertVO cert, List lbCertMap) { + SslCertResponse response = new SslCertResponse(); + + Account account = _accountDao.findByIdIncludingRemoved(cert.getAccountId()); + + response.setObjectName("sslcert"); + response.setId(cert.getUuid()); + response.setCertificate(cert.getCertificate()); + response.setPrivatekey(cert.getKey()); + response.setFingerprint(cert.getFingerPrint()); + response.setAccountName(account.getAccountName()); + + if ( cert.getChain() != null) + response.setCertchain(cert.getChain()); + + if ( lbCertMap != null && !lbCertMap.isEmpty()){ + List lbIds = new ArrayList(); + for ( LoadBalancerCertMapVO mapVO : lbCertMap ) { + LoadBalancer lb = _entityMgr.findById(LoadBalancerVO.class, mapVO.getLbId()); + lbIds.add(lb.getUuid()); + } + response.setLbIds(lbIds); + } + + return response; + } + + private void validateCert(Certificate cert, boolean chain_present) { + + if ( !( cert instanceof X509Certificate)) + throw new IllegalArgumentException("Invalid certificate format. Expected X509 certificate"); + + try { + ((X509Certificate)cert).checkValidity(); + } catch (Exception e) { + throw new IllegalArgumentException("Certificate expired or not valid"); + } + + if( !chain_present ) { + PublicKey pubKey = cert.getPublicKey(); + try { + cert.verify(pubKey); + } catch (Exception e) { + throw new IllegalArgumentException("No chain given and certificate not self signed"); + } + } + } + + private void validateKeys(PublicKey pubKey, PrivateKey privKey) { + + if (pubKey.getAlgorithm() != privKey.getAlgorithm()) + throw new IllegalArgumentException("Public and private key have different algorithms"); + + // No encryption for DSA + if ( pubKey.getAlgorithm() != "RSA") + return; + + try { + + String data = "ENCRYPT_DATA"; + SecureRandom random = new SecureRandom(); + Cipher cipher = Cipher.getInstance(pubKey.getAlgorithm()); + cipher.init(Cipher.ENCRYPT_MODE,privKey,random); + byte[] encryptedData = cipher.doFinal(data.getBytes()); + + cipher.init(Cipher.DECRYPT_MODE,pubKey,random); + String decreptedData = new String(cipher.doFinal(encryptedData)); + if (!decreptedData.equals(data)) + throw new IllegalArgumentException("Bad public-private key"); + + } catch (BadPaddingException e) { + throw new IllegalArgumentException("Bad public-private key"); + } catch (IllegalBlockSizeException e) { + throw new IllegalArgumentException("Bad public-private key"); + } catch (NoSuchPaddingException e) { + throw new IllegalArgumentException("Bad public-private key"); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("Invalid public-private key"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Invalid algorithm for public-private key"); + } + } + + + private void validateChain(List chain, Certificate cert) { + + List certs = new ArrayList(); + List root = new ArrayList(); + + Set anchors = new HashSet(); + + + certs.add(cert); // adding for self signed certs + certs.addAll(chain); + + for( Certificate c : certs) { + if ( !( c instanceof X509Certificate)) + throw new IllegalArgumentException("Invalid chain format. Expected X509 certificate"); + + X509Certificate xCert = (X509Certificate)c; + + Principal subject = xCert.getSubjectDN(); + Principal issuer = xCert.getIssuerDN(); + + if( issuer != null && subject.equals(issuer) ) { + root.add(c); + anchors.add(new TrustAnchor(xCert,null)); + } + } + + if ( root.size() == 0 ) + throw new IllegalArgumentException("No root certificates found for certificate chain",null); + + + X509CertSelector target = new X509CertSelector(); + target.setCertificate((X509Certificate)cert); + + PKIXBuilderParameters params = null; + try { + params = new PKIXBuilderParameters(anchors, target); + params.setRevocationEnabled(false); + params.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs))); + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); + builder.build(params); + } catch (InvalidAlgorithmParameterException e) { + throw new IllegalArgumentException("Invalid certificate chain",null); + } catch (CertPathBuilderException e) { + throw new IllegalArgumentException("Invalid certificate chain",null); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Invalid certificate chain",null); + } + + } + + public PrivateKey parsePrivateKey(String key, String password) throws IOException { + + PasswordFinder pGet = null; + + if( password != null ) + pGet = new KeyPassword(password.toCharArray()); + + PEMReader privateKey = new PEMReader(new StringReader(key), pGet); + Object obj = privateKey.readObject(); + + try { + + if ( obj instanceof KeyPair ) + return ((KeyPair)obj).getPrivate(); + + return (PrivateKey) obj; + + }catch (Exception e){ + e.printStackTrace(); + throw new IOException("Invalid Key format or invalid password."); + } + } + + + public Certificate parseCertificate(String cert) { + PEMReader certPem = new PEMReader(new StringReader(cert)); + try { + return (Certificate) certPem.readObject(); + } catch (Exception e) { + throw new InvalidParameterValueException("Invalid Certificate format. Expected X509 certificate"); + } + } + + + public List parseChain(String chain) throws IOException { + + List certs = new ArrayList(); + PEMReader reader = new PEMReader(new StringReader(chain)); + + Certificate crt = null; + + while ( ( crt = (Certificate) reader.readObject()) != null ) { + if ( crt instanceof X509Certificate) { + certs.add(crt); + } + } + if ( certs.size() == 0 ) + throw new IllegalArgumentException("Unable to decode certificate chain",null); + + return certs; + } + + + String generateFingerPrint(Certificate cert) { + + final char[] HEX = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + StringBuilder buffer = new StringBuilder(60); + try { + + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] data = md.digest(cert.getEncoded()); + + for ( int i = 0 ; i < data.length ; i++ ) { + if ( buffer.length() > 0 ) { + buffer.append(":"); + } + + buffer.append(HEX[(0xF0 & data[i]) >>> 4]); + buffer.append(HEX[0x0F & data[i]]); + } + + } catch (CertificateEncodingException e) { + throw new InvalidParameterValueException("Bad certificate encoding"); + } catch (NoSuchAlgorithmException e) { + throw new InvalidParameterValueException("Bad certificate algorithm"); + } + + return buffer.toString(); + } + + + public static class KeyPassword implements PasswordFinder { + + boolean password_requested=false; + char[] password; + + + KeyPassword(char[] word){ + this.password = word; + } + + public char[] getPassword() { + password_requested=true; + return password; + } + + public boolean getPasswordRequested(){ + return password_requested; + } + } +} \ No newline at end of file diff --git a/server/test/com/cloud/capacity/CapacityManagerTest.java b/server/test/com/cloud/capacity/CapacityManagerTest.java index 3faa32f0f6d..300e6b50a65 100644 --- a/server/test/com/cloud/capacity/CapacityManagerTest.java +++ b/server/test/com/cloud/capacity/CapacityManagerTest.java @@ -57,7 +57,7 @@ public class CapacityManagerTest { capMgr=setUp(); when(vm.getHostId()).thenReturn(1l); when(vm.getServiceOfferingId()).thenReturn(2l); - when(SOfferingDao.findById(anyLong())).thenReturn(svo); + when(SOfferingDao.findById(anyLong(), anyLong())).thenReturn(svo); when(CDao.findByHostIdType(anyLong(), eq(Capacity.CAPACITY_TYPE_CPU))).thenReturn(cvo_cpu); when(CDao.findByHostIdType(anyLong(), eq(Capacity.CAPACITY_TYPE_MEMORY))).thenReturn(cvo_ram); when(cvo_cpu.getUsedCapacity()).thenReturn(500l); diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java index df676d31594..0a3ed3c93b9 100755 --- a/server/test/com/cloud/vm/UserVmManagerTest.java +++ b/server/test/com/cloud/vm/UserVmManagerTest.java @@ -367,7 +367,7 @@ public class UserVmManagerTest { ServiceOffering so2 = getSvcoffering(256); when(_entityMgr.findById(eq(ServiceOffering.class), anyLong())).thenReturn(so1); - when(_offeringDao.findByIdIncludingRemoved(anyLong())).thenReturn((ServiceOfferingVO) so1); + when(_offeringDao.findByIdIncludingRemoved(anyLong(), anyLong())).thenReturn((ServiceOfferingVO) so1); Account account = new AccountVO("testaccount", 1L, "networkdomain", (short)0, UUID.randomUUID().toString()); UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java index 35fde8e277d..705a254dde5 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -520,7 +520,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches */ @Override public PhysicalNetworkTrafficType addTrafficTypeToPhysicalNetwork(Long physicalNetworkId, String trafficType, - String xenLabel, String kvmLabel, String vmwareLabel, String simulatorLabel, String vlan) { + String xenLabel, String kvmLabel, String vmwareLabel, String simulatorLabel, String vlan, String hypervLabel) { // TODO Auto-generated method stub return null; } @@ -547,7 +547,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches */ @Override public PhysicalNetworkTrafficType updatePhysicalNetworkTrafficType(Long id, String xenLabel, String kvmLabel, - String vmwareLabel) { + String vmwareLabel, String hypervLabel) { // TODO Auto-generated method stub return null; } diff --git a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java index acdd9dc8750..5c216c8f19f 100644 --- a/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java +++ b/server/test/com/cloud/vpc/MockVpcVirtualNetworkApplianceManager.java @@ -24,7 +24,7 @@ import javax.ejb.Local; import javax.naming.ConfigurationException; import com.cloud.network.vpc.NetworkACLItem; -import org.apache.cloudstack.api.command.admin.router.UpgradeRouterCmd; +import org.apache.cloudstack.api.command.admin.router.*; import org.springframework.stereotype.Component; import com.cloud.deploy.DeployDestination; @@ -420,6 +420,11 @@ VpcVirtualNetworkApplianceService { return null; } + @Override + public List upgradeRouterTemplate(UpgradeRouterTemplateCmd cmd) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + @Override public boolean setupDhcpForPvlan(boolean add, DomainRouterVO router, Long hostId, NicProfile nic) { diff --git a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java index 97e093ff415..91d6958ff61 100644 --- a/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java +++ b/server/test/org/apache/cloudstack/affinity/AffinityApiUnitTest.java @@ -63,6 +63,7 @@ import org.apache.cloudstack.test.utils.SpringUtils; import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.event.ActionEventUtils; import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEventUtils; import com.cloud.event.EventVO; @@ -88,7 +89,7 @@ import com.cloud.vm.dao.UserVmDao; public class AffinityApiUnitTest { @Inject - AffinityGroupService _affinityService; + AffinityGroupServiceImpl _affinityService; @Inject AccountManager _acctMgr; @@ -144,6 +145,10 @@ public class AffinityApiUnitTest { when(_processor.getType()).thenReturn("mock"); when(_accountDao.findByIdIncludingRemoved(0L)).thenReturn(acct); + List affinityProcessors = new ArrayList(); + affinityProcessors.add(_processor); + _affinityService.setAffinityGroupProcessors(affinityProcessors); + AffinityGroupVO group = new AffinityGroupVO("group1", "mock", "mock group", domainId, 200L, ControlledEntity.ACLType.Account); Mockito.when(_affinityGroupDao.persist(Mockito.any(AffinityGroupVO.class))).thenReturn(group); diff --git a/server/test/org/apache/cloudstack/lb/ApplicationLoadBalancerTest.java b/server/test/org/apache/cloudstack/network/lb/ApplicationLoadBalancerTest.java similarity index 99% rename from server/test/org/apache/cloudstack/lb/ApplicationLoadBalancerTest.java rename to server/test/org/apache/cloudstack/network/lb/ApplicationLoadBalancerTest.java index 9b46e683113..82b11817948 100644 --- a/server/test/org/apache/cloudstack/lb/ApplicationLoadBalancerTest.java +++ b/server/test/org/apache/cloudstack/network/lb/ApplicationLoadBalancerTest.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 org.apache.cloudstack.lb; +package org.apache.cloudstack.network.lb; import java.io.IOException; import java.lang.reflect.Field; @@ -25,6 +25,7 @@ import javax.inject.Inject; import junit.framework.TestCase; +import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -335,7 +336,7 @@ public class ApplicationLoadBalancerTest extends TestCase { public AccountManager accountManager() { return Mockito.mock(AccountManager.class); } - + @Bean public LoadBalancingRulesManager loadBalancingRulesManager() { diff --git a/server/test/org/apache/cloudstack/network/lb/CertServiceTest.java b/server/test/org/apache/cloudstack/network/lb/CertServiceTest.java new file mode 100644 index 00000000000..e47fc013f05 --- /dev/null +++ b/server/test/org/apache/cloudstack/network/lb/CertServiceTest.java @@ -0,0 +1,791 @@ +// 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.lb; + +import com.cloud.network.dao.*; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.UserVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionLegacy; +import junit.framework.TestCase; +import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; +import org.apache.cloudstack.api.command.user.loadbalancer.UploadSslCertCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.log4j.Logger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static org.apache.commons.io.FileUtils.readFileToString; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.when; + +public class CertServiceTest extends TestCase { + + private static final Logger s_logger = Logger.getLogger( CertServiceTest.class); + + @Override + @Before + public void setUp() { + Account account = new AccountVO("testaccount", 1, "networkdomain", (short)0, UUID.randomUUID().toString()); + UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString()); + CallContext.register(user, account); + } + + @Override + @After + public void tearDown() { + CallContext.unregister(); + } + + @Test + public void testUploadSslCert() throws Exception { + + /* Test1 : Given a Certificate in bad format (Not PEM), upload should fail */ + runUploadSslCertBadFormat(); + + /* Test2: Given a Certificate which is not X509, upload should fail */ + runUploadSslCertNotX509(); + + /* Test3: Given an expired certificate, upload should fail */ + runUploadSslCertExpiredCert(); + + /* Test4: Given a private key which has a different algorithm than the certificate, + upload should fail. + */ + runUploadSslCertBadkeyAlgo(); + + /* Test5: Given a private key which does not match the public key in the certificate, + upload should fail + */ + runUploadSslCertBadkeyPair(); + + /* Test6: Given an encrypted private key with a bad password. Upload should fail. */ + runUploadSslCertBadPassword(); + + /* Test7: If no chain is given, the certificate should be self signed. Else, uploadShould Fail */ + runUploadSslCertNoChain(); + + /* Test8: Chain is given but does not have root certificate */ + runUploadSslCertNoRootCert(); + + /* Test9: The chain given is not the correct chain for the certificate */ + runUploadSslCertBadChain(); + + /* Test10: Given a Self-signed Certificate with encrypted key, upload should succeed */ + runUploadSslCertSelfSignedNoPassword(); + + /* Test11: Given a Self-signed Certificate with non-encrypted key, upload should succeed */ + runUploadSslCertSelfSignedWithPassword(); + + /* Test12: Given a certificate signed by a CA and a valid CA chain, upload should succeed */ + runUploadSslCertWithCAChain(); + + } + + private void runUploadSslCertWithCAChain() throws Exception { + //To change body of created methods use File | Settings | File Templates. + + TransactionLegacy txn = TransactionLegacy.open("runUploadSslCertWithCAChain"); + + String certFile = getClass().getResource("/certs/rsa_ca_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_ca_signed.key").getFile(); + String chainFile = getClass().getResource("/certs/root_chain.crt").getFile(); + String password = "user"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + String chain = URLEncoder.encode(readFileToString(new File(chainFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + + certService._accountDao = Mockito.mock(AccountDao.class); + when(certService._accountDao.findByIdIncludingRemoved(anyLong())).thenReturn((AccountVO)account); + + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + Field chainField = _class.getDeclaredField("chain"); + chainField.setAccessible(true); + chainField.set(uploadCmd, chain); + + certService.uploadSslCert(uploadCmd); + } + + private void runUploadSslCertSelfSignedWithPassword() throws Exception { + + TransactionLegacy txn = TransactionLegacy.open("runUploadSslCertSelfSignedWithPassword"); + + String certFile = getClass().getResource("/certs/rsa_self_signed_with_pwd.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed_with_pwd.key").getFile(); + String password = "test"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + + certService._accountDao = Mockito.mock(AccountDao.class); + when(certService._accountDao.findByIdIncludingRemoved(anyLong())).thenReturn((AccountVO)account); + + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + certService.uploadSslCert(uploadCmd); + } + + private void runUploadSslCertSelfSignedNoPassword() throws Exception { + + TransactionLegacy txn = TransactionLegacy.open("runUploadSslCertSelfSignedNoPassword"); + + String certFile = getClass().getResource("/certs/rsa_self_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed.key").getFile(); + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + + certService._accountDao = Mockito.mock(AccountDao.class); + when(certService._accountDao.findByIdIncludingRemoved(anyLong())).thenReturn((AccountVO)account); + + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + certService.uploadSslCert(uploadCmd); + } + + + private void runUploadSslCertBadChain() throws IOException, IllegalAccessException, NoSuchFieldException { + //To change body of created methods use File | Settings | File Templates. + String certFile = getClass().getResource("/certs/rsa_ca_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_ca_signed.key").getFile(); + String chainFile = getClass().getResource("/certs/rsa_self_signed.crt").getFile(); + String password = "user"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + String chain = URLEncoder.encode(readFileToString(new File(chainFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + Field chainField = _class.getDeclaredField("chain"); + chainField.setAccessible(true); + chainField.set(uploadCmd, chain); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Invalid certificate chain")); + } + } + + private void runUploadSslCertNoRootCert() throws IOException, IllegalAccessException, NoSuchFieldException { + + //To change body of created methods use File | Settings | File Templates. + String certFile = getClass().getResource("/certs/rsa_ca_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_ca_signed.key").getFile(); + String chainFile = getClass().getResource("/certs/rsa_ca_signed2.crt").getFile(); + String password = "user"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + String chain = URLEncoder.encode(readFileToString(new File(chainFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + Field chainField = _class.getDeclaredField("chain"); + chainField.setAccessible(true); + chainField.set(uploadCmd, chain); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("No root certificates found")); + } + + } + + private void runUploadSslCertNoChain() throws IOException, IllegalAccessException, NoSuchFieldException { + + String certFile = getClass().getResource("/certs/rsa_ca_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_ca_signed.key").getFile(); + String password = "user"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("No chain given and certificate not self signed")); + } + + } + + private void runUploadSslCertBadPassword() throws IOException, IllegalAccessException, NoSuchFieldException { + + String certFile = getClass().getResource("/certs/rsa_self_signed_with_pwd.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed_with_pwd.key").getFile(); + String password = "bad_password"; + + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + Field passField = _class.getDeclaredField("password"); + passField.setAccessible(true); + passField.set(uploadCmd, password); + + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("please check password and data")); + } + + } + + private void runUploadSslCertBadkeyPair() throws IOException, IllegalAccessException, NoSuchFieldException { + // Reading appropritate files + String certFile = getClass().getResource("/certs/rsa_self_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_random_pkey.key").getFile(); + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Bad public-private key")); + } + } + + private void runUploadSslCertBadkeyAlgo() throws IOException, IllegalAccessException, NoSuchFieldException { + + // Reading appropritate files + String certFile = getClass().getResource("/certs/rsa_self_signed.crt").getFile(); + String keyFile = getClass().getResource("/certs/dsa_self_signed.key").getFile(); + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Public and private key have different algorithms")); + } + } + + private void runUploadSslCertExpiredCert() throws IOException, IllegalAccessException, NoSuchFieldException { + + // Reading appropritate files + String certFile = getClass().getResource("/certs/expired_cert.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed.key").getFile(); + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Certificate expired")); + } + } + + private void runUploadSslCertNotX509() throws IOException, IllegalAccessException, NoSuchFieldException { + // Reading appropritate files + String certFile = getClass().getResource("/certs/non_x509_pem.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed.key").getFile(); + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Expected X509 certificate")); + } + } + + private void runUploadSslCertBadFormat() throws IOException, IllegalAccessException, NoSuchFieldException { + + // Reading appropritate files + String certFile = getClass().getResource("/certs/bad_format_cert.crt").getFile(); + String keyFile = getClass().getResource("/certs/rsa_self_signed.key").getFile(); + + String cert = URLEncoder.encode(readFileToString(new File(certFile)), "UTF-8"); + String key = URLEncoder.encode(readFileToString(new File(keyFile)), "UTF-8"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.persist(any(SslCertVO.class))).thenReturn(new SslCertVO()); + + //creating the command + UploadSslCertCmd uploadCmd = new UploadSslCertCmdExtn(); + Class _class = uploadCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("cert"); + certField.setAccessible(true); + certField.set(uploadCmd, cert); + + Field keyField = _class.getDeclaredField("key"); + keyField.setAccessible(true); + keyField.set(uploadCmd, key); + + try { + certService.uploadSslCert(uploadCmd); + } catch (Exception e) { + assertTrue(e.getMessage().contains("Invalid certificate format")); + } + } + + + @Test + public void testDeleteSslCert() throws Exception { + /* Test1: Delete with an invalid ID should fail */ + runDeleteSslCertInvalidId(); + + /* Test2: Delete with a cert id bound to a lb should fail */ + runDeleteSslCertBoundCert(); + + /* Test3: Delete with a valid Id should succeed */ + runDeleteSslCertValid(); + + + } + + private void runDeleteSslCertValid() throws Exception { + + TransactionLegacy txn = TransactionLegacy.open("runDeleteSslCertValid"); + + CertServiceImpl certService = new CertServiceImpl(); + long certId = 1; + + //setting mock objects + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.remove(anyLong())).thenReturn(true); + when(certService._sslCertDao.findById(anyLong())).thenReturn(new SslCertVO()); + + // a rule holding the cert + + certService._lbCertDao = Mockito.mock(LoadBalancerCertMapDao.class); + when(certService._lbCertDao.listByCertId(anyLong())).thenReturn(null); + + //creating the command + DeleteSslCertCmd deleteCmd = new DeleteSslCertCmdExtn(); + Class _class = deleteCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("id"); + certField.setAccessible(true); + certField.set(deleteCmd, certId); + + certService.deleteSslCert(deleteCmd); + } + + private void runDeleteSslCertBoundCert() throws NoSuchFieldException, IllegalAccessException { + + TransactionLegacy txn = TransactionLegacy.open("runDeleteSslCertBoundCert"); + + CertServiceImpl certService = new CertServiceImpl(); + + //setting mock objects + long certId = 1; + + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.remove(anyLong())).thenReturn(true); + when(certService._sslCertDao.findById(anyLong())).thenReturn(new SslCertVO()); + + // rule holding the cert + certService._lbCertDao = Mockito.mock(LoadBalancerCertMapDao.class); + + List lbMapList = new ArrayList(); + lbMapList.add(new LoadBalancerCertMapVO()); + + certService._lbCertDao = Mockito.mock(LoadBalancerCertMapDao.class); + when(certService._lbCertDao.listByCertId(anyLong())).thenReturn(lbMapList); + + + certService._entityMgr = Mockito.mock(EntityManager.class); + when(certService._entityMgr.findById(eq(LoadBalancerVO.class), anyLong())).thenReturn(new LoadBalancerVO()); + + //creating the command + DeleteSslCertCmd deleteCmd = new DeleteSslCertCmdExtn(); + Class _class = deleteCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("id"); + certField.setAccessible(true); + certField.set(deleteCmd, certId); + + try { + certService.deleteSslCert(deleteCmd); + }catch (Exception e){ + assertTrue(e.getMessage().contains("Certificate in use by a loadbalancer")); + } + + + } + + private void runDeleteSslCertInvalidId() throws NoSuchFieldException, IllegalAccessException { + + TransactionLegacy txn = TransactionLegacy.open("runDeleteSslCertInvalidId"); + + long certId = 1; + CertServiceImpl certService = new CertServiceImpl(); + + certService._accountMgr = Mockito.mock(AccountManager.class); + Account account = new AccountVO("testaccount", 1, + "networkdomain", (short) 0, UUID.randomUUID().toString()); + when(certService._accountMgr.getAccount(anyLong())).thenReturn(account); + + certService._sslCertDao = Mockito.mock(SslCertDao.class); + when(certService._sslCertDao.remove(anyLong())).thenReturn(true); + when(certService._sslCertDao.findById(anyLong())).thenReturn(null); + + // no rule holding the cert + certService._lbCertDao = Mockito.mock(LoadBalancerCertMapDao.class); + when(certService._lbCertDao.listByCertId(anyLong())).thenReturn(null); + + //creating the command + DeleteSslCertCmd deleteCmd = new DeleteSslCertCmdExtn(); + Class _class = deleteCmd.getClass().getSuperclass(); + + Field certField = _class.getDeclaredField("id"); + certField.setAccessible(true); + certField.set(deleteCmd, certId); + + try { + certService.deleteSslCert(deleteCmd); + }catch (Exception e){ + assertTrue(e.getMessage().contains("Invalid certificate id")); + } + + } + + + public class UploadSslCertCmdExtn extends UploadSslCertCmd { + @Override + public long getEntityOwnerId() { + return 1; + } + } + + public class DeleteSslCertCmdExtn extends DeleteSslCertCmd { + @Override + public long getEntityOwnerId() { + return 1; + } + } +} diff --git a/server/test/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java b/server/test/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java index f2ebf67acf9..47e509f140c 100644 --- a/server/test/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java +++ b/server/test/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java @@ -24,6 +24,9 @@ import java.util.Set; import javax.inject.Inject; +import com.cloud.event.dao.UsageEventDao; +import com.cloud.event.dao.UsageEventDetailsDao; +import com.cloud.vm.dao.UserVmDetailsDao; import junit.framework.TestCase; import org.junit.After; @@ -78,6 +81,15 @@ public class CreateNetworkOfferingTest extends TestCase{ @Inject VpcManager vpcMgr; + @Inject + UserVmDetailsDao userVmDetailsDao; + + @Inject + UsageEventDao UsageEventDao; + + @Inject + UsageEventDetailsDao usageEventDetailsDao; + @Override @Before public void setUp() { diff --git a/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java b/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java new file mode 100644 index 00000000000..38425c6874c --- /dev/null +++ b/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.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 org.apache.cloudstack.service; + +import com.cloud.service.ServiceOfferingVO; +import com.cloud.vm.VirtualMachine; +import org.junit.Before; +import org.junit.Test; +import org.junit.Assert; +import org.mockito.MockitoAnnotations; + +public class ServiceOfferingVOTest { + ServiceOfferingVO offeringCustom; + ServiceOfferingVO offering; + + + @Before + public void setup(){ + MockitoAnnotations.initMocks(this); + offeringCustom = new ServiceOfferingVO("custom", null, null, 500, 10, 10, false, "custom", false, false, "", false, VirtualMachine.Type.User, false); + offering = new ServiceOfferingVO("normal", 1, 1000, 500, 10, 10, false, "normal", false, false, "", false, VirtualMachine.Type.User, false); + } + + // Test restoreVm when VM state not in running/stopped case + @Test + public void isDynamic() { + Assert.assertTrue(offeringCustom.isDynamic()); + } + + @Test + public void notDynamic() { + Assert.assertTrue(!offering.isDynamic()); + } + +} \ No newline at end of file diff --git a/server/test/resources/certs/bad_format_cert.crt b/server/test/resources/certs/bad_format_cert.crt new file mode 100644 index 00000000000..bab377a1720 --- /dev/null +++ b/server/test/resources/certs/bad_format_cert.crt @@ -0,0 +1 @@ +BAD FORMAT CERT diff --git a/server/test/resources/certs/dsa_self_signed.crt b/server/test/resources/certs/dsa_self_signed.crt new file mode 100644 index 00000000000..9ee3dc8e5dc --- /dev/null +++ b/server/test/resources/certs/dsa_self_signed.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEaDCCBBACCQDB6QTHSA74AzAJBgcqhkjOOAQDMEUxCzAJBgNVBAYTAkFVMRMw +EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQwHhcNMTMxMDIxMTUxMzE1WhcNMTQxMDIxMTUxMzE1WjBFMQswCQYDVQQG +EwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lk +Z2l0cyBQdHkgTHRkMIIDSDCCAjoGByqGSM44BAEwggItAoIBAQD1cLkWeTvzloom +uaHmB0ANyrmHTum6bVVMyN2V/it8BAErugqQBIaNtF2EOmJ3baxsxIROOqJ5uVx0 +pymYVr4BcS3R6H2G7lSWHa5iA/UMfVp9yHroK+WYdN5ZVKGYcUTvKZXtyr2OX3Rc +KJJ2yp4qbcQTB2GlV6eZdpX3l38G/03n/Wi5yjU2/p56R80FAaLtCr4wJoS2aXxU +Iytps+msEn0LRZuFHpW1sLIKPLRCnjFXWeWKdJu6bggUVB0GjfsZR67XzFY5nRvX +7/vW2XS5Vo4X6gSOym0O/8/vzUp1gQSwHiMQQRAoRYoZPxWo8ACMsqdj0MTbN32y +b11//OlXAiEAoR8xDStCDTaQ2Ale9swNzLB8vWFzmSLQXXOM1PY10fMCggEBAO1p +RV1kluxjYCKec12MKdTBqO2QZnOOVFsY2vpIavgqpxyGaHRwY8Cm7BGosV2chMZw +G6xMz5CRnR55KasaX1Vz2xAz7PwczXWDQA59Il5tQbQdewitm1S3NRb1n9u+cNkQ +E6ZekjdW9tRykvaNQHH8EBzgQ9KXCYzJ8nFLk2DMt2Mor/paXfobNzO/vi2UgL+B +5/E8BXoMtBFPhiz72CsCiXp/usROe/UAejow/CTHYC2mfcPhKt2jIVelXx7Baf0n +K1SWKBpNI4ucBj+/4L3Be0ldqDGmeTRUNTNpJIzmAyplUvnpOiqCbzKst57+l4ZE +xdVgv2LiIrTaqTn1L4sDggEGAAKCAQEAv0hyFYj8IMEaOCk9+6wb32NGEGoEz4re +YW1O02Kij2/0YJk0CPyg0ozIpJq9FiHvOzE+2/Eg5jLVTLlAJbjnGjSnzZJ6pJHw +zhwnJ9wT4to51jLvG1v82iL2FfFKOZFjABxC+kND9cAUXu3URIGfdYJl1rBxvOD1 +WZ03Gk90GMCLUIMqqUYBQg7XRNVf0k1AgiRgDWpdLkKOGiDntAj/unXdFrbse2xD +IbqBGhCNtPVx34CZ3bUWrvYBoj+BUinfItRK4rkZTEGJinoxbKDvdqoewgk2nSty +7CH40wFS4ni4DCvYTEwUoUuciQaU4XUOOSYFS9IjNWOR8uGBtORteTAJBgcqhkjO +OAQDA0cAMEQCIHqfrNbyMadxZ3uCPr8QPj+17XZZod8B7F8VgZW3R5vSAiAV/WMF +vQ1Dwpr4wJOjNELDe5AEH9WGvevfuc54vmdAdg== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/dsa_self_signed.key b/server/test/resources/certs/dsa_self_signed.key new file mode 100644 index 00000000000..9a9d6675d5d --- /dev/null +++ b/server/test/resources/certs/dsa_self_signed.key @@ -0,0 +1,20 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIDVwIBAAKCAQEA9XC5Fnk785aKJrmh5gdADcq5h07pum1VTMjdlf4rfAQBK7oK +kASGjbRdhDpid22sbMSETjqieblcdKcpmFa+AXEt0eh9hu5Ulh2uYgP1DH1afch6 +6CvlmHTeWVShmHFE7ymV7cq9jl90XCiSdsqeKm3EEwdhpVenmXaV95d/Bv9N5/1o +uco1Nv6eekfNBQGi7Qq+MCaEtml8VCMrabPprBJ9C0WbhR6VtbCyCjy0Qp4xV1nl +inSbum4IFFQdBo37GUeu18xWOZ0b1+/71tl0uVaOF+oEjsptDv/P781KdYEEsB4j +EEEQKEWKGT8VqPAAjLKnY9DE2zd9sm9df/zpVwIhAKEfMQ0rQg02kNgJXvbMDcyw +fL1hc5ki0F1zjNT2NdHzAoIBAQDtaUVdZJbsY2AinnNdjCnUwajtkGZzjlRbGNr6 +SGr4Kqcchmh0cGPApuwRqLFdnITGcBusTM+QkZ0eeSmrGl9Vc9sQM+z8HM11g0AO +fSJebUG0HXsIrZtUtzUW9Z/bvnDZEBOmXpI3VvbUcpL2jUBx/BAc4EPSlwmMyfJx +S5NgzLdjKK/6Wl36Gzczv74tlIC/gefxPAV6DLQRT4Ys+9grAol6f7rETnv1AHo6 +MPwkx2Atpn3D4SrdoyFXpV8ewWn9JytUligaTSOLnAY/v+C9wXtJXagxpnk0VDUz +aSSM5gMqZVL56Toqgm8yrLee/peGRMXVYL9i4iK02qk59S+LAoIBAQC/SHIViPwg +wRo4KT37rBvfY0YQagTPit5hbU7TYqKPb/RgmTQI/KDSjMikmr0WIe87MT7b8SDm +MtVMuUAluOcaNKfNknqkkfDOHCcn3BPi2jnWMu8bW/zaIvYV8Uo5kWMAHEL6Q0P1 +wBRe7dREgZ91gmXWsHG84PVZnTcaT3QYwItQgyqpRgFCDtdE1V/STUCCJGANal0u +Qo4aIOe0CP+6dd0Wtux7bEMhuoEaEI209XHfgJndtRau9gGiP4FSKd8i1EriuRlM +QYmKejFsoO92qh7CCTadK3LsIfjTAVLieLgMK9hMTBShS5yJBpThdQ45JgVL0iM1 +Y5Hy4YG05G15AiBO/b8wvyKsJJAInNbsLH7OPitH3qKsI18IxykJSl9lTw== +-----END DSA PRIVATE KEY----- diff --git a/server/test/resources/certs/expired_cert.crt b/server/test/resources/certs/expired_cert.crt new file mode 100644 index 00000000000..d133f24bb74 --- /dev/null +++ b/server/test/resources/certs/expired_cert.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVzCCAj+gAwIBAgIJAJRInPvOqxbXMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNV +BAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQg +Q29tcGFueSBMdGQwHhcNMTMxMDIxMjAwNjA5WhcNMTMxMDIwMjAwNjA5WjBCMQsw +CQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZh +dWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +3rASFvBmAF+CHn58TU5ib0HuMD4gxutohjzQjJXYRFs1cmiLb9esO0Cvk0DFaQuq +lpxw4Dhe6kxRbNQKkPFr0zsv9B8I/o8UQmclnZ0I++ixlAq4VyP6H0sewrmdVbrV +mSH0eG1t0hLqXUmxX/XX1kkbHOgZQQk90Sew23VwTVxbywq8yjkYLa2hlBXh33rX +siky7JFiF0TJE2r6wNB60BQMdi2EErtBUPL/1LD7+VuB/Y6xZpwivKwMIXeFdzSr +I2gGCn+zf01kNfHj0YrA2bH0i5FX4/9YSpd8PFIOYc/gA+3MNO0xMWchbz6AfjPQ +aR4tGLsRBjjwvLIZwHzyWQIDAQABo1AwTjAdBgNVHQ4EFgQUo/kIdWN+yxvnWUoK +/ZJYNl2wY9cwHwYDVR0jBBgwFoAUo/kIdWN+yxvnWUoK/ZJYNl2wY9cwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAJ/LwjkhXQFOcfUmAoIeBIDAMayy7 +HEJusMOA+uUPPuEemgU7DTbwABCaavfBza7QvG171LsTGtvgnvwDg7rdScUOcoX8 +w0XZbhr+dZzvashVmMz4oY6hGw29eGo7M01Rm4msX7bW1IXmdWmDcqcNT6/dKOgj +tjMR2JvN9I4qwZ41xXAeUHDUqKjgsKlhflBbOX7K07tbpHFMQZcoOs19Jqve8Gfm +MbH0hZw3LC2YNed8ne/3/lXKNZIhbyZp1IJ/rjgruEZ0ObOwqB1IGR0UjQbAw102 +1D4L8FOzmDi4LfhKPgGc/BMeuPRozxQi60TzrnXN5eQSb0ukFvScMhAIrQ== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/non_x509_pem.crt b/server/test/resources/certs/non_x509_pem.crt new file mode 100644 index 00000000000..fbbae8ed75f --- /dev/null +++ b/server/test/resources/certs/non_x509_pem.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICtzCCAZ8CAQAwcjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAwwEU3ll +ZDEcMBoGCSqGSIb3DQEJARYNc3llZEB0ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBAKmQeRXpkeGRhzdj3aJiWUcZtKdcjPCdtphx8gHk1GV1 +vHK2zYenwe23EbSsfkOAQx2p86jCpghN7b+ECilmou4C7ASPS2U8n+GhWdl4eUK4 +Gmz7+daRqcDQOCTCJWSybtShYzvfp2SaEt/TcUgDT+k6xRitqcIkAY7boGY1Cehy +O6hYdlFkg0oU1XCA0usIaSEi7gOTijpGe2jSh7CTBRRWiClQnqE8VT7x4hXQEDF6 +u26K/ptAL90bgyFUuX2dEH5dKd/5QAyAfdrLV6a3wIvikCUGZy23Twtxot4kFVYf +S2x3H9BS6j/mIvrUj95CLi2Eu6t4z5uHMOrf8k0NjN8CAwEAAaAAMA0GCSqGSIb3 +DQEBBQUAA4IBAQBa5B6xzAkx3OtJEgOkTJ0nID2TsTt6eQBrp+//jhT/9yKLHmd5 +SeNcLzMWDlTQ+zNlLf9+lreO+CmlolaOKDRXh9eXbyNq0/I2QxOEsIlXqH4dVoCB +6UkV8eA0lFeNdFJaCla5/QTKmv4f0eUakbI5Scd1PlBr+YOplvI9fpLaxHGmVzCX +s3WQ7m7SN5hZav7dO8krheZL7N3AYZoCY0QbQ3JTeoakLenmFXqvsckP6in5NQJh +ECaJprYxrxR+5GPjpUkxu7YGLloXHvnnINeZGJG0TB7kPDO+axDxloiI2eYlT/mp +HxdgL9BvDYNfI+0NcZYHguz8GWuWiTZC7F26 +-----END CERTIFICATE REQUEST----- diff --git a/server/test/resources/certs/root_chain.crt b/server/test/resources/certs/root_chain.crt new file mode 100644 index 00000000000..2ed80858532 --- /dev/null +++ b/server/test/resources/certs/root_chain.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDnTCCAoWgAwIBAgIJALXV1B5/vewgMA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNV +BAYTAkFVMQ0wCwYDVQQIDARTb21lMQwwCgYDVQQKDANPcmcxDDAKBgNVBAsMA0Rl +djENMAsGA1UEAwwETmFtZTEcMBoGCSqGSIb3DQEJARYNdGVzdEBtYWlsLmNvbTAe +Fw0xMzEwMjExNjAxMDdaFw0xNjEwMjAxNjAxMDdaMGUxCzAJBgNVBAYTAkFVMQ0w +CwYDVQQIDARTb21lMQwwCgYDVQQKDANPcmcxDDAKBgNVBAsMA0RldjENMAsGA1UE +AwwETmFtZTEcMBoGCSqGSIb3DQEJARYNdGVzdEBtYWlsLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBALQZLQa2R64GRCuoh59VFvKoXnAM+4H4sW9E +jiURT/CeH6rbD0IbEMZZ3o84VfQw1zDsVXN/GXq3IEMp8uBBpWJuZRBF1UeDMYlB +bouMqUGL05Ov51zK2aQK/83S8MoQI7KV1FGJNti4iwUzemG1fClrBqjCFJQKmv8y +0z1UaeniAR7ygedIB4I2Y4a/DxtI0e2EsS0TcViwKHGrLArO0GfvNbH+tXqTaotF +X5eyinUFqQxT3JvnlIIernk0ly8c07mqOFbFqHrhFXxddD4pZrUetHXM2MKLdCMu +fvXsmMXSAgQ5F97GWmiDEJ9zMDxGoSmhTIN96FwPCRDr7e22lEsCAwEAAaNQME4w +HQYDVR0OBBYEFIxSziB7ssNqFvhV2MSf1GYvGHQYMB8GA1UdIwQYMBaAFIxSziB7 +ssNqFvhV2MSf1GYvGHQYMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AB1rcxZL6xGuk8PSoBQ0imREcVf9BcEvGIC6uubPSXuoJ/gr584vo3pRJpTpHQyK +xUUwG6gCaDgAMYX177GBamGCt056ThSLKFROIPVrigZ5yY1NWznNq3zjWS4jIHkV +vGd+Gx8t3qYBhsn+v6Y6gRTjMOVQum+rvxvixG7n97DaxBnrwNWXbzZQ9spHrQsT +dsSF/kf9NKkWM0zKh+f/FSNmveJKKvZ+WQZC/MKUfc5VHjyLldXhrffcxzek/dOc +8YADSICSizvXCP/QjyVVl8dpKr/3c00r16Ei3QQaFhHES0bv/sKLnTwQwKDfJthu +bQj7M/XGWi33JQgoMHktYhI= +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/rsa_ca_signed.crt b/server/test/resources/certs/rsa_ca_signed.crt new file mode 100644 index 00000000000..fb74f17574b --- /dev/null +++ b/server/test/resources/certs/rsa_ca_signed.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1TCCAr2gAwIBAgIJALXV1B5/vewhMA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNV +BAYTAkFVMQ0wCwYDVQQIDARTb21lMQwwCgYDVQQKDANPcmcxDDAKBgNVBAsMA0Rl +djENMAsGA1UEAwwETmFtZTEcMBoGCSqGSIb3DQEJARYNdGVzdEBtYWlsLmNvbTAe +Fw0xMzEwMjExNjE4MjJaFw0xNDEwMjExNjE4MjJaMHIxCzAJBgNVBAYTAkFVMRMw +EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxDTALBgNVBAMMBFN5ZWQxHDAaBgkqhkiG9w0BCQEWDXN5ZWRAdGVzdC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpkHkV6ZHhkYc3Y92i +YllHGbSnXIzwnbaYcfIB5NRldbxyts2Hp8HttxG0rH5DgEMdqfOowqYITe2/hAop +ZqLuAuwEj0tlPJ/hoVnZeHlCuBps+/nWkanA0DgkwiVksm7UoWM736dkmhLf03FI +A0/pOsUYranCJAGO26BmNQnocjuoWHZRZINKFNVwgNLrCGkhIu4Dk4o6Rnto0oew +kwUUVogpUJ6hPFU+8eIV0BAxertuiv6bQC/dG4MhVLl9nRB+XSnf+UAMgH3ay1em +t8CL4pAlBmctt08LcaLeJBVWH0tsdx/QUuo/5iL61I/eQi4thLureM+bhzDq3/JN +DYzfAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wg +R2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBR2LIRfVveDgQB3dC7TOYhX +BEv71DAfBgNVHSMEGDAWgBSMUs4ge7LDahb4VdjEn9RmLxh0GDANBgkqhkiG9w0B +AQUFAAOCAQEAPjN/sdLcPfMh5ep29vp/7JTh6dUYnBNATYaXxx8j2XdnMCKeRfgP +WOJur8HDPSayWWKKlztiQbJV5jDS5vyuMWI1a5/KIAQlOJep+anpR1QnQaX4/M4Z +YUJo1fPs6tg47dXRpZZaJ+Hqwh0ZftCQoUq/sBxawXf6sbxsjoUmzxQLBqzYo1LJ +jwxBs6C9aM8LDHFz4TVlyclSFQXiLMosj1jLBQ+TqzCxS6qOfJeMM9STXI9W3F2k +duXeceqOwEkh8aeSUIztYFpX34d4SA4DDX5GUEaOeOR/abnXjH52vE6tM/m7NOve +5+I/BrlT3heRqiD6Z2ofSsFhG86YeF9Q3w== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/rsa_ca_signed.key b/server/test/resources/certs/rsa_ca_signed.key new file mode 100644 index 00000000000..d30dd8e8e99 --- /dev/null +++ b/server/test/resources/certs/rsa_ca_signed.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIN4x8YHLIb60CAggA +MBQGCCqGSIb3DQMHBAiuWe+C2SitUgSCBMhVxMKivNaQh9SQedh2GQ8vlhI2/mKf +r6zVUgODOdmFRj/PAlQu6B+VH8u2Ef5tr74O9UyZUQpC6oI0npvQOGqnhIxECR8D +zepFlC0s+6kNRqfzXiMhGTsfbs3xUE1TZK1z5556OS4GlihIB0E5F7GdlulHELeA +dnEItqDffWPQczTmSmFqtH7G/ksGQhSA2reBIDNgxWreBh9x9h2TftEvhhogdkr2 +XQpzxcby2v2z6Hut1lfVkLQNdvdHYdDG751NQltOmGvncXkTNJ+tSByXuY9ITtYz +XqwY/vAEX+YYjCrWcvMmhU1t6B9cZ3viFzIIIltqtvDo3rybicpM3xvmnYnTF35j +sGebcynzZedrLUHwD6/o0jBzFJN26QBxDGrlmn9E8HBhmjMCeSyZTXtSkwN0dAhu +EHT2NgdC/4G4+JHsb3Oetl6AbUyYs2eWTRgr6bgBezbiDqKMj1QtGECkPQE9tFFP +Jtma6lGJ1/u8zLFYNjigKZgXaNbtlMpslk+KDTGaTSbbVEaaxWSc0YlHz329ovQC +1btrTE71FoNjRsTRaDRXC7BfrrMtnLhG2SmROKzyBtoxFymfEmASpdGERO+c2712 +Z0NLpHJL5ocsRr/onVi0auFC/nuaq6QqYF5OPtZhYh3+IDd193LP3xo8hnFjTq90 +9zV1QI2uHfTfijI4OABYSSZLxrl2IFF7Z/e4XkWSXrneR2Ne5GoYX7w6wshiuubQ +bDPUHODsy7uQJ3ZEmmmd5xWvA3h3F5Yyls3WEuz8+jYR4iPKdjt8DJuz9j+7IP3C +LoRa3KhxIPAzziQw+tMFVCozLLfS1kN8mg5SwU9Dp4HUPY/n3KnjVSjUJ347KAgW ++ykPlz4S1H0A2GyGWhdyX3J839UOdXfCsfKQQ1FLOBvhQgBNGzoRIgDrsZdrxgWa +XtF+Ct2/2/O+503I6X90maXzhshmGw/NKsiNx7YOdUiu6w3N4LajIWE689UUU7Dr +EtM8HcfmBnE/cZTbeWbQBfl1GqigMp+YOAY2sw0rZdF4ocmnqoCWLVN2j8O/re5g +20y5eHkztPOZ+NNczo5PP96ng1XEMVzs/h6xDHzsak6osZb5b+Hs9kcYuqBEqr16 +3DVwaZ45dGwNN90Q9YmrgEdCqtCAU12w/YYVB/aB701Ijg0NhVsh7PgWaJIOJD56 +YTaiWIzQaZ/uM0KlgLz2eI9VXcuv24gUMAUMaI84mInnIrax1zFMrlCjEcR8Zxvk +jCCY6Uq9WH30cUo7cMHWLWMzsl7PC7xpYSHPClqzCUluUgQwqoOs5Ux4nYl+JI7C +ZfJ8BUMCD2RJtjvJhkE6LEkcrCjwnvappRaXbN54IVXpuMl2XYtp44T117QulEj1 +I/jk1mrpkXRKi9ZsKsjDH9VMy1hcKHn4CgTxmtRYN6LPA4tamxzVLxIi6YDU0142 +l1u763cT7cH14lvzUvEQMFbk/s0AUl8zeZAwjDayNlD/ljz7nZnJ3NToMlxeoK0a +F6c/RgQBwxR8NMdo19Irv8stxo9WGB2/0Q8WCxW7ENHlBpvX5zXKRJMswFKp0ft6 +dT7hOBPgJlD7C1eX5jVcSz6kDRM6gQ7K/c5QcJa6qNAC2Jw6yBBYltqiRalFWX46 +VcM= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/server/test/resources/certs/rsa_ca_signed2.crt b/server/test/resources/certs/rsa_ca_signed2.crt new file mode 100644 index 00000000000..fb74f17574b --- /dev/null +++ b/server/test/resources/certs/rsa_ca_signed2.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID1TCCAr2gAwIBAgIJALXV1B5/vewhMA0GCSqGSIb3DQEBBQUAMGUxCzAJBgNV +BAYTAkFVMQ0wCwYDVQQIDARTb21lMQwwCgYDVQQKDANPcmcxDDAKBgNVBAsMA0Rl +djENMAsGA1UEAwwETmFtZTEcMBoGCSqGSIb3DQEJARYNdGVzdEBtYWlsLmNvbTAe +Fw0xMzEwMjExNjE4MjJaFw0xNDEwMjExNjE4MjJaMHIxCzAJBgNVBAYTAkFVMRMw +EQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQxDTALBgNVBAMMBFN5ZWQxHDAaBgkqhkiG9w0BCQEWDXN5ZWRAdGVzdC5j +b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpkHkV6ZHhkYc3Y92i +YllHGbSnXIzwnbaYcfIB5NRldbxyts2Hp8HttxG0rH5DgEMdqfOowqYITe2/hAop +ZqLuAuwEj0tlPJ/hoVnZeHlCuBps+/nWkanA0DgkwiVksm7UoWM736dkmhLf03FI +A0/pOsUYranCJAGO26BmNQnocjuoWHZRZINKFNVwgNLrCGkhIu4Dk4o6Rnto0oew +kwUUVogpUJ6hPFU+8eIV0BAxertuiv6bQC/dG4MhVLl9nRB+XSnf+UAMgH3ay1em +t8CL4pAlBmctt08LcaLeJBVWH0tsdx/QUuo/5iL61I/eQi4thLureM+bhzDq3/JN +DYzfAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wg +R2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBR2LIRfVveDgQB3dC7TOYhX +BEv71DAfBgNVHSMEGDAWgBSMUs4ge7LDahb4VdjEn9RmLxh0GDANBgkqhkiG9w0B +AQUFAAOCAQEAPjN/sdLcPfMh5ep29vp/7JTh6dUYnBNATYaXxx8j2XdnMCKeRfgP +WOJur8HDPSayWWKKlztiQbJV5jDS5vyuMWI1a5/KIAQlOJep+anpR1QnQaX4/M4Z +YUJo1fPs6tg47dXRpZZaJ+Hqwh0ZftCQoUq/sBxawXf6sbxsjoUmzxQLBqzYo1LJ +jwxBs6C9aM8LDHFz4TVlyclSFQXiLMosj1jLBQ+TqzCxS6qOfJeMM9STXI9W3F2k +duXeceqOwEkh8aeSUIztYFpX34d4SA4DDX5GUEaOeOR/abnXjH52vE6tM/m7NOve +5+I/BrlT3heRqiD6Z2ofSsFhG86YeF9Q3w== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/rsa_ca_signed2.key b/server/test/resources/certs/rsa_ca_signed2.key new file mode 100644 index 00000000000..d30dd8e8e99 --- /dev/null +++ b/server/test/resources/certs/rsa_ca_signed2.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIN4x8YHLIb60CAggA +MBQGCCqGSIb3DQMHBAiuWe+C2SitUgSCBMhVxMKivNaQh9SQedh2GQ8vlhI2/mKf +r6zVUgODOdmFRj/PAlQu6B+VH8u2Ef5tr74O9UyZUQpC6oI0npvQOGqnhIxECR8D +zepFlC0s+6kNRqfzXiMhGTsfbs3xUE1TZK1z5556OS4GlihIB0E5F7GdlulHELeA +dnEItqDffWPQczTmSmFqtH7G/ksGQhSA2reBIDNgxWreBh9x9h2TftEvhhogdkr2 +XQpzxcby2v2z6Hut1lfVkLQNdvdHYdDG751NQltOmGvncXkTNJ+tSByXuY9ITtYz +XqwY/vAEX+YYjCrWcvMmhU1t6B9cZ3viFzIIIltqtvDo3rybicpM3xvmnYnTF35j +sGebcynzZedrLUHwD6/o0jBzFJN26QBxDGrlmn9E8HBhmjMCeSyZTXtSkwN0dAhu +EHT2NgdC/4G4+JHsb3Oetl6AbUyYs2eWTRgr6bgBezbiDqKMj1QtGECkPQE9tFFP +Jtma6lGJ1/u8zLFYNjigKZgXaNbtlMpslk+KDTGaTSbbVEaaxWSc0YlHz329ovQC +1btrTE71FoNjRsTRaDRXC7BfrrMtnLhG2SmROKzyBtoxFymfEmASpdGERO+c2712 +Z0NLpHJL5ocsRr/onVi0auFC/nuaq6QqYF5OPtZhYh3+IDd193LP3xo8hnFjTq90 +9zV1QI2uHfTfijI4OABYSSZLxrl2IFF7Z/e4XkWSXrneR2Ne5GoYX7w6wshiuubQ +bDPUHODsy7uQJ3ZEmmmd5xWvA3h3F5Yyls3WEuz8+jYR4iPKdjt8DJuz9j+7IP3C +LoRa3KhxIPAzziQw+tMFVCozLLfS1kN8mg5SwU9Dp4HUPY/n3KnjVSjUJ347KAgW ++ykPlz4S1H0A2GyGWhdyX3J839UOdXfCsfKQQ1FLOBvhQgBNGzoRIgDrsZdrxgWa +XtF+Ct2/2/O+503I6X90maXzhshmGw/NKsiNx7YOdUiu6w3N4LajIWE689UUU7Dr +EtM8HcfmBnE/cZTbeWbQBfl1GqigMp+YOAY2sw0rZdF4ocmnqoCWLVN2j8O/re5g +20y5eHkztPOZ+NNczo5PP96ng1XEMVzs/h6xDHzsak6osZb5b+Hs9kcYuqBEqr16 +3DVwaZ45dGwNN90Q9YmrgEdCqtCAU12w/YYVB/aB701Ijg0NhVsh7PgWaJIOJD56 +YTaiWIzQaZ/uM0KlgLz2eI9VXcuv24gUMAUMaI84mInnIrax1zFMrlCjEcR8Zxvk +jCCY6Uq9WH30cUo7cMHWLWMzsl7PC7xpYSHPClqzCUluUgQwqoOs5Ux4nYl+JI7C +ZfJ8BUMCD2RJtjvJhkE6LEkcrCjwnvappRaXbN54IVXpuMl2XYtp44T117QulEj1 +I/jk1mrpkXRKi9ZsKsjDH9VMy1hcKHn4CgTxmtRYN6LPA4tamxzVLxIi6YDU0142 +l1u763cT7cH14lvzUvEQMFbk/s0AUl8zeZAwjDayNlD/ljz7nZnJ3NToMlxeoK0a +F6c/RgQBwxR8NMdo19Irv8stxo9WGB2/0Q8WCxW7ENHlBpvX5zXKRJMswFKp0ft6 +dT7hOBPgJlD7C1eX5jVcSz6kDRM6gQ7K/c5QcJa6qNAC2Jw6yBBYltqiRalFWX46 +VcM= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/server/test/resources/certs/rsa_random_pkey.key b/server/test/resources/certs/rsa_random_pkey.key new file mode 100644 index 00000000000..8c29b3e20de --- /dev/null +++ b/server/test/resources/certs/rsa_random_pkey.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQD2ZYOSvqPjro7k +JKWggB0FJL45mz3pfvBR0NcHTGgHKdh5hml1GiHHPanYnWqRuKFI7UPWLq3xllvJ +jolMjX6FGzPFuh4wHQ3gHMFlmpi6o1gUkk5QxAeCWmsA77qJAAiLVbm27sqfpvSG +ec6KJOcePDDN/6Jp363Gf8JuSssafg6fAbfwLp7Cm1cxFM3RKN13fUdy50+OcXv7 +uvUlzHZ2ZIVK7FnlFoXOfhFHoyfdZL2Qm8xV3SuUllNPGl7aCM59Y6xR743Rq01j +3E6hHNZ82GlJGaOL413oVsp5J9ZmKV+WM7plFIn86p2JGMoA2xjtON+X6BZdz3YT +ymp7ic+7AgMBAAECggEBAKvkNd3x0TPNWzIdvs4xkg08jNhzTMXQIKdzIg+dZhVZ +RAPDmV5wVJBCnHLZnrb6LspJe8G33vFjC7WZEn+tVk5Vo9CU/uph2oQ2i1TufQ33 +VkNDrg76MqLloTCODXv14gASVfUgsYqfVodaApStGe8l3oZXiF6EBR8tkd6PnxFi +8TPAalFS/0mfjj/dA9zHXoZ0iyl+h6had6kqJNNbP8zgD+TbnfJZVkURNB0cyGVI +8/8d/eO05oQ1DpuvTJ+Q5i8wTDjikUzc4btt6coA/ho4QuOvfsZGsgCzCYsUn0+W +GA+Vn2PVEFV5hJ+MZ9gGmQ5ouC/IVAYkBBC7HlNX70ECgYEA+3DC6rTvXqdrjZz1 +TFWcITfAt7WzfGlHi2LBZ/JNmyIetXmF/sHpqGKkvp7iHDMJ4NeEBXSN9tbbEfhc +1yU+HXYlKkeaZAGaNiCSvqlr/Bouy7dX3jG8xgPGQ1o2gyluaoTEwORf2LgmApYA +q/eQyWTKHLxatTnjlqLULerxqv8CgYEA+t1Wa00XHHgB1D4was59qnpPemIUxpvm +4e3wjyQs6P2kH0F0rKyytQaSXKhkBYRcmh0EnyNSGu7wxM5l3aDTzCDgBHmb1i9P +UXLzWUYnsSgUpUB8jL6YDlUmQ1AMMDqvLagLlOLqPkRmNjN1P4/+6mdoxD5SL0rv +5FeRaOAGR0UCgYEAjWEgGDzYg058CUqCGwPgIEVrFWETpRbFZbiHq1zxChOrVLsZ +/t8l9MpSe+R2mwiPu18zGqYo1OyGjZorCcYlIQe3agiM5UKJZXn3SUGWOFC4k09q +FsO8s1KX/nMRR7raHQa+Yv+GbSNOLBIQGqG/RZ5ojrPSBSihsaeoypDahh0CgYEA ++OK3ZmVpVHlLd0LrzktnKceHKqg8bH8oJWZnj9wYIl/igI/0LYx5EFigxQTblw2m +wc+gUjI8tzPv85HCRovVFWRYXJg6H9l4HBqrjBqqLnzRXtIHv6soOLAJ8iZssTzH +p8hdFS27sGMz9PpAjPtTsUM/EdOyvfDe5/Bo91+rWvECgYEAxgvptntWbXc8QI9n +DFYGigeza0sQTmFtSsob5cux9dtPORChdb6EPNTgOPOfKD4QMkmar/VWedeXOHb9 +Cy1Qz7bgOhIUSMdXzw4hPOmLrT7IbhG5CHawwHanGycrQ/bJcV00lsv0BEj3N/NJ +PW/vxADWc9H3AcVeMi5So6gTBt4= +-----END PRIVATE KEY----- diff --git a/server/test/resources/certs/rsa_self_signed.crt b/server/test/resources/certs/rsa_self_signed.crt new file mode 100644 index 00000000000..710326ef8e5 --- /dev/null +++ b/server/test/resources/certs/rsa_self_signed.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe4CCQCEkqahWR0hjjANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEzMTAyMTEzNTIyMFoXDTE0MTAyMTEzNTIyMFowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN6wEhbwZgBfgh5+fE1OYm9B7jA+IMbraIY80IyV2ERbNXJoi2/XrDtAr5NAxWkL +qpaccOA4XupMUWzUCpDxa9M7L/QfCP6PFEJnJZ2dCPvosZQKuFcj+h9LHsK5nVW6 +1Zkh9HhtbdIS6l1JsV/119ZJGxzoGUEJPdEnsNt1cE1cW8sKvMo5GC2toZQV4d96 +17IpMuyRYhdEyRNq+sDQetAUDHYthBK7QVDy/9Sw+/lbgf2OsWacIrysDCF3hXc0 +qyNoBgp/s39NZDXx49GKwNmx9IuRV+P/WEqXfDxSDmHP4APtzDTtMTFnIW8+gH4z +0GkeLRi7EQY48LyyGcB88lkCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAlgx04bvU +/EE5z7lJsYmUM2oi8FjqZcZkD4F1ku7zvdAXaDNhOHjrR5LxGL+iI/N9S8lKVeSV +xZv+EXAT0NqkTidNFjvP7coctDrrM+JHSNTRlr2GnnYjCnjEph4+ZXNppx8vnhXe +7jDnHoXL/C5GIPOm0+LQaH1AlGTPF0lnBrtQaz1UG34vCr8SSUtRbTDDxH/liXfc +hfvVnf4OV5Duj0oUXsmB3YzITYZnZ/xvZ4Dw6rOU/U5Vetng+msOOt8momeTCnWB +/d1clA7rulJTCNZXb0YyaUNaC6eQX7S9JHnluB67b9yp4yg8f00qz4xR165eTQmq +mLiuE/U5fTODvA== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/rsa_self_signed.key b/server/test/resources/certs/rsa_self_signed.key new file mode 100644 index 00000000000..14b95e6ea1f --- /dev/null +++ b/server/test/resources/certs/rsa_self_signed.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA3rASFvBmAF+CHn58TU5ib0HuMD4gxutohjzQjJXYRFs1cmiL +b9esO0Cvk0DFaQuqlpxw4Dhe6kxRbNQKkPFr0zsv9B8I/o8UQmclnZ0I++ixlAq4 +VyP6H0sewrmdVbrVmSH0eG1t0hLqXUmxX/XX1kkbHOgZQQk90Sew23VwTVxbywq8 +yjkYLa2hlBXh33rXsiky7JFiF0TJE2r6wNB60BQMdi2EErtBUPL/1LD7+VuB/Y6x +ZpwivKwMIXeFdzSrI2gGCn+zf01kNfHj0YrA2bH0i5FX4/9YSpd8PFIOYc/gA+3M +NO0xMWchbz6AfjPQaR4tGLsRBjjwvLIZwHzyWQIDAQABAoIBAFBYus4oAsWTsEEM +ZhEGfSGjaith3zWmblowyxZOYm+XcRtMeTLrYCso1bCNqCyUlwIsg9WCwUxMKPzZ +LM7LLJpUOqMcJ4ShXy/uQ3Yw2LL7bEb77zMRugdcdUbQ7eGmvba4t5pT8VHgnUr3 +cdYrv6qDShMN8z6x9OnoJjmoj9J5Ggda6DhsXsvl5Ox85XMOJKd9yyaUfa/qy8b6 +wIyixGQ/9l/GwGONgtrF7yKW3YUE+uhoEp4pqgKsGUIke3l7aWug6dCDjKDMdbX5 +jwSSiw2ilTpQJhi1r8JIMOfMea3addf39VySK7e6cSWhsf1VTVwneJbka13xS6uR +SSdvs7kCgYEA9ynOXh/1+VkpCHJBAPWEIUtPn16KGJB6YEsEf7cfaAQfzQCwJ8E4 +I7/WsHveHXs80HLS/ZJQlIXXsdeMiWdu1rnsQiVBZMmSNOpOD8iP/6mv8eqeeNr+ +3e3JG+j+l3w+/RzX516WC5JPMIsNSIzSlyx8Yht4IYA2uvpJcLdMW2MCgYEA5qY/ +xht6UlbLevGzFFFnM5R9LWSs+Ip88HCBmEwbow7FQTc0TXYbOKxGtPqn88dM91XT +NMpPaGenuioRz+P1sbEFOP/iE4Hyob8643NMkAiwwoxzSf2Bsj+ebJ/U7oC0xKYx +yjLeFWINhDy+I3LDLEXTTfv5GNrFlitqBEA+ThMCgYEAmOrJnhyCD4JlS698nj5I +QF0a5wwTvnzs6dSf9PB0QuOCVVBerEn0FNIk3s3UL0NG7eSMu4uhxTJFr+cfMQfI +YJtpG8d2/QdlKM3p/APna5Mtoyu4XieH1gC/E0CE+25IfksxHRm9FW2xBuSRFFjk +FdnVHtHF8lwkAGzHsTAG0ucCgYEAigFdVT7psMyoEZb+7KBMXKtzPq7nZAsQ+JiI +okSfoK/czMmoLNUHMqC56d20koNkhPVAW2zVmIW08QntAHPIdZqSomlQrxVoxOjz +5lX9sIzSnoWFEfdyG+I++4Wi1VYDU0qRrgdDpI23wrDJn9Ix/5KD/TxP7lQwN0sg +swxxeysCgYBBXGBBJR7+AbreFpOHitw0h32Qdmy6zHTEF8e0SjmEgDv3uwGDdsYO +QQ7g9QPPPUsYtl0+mUmCwDrw1sJeVFtp86AQlQMV89pR2yXZLf0xwT7IN6RAH5Bi +WlV2/pmiMuWB1qSUKgdPzVEd6aqtjD0TIjtryDBHp76YHJR6SzdCCA== +-----END RSA PRIVATE KEY----- diff --git a/server/test/resources/certs/rsa_self_signed_with_pwd.crt b/server/test/resources/certs/rsa_self_signed_with_pwd.crt new file mode 100644 index 00000000000..de5e5c9ac0b --- /dev/null +++ b/server/test/resources/certs/rsa_self_signed_with_pwd.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe4CCQD5Q6qF5dVV0jANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEzMTAyMTEzNTgwNFoXDTE0MTAyMTEzNTgwNFowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN/7lJtiEs68IC1ZPxY9NA34z9T4AU4LPS/kbQtuxx4X72XOBy+y0cB/qdMD7JNV +h8Mq4URDljhSDyVPdH/+jQr+7kWx2gNe2R/DCnd/meVwwU30JJvpGVZXt+MTef5N +QAbSfDMsuT4FaUY80InbDd24HelrjwunPdY9wwKXO6zL2fLjyDRediiydxcx18Vb +Dq1cm7DRi4mNkmA3RwBQMhxGp3VsfXJ4Hy2WTRCCCxWHZphAh3EUJGK3idum6/7j +HbAwpM/t1kNWN8PZiYDZ1HbccgjmqB7Cub10BfB9g1RByiQ/C87o5cKtQha3uuXR +iBcHISoDydQrgxKgUpiqEF0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEASvulIaVb +zh8z2TysE6RFoYTAYIRXghFrmqCUyyQmUVTvs6vN8iaSXr+WMQJcpgXewWcFrDhr +mIcyRCvF91ZYb7q6lMZFSpE6u/SUGIEtxGUDAfbkbQdKYmrMcbggUUIvSzgUFisO +Kr0H9PEO4AWtCCrtOJFc7jgu03Sv06wDxn9ghkyiBRnVkbAhoKfKnI179yKruJWR +A3ieEj0eFoUbeSH8hDkToj4ynpkAvEGoHjHG9j+8FJxy/PTjkyVPl1ykTs+2Jc1B +Snx8f2afdTenPWyyBm3wFuRZjEAJJLUO0kxM7E8hAwhGsr+XYanwcr1oA1dz6M3f +cq26lpjTH5ITwQ== +-----END CERTIFICATE----- diff --git a/server/test/resources/certs/rsa_self_signed_with_pwd.key b/server/test/resources/certs/rsa_self_signed_with_pwd.key new file mode 100644 index 00000000000..d645a716632 --- /dev/null +++ b/server/test/resources/certs/rsa_self_signed_with_pwd.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,CCA6E4CB4C4039DD + +TaVCJtB0dE9xTZbX7GOaGJwwGHVAMjU1GbRIHf0jODdP+quZvbjklNqsw8Ozlia9 +q/G+UqtRJGlIPPLpce0YCrTo0P3eixZdMs0+hioAEJ4OLtL0SAC6b8q/gB6HRfAx +BvNg+umTqeF9YB68Tcuv/2g4VGKiaePQACyOzMdf7lGY7ojxoJCYZa1mfKb7jWrg +FLwmTtLLhNjb6CnOKo3klIef3A6zdutpgxF1gARzdRyXg4qCA3boYnwEptTOlJFu +ovxbhDG9iuYYr4gXYSs1pLYptEC8J6iWpG/qzkwfr4l5Cfg5uF00bbxQE5+WeRaj +YFicvXjB/kcoFZuCL7M/YRXYxkJ/EZ19xI9HZNBQ4L738StkSBKL4OhpF/qgYZ2y +ZLRV6XT4AijUA0Ef7YTuUsTL7Qt9drj09gCtAzXTA7gpZBn5SqT9kWhuwSzY302l +KF8DIC6A52igk2QKPLbleM/V8eCu6n+J4uF+0GwVRROuG7ThxAQiUlJKhoEYrndL +nzT7jHVLftjilhVWFu2On62bRf5t1QZuob+1AdK0ukvEIVsYnN4bnlAkc99Wi6C0 +ZJd9qW5L4A9XAC2gcjr3m0Rzw3RO+k17faR8YfmTuJvGyBf5fnrSFoNkrninXQXp +sk0ajRi4PJ4XTswLyxjWRSt3egNsZBSKnVCibca/QoDEdZHSKXo2FlYiUYx8JHQX +SPUsLl9OQKC1W8/+ReryqBLHCkiGEsvT8gVaXga0uhVaqe+PaVur2tbOHl4yCysC ++ZlnKwsC84LQsUvpENdCh+D7E1I1Rao9IJMR6q9azKq8Ck63cOJ1fA9xSnxJGoCA +IlGLttlXrR32EtzYwEnlqf1nI/IqNQrAXQKrP5VPzHsgMFu5uD4OEZa92Q5cVTsz +ap/1UEqiJVYUt6nuA+aqOUlyjC0oNtYL/VO4DbHTFcHa8SI2cPSS6ebPMWPGHjUm +l9bWa6Q9iyplCYS6hinAVsAaLVjPi1Eu9Pc8rxFCmoiJYJju5NZuGI5UBK64dqcX +T6trWl0kB8QY63JtnrZaoStoSPImV5KVseUKDV8TM3Y76h1nLV3MSmAD1ivk9oKs +VKeVrDhZBWUq9Dqre/+lVGO0a2wo5VTR8hfpf8QkODPLeyNZNdfGKzkkFLuXa8V5 +ELhLQJ3FnbEU3NEvMwikV9MhP/ELPTkZwJr/NKv+9JLs9eXtwz29I/Q8byQVrCCs +hAuDl0zHGRnqdpdSImeS2EXGx631zGMwSe8fhKelni5h6hXrXz52asr0k30BxWjf +WUn1uTInwVjWGy9B5j3mZlVDotFbvVAZgtR0IoFwihPl4VZd9oS13l+hMfrTy1YZ +8xFNg8ZqUQ0lSmKfOVqSBT0lP8tM8LuGxgY4cWluhsAQxR5Nl7wkundnqjcwEDDu +Jz2rD54St1EZYGLDJZSfC7mpG2PgodsdeopQCTyFhHWa8s3caZ40GFOwaR+/5+YF +1oRvkR1Yr4qIS7KbX4xsaFfAA5b8QfLA74L05PAgDwKofam2GFAlAKHOcI6mexPq +aySON9MNdnXBNxs16mBJLzCX5ljQb0ilJildVEI3aVmABptM4ehEiw== +-----END RSA PRIVATE KEY----- diff --git a/server/test/resources/createNetworkOffering.xml b/server/test/resources/createNetworkOffering.xml index 9d684ba12bb..c6228dab707 100644 --- a/server/test/resources/createNetworkOffering.xml +++ b/server/test/resources/createNetworkOffering.xml @@ -43,5 +43,7 @@ - + + + diff --git a/services/console-proxy-rdp/rdpconsole/README.txt b/services/console-proxy-rdp/rdpconsole/README.txt new file mode 100644 index 00000000000..15029d43287 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/README.txt @@ -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. +This project contains code for basic VNC and RDP clients. + +VNC client can be invoked using following command: + + mvn exec:java -Dexec.mainClass="common.Client" -Dexec.args="vnc 192.168.0.101 5901 password" + +where + * vnc - name of protcol; + * 192.168.0.101 - IP of VNC server; + * 5901 - port of VNC server screen (5900+display number); + * password - VNC server password. + + +RDP client can be invoked using following command: + + mvn exec:java -Dexec.mainClass="common.Client" -Dexec.args="rdp 192.168.0.101 3389 Administrator" + +where + * rdp - name of protcol; + * 192.168.0.101 - IP of RDP server; + * 3389 - port of RDP server; + * Administrator - user name for loging dialog. + + +Limitations of VNC client: + * only basic functionality work. + +Limitations of RDP client: + * it uses SSL/TLS; + * NLA is not supported; + * only basic functionality work. + + +To configure and start RDP service properly, run rdp-config.bat on server. diff --git a/services/console-proxy-rdp/rdpconsole/pom.xml b/services/console-proxy-rdp/rdpconsole/pom.xml new file mode 100644 index 00000000000..71d0241d671 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + rdpclient + cloudstack-service-console-proxy-rdpclient + 4.3.0-SNAPSHOT + jar + + Apache CloudStack Console Proxy - RDP Client + http://maven.apache.org + + + UTF-8 + + + + + junit + junit + 3.8.1 + test + + + org.apache.tomcat.embed + tomcat-embed-core + 7.0.30 + test + + + diff --git a/services/console-proxy-rdp/rdpconsole/rdp-config.bat b/services/console-proxy-rdp/rdpconsole/rdp-config.bat new file mode 100644 index 00000000000..e9510144426 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/rdp-config.bat @@ -0,0 +1,42 @@ +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +rem +rem Configure and start RDP service. +rem + +rem Turn off firewall + +netsh advfirewall firewall set rule group="Remote Desktop" new enable=yes + +rem Enable TS connections + +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "AllowTSConnections" /t REG_DWORD /d 1 /f +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "fDenyTSConnections" /t REG_DWORD /d 0 /f + +rem Disable RDP NLA + +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f + +rem Enable TS service + +sc config TermService start=auto + +rem Start TS service + +net start Termservice + diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.java new file mode 100644 index 00000000000..3318c090303 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtBellAdapter.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 common; + +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; + +public class AwtBellAdapter extends BaseElement { + + public AwtBellAdapter(String id) { + super(id); + declarePads(); + } + + private void declarePads() { + inputPads.put(STDIN, null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + if (buf == null) + return; + + Toolkit.getDefaultToolkit().beep(); + } + + public String toString() { + return "Bell(" + id + ")"; + } + + /** + * Example. + */ + public static void main(String args[]) { + System.setProperty("streamer.Element.debug", "true"); + + Element source = new FakeSource("source") { + { + this.incommingBufLength = 0; + this.delay = 1000; + this.numBuffers = 3; + } + }; + + Element sink = new AwtBellAdapter("sink"); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.addAndLink(source, sink); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java new file mode 100644 index 00000000000..12dd88b9cdc --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtCanvasAdapter.java @@ -0,0 +1,164 @@ +// 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; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; + +import rdpclient.ServerBitmapUpdate; +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.Order; +import streamer.Pipeline; +import streamer.PipelineImpl; + +public class AwtCanvasAdapter extends BaseElement { + + protected ScreenDescription screen; + + public AwtCanvasAdapter(String id, BufferedImageCanvas canvas, ScreenDescription screen) { + super(id); + this.canvas = canvas; + this.screen = screen; + } + + protected BufferedImageCanvas canvas; + + public String toString() { + return "AwtRdpAdapter(" + id + ")"; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + Order order = (Order) buf.getOrder(); + switch ((OrderType) order.type) { + + case BITMAP_UPDATE: + handleBitmap((BitmapOrder) order, buf); + break; + + case COPY_RECT: + handleCopyRect((CopyRectOrder) order, buf); + break; + + default: + throw new RuntimeException("Order is not implemented: " + buf + "."); + // break; + } + + buf.unref(); + } + + private void handleCopyRect(CopyRectOrder order, ByteBuffer buf) { + // TODO Auto-generated method stub + // Copy image + canvas.getOfflineGraphics().copyArea(order.srcX, order.srcY, order.width, order.height, order.x - order.srcX, order.y - order.srcY); + + // Request update of repainted area + canvas.repaint(order.x, order.y, order.width, order.height); + + } + + private void handleBitmap(BitmapOrder order, ByteBuffer buf) { + // Draw rectangle on offline buffer + BufferedImage image = canvas.getOfflineImage(); + Graphics2D g = (Graphics2D) image.getGraphics(); + + for (BitmapRectangle rectangle : order.rectangles) { + // *DEBUG*/System.out.println("["+this+"] DEBUG: Rectangle: " + + // rectangle.toString()); + + int x = rectangle.x; + int y = rectangle.y; + int width = rectangle.width; + int height = rectangle.height; + int bufferWidth = rectangle.bufferWidth; + int bufferHeight = rectangle.bufferHeight; + + 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 + "."); + } + + g.setClip(x, y, width, height); + g.drawImage(rectImage, x, y, null); + + // Request update of repainted area + canvas.repaint(x, y, width, height); + } + + } + + /** + * 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"); + 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"); + + Element bitmap = new ServerBitmapUpdate("bitmap"); + + BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768); + Element adapter = new AwtCanvasAdapter("test", canvas, null) { + { + verbose = true; + } + }; + pipeline.addAndLink(bitmap, adapter); + + bitmap.handleData(packet, null); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.java new file mode 100644 index 00000000000..aeed41ca636 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtClipboardAdapter.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 common; + +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; +import vncclient.VncMessageHandler; + +public class AwtClipboardAdapter extends BaseElement { + + public AwtClipboardAdapter(String id) { + super(id); + declarePads(); + } + + private void declarePads() { + inputPads.put(STDIN, null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + if (buf == null) + return; + + String content = (String) buf.getMetadata(VncMessageHandler.CLIPBOARD_CONTENT); + StringSelection contents = new StringSelection(content); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null); + } + + public String toString() { + return "Clipboard(" + id + ")"; + } + +} 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 new file mode 100644 index 00000000000..a754796a0d2 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtKeyEventSource.java @@ -0,0 +1,53 @@ +// 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; + +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +import streamer.BaseElement; +import streamer.ByteBuffer; + +public class AwtKeyEventSource extends BaseElement implements KeyListener { + + public AwtKeyEventSource(String id) { + super(id); + } + + @Override + public void keyTyped(KeyEvent e) { + // Nothing to do + + } + + @Override + public void keyPressed(KeyEvent e) { + sendEvent(e, true); + } + + @Override + public void keyReleased(KeyEvent e) { + sendEvent(e, false); + } + + private void sendEvent(KeyEvent e, boolean pressed) { + ByteBuffer buf=new ByteBuffer(new KeyOrder(e, pressed)); + + pushDataToAllOuts(buf); + } + +} 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 new file mode 100644 index 00000000000..f3e58fc02c1 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/AwtMouseEventSource.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. +package common; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +import streamer.BaseElement; +import streamer.ByteBuffer; + +public class AwtMouseEventSource extends BaseElement implements MouseListener, MouseMotionListener { + + public AwtMouseEventSource(String id) { + super(id); + } + + @Override + public void mouseClicked(MouseEvent e) { + // Nothing to do + } + + @Override + public void mousePressed(MouseEvent e) { + MouseOrder order = new MouseOrder(e); + order.pressed = true; + pushDataToAllOuts(new ByteBuffer(order)); + } + + @Override + public void mouseReleased(MouseEvent e) { + MouseOrder order = new MouseOrder(e); + order.released = true; + pushDataToAllOuts(new ByteBuffer(order)); + } + + @Override + public void mouseEntered(MouseEvent e) { + // Nothing to do + } + + @Override + public void mouseExited(MouseEvent e) { + // Nothing to do + } + + @Override + public void mouseDragged(MouseEvent e) { + pushDataToAllOuts(new ByteBuffer(new MouseOrder(e))); + } + + @Override + public void mouseMoved(MouseEvent e) { + pushDataToAllOuts(new ByteBuffer(new MouseOrder(e))); + } + +} 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 new file mode 100644 index 00000000000..438755f4869 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapOrder.java @@ -0,0 +1,48 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package common; + +import java.util.Arrays; + + +import streamer.Order; + +/** + * Not an order, but derived from Order class for compatibility with orders. + * + * @see http://msdn.microsoft.com/en-us/library/dd306368.aspx + */ +public class BitmapOrder extends Order { + + public BitmapOrder() { + type = OrderType.BITMAP_UPDATE; + } + + /** + * Structures, each of which contains a rectangular clipping taken from the + * server-side screen frame buffer. + */ + public BitmapRectangle rectangles[]; + + @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); + } + +} 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 new file mode 100644 index 00000000000..f0c178e6092 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BitmapRectangle.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 common; + +import streamer.ByteBuffer; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240612.aspx + */ +public class BitmapRectangle { + + /** + * Left bound of the rectangle. + */ + public int x; + + /** + * Top bound of the rectangle. + */ + public int y; + + /** + * Width of the rectangle. + */ + public int width; + + /** + * Height of the rectangle. + */ + public int height; + + /** + * Color depth of the rectangle data in bits-per-pixel. + */ + public int colorDepth; + + /** + * Variable-length array of bytes describing a raw uncompressed bitmap image. + */ + public ByteBuffer bitmapDataStream; + + /** + * Size of single horizontal scan line. + */ + public int bufferWidth; + + /** + * Number of horizontal scan lines in buffer. + */ + public int bufferHeight; + + @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); + } + +} 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 new file mode 100644 index 00000000000..6e7738fad3e --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCanvas.java @@ -0,0 +1,79 @@ +// 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; + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +/** + * A BuffereImageCanvas component represents frame buffer image on the + * screen. It also notifies its subscribers when screen is repainted. + */ +public class BufferedImageCanvas extends Canvas { + private static final long serialVersionUID = 1L; + + // Offline screen buffer + private BufferedImage offlineImage; + + // Cached Graphics2D object for offline screen buffer + private Graphics2D graphics; + + public BufferedImageCanvas(int width, int height) { + super(); + + setBackground(Color.black); + + setFocusable(true); + + // Don't intercept TAB key + setFocusTraversalKeysEnabled(false); + + setCanvasSize(width, height); + } + + public void setCanvasSize(int width, int height) { + this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + graphics = offlineImage.createGraphics(); + + setSize(offlineImage.getWidth(), offlineImage.getHeight()); + } + + @Override + public void update(Graphics g) { + // Call paint() directly, without clearing screen first + paint(g); + } + + @Override + public void paint(Graphics g) { + // Only part of image, requested with repaint(Rectangle), will be + // painted on screen. + g.drawImage(offlineImage, 0, 0, this); + } + + public BufferedImage getOfflineImage() { + return offlineImage; + } + + public Graphics2D getOfflineGraphics() { + return graphics; + } + +} 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 new file mode 100644 index 00000000000..bbd5142cad6 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImageCopyRectAdapter.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 common; + +import java.awt.image.DataBufferInt; +import java.util.Arrays; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; + +public class BufferedImageCopyRectAdapter extends BaseElement { + + public static final String SRC_X = "srcX"; + public static final String SRC_Y = "srcY"; + public static final String TARGET_X = "x"; + public static final String TARGET_Y = "y"; + public static final String WIDTH = "width"; + public static final String HEIGHT = "height"; + + protected BufferedImageCanvas canvas; + + public BufferedImageCopyRectAdapter(String id, BufferedImageCanvas canvas) { + super(id); + this.canvas = canvas; + declarePads(); + } + + private void declarePads() { + inputPads.put(STDIN, null); + } + + public String toString() { + return "Renderer(" + id + ")"; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + int x = (Integer) buf.getMetadata(TARGET_X); + int y = (Integer) buf.getMetadata(TARGET_Y); + int width = (Integer) buf.getMetadata(WIDTH); + int height = (Integer) buf.getMetadata(HEIGHT); + int srcX = (Integer) buf.getMetadata(SRC_X); + int srcY = (Integer) buf.getMetadata(SRC_Y); + buf.unref(); + + // Copy image + canvas.getOfflineGraphics().copyArea(srcX, srcY, width, height, x - srcX, y - srcY); + + // Request update of repainted area + canvas.repaint(x, y, width, height); + } + + public static void main(String args[]) { + System.setProperty("streamer.Element.debug", "true"); + + BufferedImageCanvas canvas = new BufferedImageCanvas(4, 4); + + 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 }; + int[] pixelsAfterCopy = new int[] { + // 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(); + System.arraycopy(pixelsBeforeCopy, 0, data, 0, pixelsBeforeCopy.length); + + ByteBuffer buf = new ByteBuffer(new byte[0]); + buf.putMetadata(TARGET_X, 0); + buf.putMetadata(TARGET_Y, 0); + buf.putMetadata(WIDTH, 2); + buf.putMetadata(HEIGHT, 2); + buf.putMetadata(SRC_X, 2); + buf.putMetadata(SRC_Y, 2); + + renderer.handleData(buf, null); + + data = ((DataBufferInt) canvas.getOfflineImage().getRaster().getDataBuffer()).getData(); + String actualData = Arrays.toString(data); + String expectedData = Arrays.toString(pixelsAfterCopy); + if (!actualData.equals(expectedData)) + System.err.println("Actual image: " + actualData + "\nExpected image: " + expectedData + "."); + + } + +} 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 new file mode 100644 index 00000000000..95e7738b0e0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/BufferedImagePixelsAdapter.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; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferInt; +import java.util.Arrays; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; + +public class BufferedImagePixelsAdapter extends BaseElement { + + public static final String TARGET_X = "x"; + public static final String TARGET_Y = "y"; + public static final String WIDTH = "width"; + public static final String HEIGHT = "height"; + public static final String RGB888LE32 = "RGB888LE32"; + public static final String PIXEL_FORMAT = "pixel_format"; + + protected BufferedImageCanvas canvas; + + public BufferedImagePixelsAdapter(String id, BufferedImageCanvas canvas) { + super(id); + this.canvas = canvas; + declarePads(); + } + + private void declarePads() { + inputPads.put(STDIN, null); + } + + public String toString() { + return "Renderer(" + id + ")"; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + int x = (Integer) buf.getMetadata(TARGET_X); + int y = (Integer) buf.getMetadata(TARGET_Y); + int rectWidth = (Integer) buf.getMetadata(WIDTH); + int rectHeight = (Integer) buf.getMetadata(HEIGHT); + String format = (String) buf.getMetadata(PIXEL_FORMAT); + + int bpp; + // Support RGB888/32 little endian only + if (format != null && RGB888LE32.equals(format)) { + bpp = 4; + // TODO: support more formats + } else + throw new RuntimeException("Unsupported format: " + format + ". Supported formats: " + RGB888LE32 + "."); + + int dataLength = rectWidth * rectHeight * bpp; + if (!cap(buf, dataLength, dataLength, link, false)) + return; + + // Draw rectangle on offline buffer + BufferedImage image = canvas.getOfflineImage(); + + DataBuffer dataBuf = image.getRaster().getDataBuffer(); + + switch (dataBuf.getDataType()) { + + case DataBuffer.TYPE_INT: { + + // 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; + + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + + // 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) { + } + } + 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()); + } + + // Request update of repainted area + canvas.repaint(x, y, rectWidth, rectHeight); + + buf.unref(); + } + + public static void main(String args[]) { + System.setProperty("streamer.Element.debug", "true"); + + BufferedImageCanvas canvas = new BufferedImageCanvas(4, 4); + + 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 }; + + 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); + buf.putMetadata(TARGET_Y, 0); + buf.putMetadata(WIDTH, 4); + buf.putMetadata(HEIGHT, 4); + buf.putMetadata(PIXEL_FORMAT, RGB888LE32); + + renderer.handleData(buf, null); + + String actualData = Arrays.toString(((DataBufferInt) canvas.getOfflineImage().getRaster().getDataBuffer()).getData()); + String expectedData = Arrays.toString(pixelsLE); + if (!actualData.equals(expectedData)) + System.err.println("Actual image: " + actualData + "\nExpected image: " + expectedData + "."); + + } + +} 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 new file mode 100644 index 00000000000..343fe0abec4 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/Client.java @@ -0,0 +1,137 @@ +// 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; + +import java.awt.Frame; +import java.awt.ScrollPane; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.net.InetSocketAddress; + +import rdpclient.RdpClient; +import streamer.Element; +import streamer.Pipeline; +import streamer.PipelineImpl; +import streamer.SocketWrapper; +import vncclient.VncClient; + +public class Client { + + 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"); + + 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]; + + // Create address from arguments + InetSocketAddress address = new InetSocketAddress(hostname, port); + + // Create socket wrapper + socket = new SocketWrapper("socket"); + + screen = new ScreenDescription(); + canvas = new BufferedImageCanvas(1024, 768); + screen.addSizeChangeListener(new SizeChangeListener() { + @Override + public void sizeChanged(int width, int height) { + if (canvas != null) { + canvas.setCanvasSize(width, height); + if (scroller != null) + scroller.setSize(canvas.getWidth(), canvas.getHeight()); + } + } + }); + + // Assemble pipeline + 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 + "\"."); + } + + Pipeline pipeline = new PipelineImpl("Client"); + pipeline.add(socket, main); + pipeline.link("socket", main.getId(), "socket"); + + pipeline.validate(); + + frame = createVncClientMainWindow(canvas, "VNC", new WindowAdapter() { + public void windowClosing(WindowEvent evt) { + shutdown(); + } + }); + + try { + // Connect socket to remote server and run main loop(s) + socket.connect(address); + } finally { + shutdown(); + } + + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + protected static void shutdown() { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + if (socket != null) + socket.shutdown(); + } + + protected static Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title, WindowListener windowListener) { + // Create AWT windows + Frame frame = new Frame(title + " - RDP"); + + // Use scrolling pane to support screens, which are larger than ours + scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + scroller.add(canvas); + scroller.setSize(canvas.getWidth(), canvas.getHeight()); + + frame.add(scroller); + frame.pack(); + frame.setVisible(true); + + frame.addWindowListener(windowListener); + + return frame; + } + +} 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 new file mode 100644 index 00000000000..5f0c0f77ebc --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/CopyRectOrder.java @@ -0,0 +1,39 @@ +// 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; + + +import streamer.Order; + +public class CopyRectOrder extends Order { + public int srcX; + public int srcY; + public int width; + public int height; + public int x; + public int y; + + public CopyRectOrder() { + type = OrderType.COPY_RECT; + } + + @Override + public String toString() { + return "CopyRectOrder [srcX=" + srcX + ", srcY=" + srcY + ", width=" + width + ", height=" + height + ", x=" + x + ", y=" + y + "]"; + } + +} 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 new file mode 100644 index 00000000000..726d8a094d5 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/KeyOrder.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 common; + +import java.awt.event.KeyEvent; + +import streamer.Order; + +public class KeyOrder extends Order { + public KeyOrder() { + type = "key event"; + } + + public KeyOrder(KeyEvent event, boolean pressed) { + type = "key event"; + + this.event = event; + this.pressed = pressed; + } + + public KeyEvent event; + + public boolean pressed; + + @Override + public String toString() { + return "KeyOrder [event=" + event + ", pressed=" + pressed + "]"; + } + + +} 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 new file mode 100644 index 00000000000..d6670fad58b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/MouseOrder.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; + +import java.awt.event.MouseEvent; + +import streamer.Order; + +public class MouseOrder extends Order { + + public MouseOrder() { + type = "mouse order"; + } + + public MouseOrder(MouseEvent event) { + type = "mouse order"; + this.event = event; + } + + public MouseEvent event; + public boolean pressed; + public boolean released; + @Override + public String toString() { + return "MouseOrder [event=" + event + ", pressed=" + pressed + ", released=" + released + "]"; + } +} 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 new file mode 100644 index 00000000000..1e77ea91434 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/OrderType.java @@ -0,0 +1,22 @@ +// 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; + +public enum OrderType { + BITMAP_UPDATE, COPY_RECT, + +} 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 new file mode 100644 index 00000000000..a01f22e8459 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/ScreenDescription.java @@ -0,0 +1,168 @@ +// 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; + +import java.awt.image.IndexColorModel; +import java.util.HashSet; +import java.util.Set; + +/** + * VncScreenDescription - contains information about remote VNC screen. + */ +public class ScreenDescription { + + protected Set sizeChangeListeners = new HashSet(); + + // Frame buffer size + protected int framebufferWidth = -1; + protected int framebufferHeight = -1; + + // Desktop name + protected String desktopName = null; + + // Bytes per pixel + protected int bytesPerPixel; + protected int colorDepth; + protected int bitsPerPixel; + protected int redShift; + protected int greenShift; + protected int blueShift; + protected int redMax; + protected int greenMax; + protected int blueMax; + protected boolean bigEndianFlag; + protected boolean trueColorFlag; + + public IndexColorModel colorMap = null; + + public 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) { + + this.bytesPerPixel = (bitsPerPixel + 7) / 8; + + this.bitsPerPixel = bitsPerPixel; + this.colorDepth = depth; + this.bigEndianFlag = bigEndianFlag; + this.trueColorFlag = trueColorFlag; + + this.redMax = redMax; + this.greenMax = greenMax; + this.blueMax = blueMax; + this.redShift = redShift; + this.greenShift = greenShift; + this.blueShift = blueShift; + } + + /** + * Store information about server pixel format. + */ + 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."); + } + + } + + /** + * Store information about server screen size. + */ + public void setFramebufferSize(int width, int height) { + if (height <= 0 || width <= 0) + throw new RuntimeException("Incorrect framebuffer size: " + width + "x" + height + "."); + + this.framebufferWidth = width; + this.framebufferHeight = height; + + callSizeChangeListeners(width, height); + } + + protected void callSizeChangeListeners(int width, int height) { + for(SizeChangeListener listener:sizeChangeListeners) { + listener.sizeChanged(width, height); + } + } + + /** + * Store server desktop name. + */ + public void setDesktopName(String desktopName) { + this.desktopName = desktopName; + } + + // Getters for variables, as usual + + public String getDesktopName() { + return desktopName; + } + + public int getBytesPerPixel() { + return bytesPerPixel; + } + + public int getFramebufferHeight() { + return framebufferHeight; + } + + public int getFramebufferWidth() { + return framebufferWidth; + } + + public boolean isRGB888_32_LE() { + 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 + "]"; + } + + public void addSizeChangeListener(SizeChangeListener sizeChangeListener) { + sizeChangeListeners.add(sizeChangeListener); + } + + public int getColorDeph() { + return colorDepth; + } + +} 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 new file mode 100644 index 00000000000..d658d1b95f2 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java @@ -0,0 +1,22 @@ +// 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; + +public interface SizeChangeListener { + + public void sizeChanged(int width, int height); +} 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 new file mode 100644 index 00000000000..f19f09c0b68 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpKeyboardAdapter.java @@ -0,0 +1,351 @@ +// 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/AwtRdpMouseAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.java new file mode 100644 index 00000000000..371bffc8ae1 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/AwtRdpMouseAdapter.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; + +import java.awt.event.MouseEvent; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +import common.MouseOrder; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240594.aspx + */ +public class AwtRdpMouseAdapter extends BaseElement { + public 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 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 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 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 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 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 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 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 int PTRFLAGS_BUTTON3 = 0x4000; + + public AwtRdpMouseAdapter(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Get mouse event + MouseOrder order = (MouseOrder) buf.getOrder(); + + ByteBuffer outBuf = new ByteBuffer(7, true); + + // eventHeader (1 byte): An 8-bit, unsigned integer. EventCode bitfield (top + // 3 bits) MUST be set to FASTPATH_INPUT_EVENT_MOUSE (1). The + // eventFlags bitfield (low 5 bits) MUST be zeroed out. + outBuf.writeByte(FASTPATH_INPUT_EVENT_MOUSE << 5); + + // pointerFlags (2 bytes): A 16-bit, unsigned integer. + outBuf.writeShortLE(getPointerFlags(order)); + + // xPos (2 bytes): A 16-bit, unsigned integer. The x-coordinate of the + // pointer. + outBuf.writeShortLE(order.event.getX()); + + // yPos (2 bytes): A 16-bit, unsigned integer. The y-coordinate of the + // pointer. + outBuf.writeShortLE(order.event.getY()); + + // Push buffer to one pad only, so it can be modified without copying of + // data + pushDataToPad(STDOUT, outBuf); + } + + // Remember mouse buttons + protected boolean button1, button2, button3; + + protected int getPointerFlags(MouseOrder order) { + int flags = 0; + + int modifiers = order.event.getModifiersEx(); + + if (order.pressed) { + // Mouse pressed + flags |= PTRFLAGS_DOWN; + + // Check, which one of buttons is released + boolean b1 = ((modifiers & MouseEvent.BUTTON1_DOWN_MASK) > 0) && !button1; + boolean b2 = ((modifiers & MouseEvent.BUTTON2_DOWN_MASK) > 0) && !button2; + boolean b3 = ((modifiers & MouseEvent.BUTTON3_DOWN_MASK) > 0) && !button3; + + if (b1) { + flags |= PTRFLAGS_BUTTON1; + button1 = true; + } + + if (b2) { + flags |= PTRFLAGS_BUTTON3; + button2 = true; + } + + if (b3) { + flags |= PTRFLAGS_BUTTON2; + button3 = true; + } + } else if (order.released) { + // Mouse released + + // Check, which one of buttons is released + boolean b1 = !((modifiers & MouseEvent.BUTTON1_DOWN_MASK) > 0) && button1; + boolean b2 = !((modifiers & MouseEvent.BUTTON2_DOWN_MASK) > 0) && button2; + boolean b3 = !((modifiers & MouseEvent.BUTTON3_DOWN_MASK) > 0) && button3; + + if (b1) { + flags |= PTRFLAGS_BUTTON1; + button1 = false; + } + + if (b2) { + flags |= PTRFLAGS_BUTTON3; + button2 = false; + } + + if (b3) { + flags |= PTRFLAGS_BUTTON2; + button3 = false; + } + } else { + // Mouse moved + flags |= PTRFLAGS_MOVE; + } + + return flags; + } + +} 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 new file mode 100644 index 00000000000..9a1caab0c88 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientConfirmActivePDU.java @@ -0,0 +1,1132 @@ +// 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/ClientFastPathPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.java new file mode 100644 index 00000000000..4c7dcfc50b7 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientFastPathPDU.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 rdpclient; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240589.aspx + */ +public class ClientFastPathPDU extends BaseElement { + + public ClientFastPathPDU(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + if (buf.length > 32767-3) + throw new RuntimeException("Packet is too long: " + buf + "."); + + ByteBuffer data = new ByteBuffer(6); + + // FastPath, 1 event, no checksum, not encrypted + data.writeByte(0x4); + + // Length of full packet, including length field, in network order. + // Topmost bit of first byte indicates that field has 2 bytes + data.writeShort((1 + 2 + buf.length) | 0x8000); + data.trimAtCursor(); + + buf.prepend(data); + + pushDataToAllOuts(buf); + } + +} 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 new file mode 100644 index 00000000000..f5efc2c35b1 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/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; + +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/ClientMCSAttachUserRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.java new file mode 100644 index 00000000000..10a7eff4bad --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSAttachUserRequest.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 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/cc240684.aspx + */ +public class ClientMCSAttachUserRequest extends OneTimeSwitch { + + public ClientMCSAttachUserRequest(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 = 1; + ByteBuffer buf = new ByteBuffer(length, true); + + buf.writeByte(0x28); // AttachUserRequest + + 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[] { + + 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, + + // 0x28: + // 0 - --\ + // 0 - | + // 1 - | CHOICE: From DomainMCSPDU select attachUserRequest (10) + // 0 - | of type AttachUserRequest + // 1 - | + // 0 - --/ + // 0 - padding + // 0 - padding + + }; + /* @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 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", "TODO", "mainSink"); + pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java new file mode 100644 index 00000000000..f0825397680 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs.java @@ -0,0 +1,222 @@ +// 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; + +/** + * The MCS Channel Join Request PDUs are sent sequentially. The first PDU is + * sent after receiving the MCS Attach User Confirm PDU and subsequent PDUs are + * sent after receiving the MCS Channel Join Confirm PDU for the previous + * request. Sending of the MCS Channel Join Request PDUs MUST continue until all + * channels have been successfully joined. + * + * @see http://msdn.microsoft.com/en-us/library/cc240686.aspx + */ +public class ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs extends OneTimeSwitch { + + private static final int MCS_CHANNEL_CONFIRM_PDU = 15; + + protected int[] channels; + protected int channelRequestsSent = 0; + + protected RdpState state; + + public ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs(String id, int[] channels, RdpState state) { + super(id); + this.channels = channels; + this.state=state; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + // Parse channel confirm response + int typeAndFlags = buf.readUnsignedByte(); + int type = typeAndFlags >> 2; + // 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 + "."); + + 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 + "."); + + // Initiator and requested fields MAY be ignored, however, the channelId + // field MUST be examined. If the value of the channelId field does not + // correspond with the value of the channelId field sent in the previous MCS + // Channel Join Request PDU the connection SHOULD be dropped. + + // Initiator: 1007 (6+1001) + // int initator=buf.readUnsignedShort(); + buf.skipBytes(2); + + // Requested channel + // int requestedChannel=buf.readUnsignedShort(); + buf.skipBytes(2); + + // 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 + "."); + + state.channelJoined(actualChannel); + + buf.unref(); + + + if (channelRequestsSent < channels.length) + sendChannelRequest(channels[channelRequestsSent++]); + else + switchOff(); + } + + @Override + protected void onStart() { + super.onStart(); + + sendChannelRequest(channels[channelRequestsSent++]); + + // Switch off after receiving response(s) + } + + private void sendChannelRequest(int channel) { + ByteBuffer buf = new ByteBuffer(5, true); + + buf.writeByte(0x38); // Channel Join request + + buf.writeShort(0x03); // ChannelJoinRequest::initiator: 1004 + buf.writeShort(channel); + + pushDataToOTOut(buf); + } + + /** + * Example. + * + * @see http://msdn.microsoft.com/en-us/library/cc240834.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[] 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, + + // 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 - --/ + + // 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, + + // result: rt-successful (0) + (byte)0x00, + + // Initiator: 1007 (6+1001) + (byte)0x00, (byte)0x06, + + // Requested channel + (byte)0x03, (byte)0xef, + + // Actual channel + (byte)0x03, (byte)0xef, + }; + /* @formatter:on */ + + RdpState rdpState = new RdpState(); + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(serverResponsePacket, new byte[] { 1, 2, 3 })); + Element todo = new ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs("channels", new int[] { 1007 }, rdpState); + 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 })); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, todo, x224, tpkt, sink, mainSink); + pipeline.link("source", "channels", "mainSink"); + pipeline.link("channels >" + 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 new file mode 100644 index 00000000000..01899877299 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSConnectInitial.java @@ -0,0 +1,669 @@ +// 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/ClientMCSErectDomainRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java new file mode 100644 index 00000000000..4a0fd0413a9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientMCSErectDomainRequest.java @@ -0,0 +1,190 @@ +// 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/cc240683.aspx + */ +public class ClientMCSErectDomainRequest extends OneTimeSwitch { + + public ClientMCSErectDomainRequest(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 = 5; + ByteBuffer buf = new ByteBuffer(length, true); + + buf.writeByte(0x04); // Erect Domain Request + + // Client SHOULD initialize both the subHeight and subinterval fields of the MCS Erect Domain Request PDU to zero. + + buf.writeByte(1); // ErectDomainRequest::subHeight length = 1 byte + buf.writeByte(0); // ErectDomainRequest::subHeight + + buf.writeByte(1); // ErectDomainRequest::subInterval length = 1 byte + buf.writeByte(0); // ErectDomainRequest::subInterval + + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + * @see http://msdn.microsoft.com/en-us/library/cc240837.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[] { + + 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, + + // 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 - --/ + + // 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 - --/ + + // 0x00: + // 0 - --\ + // 0 - | + // 0 - | + // 0 - | ErectDomainRequest::subInterval = 0 + // 0 - | + // 0 - | + // 0 - | + // 0 - --/ + + + }; + /* @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 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", "TODO", "mainSink"); + pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} + +/* + * 03 00 00 0C 02 F0 80 04 01 00 01 00 + + Frame: Number = 14, Captured Frame Length = 69, 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: 12 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 12 (0xC) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Erect Domain Request, SubHeight = 0, SubInterval = 0 + - MCSHeader: Type=Erect Domain Request + - Type: Erect Domain Request + - RootIndex: 1 + Value: (000001..) 0x1 + - MCSErectDomainRequest: SubHeight = 0, SubInterval = 0 + - SubHeight: 0x0 + - Length: 1 + - Align: No Padding + Padding2: (00......) 0x0 + Length: 1 + Value: 0 (0x0) + - SubInterval: 0x0 + - Length: 1 + Align: No Padding + Length: 1 + Value: 0 (0x0) + + */ 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 new file mode 100644 index 00000000000..651d002af7e --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientPacketSniffer.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 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/ClientSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java new file mode 100644 index 00000000000..9631976c848 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientSynchronizePDU.java @@ -0,0 +1,248 @@ +// 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/cc240489.aspx + */ +public class ClientSynchronizePDU extends OneTimeSwitch { + + public ClientSynchronizePDU(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[] { + // 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, + }); + /* @formatter:on */ + + // Trim buffer to actual length of data written + buf.trimAtCursor(); + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + * + * @see http://msdn.microsoft.com/en-us/library/cc240841.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 + (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, + + // 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 */ + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 })); + Element todo = new ClientSynchronizePDU("TODO"); + 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", "TODO", "mainSink"); + pipeline.link("TODO >" + OTOUT, "x224", "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} + +/* + * @formatting:off + + * 03 00 00 25 02 F0 80 64 00 03 03 EB 70 80 16 16 00 17 00 EC 03 EA 03 01 00 00 01 08 00 1F 00 00 00 01 00 EA 03 + + Frame: Number = 40, Captured Frame Length = 94, 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: 37 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 37 (0x25) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Data Packet + - MCSHeader: Type=Send Data Request, UserID=1004, ChannelID=1003 + - Type: Send Data Request + - RootIndex: 25 + Value: (011001..) 0x19 + - UserID: 0x3ec + - UserID: 0x3ec + - ChannelId: 1004 + - Align: No Padding + Padding2: (00......) 0x0 + Value: 3 (0x3) + - Channel: 0x3eb + - ChannelId: 1003 + Align: No Padding + Value: 1003 (0x3EB) + - DataPriority: high + - DataPriority: high + - RootIndex: 1 + Value: (01......) 0x1 + - Segmentation: Begin End + Begin: (1.......) Begin + End: (.1......) End + - Length: 22 + - Align: No Padding + Padding4: (0000....) 0x0 + Length: 22 + RDP: RDPBCGR +- RDPBCGR: SynchronizePDU + - SlowPathPacket: SynchronizePDU + - SlowPath: Type = TS_PDUTYPE_DATAPDU + - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU + TotalLength: 22 (0x16) + - PDUType: 23 (0x17) + Type: (............0111) TS_PDUTYPE_DATAPDU + ProtocolVersion: (000000000001....) 1 + PDUSource: 1004 (0x3EC) + - SlowPathIoPacket: 0x0 + - ShareDataHeader: TS_PDUTYPE2_SYNCHRONIZE + ShareID: 66538 (0x103EA) + Pad1: 0 (0x0) + StreamID: TS_STREAM_LOW + UncompressedLength: 8 (0x8) + PDUType2: TS_PDUTYPE2_SYNCHRONIZE + - CompressedType: Not Compressed + MPPC: (....0000) MPPC 8K + Reserved: (...0....) + Compressed: (..0.....) Not Compressed + Front: (.0......) Not At Front + Flush: (0.......) Not Flushed + CompressedLength: 0 (0x0) + - TsSynchronizePDU: 0x1 + MessageType: 0x1, MUST be set to SYNCMSGTYPE_SYNC (1) + TargetUser: 1002 (0x3EA) + */ + diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.java new file mode 100644 index 00000000000..c92d8321a8f --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientTpkt.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 rdpclient; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class ClientTpkt extends BaseElement { + + public ClientTpkt(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 + "."); + + if (buf.length + 4 > 65535) + throw new RuntimeException("Packet is too long for TPKT (max length 65535-4): " + buf + "."); + + ByteBuffer data = new ByteBuffer(4); + // TPKT version + data.writeByte(3); + // Reserved + data.writeByte(0); + // Packet length, including length of the header + data.writeShort(buf.length + 4); + + buf.prepend(data); + data.unref(); + + pushDataToPad(STDOUT, buf); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java new file mode 100644 index 00000000000..48ac08933e6 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224ConnectionRequestPDU.java @@ -0,0 +1,156 @@ +// 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/cc240470.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240663.aspx + */ +public class ClientX224ConnectionRequestPDU extends OneTimeSwitch { + + public static final int X224_TPDU_CONNECTION_REQUEST = 0xe0; + public static final int X224_TPDU_CONNECTION_CONFIRM = 0xd0; + public static final int X224_TPDU_DISCONNECTION_REQUEST = 0x80; + public static final int X224_TPDU_DISCONNECTION_CONFIRM = 0xc0; + public static final int X224_TPDU_EXPEDITED_DATA = 0x10; + public static final int X224_TPDU_DATA_ACKNOWLEDGE = 0x61; + public static final int X224_TPDU_EXPEDITET_ACKNOWLEDGE = 0x40; + public static final int X224_TPDU_REJECT = 0x51; + public static final int X224_TPDU_ERROR = 0x70; + public static final int X224_TPDU_PROTOCOL_IDENTIFIER = 0x01; + + /** + * Reconnection cookie. + */ + protected String userName; + + public ClientX224ConnectionRequestPDU(String id, String userName) { + super(id); + this.userName = userName; + } + + @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 without length field + int length = 33 + userName.length(); + ByteBuffer buf = new ByteBuffer(length, true); + + // Type (high nibble) = 0xe = CR TPDU; credit (low nibble) = 0 + buf.writeByte(X224_TPDU_CONNECTION_REQUEST); + + buf.writeShort(0); // Destination reference = 0 + buf.writeShort(0); // Source reference = 0 + buf.writeByte(0); // Class and options = 0 + buf.writeString("Cookie: mstshash=" + userName + "\r\n", RdpConstants.CHARSET_8); // Cookie + + // RDP_NEG_REQ::type + buf.writeByte(RdpConstants.RDP_NEG_REQ_TYPE_NEG_REQ); + // RDP_NEG_REQ::flags (0) + buf.writeByte(RdpConstants.RDP_NEG_REQ_FLAGS); + // RDP_NEG_REQ::length (constant: 8) short int in LE format + buf.writeByte(0x08); + buf.writeByte(0x00); + + // RDP_NEG_REQ: Requested protocols: PROTOCOL_SSL + buf.writeIntLE(RdpConstants.RDP_NEG_REQ_PROTOCOL_SSL); + + // Calculate length of packet and prepend it to buffer + ByteBuffer data = new ByteBuffer(5); + + // Write length + data.writeVariableIntLE(buf.length); + + // Reset length of buffer to actual length of data written + data.length = data.cursor; + + buf.prepend(data); + data.unref(); + + pushDataToOTOut(buf); + + switchOff(); + } + + /** + * Example. + * + * @see http://msdn.microsoft.com/en-us/library/cc240842.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240500.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"); + + String cookie = "eltons"; + + 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 + + '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) + }; + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 })); + Element cr = new ClientX224ConnectionRequestPDU("cr", cookie); + 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, cr, tpkt, sink, mainSink); + pipeline.link("source", "cr", "mainSink"); + pipeline.link("cr >" + OTOUT, "tpkt", "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.java new file mode 100644 index 00000000000..2cf9b72612c --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ClientX224DataPdu.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 rdpclient; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +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) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + ByteBuffer data = new ByteBuffer(3); + // X224 header + data.writeByte(2); // Header length indicator + data.writeByte(X224_TPDU_DATA); + data.writeByte(X224_TPDU_LAST_DATA_UNIT); + + buf.prepend(data); + data.unref(); + + pushDataToPad(STDOUT, buf); + } + + + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.java new file mode 100644 index 00000000000..62242de7943 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/HandshakeEnd.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 rdpclient; + +import streamer.BaseElement; + +public class HandshakeEnd extends BaseElement { + + public HandshakeEnd(String id) { + super(id); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java new file mode 100644 index 00000000000..b1419174911 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/MockServer.java @@ -0,0 +1,197 @@ +// 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.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +public class MockServer implements Runnable { + + private boolean shutdown = false; + private ServerSocket serverSocket; + private Packet[] packets; + private Throwable exception; + private boolean shutdowned; + + /** + * Set to true to enable debugging messages. + */ + protected boolean verbose = System.getProperty("rdpclient.MockServer.debug", "false").equals("true"); + + public MockServer(Packet packets[]) { + this.packets = packets; + } + + public void start() throws IOException { + serverSocket = new ServerSocket(0); + + shutdown = false; + exception = null; + shutdowned = false; + + new Thread(this).start(); + } + + public void run() { + + try { + Socket socket = serverSocket.accept(); + + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + + try { + for (int i = 0; i < packets.length && !shutdown; i++) { + + 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); + + 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 + "."); + } + + 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] + "."); + } + } + } + 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) + "}."); + + 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(); + + break; + } + default: + throw new RuntimeException("Unknown packet type: " + packet.type); + } + + } + } finally { + try { + is.close(); + } catch (Throwable e) { + } + try { + os.close(); + } catch (Throwable e) { + } + try { + socket.close(); + } catch (Throwable e) { + } + try { + serverSocket.close(); + } catch (Throwable e) { + } + } + } catch (Throwable e) { + System.err.println("Error in mock server: " + e.getMessage()); + e.printStackTrace(System.err); + exception = e; + } + shutdowned = true; + if (verbose) + System.out.println("[" + this + "] INFO: Mock server shutdowned."); + + } + + public void shutdown() { + shutdown = true; + } + + public InetSocketAddress getAddress() { + return (InetSocketAddress) serverSocket.getLocalSocketAddress(); + } + + public Throwable getException() { + return exception; + } + + public static class Packet { + public static enum PacketType { + SERVER, CLIENT, UPGRADE_TO_SSL; + } + + public String id = ""; + + public Packet() { + } + + public Packet(String id) { + this.id = id; + } + + public PacketType type; + + public boolean ignore = false; + + public byte data[]; + } + + public boolean isShutdowned() { + return shutdowned; + } + + public void waitUntilShutdowned(long timeToWaitMiliseconds) throws InterruptedException { + long deadline = System.currentTimeMillis() + timeToWaitMiliseconds; + while (!shutdowned && System.currentTimeMillis() < deadline) { + Thread.sleep(10); + } + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java new file mode 100644 index 00000000000..9fe2499ebc5 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/PacketSniffer.java @@ -0,0 +1,75 @@ +// 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.util.regex.Pattern; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +/** + * Try to determine packet content by it header fingerprint. + */ +public class PacketSniffer extends BaseElement { + + protected Pair regexps[]=null; + + public PacketSniffer(String id, Pair[] regexps) { + super(id); + this.regexps=regexps; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + + matchPacket(buf); + + super.handleData(buf, link); + } + + private void matchPacket(ByteBuffer buf) { + String header = buf.toPlainHexString(100); + for (Pair pair : regexps) { + if (pair.regexp.matcher(header).find()) { + System.out.println("[" + this + "] INFO: Packet: " + pair.name + "."); + return; + } + } + + System.out.println("[" + this + "] INFO: Unknown packet: " + header + "."); + } + + protected static class Pair { + String name; + Pattern regexp; + + protected Pair(String name, String regexp) { + this.name = name; + this.regexp = Pattern.compile("^" + replaceShortcuts(regexp), Pattern.CASE_INSENSITIVE); + } + + private static String replaceShortcuts(String regexp) { + String result = regexp; + result = result.replaceAll("XX\\*", "([0-9a-fA-F]{2} )*?"); + result = result.replaceAll("XX\\?", "([0-9a-fA-F]{2} )?"); + result = result.replaceAll("XX", "[0-9a-fA-F]{2}"); + return result; + } + } + +} 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 new file mode 100644 index 00000000000..53e6d2f3bb0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/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; + +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 new file mode 100644 index 00000000000..cc8d1f16acc --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpClient.java @@ -0,0 +1,214 @@ +// 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.PipelineImpl; +import streamer.Queue; + +import common.AwtCanvasAdapter; +import common.AwtKeyEventSource; +import common.AwtMouseEventSource; +import common.BufferedImageCanvas; +import common.ScreenDescription; + +public class RdpClient extends PipelineImpl { + + /** + * Name of last OneTimePacket in handshake sequence. + */ + private static final String HANDSHAKE_END = "server_valid_client"; + + public RdpClient(String id, String userName, ScreenDescription screen, BufferedImageCanvas canvas) { + super(id); + assembleRDPPipeline(userName, screen, canvas); + } + +// /* DEBUG */ +// @Override +// protected HashMap initElementMap(String id) { +// HashMap map = new HashMap(); +// map.put("IN", new ServerPacketSniffer("server <")); +// map.put("OUT", new ClientPacketSniffer("> client")); +// return map; +// } + + private void assembleRDPPipeline(String userName, ScreenDescription screen, BufferedImageCanvas canvas) { + // + // Handshake chain + // + + RdpState state = new RdpState(); + int[] channelsToJoin = new int[] { RdpConstants.CHANNEL_RDPRDR, RdpConstants.CHANNEL_IO }; + + // Add elements + + add( + + new ClientX224ConnectionRequestPDU("client_connection_req", userName), new ServerX224ConnectionConfirmPDU("server_connection_conf"), + + new UpgradeSocketToSSL("upgrade_to_ssl"), + + new ClientMCSConnectInitial("client_initial_conference_create"), new ServerMCSConnectResponse("server_initial_conference_create"), + + new ClientMCSErectDomainRequest("client_erect_domain"), + + new ClientMCSAttachUserRequest("client_atach_user"), new ServerMCSAttachUserConfirmPDU("server_atach_user_confirm", state), + + new ClientMCSChannelJoinRequest_ServerMCSChannelConfirmPDUs("client_channel_join_rdprdr", channelsToJoin, state), + + new ClientInfoPDU("client_info_req", userName), + + new ServerLicenseErrorPDUValidClient("server_valid_client"), + + new ServerFastPath("server_fastpath"), + + new ServerTpkt("server_tpkt"), + + new ServerX224DataPdu("server_x224_data"), + + // These TPKT and X224 wrappers are connected directly to OUT for handshake + // sequence + new ClientTpkt("client_tpkt_ot"), + + new ClientX224DataPdu("client_x224_data_ot") + + ); + + // Handshake sequence (via SlowPath) + link("IN", + + "server_fastpath >tpkt", "server_tpkt", + + "client_connection_req", "server_connection_conf", + + "upgrade_to_ssl", + + "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"); + + // Connect one time outputs to client TPKT input + String tpkt_peers[] = new String[] { "client_connection_req", "server_connection_conf", "upgrade_to_ssl", "client_x224_data_ot" }; + for (String element : tpkt_peers) { + link(element + " >otout", element + "< client_tpkt_ot"); + } + + // 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", + "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"); + } + + // + // Transition + // + + add( + // To transfer packets between input threads and output thread. + new Queue("queue"), + + // 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 + // queue, from push mode to pull mode. + link(HANDSHAKE_END + " >queue", "queue", "OUT"); + + // Transition from handshake sequence for slow path packets + link(HANDSHAKE_END, "server_mcs"); + + // + // Main network + // + + AwtMouseEventSource mouseEventSource = new AwtMouseEventSource("mouse"); + AwtKeyEventSource keyEventSource = new AwtKeyEventSource("keyboard"); + + // Subscribe packet sender to various events + canvas.addMouseListener(mouseEventSource); + canvas.addMouseMotionListener(mouseEventSource); + canvas.addKeyListener(keyEventSource); + + // Add elements + add( + + new ServerChannel1003Router("server_channel_1003", state), + + new ServerDemandActivePDU("server_demand_active", screen, state), + + new ClientConfirmActivePDU("client_confirm_active", screen, state), + + new ServerBitmapUpdate("server_bitmap_update"), + + new AwtCanvasAdapter("canvas_adapter", canvas, screen), + + new ServerPaletteUpdate("server_palette", screen), + + keyEventSource, new AwtRdpKeyboardAdapter("keyboard_adapter"), + + mouseEventSource, new AwtRdpMouseAdapter("mouse_adapter"), + + // These FastPath, TPKT, and X224 wrappers are connected to queue + new ClientTpkt("client_tpkt_queue"), + + new ClientX224DataPdu("client_x224_data_queue"), + + new ClientFastPathPDU("client_fastpath_queue")); + + // Server packet handlers + link("server_mcs >channel_1003", "server_channel_1003"); + link("server_fastpath >bitmap", "fastpath< server_bitmap_update", "server_bitmap_update< canvas_adapter"); + link("server_channel_1003 >bitmap", "slowpath< server_bitmap_update"); + + link("server_fastpath >palette", "fastpath< server_palette"); + link("server_channel_1003 >palette", "slowpath< server_palette"); + + link("server_channel_1003 >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"); + + // Link mouse and keyboard to socket via adapters and send them using + // FastPath protocol + link(mouseEventSource.getId(), "mouse_adapter", "mouse_adapter< client_fastpath_queue"); + link(keyEventSource.getId(), "keyboard_adapter", "keyboard_adapter< client_fastpath_queue"); + + // Link packet wrappers to outgoing queue + link("client_fastpath_queue", "client_fastpath_queue< queue"); + link("client_x224_data_queue", "client_tpkt_queue", "client_tpkt_queue< queue"); + + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.java new file mode 100644 index 00000000000..1e3646a9ac4 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpConstants.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 rdpclient; + +import java.nio.charset.Charset; + +public interface RdpConstants { + + /** + * Default charset to use when communicating with server using 8 bit strings. + */ + public static final Charset CHARSET_8 = Charset.availableCharsets().get("US-ASCII"); + + /** + * Default charset to use when communicating with server using 16 bit strings. + */ + public static final Charset CHARSET_16 = Charset.availableCharsets().get("UTF-16LE"); + + + /** + * Negotiate SSL protocol to use to protect RDP connection. + * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx + */ + public static final int RDP_NEG_REQ_PROTOCOL_SSL = 1; + + /** + * Negotiate CredSSP protocol to use to protect RDP connection. + * @see http://msdn.microsoft.com/en-us/library/cc240500.aspx + * When used, client must set @see RDP_NEG_REQ_PROTOCOL_SSL too. + */ + public static final int RDP_NEG_REQ_PROTOCOL_HYBRID = 2; + + /** + * RDP negotiation: flags (not used, always 0). + */ + public static final int RDP_NEG_REQ_FLAGS = 0; + + /** + * RDP Negotiation: request. + */ + public static final int RDP_NEG_REQ_TYPE_NEG_REQ = 1; + + /** + * RDP Negotiation: response. + */ + public static final int RDP_NEG_REQ_TYPE_NEG_RSP = 2; + + /** + * RDP Negotiation: failure. + */ + public static final int RDP_NEG_REQ_TYPE_NEG_FAILURE = 3; + + + public static final int CHANNEL_IO = 1003; + + public static final int CHANNEL_RDPRDR = 1004; + + + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.java new file mode 100644 index 00000000000..c85972de7da --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/RdpState.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 rdpclient; + +import java.util.HashSet; +import java.util.Set; + +public class RdpState { + + public long serverShareId; + public int serverUserChannelId; + + public Set channels=new HashSet(); + + public void channelJoined(int actualChannel) { + channels.add(actualChannel); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.java new file mode 100644 index 00000000000..0e5b79aff28 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerBitmapUpdate.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; + +import common.BitmapOrder; +import common.BitmapRectangle; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Element; +import streamer.FakeSink; +import streamer.Link; +import streamer.Pipeline; +import streamer.PipelineImpl; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240624.aspx + */ +public class ServerBitmapUpdate extends BaseElement { + public static final int UPDATETYPE_BITMAP = 0x0001; + + /** + * Indicates that the bitmap data is compressed. The bitmapComprHdr field MUST + * be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not set. + */ + public static final int BITMAP_COMPRESSION = 0x0001; + + /** + * Indicates that the bitmapComprHdr field is not present (removed for + * bandwidth efficiency to save 8 bytes). + */ + private static final int NO_BITMAP_COMPRESSION_HDR = 0x0400; + + public ServerBitmapUpdate(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // * DEBUG */System.out.println(buf.toHexString(buf.length)); + + BitmapOrder order = new BitmapOrder(); + + // (2 bytes): A 16-bit, unsigned integer. The update type. This field MUST + // 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 + + "."); + + // (2 bytes): A 16-bit, unsigned integer. The number of screen rectangles + // present in the rectangles field. + int numberRectangles = buf.readSignedShortLE(); + + // (variable): Variable-length array of TS_BITMAP_DATA structures, each of + // which contains a rectangular clipping taken from the server-side screen + // frame buffer. The number of screen clippings in the array is specified by + // the numberRectangles field. + BitmapRectangle[] rectangles = new BitmapRectangle[numberRectangles]; + for (int i = 0; i < numberRectangles; i++) { + rectangles[i] = readRectangle(buf); + } + order.rectangles = rectangles; + + buf.assertThatBufferIsFullyRead(); + + ByteBuffer data = new ByteBuffer(0); + data.setOrder(order); + pushDataToAllOuts(data); + + buf.unref(); + } + + public BitmapRectangle readRectangle(ByteBuffer buf) { + + BitmapRectangle rectangle = new BitmapRectangle(); + + // (2 bytes): A 16-bit, unsigned integer. Left bound of the rectangle. + rectangle.x = buf.readSignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. Top bound of the rectangle. + rectangle.y = buf.readSignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. Inclusive right bound of the + // rectangle. + int destRight = buf.readSignedShortLE(); + rectangle.width=destRight-rectangle.x+1; + + // (2 bytes): A 16-bit, unsigned integer. Inclusive bottom bound of the + // rectangle. + int destBottom = buf.readSignedShortLE(); + rectangle.height=destBottom-rectangle.y+1; + + // (2 bytes): A 16-bit, unsigned integer. The width of the rectangle. + rectangle.bufferWidth = buf.readSignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The height of the rectangle. + rectangle.bufferHeight = buf.readSignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The color depth of the rectangle + // data in bits-per-pixel. + rectangle.colorDepth = buf.readSignedShortLE(); + + // (2 bytes): A 16-bit, unsigned integer. The flags describing the format of + // the bitmap data in the bitmapDataStream field. + int flags = buf.readSignedShortLE(); + + // BITMAP_COMPRESSION 0x0001 + // Indicates that the bitmap data is compressed. The bitmapComprHdr field + // MUST be present if the NO_BITMAP_COMPRESSION_HDR (0x0400) flag is not + // set. + boolean compressed=((flags & BITMAP_COMPRESSION) > 0); + + // (2 bytes): A 16-bit, unsigned integer. The size in bytes of the data in + // the bitmapComprHdr and bitmapDataStream fields. + int bitmapLength = buf.readSignedShortLE(); + + // NO_BITMAP_COMPRESSION_HDR 0x0400 + // Indicates that the bitmapComprHdr field is not present (removed for + // bandwidth efficiency to save 8 bytes). + if (compressed && (flags & NO_BITMAP_COMPRESSION_HDR) == 0) { + // (8 bytes): Optional Compressed Data Header structure specifying the + // bitmap data in the bitmapDataStream. + // This field MUST be present if the BITMAP_COMPRESSION (0x0001) flag is + // present in the Flags field, but the NO_BITMAP_COMPRESSION_HDR (0x0400) + // flag is not. + + // Note: Even when compression header is enabled, server sends nothing. + // rectangle.compressedBitmapHeader = buf.readBytes(8); + } + + // (variable): A variable-length array of bytes describing a bitmap image. + // Bitmap data is either compressed or uncompressed, depending on whether + // the BITMAP_COMPRESSION flag is present in the Flags field. Uncompressed + // bitmap data is formatted as a bottom-up, left-to-right series of pixels. + // Each pixel is a whole number of bytes. Each row contains a multiple of + // four bytes (including up to three bytes of padding, as necessary). + // Compressed bitmaps not in 32 bpp format are compressed using Interleaved + // RLE and encapsulated in an RLE Compressed Bitmap Stream structure, + // while compressed bitmaps at a color depth of 32 bpp are compressed + // using RDP 6.0 Bitmap Compression and stored inside + // an RDP 6.0 Bitmap Compressed Stream structure. + if (!compressed) { + rectangle.bitmapDataStream = buf.readBytes(bitmapLength); + } else { + ByteBuffer compressedImage = buf.readBytes(bitmapLength); + //* DEBUG */System.out.println("Compressed image: " + compressedImage + ", depth: " + rectangle.bitsPerPixel + "."); + rectangle.bitmapDataStream = RLEBitmapDecompression.rleDecompress(compressedImage, rectangle.bufferWidth, rectangle.bufferHeight, rectangle.colorDepth); + compressedImage.unref(); + } + + return rectangle; + } + + /** + * 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 }); + + Element bitmap = new ServerBitmapUpdate("bitmap") { + { + verbose = true; + } + }; + FakeSink fakeSink = new FakeSink("sink") { + { + verbose = true; + } + }; + Pipeline pipeline = new PipelineImpl("test"); + + // BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768); + // Element adapter = new AwtRdpAdapter("test",canvas ); + // pipeline.addAndLink(bitmap, adapter); + pipeline.addAndLink(bitmap, fakeSink); + + bitmap.handleData(packet, null); + + } + +} 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 new file mode 100644 index 00000000000..fdad522a269 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerChannel1003Router.java @@ -0,0 +1,530 @@ +// 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/ServerControlPDUCooperate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java new file mode 100644 index 00000000000..f2d3d368f00 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUCooperate.java @@ -0,0 +1,117 @@ +// 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.Link; +import streamer.OneTimeSwitch; + +public class ServerControlPDUCooperate extends OneTimeSwitch { + + public ServerControlPDUCooperate(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + +} + +/* @formatter:off */ +/* +03 00 00 28 02 F0 80 68 00 01 03 EB 70 1A 1A 00 17 00 EA 03 EA 03 01 00 9A 02 1A 00 14 00 00 00 04 00 00 00 00 00 00 00 + + + Frame: Number = 38, Captured Frame Length = 97, 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: 40 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 40 (0x28) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Data Packet + - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003 + - Type: Send Data Indication + - RootIndex: 26 + Value: (011010..) 0x1a + - UserID: 0x3ea + - UserID: 0x3ea + - ChannelId: 1002 + - Align: No Padding + Padding2: (00......) 0x0 + Value: 1 (0x1) + - Channel: 0x3eb + - ChannelId: 1003 + Align: No Padding + Value: 1003 (0x3EB) + - DataPriority: high + - DataPriority: high + - RootIndex: 1 + Value: (01......) 0x1 + - Segmentation: Begin End + Begin: (1.......) Begin + End: (.1......) End + - Length: 26 + - Align: No Padding + Padding4: (0000....) 0x0 + Length: 26 + RDP: RDPBCGR +- RDPBCGR: TsControlPDU + - SlowPathPacket: TsControlPDU + - SlowPath: Type = TS_PDUTYPE_DATAPDU + - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU + TotalLength: 26 (0x1A) + - PDUType: 23 (0x17) + Type: (............0111) TS_PDUTYPE_DATAPDU + ProtocolVersion: (000000000001....) 1 + PDUSource: 1002 (0x3EA) + - SlowPathIoPacket: 0x0 + - ShareDataHeader: TS_PDUTYPE2_CONTROL + ShareID: 66538 (0x103EA) + Pad1: 154 (0x9A) + StreamID: TS_STREAM_MED + UncompressedLength: 26 (0x1A) + PDUType2: TS_PDUTYPE2_CONTROL + - CompressedType: Not Compressed + MPPC: (....0000) MPPC 8K + Reserved: (...0....) + Compressed: (..0.....) Not Compressed + Front: (.0......) Not At Front + Flush: (0.......) Not Flushed + CompressedLength: 0 (0x0) + - TsControlPDU: Action = Cooperate + Action: Cooperate + GrantID: 0 (0x0) + ControlID: 0 (0x0) + + */ diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.java new file mode 100644 index 00000000000..e050e8a8ae8 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerControlPDUGrantedControl.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 rdpclient; + +import streamer.ByteBuffer; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class ServerControlPDUGrantedControl extends OneTimeSwitch { + + public ServerControlPDUGrantedControl(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + +} +/* @formatter:off */ +/* +03 00 00 28 02 F0 80 68 00 01 03 EB 70 1A 1A 00 17 00 EA 03 EA 03 01 00 50 02 1A 00 14 00 00 00 02 00 EC 03 EA 03 00 00 + + Frame: Number = 45, Captured Frame Length = 97, 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: 40 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 40 (0x28) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Data Packet + - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003 + - Type: Send Data Indication + - RootIndex: 26 + Value: (011010..) 0x1a + - UserID: 0x3ea + - UserID: 0x3ea + - ChannelId: 1002 + - Align: No Padding + Padding2: (00......) 0x0 + Value: 1 (0x1) + - Channel: 0x3eb + - ChannelId: 1003 + Align: No Padding + Value: 1003 (0x3EB) + - DataPriority: high + - DataPriority: high + - RootIndex: 1 + Value: (01......) 0x1 + - Segmentation: Begin End + Begin: (1.......) Begin + End: (.1......) End + - Length: 26 + - Align: No Padding + Padding4: (0000....) 0x0 + Length: 26 + RDP: RDPBCGR +- RDPBCGR: TsControlPDU + - SlowPathPacket: TsControlPDU + - SlowPath: Type = TS_PDUTYPE_DATAPDU + - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU + TotalLength: 26 (0x1A) + - PDUType: 23 (0x17) + Type: (............0111) TS_PDUTYPE_DATAPDU + ProtocolVersion: (000000000001....) 1 + PDUSource: 1002 (0x3EA) + - SlowPathIoPacket: 0x0 + - ShareDataHeader: TS_PDUTYPE2_CONTROL + ShareID: 66538 (0x103EA) + Pad1: 80 (0x50) + StreamID: TS_STREAM_MED + UncompressedLength: 26 (0x1A) + PDUType2: TS_PDUTYPE2_CONTROL + - CompressedType: Not Compressed + MPPC: (....0000) MPPC 8K + Reserved: (...0....) + Compressed: (..0.....) Not Compressed + Front: (.0......) Not At Front + Flush: (0.......) Not Flushed + CompressedLength: 0 (0x0) + - TsControlPDU: Action = Granted Control + Action: Granted Control + GrantID: 1004 (0x3EC) + ControlID: 1002 (0x3EA) + */ 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 new file mode 100644 index 00000000000..9ce87d31881 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerDemandActivePDU.java @@ -0,0 +1,661 @@ +// 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 new file mode 100644 index 00000000000..fbec1cea7aa --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerFastPath.java @@ -0,0 +1,259 @@ +// 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/ServerLicenseErrorPDUValidClient.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.java new file mode 100644 index 00000000000..194ffe6db6d --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerLicenseErrorPDUValidClient.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; + +import streamer.ByteBuffer; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class ServerLicenseErrorPDUValidClient extends OneTimeSwitch { + + public ServerLicenseErrorPDUValidClient(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + + /* @formatter:off */ +// * Server Error alert +// +//03 00 00 22 02 F0 80 68 00 01 03 EB 70 14 80 00 F1 BC FF 03 10 00 07 00 00 00 02 00 00 00 04 00 00 00 +// +// +// Frame: Number = 30, Captured Frame Length = 91, 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: 34 +// version: 3 (0x3) +// Reserved: 0 (0x0) +// PacketLength: 34 (0x22) +//- X224: Data +// Length: 2 (0x2) +// Type: Data +// EOT: 128 (0x80) +//- T125: Data Packet +// - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003 +// - Type: Send Data Indication +// - RootIndex: 26 +// Value: (011010..) 0x1a +// - UserID: 0x3ea +// - UserID: 0x3ea +// - ChannelId: 1002 +// - Align: No Padding +// Padding2: (00......) 0x0 +// Value: 1 (0x1) +// - Channel: 0x3eb +// - ChannelId: 1003 +// Align: No Padding +// Value: 1003 (0x3EB) +// - DataPriority: high +// - DataPriority: high +// - RootIndex: 1 +// Value: (01......) 0x1 +// - Segmentation: Begin End +// Begin: (1.......) Begin +// End: (.1......) End +// - Length: 20 +// - Align: No Padding +// Padding4: (0000....) 0x0 +// Length: 20 +// RDP: RDPBCGR +//- RDPBCGR: RDPELE +// - SecurityHeader: License Packet +// - Flags: 128 (0x80) +// SecurityExchange: (...............0) Not Security Exchange PDU +// Reserved1: (.............00.) Reserved +// Encrypted: (............0...) Not Encrypted packet +// ResetSeqNumber: (...........0....) MUST be ignored. +// IgnoreSeqNumber: (..........0.....) MUST be ignored. +// InfoPacket: (.........0......) Not Client Info PDU +// LicensePacket: (........1.......) License Packet +// Reserved2: (.......0........) Reserved +// LicensePacketEncryption: (......0.........) Not License Packet Encryption +// ServerRedirectionPacket: (.....0..........) Not Standard Security Server Redirection PDU +// ImprovedChecksumForMACG: (....0...........) Not Improved Checksum for MAC Generation +// Reserved3: (.000............) Reserved +// FlagsHiValid: (0...............) FlagsHi should be ignored +// FlagsHi: Should be ignored +//- RDPELE: GM_ERROR_ALERT +// - TsPreambleHeader: Type = GM_ERROR_ALERT +// MsgType: GM_ERROR_ALERT +// - Flags: 3 (0x3) +// LicenseProtocolVersionMask: (....0011) RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 +// Unused: (.000....) +// ExtendedErrorMSGsupported: (0.......) that extended error information using the License Error Message is NOT supported. +// MsgSize: 16 (0x10) +// - TsLicenseErrorMessage: ErrorCode = STATUS_VALID_CLIENT +// ErrorCode: STATUS_VALID_CLIENT +// StateTransition: ST_NO_TRANSITION +// - LiceseBinaryBlob: Type = Not Available +// RandomData: This value should be ignored +// BlobLen: 0 (0x0) +// + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.java new file mode 100644 index 00000000000..8373b83c725 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSAttachUserConfirmPDU.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 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; + +/** + * Server response to MCS Attach User request. + * + * Once the User Channel ID has been extracted, the client MUST send an MCS + * Channel Join Request PDU for the user channel. + * + * @see http://msdn.microsoft.com/en-us/library/cc240685.aspx + */ +public class ServerMCSAttachUserConfirmPDU extends OneTimeSwitch { + + public static final int MCS_ATTACH_USER_CONFIRM_PDU = 0xb; + + public static final int INITIATOR_PRESENT = 0x2; + + protected RdpState state; + + public ServerMCSAttachUserConfirmPDU(String id, RdpState state) { + super(id); + this.state = state; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + int typeAndFlags = buf.readUnsignedByte(); + int type = typeAndFlags >> 2; + 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 + "."); + + if (flags != INITIATOR_PRESENT) + throw new RuntimeException("Initator field is not present in MCS AttachUserConfirm PDU. Data: " + buf + "."); + + int rtSuccess = buf.readUnsignedByte() >> 4; + if (rtSuccess != 0) + throw new RuntimeException("["+this+"] ERROR: Cannot attach user: request failed. Error code: " + rtSuccess + ", data: " + buf + "."); + + // If the initiator field is present, the client stores the value of the + // initiator in the User Channel ID store , because the initiator specifies + // the User Channel ID. + state.serverUserChannelId = buf.readUnsignedShort() + 1001; + + buf.unref(); + + // Next: client MCS Channel Join Request PDU (s) + switchOff(); + } + + /** + * Example. + */ + /** + * Example. + * + * @see http://msdn.microsoft.com/en-us/library/cc240842.aspx + * @see http://msdn.microsoft.com/en-us/library/cc240500.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"); + + 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, }; + + RdpState rdpState = new RdpState(); + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet, new byte[] { 1, 2, 3 })); + Element atachUserConfirm = new ServerMCSAttachUserConfirmPDU("attach_user_confirm", rdpState); + Element sink = new MockSink("sink"); + Element mainSink = new MockSink("mainSink", ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 })); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, atachUserConfirm, sink, mainSink); + pipeline.link("source", "attach_user_confirm", "mainSink"); + pipeline.link("attach_user_confirm >" + OTOUT, "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + + if (rdpState.serverUserChannelId != 1004) + System.err.println("Incorrect user channel ID. Expected value: 1004, actual value: " + rdpState.serverUserChannelId + "."); + } + +} + +/* + * 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/ServerMCSChannelJoinConfirmPDU.java new file mode 100644 index 00000000000..d0a8e81380e --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSChannelJoinConfirmPDU.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 rdpclient; + +import streamer.ByteBuffer; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class ServerMCSChannelJoinConfirmPDU extends OneTimeSwitch { + + protected int channel; + + public ServerMCSChannelJoinConfirmPDU(String id, int channel) { + super(id); + this.channel=channel; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + +} + +/* + * 03 00 00 0F 02 F0 80 3E 00 00 03 03 EC 03 EC + + Frame: Number = 22, Captured Frame Length = 72, 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: 15 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 15 (0xF) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Channel Join Confirm, ChannelId = 1004, Result = rt-successful + - MCSHeader: Type=Channel Join Confirm + - Type: Channel Join Confirm + - RootIndex: 15 + Value: (001111..) 0xf + - MCSChannelJoinConfirm: ChannelId = 1004, Result = rt-successful + ChannelIdPresent: 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) + - Requested: 0x3ec + - ChannelId: 1004 + Align: No Padding + Value: 1004 (0x3EC) + - ChannelId: 0x3ec + - ChannelId: 1004 + Align: No Padding + Value: 1004 (0x3EC) + + */ diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java new file mode 100644 index 00000000000..30b196b94ee --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerMCSConnectResponse.java @@ -0,0 +1,283 @@ +// 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.Link; +import streamer.OneTimeSwitch; + +/** + * Once the basic server settings data blocks have been processed successfully, the client MUST send the MCS Attach User Request PDU to the server. + * + * @see http://msdn.microsoft.com/en-us/library/cc240682.aspx + */ +public class ServerMCSConnectResponse extends OneTimeSwitch { + + public ServerMCSConnectResponse(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + +} + +/* + * @formatter:off + * 03 00 00 64 02 F0 80 7F 66 5A 0A 01 00 02 01 00 30 1A 02 01 22 02 01 03 02 01 00 02 01 01 02 01 00 02 01 01 02 03 00 FF F8 02 01 02 04 36 00 05 00 14 7C 00 01 2A 14 76 0A 01 01 00 01 C0 00 4D 63 44 6E 20 01 0C 0C 00 04 00 08 00 01 00 00 00 03 0C 08 00 EB 03 00 00 02 0C 0C 00 00 00 00 00 00 00 00 00 + + Frame: Number = 12, Captured Frame Length = 157, 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: 100 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 100 (0x64) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: MCSConnect Response + - MCSConnectResponse: Result = rt-successful + - ConnectResponseHeader: + - AsnId: Application Constructed Tag (102) + - HighTag: + Class: (01......) Application (1) + Type: (..1.....) Constructed + TagNumber: (...11111) + TagValueEnd: 102 (0x66) + - AsnLen: Length = 90, LengthOfLength = 0 + Length: 90 bytes, LengthOfLength = 0 + - Result: rt-successful + - Value: 0 + - AsnIntegerHeader: + - AsnId: Enumerated type (Universal 10) + - LowTag: + Class: (00......) Universal (0) + Type: (..0.....) Primitive + TagValue: (...01010) 10 + - AsnLen: Length = 1, LengthOfLength = 0 + Length: 1 bytes, LengthOfLength = 0 + AsnInt: 0 (0x0) + - CalledConnectId: 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) + - DomainParameters: Length = 26, LengthOfLength = 0 + - 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 + - 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) + - UserIDs: 3 + - 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: 3 (0x3) + - 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) + - 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) + - 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) + - 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) + - MCSPDUsize: 65528 + - 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: 65528 (0xFFF8) + - 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) + - UserDataHeader: + - AsnId: OctetString type (Universal 4) + - LowTag: + Class: (00......) Universal (0) + Type: (..0.....) Primitive + TagValue: (...00100) 4 + - AsnLen: Length = 54, LengthOfLength = 0 + Length: 54 bytes, LengthOfLength = 0 + - 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) + - ConnectPDULength: 42 + Align: No Padding + Length: 42 + - ConnectGCCPDU: conferenceCreateResponse + ExtensionBit: 0 (0x0) + - ChoiceValue: conferenceCreateResponse + Value: (001.....) 0x1 + - conferenceCreateResponse: + ExtensionBit: 0 (0x0) + userDataPresent: 1 (0x1) + - nodeID: 0x79f3 + - UserID: 31219 + - Align: No Padding + Padding2: (00......) 0x0 + Value: 30218 (0x760A) + - tag: 1 (0x1) + - Length: 1 + Align: No Padding + Length: 1 + Value: 1 (0x1) + - result: success + ExtensionBit: 0 (0x0) + - RootIndex: 0 + Value: (000.....) 0x0 + - userData: + - Size: 1 + - Align: No Padding + Padding4: (0000....) 0x0 + Length: 1 + - UserData: 0x4d63446e + valuePresent: 1 (0x1) + - key: h221NonStandard + - 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) + - ServerMcsConnectResponsePdu: + - RDPGCCUserDataResponseLength: 32 + Align: No Padding + Length: 32 + - TsUd: SC_CORE + - TsUdHeader: Type = SC_CORE, Length = 12 + Type: SC_CORE + Length: 12 (0xC) + - TsUdScCore: + Version: RDP 5.0, 5.1, 5.2, 6.0, 6.1, and 7.0 + ClientRequestedProtocols: TLS 1.0 + - TsUd: SC_NET + - TsUdHeader: Type = SC_NET, Length = 8 + Type: SC_NET + Length: 8 (0x8) + - TsUdScNet: + MCSChannelID: 1003 (0x3EB) + ChannelCount: 0 (0x0) + Pad: 0 Bytes + - TsUd: SC_SECURITY + - TsUdHeader: Type = SC_SECURITY, Length = 12 + Type: SC_SECURITY + Length: 12 (0xC) + - TsUdSCSec1: + - 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.....) + EncryptionLevel: TS_ENCRYPTION_NONE + */ 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 new file mode 100644 index 00000000000..5862e196b5c --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/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; + +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 new file mode 100644 index 00000000000..8c39a021414 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPacketSniffer.java @@ -0,0 +1,52 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package 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/ServerPaletteUpdate.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.java new file mode 100644 index 00000000000..3b0762e6995 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerPaletteUpdate.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 rdpclient; + +import java.awt.image.IndexColorModel; + +import common.ScreenDescription; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +/** + * @see http://msdn.microsoft.com/en-us/library/cc240623.aspx + */ +public class ServerPaletteUpdate extends BaseElement { + + public static final int UPDATETYPE_PALETTE = 0x0002; + protected ScreenDescription screen; + + public ServerPaletteUpdate(String id, ScreenDescription screen) { + super(id); + this.screen = screen; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // (2 bytes): A 16-bit, unsigned integer. The update type. This field MUST + // be set to UPDATETYPE_PALETTE (0x0002). + int updateType = buf.readUnsignedShortLE(); + if (updateType != UPDATETYPE_PALETTE) + throw new RuntimeException("Unexpected update type. Expected type: UPDATETYPE_PALETTE (0x0002), actual value: " + updateType + ", data: " + buf + "."); + + // pad2Octets (2 bytes): A 16-bit, unsigned integer. Padding. Values in this + // field MUST be ignored. + buf.skipBytes(2); + + // (4 bytes): A 32-bit, unsigned integer. The number of RGB triplets in the + // paletteData field. This field MUST be set to 256 (the number of entries + // 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 + "."); + + // (variable): An array of palette entries in RGB triplet format packed on + // byte boundaries. The number of triplet entries is given by the + // numberColors field. + ByteBuffer paletteEntries = buf.readBytes(numberColors * 3); + + // In the case of a Palette Update, the client MUST update the global + // palette on all drawing surfaces + screen.colorMap = new IndexColorModel(8, numberColors, paletteEntries.data, paletteEntries.offset, false); + + /* DEBUG */buf.assertThatBufferIsFullyRead(); + + buf.unref(); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java new file mode 100644 index 00000000000..315fbfed00a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerSynchronizePDU.java @@ -0,0 +1,115 @@ +// 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.Link; +import streamer.OneTimeSwitch; + +public class ServerSynchronizePDU extends OneTimeSwitch { + + public ServerSynchronizePDU(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Ignore packet + buf.unref(); + switchOff(); + } + +} + +/* @formatter:off */ +/* + + * 03 00 00 24 02 F0 80 68 00 01 03 EB 70 16 16 00 17 00 EA 03 EA 03 01 00 08 00 16 00 1F 00 00 00 01 00 86 A4 + + Frame: Number = 36, Captured Frame Length = 93, 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: 36 + version: 3 (0x3) + Reserved: 0 (0x0) + PacketLength: 36 (0x24) +- X224: Data + Length: 2 (0x2) + Type: Data + EOT: 128 (0x80) +- T125: Data Packet + - MCSHeader: Type=Send Data Indication, UserID=1002, ChannelID=1003 + - Type: Send Data Indication + - RootIndex: 26 + Value: (011010..) 0x1a + - UserID: 0x3ea + - UserID: 0x3ea + - ChannelId: 1002 + - Align: No Padding + Padding2: (00......) 0x0 + Value: 1 (0x1) + - Channel: 0x3eb + - ChannelId: 1003 + Align: No Padding + Value: 1003 (0x3EB) + - DataPriority: high + - DataPriority: high + - RootIndex: 1 + Value: (01......) 0x1 + - Segmentation: Begin End + Begin: (1.......) Begin + End: (.1......) End + - Length: 22 + - Align: No Padding + Padding4: (0000....) 0x0 + Length: 22 + RDP: RDPBCGR +- RDPBCGR: SynchronizePDU + - SlowPathPacket: SynchronizePDU + - SlowPath: Type = TS_PDUTYPE_DATAPDU + - TsShareControlHeader: Type = TS_PDUTYPE_DATAPDU + TotalLength: 22 (0x16) + - PDUType: 23 (0x17) + Type: (............0111) TS_PDUTYPE_DATAPDU + ProtocolVersion: (000000000001....) 1 + PDUSource: 1002 (0x3EA) + - SlowPathIoPacket: 0x0 + - ShareDataHeader: TS_PDUTYPE2_SYNCHRONIZE + ShareID: 66538 (0x103EA) + Pad1: 8 (0x8) + StreamID: STREAM_UNDEFINED + UncompressedLength: 22 (0x16) + PDUType2: TS_PDUTYPE2_SYNCHRONIZE + - CompressedType: Not Compressed + MPPC: (....0000) MPPC 8K + Reserved: (...0....) + Compressed: (..0.....) Not Compressed + Front: (.0......) Not At Front + Flush: (0.......) Not Flushed + CompressedLength: 0 (0x0) + - TsSynchronizePDU: 0x1 + MessageType: 0x1, MUST be set to SYNCMSGTYPE_SYNC (1) + TargetUser: 42118 (0xA486) + */ diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.java new file mode 100644 index 00000000000..0d19fa4926c --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerTpkt.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; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class ServerTpkt extends BaseElement { + + /** + * TPKT protocol version (first byte). + */ + public static final int PROTOCOL_TPKT = 3; + + public ServerTpkt(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 + "."); + + // We need at least 4 bytes to get packet length + if(!cap(buf, 4, UNLIMITED, link, false)) + return; + + int version = buf.readUnsignedByte(); + if (version != PROTOCOL_TPKT) + throw new RuntimeException("Unexpected data in TPKT header. Expected TPKT version: 0x03, actual value: " + buf + "."); + + buf.skipBytes(1); // Reserved byte + + // Length of whole packet, including header + int length = buf.readUnsignedShort(); + if(!cap(buf, length, length, link, false)) + return; + + int payloadLength = length - buf.cursor; + + // Extract payload + ByteBuffer outBuf = buf.slice(buf.cursor, payloadLength, true); + buf.unref(); + + if(verbose) { + outBuf.putMetadata("source", this); + } + + pushDataToAllOuts(outBuf); + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.java new file mode 100644 index 00000000000..a33527773f3 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224ConnectionConfirmPDU.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; + +import streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.MockSink; +import streamer.MockSource; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; + +/** + * Once the External Security Protocol handshake has run to completion, the + * client MUST continue with the connection sequence by sending the MCS Connect + * Initial PDU to the server over the newly established secure channel. + * + * + * @see http://msdn.microsoft.com/en-us/library/cc240663.aspx + */ +public class ServerX224ConnectionConfirmPDU extends OneTimeSwitch { + + public static final int X224_TPDU_CONNECTION_REQUEST = 0xe0; + public static final int X224_TPDU_CONNECTION_CONFIRM = 0xd0; + public static final int X224_TPDU_DISCONNECTION_REQUEST = 0x80; + public static final int X224_TPDU_DISCONNECTION_CONFIRM = 0xc0; + public static final int X224_TPDU_EXPEDITED_DATA = 0x10; + public static final int X224_TPDU_DATA_ACKNOWLEDGE = 0x61; + public static final int X224_TPDU_EXPEDITET_ACKNOWLEDGE = 0x40; + public static final int X224_TPDU_REJECT = 0x51; + public static final int X224_TPDU_ERROR = 0x70; + public static final int X224_TPDU_PROTOCOL_IDENTIFIER = 0x01; + + /** + * 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. + */ + public static final int SSL_REQUIRED_BY_SERVER = 0x00000001; + + /** + * The server is configured to only use Standard RDP Security mechanisms and + * does not support any External Security Protocols. + */ + public static final int SSL_NOT_ALLOWED_BY_SERVER = 0x00000002; + + /** + * The server does not possess a valid authentication certificate and cannot + * initialize the External Security Protocol Provider. + */ + public static final int SSL_CERT_NOT_ON_SERVER = 0x00000003; + + /** + * 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. + */ + public static final int INCONSISTENT_FLAGS = 0x00000004; + + /** + * The server requires that the client support Enhanced RDP Security with + * CredSSP. + */ + public static final int HYBRID_REQUIRED_BY_SERVER = 0x00000005; + + /** + * The server requires that the client support Enhanced RDP Security with TLS + * 1.0, 1.1 or 1.2 and certificate-based client authentication. + */ + public static final int SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER = 0x00000006; + + public ServerX224ConnectionConfirmPDU(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + int x224Length = buf.readVariableSignedIntLE(); + + 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 + "."); + + // Ignore destination reference, because client side has only one node + buf.skipBytes(2); + + // Source reference + // int srcRef = buf.readUnsignedShort(); + buf.skipBytes(2); + + // Ignore class and options + buf.skipBytes(1); + + // RDP_NEG_RSP::type (TYPE_RDP_NEG_RSP) + int negType = buf.readUnsignedByte(); + + // RDP_NEG_RSP::flags (0) + buf.skipBytes(1); // Ignore: always 0 + + // RDP_NEG_RSP::length (always 8 bytes) + int length = buf.readUnsignedShortLE(); + + if (length != 8) + throw new RuntimeException("Unexpected length of buffer. Expected value: 8, actual value: " + length + ", RDP NEG buf: " + buf + "."); + + // RDP_NEG_RSP: Selected protocols (PROTOCOL_SSL) + int protocol = buf.readSignedIntLE(); + + if (negType != RdpConstants.RDP_NEG_REQ_TYPE_NEG_RSP) { + // Parse error code, see + // http://msdn.microsoft.com/en-us/library/cc240507.aspx + 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_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 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 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 (verbose) + System.out.println("[" + this + "] INFO: RDP Negotiation response. Type: " + negType + ", protocol: " + protocol + "."); + + // Next: upgrade socket to SSL, send ConnectInitial packet + 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"); + +// 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 +// +// 0x02, // RDP_NEG_RSP::type (TYPE_RDP_NEG_RSP) +// 0x00, // RDP_NEG_RSP::flags (0) +// 0x08, 0x00, // RDP_NEG_RSP::length (8 bytes) +// 0x01, 0x00, 0x00, 0x00 // RDP_NEG_RSP: Selected protocols (PROTOCOL_SSL) +// }; + + // Connection failure + // 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 + + }; + + MockSource source = new MockSource("source", ByteBuffer.convertByteArraysToByteBuffers(packet)); + Element cc = new ServerX224ConnectionConfirmPDU("cc"); + Element tpkt = new ServerTpkt("tpkt"); + Element sink = new MockSink("sink", new ByteBuffer[] {}); + Element mainSink = new MockSink("mainSink", new ByteBuffer[] {}); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source, tpkt, cc, sink, mainSink); + pipeline.link("source", "tpkt", "cc", "mainSink"); + pipeline.link("cc >" + OTOUT, "sink"); + pipeline.runMainLoop("source", STDOUT, false, false); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.java new file mode 100644 index 00000000000..15489041a08 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/ServerX224DataPdu.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; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class ServerX224DataPdu extends BaseElement { + + public static final int X224_TPDU_LAST_DATA_UNIT = 0x80; + public static final int X224_TPDU_DATA = 0xF0; + + public ServerX224DataPdu(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 + "."); + + int headerLength = buf.readVariableSignedIntLE(); + + if (headerLength != 2) + throw new RuntimeException("Unexpected X224 Data PDU header length. Expected header length: 2 , actual header length: " + headerLength + "."); + + // Read X224 type and options + 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 + "."); + + 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 + "."); + + ByteBuffer payload = buf.readBytes(buf.length - buf.cursor); + + buf.unref(); + + pushDataToAllOuts(payload); + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.java new file mode 100644 index 00000000000..aaf93b0cc90 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/TrustAllX509TrustManager.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; + +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +public class TrustAllX509TrustManager implements X509TrustManager { + @Override + public void checkClientTrusted(final X509Certificate[] chain, final String authType) { + // TODO: ask user to confirm self-signed certificates + } + + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) { + // TODO: ask user to confirm self-signed certificates + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + // TODO: use system CA certificates here + return null; + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.java new file mode 100644 index 00000000000..d4c08f189c7 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/rdpclient/UpgradeSocketToSSL.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 rdpclient; + +import streamer.ByteBuffer; +import streamer.Direction; +import streamer.Event; +import streamer.Link; +import streamer.OneTimeSwitch; + +public class UpgradeSocketToSSL extends OneTimeSwitch { + + public UpgradeSocketToSSL(String id) { + super(id); + } + + @Override + protected void onStart() { + + sendEventToAllPads(Event.SOCKET_UPGRADE_TO_SSL, Direction.IN); + switchOff(); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + throw new RuntimeException("Unexpected data: " + buf + "."); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java new file mode 100644 index 00000000000..195847524a3 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/AssertingByteBuffer.java @@ -0,0 +1,107 @@ +// 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.nio.charset.Charset; + +/** + * Assert that writes to this buffer are matching expected data. + */ +public class AssertingByteBuffer extends ByteBuffer { + + public AssertingByteBuffer(byte[] expectedData) { + super(expectedData); + } + + private void assertEquals(int expected, int actual) { + if (expected != actual) + throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + ", buf: "+this+"."); + } + + @Override + public void writeByte(int b) { + if(b<0) + throw new RuntimeException(); + //*DEBUG*/System.out.println("WriteByte: "+b+", cursor:"+cursor+"."); + assertEquals(readUnsignedByte(), b&0xff); + } + + @Override + public void writeShort(int x) { + //*DEBUG*/System.out.println("WriteShort: "+x+", cursor:"+cursor+"."); + assertEquals(readUnsignedShort(), x&0xFFff); + } + + @Override + public void writeShortLE(int x) { + //*DEBUG*/System.out.println("WriteShortLE: "+x+", cursor:"+cursor+"."); + assertEquals(readUnsignedShortLE(), x&0xFFff); + } + + @Override + public void writeInt(int i) { + //*DEBUG*/System.out.println("WriteInt: "+i+", cursor:"+cursor+"."); + assertEquals(readSignedInt(), i); + } + + @Override + public void writeIntLE(int i) { + //*DEBUG*/System.out.println("WriteIntLE: "+i+", cursor:"+cursor+"."); + assertEquals(readSignedIntLE(), i); + } + + @Override + public void writeVariableIntLE(int i) { + //*DEBUG*/System.out.println("WriteVariableIntLE: "+i+", cursor:"+cursor+"."); + assertEquals(readVariableSignedIntLE(), i); + } + + @Override + public void writeString(String actual, Charset charset) { + //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+"."); + String expected = readString(actual.length(), charset); + if (!actual.equals(expected)) + throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + "."); + } + + @Override + public void writeBytes(ByteBuffer actual) { + //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+"."); + ByteBuffer expected = readBytes(actual.length); + if (!actual.equals(expected)) + throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + "."); + } + + @Override + public void writeBytes(byte[] actualData) { + ByteBuffer actual = new ByteBuffer(actualData); + //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+"."); + ByteBuffer expected = readBytes(actual.length); + if (!actual.equals(expected)) + throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + "."); + } + + @Override + public void writeBytes(byte[] actualData, int offset, int length) { + ByteBuffer actual = new ByteBuffer(actualData, offset, length); + //*DEBUG*/System.out.println("WriteString: "+actual+", cursor:"+cursor+"."); + ByteBuffer expected = readBytes(actual.length); + if (!actual.equals(expected)) + throw new RuntimeException("Expected value does not match actual value. Expected value: " + expected + ", actual value: " + actual + "."); + } + +} 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 new file mode 100644 index 00000000000..86f9be35ba2 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BaseElement.java @@ -0,0 +1,417 @@ +// 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.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class BaseElement implements Element { + + protected String id; + + /** + * Constant for @see cap() method to indicate that length is not restricted. + */ + public static final int UNLIMITED = -1; + + /** + * Set to true to enable debugging messages. + */ + protected boolean verbose = false; + + /** + * Limit on number of packets sent to sink from this element. Can be handy for + * fake elements and handshake-related elements. + */ + protected int numBuffers = 0; + + /** + * Number of packets sent to sink. + */ + protected int packetNumber = 0; + + /** + * Recommended size for incoming buffer in pull mode. + */ + protected int incommingBufLength = -1; + + protected Map inputPads = new HashMap(); + protected Map outputPads = new HashMap(); + + public BaseElement(String id) { + this.id = id; + + verbose = System.getProperty("streamer.Element.debug", "false").equals("true") || System.getProperty("streamer.Element.debug", "").contains(id); + } + + @Override + public String toString() { + return "Element(" + id + ")"; + } + + @Override + public Link getLink(String padName) { + if (inputPads.containsKey(padName)) + return (Link) inputPads.get(padName); + else if (outputPads.containsKey(padName)) + return (Link) outputPads.get(padName); + else + return null; + } + + @Override + public Set getPads(Direction direction) { + switch (direction) { + case IN: + return inputPads.keySet(); + + case OUT: + return outputPads.keySet(); + } + return null; + } + + @Override + public void validate() { + for (String padName : inputPads.keySet()) { + if (inputPads.get(padName) == null) + throw new RuntimeException("[ " + this + "] Required input pad is not connected. Pad name: " + padName + "."); + } + + for (String padName : outputPads.keySet()) { + if (outputPads.get(padName) == null) + throw new RuntimeException("[ " + this + "] Required output pad is not connected. Pad name: " + padName + "."); + } + } + + @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); + + 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) + "."); + + outputPads.put(padName, link); + link.setSource(this); + + break; + } + } + + @Override + public void dropLink(String padName) { + if (inputPads.containsKey(padName)) { + Link link = (Link) inputPads.remove(padName); + if (link != null) + link.setSink(null); + } + + if (outputPads.containsKey(padName)) { + Link link = (Link) outputPads.remove(padName); + if (link != null) + link.setSource(null); + } + } + + /** + * By default, try to pull data from input links. + * + * Override this method in data source elements. + */ + @Override + public void poll(boolean block) { + for (DataSource source : inputPads.values()) { + Link link = (Link) source; + ByteBuffer buf = link.pull(block); + + if (buf != null) { + handleData(buf, link); + } + } + } + + /** + * By default, do nothing with incoming data and retransmit data to all output + * pads. + */ + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + pushDataToAllOuts(buf); + } + + /** + * Send data to all output pads. + */ + protected final void pushDataToAllOuts(ByteBuffer buf) { + + if (buf == null) + return; + + if (outputPads.size() == 0) + throw new RuntimeException("Number of outgoing connection is zero. Cannot send data to output. Data: " + buf + "."); + + // Send data to all pads with OUT direction + for (DataSink out : outputPads.values()) { + if (verbose) + System.out.println("[" + this + "] INFO: Sending buf " + buf + " to " + out + "."); + + buf.rewindCursor(); + buf.ref(); + out.sendData(buf); + } + + buf.unref(); + packetNumber++; + } + + /** + * Send data to given pad only. + */ + protected void pushDataToPad(String padName, ByteBuffer buf) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Sending buf " + buf + " to " + padName + "."); + + DataSink link = outputPads.get(padName); + if (link == null) + throw new RuntimeException("Output pad of " + this + " element is not connected to a link. Pad name: " + padName + "."); + + buf.rewindCursor(); + link.sendData(buf); + } + + /** + * By default, do nothing with incoming event and retransmit event to all + * pads. + */ + @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; + } + + sendEventToAllPads(event, direction); + } + + /** + * Override this method to do something when STREAM_CLOSE event arrives. + */ + protected void onClose() { + } + + /** + * Override this method to do something when STREAM_START event arrives. + */ + protected void onStart() { + } + + /** + * Send event to all outputs. + * + * @param event + * a event + * @param direction + * IN to send event to input pads, OUT to send event to all output + * pads + */ + protected final void sendEventToAllPads(Event event, Direction direction) { + if (verbose) + 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; + } + } + + /** + * Ensure that packet has required minimum and maximum length, cuts tail when + * necessary. + * + * @param buf + * a buffer + * @param minLength + * minimum length of packet or -1 + * @param maxLength + * maximum length of packet or -1 + * @param link + * source link, to push unnecessary data back + * @param fromCursor + * if true, then position will be included into calculation + * @return true, + */ + public boolean cap(ByteBuffer buf, int minLength, int maxLength, Link link, boolean fromCursor) { + + if (buf == null) + return false; + + int length = buf.length; + + int cursor; + if (fromCursor) + cursor = buf.cursor; + else + cursor = 0; + + length -= cursor; + + if ((minLength < 0 || length >= minLength) && (maxLength < 0 || length <= maxLength)) + // Buffer is already in bounds + return true; + + // Buffer is too small, wait for the rest of buffer + if (minLength >= 0 && length < minLength) { + + if (verbose) + System.out.println("[" + this + "] INFO: Buffer is too small. Min length: " + minLength + ", data length (after cursor): " + length + "."); + + link.pushBack(buf.slice(0, length + cursor, true), minLength + cursor); + return false; + } else if (maxLength >= 0 && length > maxLength) { + + if (verbose) + System.out.println("[" + this + "] INFO: Buffer is too big. Max length: " + maxLength + ", data length (after cursor): " + length + "."); + + // Buffer is too big, cut unnecessary tail + link.pushBack(buf.slice(maxLength + cursor, length - maxLength, true)); + buf.length = maxLength + cursor; + + } + + return true; + } + + @Override + public void dropLink(Link link) { + if (inputPads.containsValue(link)) { + for (Entry entry : inputPads.entrySet()) + if (link.equals(entry.getValue())) { + inputPads.remove(entry.getKey()); + break; + } + } + + if (outputPads.containsValue(link)) { + for (Entry entry : outputPads.entrySet()) + if (link.equals(entry.getValue())) { + outputPads.remove(entry.getKey()); + break; + } + } + } + + @Override + public void replaceLink(Link existingLink, Link newLink) { + if (inputPads.containsValue(existingLink)) { + for (Entry entry : inputPads.entrySet()) + if (existingLink.equals(entry.getValue())) { + inputPads.put(entry.getKey(), newLink); + newLink.setSink(this); + break; + } + } + + if (outputPads.containsValue(existingLink)) { + for (Entry entry : outputPads.entrySet()) + if (existingLink.equals(entry.getValue())) { + outputPads.put(entry.getKey(), newLink); + newLink.setSource(this); + break; + } + } + } + + @Override + public String getId() { + return id; + } + + /** + * Example. + */ + public static void main(String args[]) { + Element source = new FakeSource("source") { + { + this.verbose = true; + this.numBuffers = 10; + this.incommingBufLength = 3; + this.delay = 100; + } + }; + + Element sink = new FakeSink("sink") { + { + this.verbose = true; + } + }; + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source); + pipeline.add(new BaseElement("t1")); + pipeline.add(new BaseElement("t2")); + pipeline.add(new BaseElement("t3")); + pipeline.add(new BaseElement("t4")); + pipeline.add(sink); + + pipeline.link("source", "t1", "t2", "t3", "t4", "sink"); + + // Links between source-t1-t2 will operate in pull mode. + // 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(); + } + +} 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 new file mode 100644 index 00000000000..47f1435a335 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/BufferPool.java @@ -0,0 +1,36 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package streamer; + +public class BufferPool { + public static byte[] allocateNewBuffer(int minSize) { + // TODO: search for free buffer in pool + if (minSize >= 0) + return new byte[minSize]; + else + // Return large buffer by default, too minimize number of round trips + // between to read full packet when packet is large, but it is important + // to return buffer to pool to reuse it (or null-ify links to it for + // faster GC) + // TODO: get free buffer from pool + return new byte[128 * 1024]; + } + + public static void recycleBuffer(byte[] buf) { + // TODO: return buffer to pool + } +} 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 new file mode 100644 index 00000000000..832c731257f --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/ByteBuffer.java @@ -0,0 +1,826 @@ +// 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.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This class represents a slice in a buffer. + */ +public class ByteBuffer { + + public static final String SEQUENCE_NUMBER = "seq"; + + public byte data[]; + public int offset = 0; + public int length = 0; + public int cursor = 0; + + private int refCount = 1; + private ByteBuffer parentByteBuffer = null; + + private Order order; + + /** + * Create buffer of size no less than length. Buffer can be a bit larger than + * length. Offset also can be set to non-zero value to leave some place for + * future headers. + */ + public ByteBuffer(int minLength) { + // Get buffer of acceptable size from buffer pool + this.data = BufferPool.allocateNewBuffer(minLength); + this.offset = 0; + this.length = minLength; + } + + public ByteBuffer(byte data[]) { + if (data == null) + throw new NullPointerException("Data must be non-null."); + + this.data = data; + this.offset = 0; + this.length = data.length; + } + + public ByteBuffer(byte[] data, int offset, int length) { + if (data == null) + throw new NullPointerException("Data must be non-null."); + + this.data = data; + this.offset = offset; + this.length = length; + } + + /** + * Create byte buffer of requested size with some space reserved for future + * headers. + */ + 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; + } + + /** + * Create empty buffer with given order only. + */ + public ByteBuffer(Order order) { + this.order = order; + } + + public void setOrder(Order order) { + this.order = order; + } + + public Order getOrder() { + return order; + } + + @Override + public String toString() { + return toString(100); + } + + /** + * Return string representation of this byte buffer. + * + * @param maxLength + * 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 string representation of this byte buffer as hexadecimal numbers, + * e.g. "[0x01, 0x02]". + * + * @param maxLength + * number of bytes to show in string + */ + public String toHexString(int maxLength) { + StringBuilder builder = new StringBuilder(maxLength * 6); + builder.append('['); + for (int i = 0; i < maxLength && i < length; i++) { + if (i > 0) + builder.append(", "); + int b = data[offset + i] & 0xff; + builder.append("0x" + ((b < 16) ? "0" : "") + Integer.toString(b, 16)); + } + builder.append(']'); + return builder.toString(); + } + + /** + * Return string representation of this byte buffer as hexadecimal numbers, + * e.g. "01 02". + * + * @param maxLength + * number of bytes to show in string + */ + public String toPlainHexString(int maxLength) { + StringBuilder builder = new StringBuilder(maxLength * 3); + for (int i = 0; i < maxLength && i < length; i++) { + if (i > 0) + builder.append(" "); + int b = data[offset + i] & 0xff; + builder.append(((b < 16) ? "0" : "") + Integer.toString(b, 16)); + } + return builder.toString(); + } + + public void dump() { + System.out.println(toString(length)); + } + + public void extend(int newLength) { + if (data.length < newLength) + Arrays.copyOf(data, newLength); + } + + public void ref() { + refCount++; + } + + public void unref() { + refCount--; + + if (refCount == 0) { + + if (parentByteBuffer != null) { + parentByteBuffer.unref(); + parentByteBuffer = null; + } else { + // Return buffer to buffer pool + BufferPool.recycleBuffer(data); + } + + data = null; + } + + } + + public boolean isSoleOwner() { + return refCount == 1 && (parentByteBuffer == null); + } + + /** + * Create shared lightweight copy of part of this buffer. + */ + public ByteBuffer slice(int offset, int length, boolean copyMetadata) { + 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 + "."); + + ByteBuffer slice = new ByteBuffer(data, this.offset + offset, length); + + if (copyMetadata && this.metadata != null) + slice.metadata = new HashMap(metadata); + + return slice; + } + + private Map metadata = null; + + public Object putMetadata(String key, Object value) { + if (metadata == null) + metadata = new HashMap(); + return metadata.put(key, value); + } + + public Object getMetadata(String key) { + return (metadata != null) ? metadata.get(key) : null; + } + + /** + * Create new buffer, which holds data from both buffers. Expensive operation. + * + * @TODO if only one reference to this ByteBuffer exists, then extend this + * buffer instead of creating new buffer + * @TODO support list of buffers to avoid expensive joins until absolute + * necessary + */ + public ByteBuffer join(ByteBuffer buf) { + // Extend byte array for new data + int newLength = length + buf.length; + byte newData[] = new byte[newLength]; + + // Copy data from our buffer + System.arraycopy(data, offset, newData, 0, length); + + // Copy data from other buffer + System.arraycopy(buf.data, buf.offset, newData, length, buf.length); + + ByteBuffer newBuf = new ByteBuffer(newData); + + // Copy our (older) metadata to new buffer, because handler might store some + // metadata in buffer, which is pushed back. + if (metadata != null) + newBuf.metadata = new HashMap(metadata); + + return newBuf; + } + + /** + * Copy used portion of buffer to new byte array. Expensive operation. + */ + public byte[] toByteArray() { + return Arrays.copyOfRange(data, offset, offset + length); + } + + 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) + "."); + + short[] buf = new short[length / 2]; + + for (int i = 0, j = offset; i < buf.length; i++, j += 2) { + buf[i] = (short) ((data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8)); + } + return buf; + } + + /** + * Return array of int's in little endian order. + */ + 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) + "."); + + int[] buf = new int[length / 4]; + + for (int i = 0, j = offset; i < buf.length; i++, j += 4) { + buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16) | ((data[j + 3] & 0xFF) << 24); + } + return buf; + } + + /** + * 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) + "."); + + int[] buf = new int[length / 3]; + + for (int i = 0, j = offset; i < buf.length; i++, j += 3) { + buf[i] = (data[j + 0] & 0xFF) | ((data[j + 1] & 0xFF) << 8) | ((data[j + 2] & 0xFF) << 16); + } + return buf; + } + + /** + * Helper method for test cases to convert array of byte arrays to array of + * byte buffers. + */ + public static ByteBuffer[] convertByteArraysToByteBuffers(byte[]... bas) { + ByteBuffer bufs[] = new ByteBuffer[bas.length]; + + int i = 0; + for (byte[] ba : bas) { + bufs[i++] = new ByteBuffer(ba); + } + return bufs; + } + + /** + * Read signed int in network order. Cursor is advanced by 4. + */ + public int readSignedInt() { + 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)); + cursor += 4; + return result; + } + + /** + * Read signed int in little endian order. Cursor is advanced by 4. + */ + public int readSignedIntLE() { + 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)); + cursor += 4; + return result; + } + + /** + * Read unsigned int in little endian order. Cursor is advanced by 4. + */ + public long readUnsignedIntLE() { + 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) + (long) (data[offset + cursor] & 0xff)); + cursor += 4; + return result; + } + + /** + * Read signed int in variable length format. Top most bit of each byte + * indicates that next byte contains additional bits. Cursor is advanced by + * 1-5 bytes. + */ + public int readVariableSignedIntLE() { + int result = 0; + + for (int shift = 0; shift < 32; shift += 7) { + int b = readUnsignedByte(); + result |= (b & 0x7f) << shift; + if ((b & 0x80) == 0) + break; + } + + return result; + } + + /** + * Read unsigned int in network order in variable length format. Cursor is + * advanced by 1 to 4 bytes. + * + * Two most significant bits of first byte indicates length of field: 0x00 - 1 + * byte, 0x40 - 2 bytes, 0x80 - 3 bytes, 0xc0 - 4 bytes. + * + * @see http://msdn.microsoft.com/en-us/library/cc241614.aspx + */ + public int readEncodedUnsignedInt() { + 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; + } + + return result; + } + + /** + * Read unsigned byte. Cursor is advanced by 1. + */ + public int readUnsignedByte() { + if (cursor + 1 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + "."); + + int b = data[offset + cursor] & 0xff; + cursor += 1; + return b; + } + + /** + * Read signed byte. Cursor is advanced by 1. + */ + public byte readSignedByte() { + if (cursor + 1 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 1 byte from this buffer: " + this + "."); + + byte b = data[offset + cursor]; + cursor += 1; + return b; + } + + /** + * Read unsigned short in network order. Cursor is advanced by 2. + */ + public int readUnsignedShort() { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + "."); + + int result = (((data[offset + cursor] & 0xff) << 8) | (data[offset + cursor + 1] & 0xff)); + cursor += 2; + return result; + } + + /** + * Read signed short in little endian order. Cursor is advanced by 2. + */ + public short readSignedShortLE() { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + "."); + + short result = (short) (((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff)); + cursor += 2; + return result; + } + + /** + * Read unsigned short in network order in variable length format. Cursor is + * advanced by 1 or 2 bytes. + * + * Most significant bit of first byte indicates length of field: 0 - 1 byte, 1 + * - 2 bytes. + */ + public int readVariableUnsignedShort() { + int firstByte = readUnsignedByte(); + + int result; + if ((firstByte & 0x80) == 0) + result = firstByte & 0x7f; + else { + int secondByte = readUnsignedByte(); + result = (((firstByte & 0x7f) << 8) | secondByte); + } + + return result; + } + + /** + * Read unsigned short in little endian order. Cursor is advanced by 2. + */ + public int readUnsignedShortLE() { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot read 2 bytes from this buffer: " + this + "."); + + int result = (((data[offset + cursor + 1] & 0xff) << 8) | (data[offset + cursor] & 0xff)); + cursor += 2; + return result; + } + + /** + * Read unsigned short in network order in variable length format. Cursor is + * advanced by 1 or 2 bytes. + * + * Most significant bit of first byte indicates length of field: 0x00 - 1 + * byte, 0x80 - 2 bytes. + * + * @see http://msdn.microsoft.com/en-us/library/cc241612.aspx + */ + public int readEncodedUnsignedShort() { + int firstByte = readUnsignedByte(); + + int result; + if ((firstByte & 0x80) == 0) + result = firstByte & 0x7f; + else { + int secondByte = readUnsignedByte(); + result = (((firstByte & 0x7f) << 8) | secondByte); + } + + return result; + } + + /** + * Read signed short in network order in variable length format. Cursor is + * advanced by 1 or 2 bytes. + * + * Most significant bit of first byte indicates length of field: 0x00 - 1 + * byte, 0x80 - 2 bytes. Second most significant bit indicates is value + * positive or negative. + * + * @see http://msdn.microsoft.com/en-us/library/cc241613.aspx + */ + public int readEncodedSignedShort() { + int firstByte = readUnsignedByte(); + + int result; + if ((firstByte & 0x80) == 0) + result = firstByte & 0x3f; + else { + int secondByte = readUnsignedByte(); + result = (((firstByte & 0x3f) << 8) | secondByte); + } + + if ((firstByte & 0x40) > 0) + return -result; + else + return result; + } + + /** + * Read signed long in little endian order. Cursor is advanced by 8 bytes. + */ + public long readSignedLongLE() { + return (((long) readSignedIntLE()) & 0xffFFffFFL) | (((long) readSignedIntLE()) << 32); + } + + /** + * Read string from buffer. Cursor is advanced by string length. + */ + public String readString(int length, Charset charset) { + if (cursor + length > this.length) + throw new ArrayIndexOutOfBoundsException("Cannot read " + length + " bytes from this buffer: " + this + "."); + + String string = new String(data, offset + cursor, length, charset); + cursor += length; + return string; + } + + /** + * Get bytes as lightweight slice. Cursor is advanced by data length. + */ + public ByteBuffer readBytes(int dataLength) { + if (cursor + dataLength > length) + throw new ArrayIndexOutOfBoundsException("Cannot read " + dataLength + " bytes from this buffer: " + this + "."); + + ByteBuffer slice = slice(cursor, dataLength, false); + cursor += dataLength; + return slice; + } + + /** + * Cursor is advanced by given number of bytes. + */ + public void skipBytes(int numOfBytes) { + if (cursor + numOfBytes > length) + throw new ArrayIndexOutOfBoundsException("Cannot read " + numOfBytes + " bytes from this buffer: " + this + "."); + + cursor += numOfBytes; + } + + /** + * Write byte. Cursor is advanced by 1. + */ + public void writeByte(int b) { + if (cursor + 1 > length) + throw new ArrayIndexOutOfBoundsException("Cannot write 1 byte to this buffer: " + this + "."); + + data[offset + cursor] = (byte) b; + cursor += 1; + } + + /** + * Write short in network order. Cursor is advanced by 2. + */ + public void writeShort(int x) { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + "."); + + data[offset + cursor] = (byte) (x >> 8); + data[offset + cursor + 1] = (byte) x; + cursor += 2; + } + + /** + * Write short in little endian order. Cursor is advanced by 2. + */ + public void writeShortLE(int x) { + if (cursor + 2 > length) + throw new ArrayIndexOutOfBoundsException("Cannot write 2 bytes to this buffer: " + this + "."); + + data[offset + cursor + 1] = (byte) (x >> 8); + data[offset + cursor] = (byte) x; + cursor += 2; + } + + /** + * Write int in network order. Cursor is advanced by 4. + */ + public void writeInt(int i) { + if (cursor + 4 > length) + throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + "."); + + data[offset + cursor] = (byte) (i >> 24); + data[offset + cursor + 1] = (byte) (i >> 16); + data[offset + cursor + 2] = (byte) (i >> 8); + data[offset + cursor + 3] = (byte) i; + cursor += 4; + } + + public void writeIntLE(int i) { + if (cursor + 4 > length) + throw new ArrayIndexOutOfBoundsException("Cannot write 4 bytes to this buffer: " + this + "."); + + data[offset + cursor] = (byte) i; + data[offset + cursor + 1] = (byte) (i >> 8); + data[offset + cursor + 2] = (byte) (i >> 16); + data[offset + cursor + 3] = (byte) (i >> 24); + cursor += 4; + } + + /** + * Write int in variable length format. Cursor is advanced by number of bytes + * written (1-5). + * + * Topmost bit of each byte is set to 1 to indicate that next byte has data. + */ + public void writeVariableIntLE(int i) { + while (i != 0) { + // Get lower bits of number + int b = i & 0x7f; + i >>= 7; + + if (i > 0) + // Set topmost bit of byte to indicate that next byte(s) contains + // remainder bits + b |= 0x80; + + writeByte(b); + } + } + + /** + * Write short in variable length format. Cursor is advanced by number of + * bytes written (1-2). + * + * Topmost bit of first byte is set to 1 to indicate that next byte has data. + */ + public void writeVariableShort(int length) { + if (length > 0x7f | length < 0) + writeShort(length | 0x8000); + else + writeByte(length); + } + + /** + * Prepend given data to this byte buffer. + */ + public void prepend(ByteBuffer buf) { + prepend(buf.data, buf.offset, buf.length); + } + + /** + * Prepend given data to this byte buffer. + */ + public void prepend(byte[] data) { + prepend(data, 0, data.length); + } + + /** + * Prepend given data to this byte buffer. + */ + 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 + "."); + } + + // If there is no enough space for header to prepend + if (!(this.offset >= length)) { + throw new RuntimeException("Reserve data to have enough space for header."); + } + + // Copy header + System.arraycopy(data, offset, this.data, this.offset - length, length); + + // Extend byte range to include header + this.offset -= length; + this.length += length; + this.cursor += length; + } + + public void writeString(String str, Charset charset) { + writeBytes(str.getBytes(charset)); + } + + /** + * Write string of fixed size. When string is shorted, empty space is filled + * with zeros. When string is larger, it is truncated. + */ + public void writeFixedString(int length, String str, Charset charset) { + byte[] bytes = str.getBytes(charset); + writeBytes(bytes, 0, Math.min(bytes.length, length)); + + for (int i = bytes.length; i < length; i++) + writeByte(0); + } + + public void writeBytes(ByteBuffer buf) { + writeBytes(buf.data, buf.offset, buf.length); + } + + public void writeBytes(byte[] bytes) { + writeBytes(bytes, 0, bytes.length); + } + + public void writeBytes(byte[] bytes, int offset, int length) { + System.arraycopy(bytes, offset, this.data, this.offset + this.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. + */ + public void trimAtCursor() { + length = cursor; + } + + /** + * Rewind cursor to beginning of buffer. + */ + public void rewindCursor() { + cursor = 0; + } + + /** + * Read RGB color in LE order. Cursor is advanced by 3. + * + * @return color as int, with red in lowest octet. + */ + public int readRGBColor() { + return readUnsignedByte() | (readUnsignedByte() << 8) | (readUnsignedByte() << 16); + } + + public void assertThatBufferIsFullyRead() { + if (cursor != length) + throw new RuntimeException("Data in buffer is not read fully. Buf: " + this + "."); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + + int end = offset + length; + for (int i = offset; i < end; i++) + result = 31 * result + data[i]; + + result = prime * result + length; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + + ByteBuffer other = (ByteBuffer) obj; + if (length != other.length) + return false; + + for (int i = 0; i < length; i++) + if (data[offset + i] != other.data[other.offset + i]) + return false; + + return true; + } + +} 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 new file mode 100644 index 00000000000..e3de289c513 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSink.java @@ -0,0 +1,24 @@ +// 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; + +public interface DataSink { + + void sendData(ByteBuffer buf); + + void sendEvent(Event event, Direction direction); +} 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 new file mode 100644 index 00000000000..152be2e960c --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/DataSource.java @@ -0,0 +1,60 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package streamer; + +public interface DataSource { + + /** + * Get data from source. + * + * @param block + * if false, then return immediately when no data is available, + * otherwise wait for data + * @return new data or null, when no data is available + */ + ByteBuffer pull(boolean block); + + /** + * Hold data temporary to use at next pull or push. + * + * @param buf + * a data + */ + void pushBack(ByteBuffer buf); + + /** + * Hold data temporary to use at next pull. Don't return abything untill given + * amount of data will be read from source, because data will be pushed back + * anyway. + * + * @param buf + * a data + * @param lengthOfFullPacket + * length of full block of data to read from source + */ + void pushBack(ByteBuffer buf, int lengthOfFullPacket); + + /** + * Send event to pads. + * + * @param event + * a event + * @param direction + * pad direction + */ + void sendEvent(Event event, Direction direction); +} 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 new file mode 100644 index 00000000000..c9dede88e45 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Direction.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 streamer; + +public enum Direction { + IN, OUT +} 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 new file mode 100644 index 00000000000..c927deafe4d --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Element.java @@ -0,0 +1,120 @@ +// 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.Set; + +/** + * Element is for processing of data. It has one or more contact pads, which can + * be wired with other elements using links. + */ +public interface Element { + + /** + * Name of pad for standard input. Should be set in all elements except pure + * sinks. + */ + public static final String STDIN = "stdin"; + + /** + * Name of pad for standard output. Should be set in all elements except pure + * sources. + */ + public static final String STDOUT = "stdout"; + + /** + * Get link connected to given pad. + * + * @param padName + * Standard pads are "stdin" and "stdout". + */ + Link getLink(String padName); + + /** + * Get pads of this element. + */ + Set getPads(Direction direction); + + /** + * Connect link to given pad. + * + * @param padName + * a pad name. Standard pads are "stdin" and "stdout". + */ + void setLink(String padName, Link link, Direction direction); + + /** + * Disconnect link from given pad. + * + * @param padName + * Standard pads are "stdin" and "stdout". + */ + void dropLink(String padName); + + /** + * Pull data from element and handle it. Element should ask one of it input + * pads for data, handle data and push result to it sink(s), if any. + * + * @param block + * block until data will be available, or do a slight delay at least, + * when data is not available + */ + void poll(boolean block); + + /** + * Handle incoming data. + * + * @param buf + * a data + * @param link + * TODO + */ + void handleData(ByteBuffer buf, Link link); + + /** + * Handle event. + * + * @param event + * an event + * @param direction + * if IN, then send event to input pads, when OUT, then send to + * output pads + */ + void handleEvent(Event event, Direction direction); + + /** + * Get element ID. + */ + String getId(); + + /** + * Validate element: check is all required pads are connected. + */ + void validate(); + + /** + * Drop link. + * + * @param link a link to drop + */ + void dropLink(Link link); + + /** + * Drop existing link and replace it by new link. + */ + void replaceLink(Link existingLink, Link newLink); +} 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 new file mode 100644 index 00000000000..5e1a3893c52 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Event.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 streamer; + +public enum Event { + STREAM_START, + STREAM_CLOSE, + + /** + * Upgrade socket to SSL. + */ + SOCKET_UPGRADE_TO_SSL, + + /** + * Switch links to input mode. + */ + LINK_SWITCH_TO_PULL_MODE + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java new file mode 100644 index 00000000000..65fb29e3f0f --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSink.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package streamer; + +public class FakeSink extends BaseElement { + + public FakeSink(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Received buf #" + (packetNumber) + " " + buf + "."); + + if (buf == null) + return; + + // Use packetNumber variable to count incoming packets + packetNumber++; + + buf.unref(); + } + + @Override + public String toString() { + return "FakeSink(" + id + ")"; + } + + @Override + public void handleEvent(Event event, Direction direction) { + if (verbose) + System.out.println("[" + this + "] INFO: Event received: " + event + "."); + + } + + /** + * Example. + */ + public static void main(String args[]) { + + Element sink = new FakeSink("sink") { + { + verbose = true; + } + }; + + byte[] data = new byte[] { 1, 2, 3 }; + ByteBuffer buf = new ByteBuffer(data); + sink.setLink(STDIN, new SyncLink(), Direction.IN); + sink.getLink(STDIN).sendData(buf); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.java new file mode 100644 index 00000000000..4cf65034a73 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/FakeSource.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 streamer; + +public class FakeSource extends BaseElement { + + /** + * Delay for null packets in poll method when blocking is requested, in + * milliseconds. + */ + protected long delay = SyncLink.STANDARD_DELAY_FOR_EMPTY_PACKET; + + public FakeSource(String id) { + super(id); + } + + @Override + public void poll(boolean block) { + if (numBuffers > 0 && packetNumber >= numBuffers) { + // Close stream when limit of packets is reached + sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT); + return; + } + + // Prepare new packet + ByteBuffer buf = initializeData(); + + // Push it to output(s) + pushDataToAllOuts(buf); + + // Make slight delay when blocking input was requested (to avoid + // consuming of 100% in parent loop) + if (block) + delay(); + + } + + /** + * Make slight delay. Should be used when blocking input is requested in pull + * mode, but null packed was returned by input. + */ + protected void delay() { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + } + } + + /** + * Initialize data. + */ + public ByteBuffer initializeData() { + ByteBuffer buf = new ByteBuffer(incommingBufLength); + + // Set first byte of package to it sequance number + buf.data[buf.offset] = (byte) (packetNumber % 128); + + // Initialize rest of bytes with sequential values, which are + // corresponding with their position in byte buffer + for (int i = buf.offset + 1; i < buf.length; i++) + buf.data[i] = (byte) (i % 128); + + buf.putMetadata(ByteBuffer.SEQUENCE_NUMBER, packetNumber); + buf.putMetadata("src", id); + + return buf; + } + + @Override + public String toString() { + return "FakeSource(" + id + ")"; + } + + 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; + } + }; + + Element fakeSink = new FakeSink("sink") { + { + this.verbose = true; + } + }; + + Element fakeSink2 = new FakeSink("sink2") { + { + this.verbose = true; + } + }; + + Link link = new SyncLink(); + + fakeSource.setLink(STDOUT, link, Direction.OUT); + fakeSink.setLink(STDIN, link, Direction.IN); + + Link link2 = new SyncLink(); + + fakeSource.setLink("out2", link2, Direction.OUT); + fakeSink2.setLink(STDIN, link2, Direction.IN); + + link.run(); + + } + +} 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 new file mode 100644 index 00000000000..b05637f0db0 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/InputStreamSource.java @@ -0,0 +1,194 @@ +// 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.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Source element, which reads data from InputStream. + */ +public class InputStreamSource extends BaseElement { + + protected InputStream is; + protected SocketWrapper socketWrapper; + + public InputStreamSource(String id) { + super(id); + } + + public InputStreamSource(String id, InputStream is) { + super(id); + this.is = is; + } + + public InputStreamSource(String id, SocketWrapper socketWrapper) { + super(id); + this.socketWrapper = socketWrapper; + } + + @Override + public void handleEvent(Event event, Direction direction) { + switch (event) { + 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); + + 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 + "."); + } + } + + public void setInputStream(InputStream is) { + this.is = is; + + // 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) { + try { + if (!block && is.available() == 0) { + + if (verbose) + System.out.println("[" + this + "] INFO: No data in stream is available now, returning."); + + return; + } + + // 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."); + + int actualLength = is.read(buf.data, buf.offset, buf.data.length - buf.offset); + + 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 (IOException e) { + System.err.println("[" + this + "] ERROR: " + e.getMessage()); + closeStream(); + } + } + + @Override + protected void onClose() { + closeStream(); + } + + private void closeStream() { + if (verbose) + System.out.println("[" + this + "] INFO: Closing stream."); + + try { + is.close(); + } catch (IOException e) { + } + try { + sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT); + } catch (Exception e) { + } + } + + @Override + public String toString() { + return "InputStreamSource(" + id + ")"; + } + + /** + * Example. + */ + public static void main(String args[]) { + InputStream is = new ByteArrayInputStream(new byte[] { 1, 2, 3 }); + + InputStreamSource source = new InputStreamSource("source") { + { + verbose = true; + } + }; + Element fakeSink = new FakeSink("sink") { + { + verbose = true; + } + }; + + Link link = new SyncLink() { + { + verbose = true; + } + }; + + source.setLink(STDOUT, link, Direction.OUT); + fakeSink.setLink(STDIN, link, Direction.IN); + + source.setInputStream(is); + + link.sendEvent(Event.STREAM_START, Direction.OUT); + link.run(); + + } + +} 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 new file mode 100644 index 00000000000..bd970f08afd --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Link.java @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package streamer; + +/** + * Link is wire between two elements. It always must contain source and sink + * elements. + */ +public interface Link extends DataSource, DataSink, Runnable { + + /** + * Wire this link with given sink. + * + * @param sink + * an Element + * @return same sink element, for chaining + */ + Element setSink(Element sink); + + /** + * Wire this link with given source. + * + * @param source + * an Element + * @return same source element, for chaining + */ + Element setSource(Element source); + + Element getSource(); + + Element getSink(); + + /** + * Hold all data in cache, don't pass data to sink until resumed. + */ + void pause(); + + /** + * Resume transfer. + */ + void resume(); + + /** + * Change mode of operation of this link from push mode to pull mode. + */ + void setPullMode(); + + /** + * Drop this link. + */ + void drop(); +} 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 new file mode 100644 index 00000000000..ce9fdf91078 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSink.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 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/MockSource.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.java new file mode 100644 index 00000000000..db47db2ca50 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/MockSource.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 streamer; + +public class MockSource extends FakeSource { + + protected ByteBuffer bufs[] = null; + + public MockSource(String id) { + super(id); + } + + public MockSource(String id, ByteBuffer bufs[]) { + super(id); + this.bufs = bufs; + } + + /** + * Initialize data. + */ + @Override + public ByteBuffer initializeData() { + if (packetNumber >= bufs.length) { + sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT); + return null; + } + + ByteBuffer buf = bufs[packetNumber]; + + buf.putMetadata(ByteBuffer.SEQUENCE_NUMBER, packetNumber); + return buf; + } + + @Override + public void handleEvent(Event event, Direction direction) { + if (verbose) + System.out.println("[" + this + "] INFO: Event received: " + event + "."); + + } + + @Override + public String toString() { + return "MockSource(" + 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 fakeSink = new FakeSink("sink") { + { + this.verbose = true; + } + }; + + Link link = new SyncLink(); + + mockSource.setLink(STDOUT, link, Direction.OUT); + fakeSink.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 new file mode 100644 index 00000000000..a7d48482621 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OneTimeSwitch.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 streamer; + +/** + * One time switch for handshake and initialization stages. + * + * At beginning, element handles data internally, sending output to "otout" pad. + * After switchOff() method is called, element drops its links, so packets from + * "stdin" pad are forwarded directly to "stdout" pad, without processing. + * + * Event STREAM_START is captured by this element and not propagated further. + * When switchOff() method is called, event STREAM_START is generated and sent + * to "stdout". + */ +public abstract class OneTimeSwitch extends BaseElement { + + /** + * One-time out - name of output pad for one time logic. By default, output + * directly to socket. + */ + public static final String OTOUT = "otout"; + + private boolean switched = false; + + public OneTimeSwitch(String id) { + super(id); + declarePads(); + } + + protected void declarePads() { + inputPads.put(STDIN, null); + outputPads.put(OTOUT, null); + outputPads.put(STDOUT, null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (switched) + throw new RuntimeException(this + " element is switched off and must not receive any data or events anymore."); + + if (buf == null) + return; + + handleOneTimeData(buf, link); + } + + public void pushDataToOTOut(ByteBuffer buf) { + if (verbose) + System.out.println("[" + this + "] INFO: Sending data: " + buf + "."); + + outputPads.get(OTOUT).sendData(buf); + } + + /** + * Switch this element off. Pass data directly to main output(s). + */ + public void switchOff() { + if (verbose) + System.out.println("[" + this + "] INFO: Switching OFF."); + + switched = true; + verbose = false; + + // Rewire links: drop otout link, replace stdout link by stdin to send data + // directly to stdout + Link stdout = (Link) outputPads.get(STDOUT); + Link stdin = (Link) inputPads.get(STDIN); + Link otout = (Link) outputPads.get(OTOUT); + + otout.drop(); + + // Wake up next peer(s) + sendEventToAllPads(Event.STREAM_START, Direction.OUT); + + stdin.setSink(null); + inputPads.remove(STDIN); + + Element nextPeer = stdout.getSink(); + nextPeer.replaceLink(stdout, stdin); + stdout.drop(); + + for (Object link : inputPads.values().toArray()) + ((Link) link).drop(); + for (Object link : outputPads.values().toArray()) + ((Link) link).drop(); + + } + + public void switchOn() { + if (verbose) + System.out.println("[" + this + "] INFO: Switching ON."); + + switched = false; + } + + /** + * Override this method to handle one-time packet(s) at handshake or + * initialization stages. Execute method @see switchRoute() when this method + * is no longer necessary. + */ + protected abstract void handleOneTimeData(ByteBuffer buf, Link link); + + @Override + public void handleEvent(Event event, Direction direction) { + if (event == Event.STREAM_START) { + if (verbose) + System.out.println("[" + this + "] INFO: Event " + event + " is received."); + + switchOn(); + + // Execute this element onStart(), but do not propagate event further, + // to not wake up next elements too early + onStart(); + } else + super.handleEvent(event, direction); + } + +} 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 new file mode 100644 index 00000000000..1d63850d9ca --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Order.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 streamer; + +public class Order { + + public Object type; + +} 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 new file mode 100644 index 00000000000..d1aa5ce4aca --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/OutputStreamSink.java @@ -0,0 +1,153 @@ +// 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.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class OutputStreamSink extends BaseElement { + + protected OutputStream os; + protected SocketWrapper socketWrapper; + + public OutputStreamSink(String id) { + super(id); + } + + public OutputStreamSink(String id, OutputStream os) { + super(id); + this.os = os; + } + + public OutputStreamSink(String id, SocketWrapper socketWrapper) { + super(id); + this.socketWrapper = socketWrapper; + } + + public void setOutputStream(OutputStream os) { + this.os = os; + // Resume links + resumeLinks(); + } + + /** + * Send incoming data to stream. + */ + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + try { + if (verbose) + System.out.println("[" + this + "] INFO: Writing data to stream: " + buf + "."); + + os.write(buf.data, buf.offset, buf.length); + os.flush(); + } catch (IOException 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; + 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 (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 + "."); + } + } + + private void resumeLinks() { + for (DataSource source : inputPads.values()) + ((Link) source).resume(); + } + + @Override + protected void onClose() { + closeStream(); + } + + private void closeStream() { + if (verbose) + System.out.println("[" + this + "] INFO: Closing stream."); + + try { + os.close(); + } catch (IOException e) { + } + try { + sendEventToAllPads(Event.STREAM_CLOSE, Direction.IN); + } catch (Exception e) { + } + } + + @Override + public String toString() { + return "OutputStreamSink(" + id + ")"; + } + + /** + * Example. + */ + public static void main(String args[]) { + Element source = new FakeSource("source") { + { + this.verbose = true; + this.numBuffers = 3; + this.incommingBufLength = 5; + this.delay = 100; + } + }; + + OutputStreamSink sink = new OutputStreamSink("sink") { + { + verbose = true; + } + }; + + Link link = new SyncLink(); + + source.setLink(STDOUT, link, Direction.OUT); + sink.setLink(STDIN, link, Direction.IN); + + sink.setOutputStream(new ByteArrayOutputStream()); + + link.sendEvent(Event.STREAM_START, Direction.IN); + link.run(); + + } +} 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 new file mode 100644 index 00000000000..c369350cf38 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Pipeline.java @@ -0,0 +1,91 @@ +// 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; + +/** + * Pipeline groups multiple elements. + */ +public interface Pipeline extends Element { + + static final String IN = Direction.IN.toString(); + static final String OUT = Direction.OUT.toString(); + + /** + * Add elements to pipeline. + * + * @param elements + */ + void add(Element... elements); + + /** + * Add elements to pipeline and link them in given order. + * + * @param elements + */ + void addAndLink(Element... elements); + + /** + * Link elements in given order using SyncLink. Element name can have prefix + * "PADNAME< " or/and suffix " >PADNAME" to use given named pads instead of + * "stdin" and "stdout". I.e. link("foo", "bar", "baz"); is equal + * to link("foo >stdin", "stdout< bar >stdin", "stdout< baz"); . + * + * Special elements "IN" and "OUT" are pointing to pipeline outer interfaces, + * so when pipeline will be connected with other elements, outside of this + * pipeline, they will be connected to IN and OUT elements. + * + * Example: + * + *
+   * pipeline.link("IN", "foo", "bar", "OUT");
+   * // Make additional branch from foo to baz, and then to OUT
+   * pipeline.link("foo >baz_out", "baz", "baz_in< OUT");
+   * 
+ * + * @param elements + * elements to link + */ + void link(String... elements); + + /** + * Get element by name. + * + * @return an element + */ + Element get(String elementName); + + /** + * Get link by element name and pad name. + */ + Link getLink(String elementName, String padName); + + /** + * Set link by element name and pad name. Allows to link external elements + * into internal elements of pipeline. Special elements "IN" and "OUT" are + * pointing to pipeline outer interfaces. + */ + void setLink(String elementName, String padName, Link link, Direction direction); + + /** + * Get link connected to given pad in given element and run it main loop. + * @param separateThread + * set to true to start main loop in separate thread. + * @param waitForStartEvent TODO + */ + void runMainLoop(String element, String padName, boolean separateThread, boolean waitForStartEvent); + +} 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 new file mode 100644 index 00000000000..abf132f6aef --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/PipelineImpl.java @@ -0,0 +1,309 @@ +// 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.HashMap; +import java.util.Map; +import java.util.Set; + +public class PipelineImpl implements Pipeline { + + protected String id; + protected boolean verbose = System.getProperty("streamer.Pipeline.debug", "false").equals("true"); + + public PipelineImpl(String id) { + this.id = id; + elements = initElementMap(id); + } + + protected Map elements; + + protected HashMap initElementMap(String id) { + HashMap map = new HashMap(); + + map.put(IN, new BaseElement(id + "." + IN)); + map.put(OUT, new BaseElement(id + "." + OUT)); + return map; + } + + @Override + public Link getLink(String padName) { + Link link = elements.get(IN).getLink(padName); + if (link == null) + link = elements.get(OUT).getLink(padName); + return link; + } + + @Override + public Set getPads(Direction direction) { + switch (direction) { + case IN: + return elements.get(IN).getPads(direction); + + case OUT: + return elements.get(OUT).getPads(direction); + } + return null; + } + + @Override + public void validate() { + for (Element element : elements.values()) + element.validate(); + + // Check IN element + { + Element element = get(IN); + 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() + "."); + } + + // Check OUT element + { + Element element = get(OUT); + 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() + "."); + } + + } + + @Override + public void dropLink(String padName) { + if (elements.get(IN).getLink(padName) != null) + elements.get(IN).dropLink(padName); + + if (elements.get(OUT).getLink(padName) != null) + elements.get(OUT).dropLink(padName); + } + + @Override + public void dropLink(Link link) { + elements.get(IN).dropLink(link); + elements.get(OUT).dropLink(link); + } + + @Override + public void replaceLink(Link existingLink, Link newLink) { + elements.get(IN).replaceLink(existingLink, newLink); + elements.get(OUT).replaceLink(existingLink, newLink); + } + + @Override + public void setLink(String padName, Link link, Direction direction) { + // Wire links to internal elements instead + elements.get(direction.toString()).setLink(padName, link, direction); + } + + @Override + public void poll(boolean block) { + throw new RuntimeException("Not implemented."); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + get(IN).handleData(buf, link); + } + + @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; + } + } + + @Override + public void add(Element... elements) { + for (Element element : elements) { + 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) + "."); + + this.elements.put(id, element); + } + } + + @Override + public void link(String... elementNames) { + + if (elementNames.length < 2) + throw new RuntimeException("At least two elements are necessary to create link between them."); + + // Parse array of element and pad names + + Element elements[] = new Element[elementNames.length]; + String inputPads[] = new String[elementNames.length]; + String outputPads[] = new String[elementNames.length]; + + int i = 0; + for (String elementName : elementNames) { + if (elementName.contains("< ")) { + inputPads[i] = elementName.substring(0, elementName.indexOf("< ")); + elementName = elementName.substring(elementName.indexOf("< ") + 2); + } else { + inputPads[i] = STDIN; + } + + if (elementName.contains(" >")) { + outputPads[i] = elementName.substring(elementName.indexOf(" >") + 2); + elementName = elementName.substring(0, elementName.indexOf(" >")); + } else { + outputPads[i] = STDOUT; + } + + 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 + "."); + + i++; + } + + // Link elements + for (i = 0; i < elements.length - 1; i++) { + Element leftElement = elements[i]; + Element rightElement = elements[i + 1]; + String leftPad = outputPads[i]; + String rightPad = inputPads[i + 1]; + + String linkId = leftElement.getId() + " >" + leftPad + " | " + rightPad + "< " + rightElement.getId(); + + if (verbose) + System.out.println("[" + this + "] INFO: Linking: " + linkId + "."); + + Link link = new SyncLink(linkId); + leftElement.setLink(leftPad, link, Direction.OUT); + rightElement.setLink(rightPad, link, Direction.IN); + } + } + + @Override + public void addAndLink(Element... elements) { + add(elements); + link(elements); + } + + private void link(Element... elements) { + String elementNames[] = new String[elements.length]; + + int i = 0; + for (Element element : elements) { + elementNames[i++] = element.getId(); + } + + link(elementNames); + } + + @Override + public Element get(String elementName) { + return elements.get(elementName); + } + + @Override + public Link getLink(String elementName, String padName) { + return elements.get(elementName).getLink(padName); + + } + + @Override + public void setLink(String elementName, String padName, Link link, Direction direction) { + elements.get(elementName).setLink(padName, link, direction); + } + + @Override + public String getId() { + return id; + } + + @Override + public void runMainLoop(String elementName, String padName, boolean separateThread, boolean waitForStartEvent) { + validate(); + + Link link = getLink(elementName, padName); + + if (link == null) + throw new NullPointerException("Cannot find link. Element name: " + elementName + ", element: " + get(elementName) + ", pad: " + padName + "."); + + if (!waitForStartEvent) + link.sendEvent(Event.STREAM_START, Direction.OUT); + + if (separateThread) { + Thread thread = new Thread(link); + thread.setDaemon(true); + thread.start(); + } else { + link.run(); + } + } + + @Override + public String toString() { + return "Pipeline(" + id + ")"; + } + + /** + * 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"); + + Pipeline pipeline = new PipelineImpl("main"); + + // Create elements + pipeline.add(new FakeSource("source") { + { + this.incommingBufLength = 3; + this.numBuffers = 10; + this.delay = 100; + } + }); + pipeline.add(new BaseElement("tee")); + pipeline.add(new FakeSink("sink") { + { + this.verbose = true; + } + }); + pipeline.add(new FakeSink("sink2") { + { + this.verbose = true; + } + }); + + // Link elements + pipeline.link("source", "tee", "sink"); + pipeline.link("tee >out2", "sink2"); + + // Run main loop + pipeline.runMainLoop("source", STDOUT, false, false); + } + +} 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 new file mode 100644 index 00000000000..7a1734053d9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/Queue.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package streamer; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Message queue for safe transfer of packets between threads. + */ +public class Queue extends BaseElement { + + protected LinkedBlockingQueue queue = new LinkedBlockingQueue(); + + public Queue(String id) { + super(id); + } + + @SuppressWarnings("incomplete-switch") + @Override + public void poll(boolean block) { + try { + ByteBuffer buf = null; + if (block) { + buf = queue.take(); + } else { + buf = queue.poll(100, TimeUnit.MILLISECONDS); + } + + if (buf != null) + pushDataToAllOuts(buf); + + } catch (Exception e) { + sendEventToAllPads(Event.STREAM_CLOSE, Direction.OUT); + closeQueue(); + } + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Put incoming data into queue + try { + queue.put(buf); + } catch (Exception e) { + sendEventToAllPads(Event.STREAM_CLOSE, Direction.IN); + closeQueue(); + } + } + + @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); + } + } + + @Override + protected void onClose() { + super.onClose(); + closeQueue(); + } + + private void closeQueue() { + queue.clear(); + queue.add(null); + // Drop queue to indicate that upstream is closed. + // May produce NPE in poll(). + queue = null; + } + + @Override + public String toString() { + return "Queue(" + id + ")"; + } + + /** + * Example. + */ + public static void main(String args[]) { + // System.setProperty("streamer.Link.debug", "true"); + System.setProperty("streamer.Element.debug", "true"); + + Element source1 = new FakeSource("source1") { + { + this.delay = 100; + this.numBuffers = 10; + this.incommingBufLength = 10; + } + }; + + Element source2 = new FakeSource("source2") { + { + this.delay = 100; + this.numBuffers = 10; + this.incommingBufLength = 10; + } + }; + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.add(source1); + pipeline.add(source2); + pipeline.add(new Queue("queue")); + pipeline.add(new FakeSink("sink")); + + // Main flow + pipeline.link("source1", "in1< queue"); + pipeline.link("source2", "in2< queue"); + pipeline.link("queue", "sink"); + + new Thread(pipeline.getLink("source1", STDOUT)).start(); + new Thread(pipeline.getLink("source2", STDOUT)).start(); + pipeline.getLink("sink", STDIN).run(); + } +} 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 new file mode 100644 index 00000000000..2ddf0b635b9 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SocketWrapper.java @@ -0,0 +1,239 @@ +// 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 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 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; + } + + /** + * 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 + */ + 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; + } + } + + 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.getHostString(), 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/SyncLink.java b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java new file mode 100644 index 00000000000..32c14bb947f --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/streamer/SyncLink.java @@ -0,0 +1,402 @@ +// 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; + +/** + * Link to transfer data in bounds of single thread (synchronized transfer). + * Must not be used to send data to elements served in different threads. + */ +public class SyncLink implements Link { + + /** + * When null packet is pulled from source element, then make slight delay to + * 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 + + /** + * Delay for null packets in poll method when blocking is requested, in + * milliseconds. + */ + protected long delay = STANDARD_DELAY_FOR_EMPTY_PACKET; + + /** + * Set to true to print debugging messages. + */ + protected boolean verbose = System.getProperty("streamer.Link.debug", "false").equals("true");; + + /** + * ID of this link. + */ + protected String id = null; + + /** + * Buffer with data to hold because link is paused, or data is pushed back. + */ + protected ByteBuffer cacheBuffer = null; + + /** + * Size of expected packet. Data must be hold in link until full packet will + * be read. + */ + protected int expectedPacketSize = 0; + + /** + * Number of packets and packet header transferred to element. + */ + 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. + */ + protected Element source = null; + + /** + * Element to send data to in both pull and push modes. + */ + protected Element sink = null; + + /** + * When in loop, indicates that loop must be stopped. + * + * @see run() + */ + private boolean shutdown = false; + + /** + * 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; + + /** + * Operate in pull mode. + */ + protected boolean pullMode; + + public SyncLink() { + } + + public SyncLink(String id) { + this.id = id; + } + + @Override + public void pushBack(ByteBuffer buf) { + if (verbose) + System.out.println("[" + this + "] INFO: Buffer pushed back: " + buf + "."); + + if (cacheBuffer != null) { + ByteBuffer tmp = cacheBuffer.join(buf); + cacheBuffer.unref(); + cacheBuffer = tmp; + } else { + cacheBuffer = buf; + cacheBuffer.ref(); + } + + resetCursor(); + } + + private void resetCursor() { + // Reset cursor + cacheBuffer.cursor = 0; + } + + @Override + public void pushBack(ByteBuffer buf, int lengthOfFullPacket) { + pushBack(buf); + expectedPacketSize = lengthOfFullPacket; + } + + @Override + public String toString() { + return "SyncLink(" + ((id != null) ? id + ", " : "") + source + ":" + sink + ")"; + } + + /** + * Push data to sink. Call with null to push cached data. + */ + @Override + public void sendData(ByteBuffer buf) { + if (!paused && pullMode) + throw new RuntimeException("[" + this + "] ERROR: link is not in push mode."); + + if (verbose) + System.out.println("[" + this + "] INFO: Incoming buffer: " + buf + "."); + + if (buf == null && cacheBuffer == null) + return; + + if (cacheBuffer != null && buf != null) { + // Join old data with fresh data + buf = cacheBuffer.join(buf); + cacheBuffer.unref(); + cacheBuffer = buf; + } + + // Store buffer in cache field to simplify following loop + if (buf != null) + cacheBuffer = buf; + + // 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 (verbose) + System.out.println("[" + this + "] INFO: Transfer is paused. Data in cache buffer: " + cacheBuffer + "."); + + // Wait until rest of packet will be read + return; + } + + 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 + "."); + + // Wait until rest of packet will be read + return; + } + + // Full packet or packet header is read, feed it to element + buf = cacheBuffer; + cacheBuffer = null; + expectedPacketSize = 0; + packetNumber++; + + if (sink == null) + throw new NullPointerException("[" + this + "] ERROR: Cannot send data to sink: sink is null. Data: " + buf + "."); + + sink.handleData(buf, this); + // cacheBuffer and expectedPacketSize can be changed at this time + } + + } + + @SuppressWarnings("incomplete-switch") + @Override + public void sendEvent(Event event, Direction direction) { + + if (verbose) + System.out.println("[" + this + "] INFO: Event " + event + " is received."); + + // 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; + } + + } + + switch (direction) { + 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."); + + if (paused) { + if (verbose) + System.out.println("[" + this + "] INFO: Cannot pull, link is paused."); + + // Make slight delay in such case, to avoid consuming 100% of CPU + if (block) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + + return null; + } + + // If data in cache can be sent immediately, + // then return it instead of asking for more data from source + if (cacheBuffer != null && (expectedPacketSize == 0 || (expectedPacketSize > 0 && cacheBuffer.length >= expectedPacketSize))) { + if (verbose) + System.out.println("[" + this + "] INFO: Data pulled from cache buffer: " + cacheBuffer + "."); + + ByteBuffer tmp = cacheBuffer; + cacheBuffer = null; + return tmp; + } + + // 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(); + + // Can return something only when data was stored in buffer + if (cacheBuffer != null && (expectedPacketSize == 0 || (expectedPacketSize > 0 && cacheBuffer.length >= expectedPacketSize))) { + if (verbose) + System.out.println("[" + this + "] INFO: Data pulled from source: " + cacheBuffer + "."); + + ByteBuffer tmp = cacheBuffer; + cacheBuffer = null; + return tmp; + } else { + return null; + } + } + + @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 + "."); + + if (sink == null && cacheBuffer != null) + throw new RuntimeException("Cannot drop link: cache is not empty. Link: " + this + ", cache: " + cacheBuffer); + + this.sink = sink; + + return sink; + } + + @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 + + "."); + + this.source = source; + return source; + } + + @Override + public Element getSource() { + return source; + } + + @Override + public Element getSink() { + return sink; + } + + @Override + public void pause() { + if (paused) + throw new RuntimeException("Link is already paused."); + + paused = true; + + } + + @Override + public void resume() { + paused = false; + } + + /** + * Run pull loop to actively pull data from source and push it to sink. It + * must be only one pull loop per thread. + * + * Pull loop will start after event STREAM_START. This link and source element + * incomming links will be switched to pull mode before pull loop will be + * started using event LINK_SWITCH_TO_PULL_MODE. + */ + @Override + public void run() { + // Wait until even STREAM_START will arrive + while (!start) { + delay(); + } + + sendEvent(Event.LINK_SWITCH_TO_PULL_MODE, Direction.IN); + + if (verbose) + System.out.println("[" + this + "] INFO: Starting pull loop."); + + // Pull source in loop + while (!shutdown) { + // Pull data from source element and send it to sink element + ByteBuffer data = pull(true); + if (data != null) + sink.handleData(data, this); + + if (!shutdown && data == null) { + // Make slight delay to avoid consuming of 100% of CPU + delay(); + } + } + + if (verbose) + System.out.println("[" + this + "] INFO: Pull loop finished."); + + } + + protected void delay() { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + e.printStackTrace(System.err); + throw new RuntimeException("Interrupted in main loop.", e); + } + } + + @Override + public void setPullMode() { + if (verbose) + System.out.println("[" + this + "] INFO: Switching to PULL mode."); + + this.pullMode = true; + } + + @Override + public void drop() { + if (pullMode) + throw new RuntimeException("Cannot drop link in pull mode."); + + if (cacheBuffer != null) + throw new RuntimeException("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/vncclient/AwtKeyboardEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.java new file mode 100644 index 00000000000..5537d24d29b --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtKeyboardEventToVncAdapter.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; + +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/AwtMouseEventToVncAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.java new file mode 100644 index 00000000000..dd933947503 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/AwtMouseEventToVncAdapter.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. +package vncclient; + +import java.awt.event.MouseEvent; + +import common.MouseOrder; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class AwtMouseEventToVncAdapter extends BaseElement { + + public AwtMouseEventToVncAdapter(String id) { + super(id); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Get mouse event + MouseOrder order = (MouseOrder)buf.getOrder(); + + ByteBuffer outBuf = new ByteBuffer(6); + + outBuf.writeByte(RfbConstants.CLIENT_POINTER_EVENT); + + int buttonMask = mapAwtModifiersToVncButtonMask(order.event.getModifiersEx()); + outBuf.writeByte(buttonMask); + outBuf.writeShort(order.event.getX()); + outBuf.writeShort(order.event.getY()); + + pushDataToAllOuts(outBuf); + } + + /** + * Current state of buttons 1 to 8 are represented by bits 0 to 7 of + * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a + * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and + * right buttons on the mouse. On a wheel mouse, each step of the wheel + * upwards is represented by a press and release of button 4, and each step + * downwards is represented by a press and release of button 5. + * + * @param modifiers + * extended modifiers from AWT mouse event + * @return VNC mouse button mask + */ + public static int mapAwtModifiersToVncButtonMask(int modifiers) { + int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) + | (((modifiers & MouseEvent.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/EncodingsMessage.java new file mode 100644 index 00000000000..9ee3566efae --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/EncodingsMessage.java @@ -0,0 +1,63 @@ +// 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 streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class EncodingsMessage extends BaseElement { + + protected final int[] encodings; + + public EncodingsMessage(String id, int[] encodings) { + super(id); + this.encodings = encodings; + declarePads(); + } + + protected void declarePads() { + inputPads.put(STDIN, null); + outputPads.put(STDOUT, null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + buf.unref(); + + ByteBuffer outBuf = new ByteBuffer(4 + encodings.length * 4); + + outBuf.writeByte(RfbConstants.CLIENT_SET_ENCODINGS); + + outBuf.writeByte(0);// padding + + outBuf.writeShort(encodings.length); + + for (int i = 0; i < encodings.length; i++) { + outBuf.writeInt(encodings[i]); + } + + pushDataToAllOuts(outBuf); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java new file mode 100644 index 00000000000..c8fab216a62 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/FrameBufferUpdateRequest.java @@ -0,0 +1,127 @@ +// 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 common.ScreenDescription; + +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 FrameBufferUpdateRequest extends BaseElement { + // TODO: use object with fields instead of raw values in map + public static final String INCREMENTAL_UPDATE = "incremental"; + public static final String TARGET_X = "x"; + public static final String TARGET_Y = "y"; + public static final String WIDTH = "width"; + public static final String HEIGHT = "height"; + + protected ScreenDescription screen; + + public FrameBufferUpdateRequest(String id, ScreenDescription screen) { + super(id); + this.screen = screen; + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + Boolean incremental = (Boolean) buf.getMetadata(INCREMENTAL_UPDATE); + Integer x = (Integer) buf.getMetadata(TARGET_X); + Integer y = (Integer) buf.getMetadata(TARGET_Y); + Integer width = (Integer) buf.getMetadata(WIDTH); + Integer height = (Integer) buf.getMetadata(HEIGHT); + buf.unref(); + + // Set default values when parameters are not set + if (incremental == null) + incremental = false; + + if (x == null) + x = 0; + if (y == null) + y = 0; + + if (width == null) + width = screen.getFramebufferWidth(); + if (height == null) + height = screen.getFramebufferHeight(); + + ByteBuffer outBuf = new ByteBuffer(10); + + outBuf.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST); + outBuf.writeByte((incremental) ? RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST : RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST); + outBuf.writeShort(x); + outBuf.writeShort(y); + outBuf.writeShort(width); + outBuf.writeShort(height); + + if (verbose) { + outBuf.putMetadata("sender", this); + outBuf.putMetadata("dimensions", width + "x" + height + "@" + x + "x" + y); + } + + pushDataToAllOuts(outBuf); + } + + public static void main(String args[]) { + System.setProperty("streamer.Element.debug", "true"); + + ScreenDescription screen = new ScreenDescription(); + screen.setFramebufferSize(120, 80); + 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 })); + + ByteBuffer buf = new ByteBuffer(new byte[0]); + buf.putMetadata(TARGET_X, 1); + buf.putMetadata(TARGET_Y, 2); + buf.putMetadata(WIDTH, 3); + buf.putMetadata(HEIGHT, 4); + + Element source = new MockSource("source", new ByteBuffer[] { buf }); + + Pipeline pipeline = new PipelineImpl("test"); + + pipeline.addAndLink(source, adapter, sink); + pipeline.runMainLoop("source", STDOUT, false, false); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.java new file mode 100644 index 00000000000..8c691e72839 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RGB888LE32PixelFormatRequest.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 vncclient; + +import common.ScreenDescription; + +import streamer.BaseElement; +import streamer.ByteBuffer; +import streamer.Link; + +public class RGB888LE32PixelFormatRequest extends BaseElement { + protected int bitsPerPixel = 32; + protected int depth = 24; + protected int bigEndianFlag = RfbConstants.LITTLE_ENDIAN; + protected int trueColourFlag = RfbConstants.TRUE_COLOR; + protected int redMax = 255; + protected int greenMax = 255; + protected int blueMax = 255; + protected int redShift = 0; + protected int greenShift = 8; + protected int blueShift = 16; + + protected ScreenDescription screen; + + public RGB888LE32PixelFormatRequest(String id, ScreenDescription screen) { + super(id); + this.screen = screen; + } + + protected void declarePads() { + inputPads.put(STDIN, null); + outputPads.put(STDOUT, null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + buf.unref(); + + ByteBuffer outBuf = new ByteBuffer(20); + + outBuf.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT); + + // Padding + outBuf.writeByte(0); + outBuf.writeByte(0); + outBuf.writeByte(0); + + // Send pixel format + outBuf.writeByte(bitsPerPixel); + outBuf.writeByte(depth); + outBuf.writeByte(bigEndianFlag); + outBuf.writeByte(trueColourFlag); + outBuf.writeShort(redMax); + outBuf.writeShort(greenMax); + outBuf.writeShort(blueMax); + outBuf.writeByte(redShift); + outBuf.writeByte(greenShift); + outBuf.writeByte(blueShift); + + // Padding + outBuf.writeByte(0); + outBuf.writeByte(0); + outBuf.writeByte(0); + + 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/RfbConstants.java new file mode 100644 index 00000000000..c2d63bb7761 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/RfbConstants.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 vncclient; + +import java.nio.charset.Charset; + +public interface RfbConstants { + + public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003."; + public static final String VNC_PROTOCOL_VERSION_MINOR = "003"; + public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR; + + /** + * Server message types. + */ + final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3; + + /** + * Client message types. + */ + public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, + CLIENT_KEYBOARD_EVENT = 4, CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6; + + /** + * Server authorization type + */ + public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2; + + /** + * Server authorization reply. + */ + public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2; + + /** + * Encodings. + */ + public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16; + + /** + * Pseudo-encodings. + */ + public final static int ENCODING_CURSOR = -239 /*0xFFFFFF11*/, ENCODING_DESKTOP_SIZE = -223 /*0xFFFFFF21*/; + + /** + * Encodings, which we support. + */ + public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE }; + + /** + * Frame buffer update request type: update of whole screen or partial update. + */ + public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1; + + public static final int KEY_UP = 0, KEY_DOWN = 1; + + public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1; + + public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1; + + public static final int PALETTE = 0, TRUE_COLOR = 1; + + /** + * Default 8 bit charset to use when communicating with server. + */ + public static final Charset US_ASCII_CHARSET = Charset.availableCharsets().get("US-ASCII"); + + /** + * Default 16 bit charset to use when communicating with server. + */ + public static final Charset UCS2_CHARSET = Charset.availableCharsets().get("UTF-16LE"); +} 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 new file mode 100644 index 00000000000..2b77e0a9603 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncClient.java @@ -0,0 +1,107 @@ +// 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 streamer.PipelineImpl; +import streamer.Queue; + +import common.AwtBellAdapter; +import common.AwtCanvasAdapter; +import common.AwtClipboardAdapter; +import common.AwtKeyEventSource; +import common.AwtMouseEventSource; +import common.BufferedImageCanvas; +import common.ScreenDescription; + +public class VncClient extends PipelineImpl { + + public VncClient(String id, String password, ScreenDescription screen, BufferedImageCanvas canvas) { + super(id); + assembleVNCPipeline(password, screen, canvas); + } + + private void assembleVNCPipeline(String password, ScreenDescription screen, BufferedImageCanvas canvas) { + + AwtMouseEventSource mouseEventSource = new AwtMouseEventSource("mouse"); + AwtKeyEventSource keyEventSource = new AwtKeyEventSource("keyboard"); + + // Subscribe packet sender to various events + canvas.addMouseListener(mouseEventSource); + canvas.addMouseMotionListener(mouseEventSource); + canvas.addKeyListener(keyEventSource); + + add( + // Handshake + + // RFB protocol version exchanger + new Vnc_3_3_Hello("hello"), + // Authenticator + new Vnc_3_3_Authentication("auth", password), + // Initializer + new VncInitializer("init", true, screen), + + new EncodingsMessage("encodings", RfbConstants.SUPPORTED_ENCODINGS_ARRAY), + + new RGB888LE32PixelFormatRequest("pixel_format", screen), + + // Main + + // Packet receiver + new VncMessageHandler("message_handler", screen), + + new AwtBellAdapter("bell"), + + new AwtClipboardAdapter("clipboard"), + + new AwtCanvasAdapter("pixels", canvas, screen), + + new Queue("queue"), + + new FrameBufferUpdateRequest("fbur", screen), + + new AwtKeyboardEventToVncAdapter("keyboard_adapter"), + + new AwtMouseEventToVncAdapter("mouse_adapter"), + + mouseEventSource, keyEventSource + + ); + + // Link handshake elements + link("IN", "hello", "auth", "init", "message_handler"); + link("hello >otout", "hello< OUT"); + link("auth >otout", "auth< OUT"); + link("init >otout", "init< OUT"); + link("init >encodings", "encodings"); + link("init >pixel_format", "pixel_format"); + link("encodings", "encodings< OUT"); + link("pixel_format", "pixel_format< OUT"); + + // Link main elements + link("message_handler >bell", "bell"); + link("message_handler >clipboard", "clipboard"); + link("message_handler >pixels", "pixels"); + link("message_handler >fbur", "fbur"); + + link("fbur", "fbur< queue"); + link("keyboard", "keyboard_adapter", "keyboard< queue"); + link("mouse", "mouse_adapter", "mouse< queue"); + link("queue", "OUT"); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java new file mode 100644 index 00000000000..0882d13ef82 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncInitializer.java @@ -0,0 +1,244 @@ +// 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 streamer.ByteBuffer; +import streamer.Element; +import streamer.Link; +import streamer.MockSink; +import streamer.MockSource; +import streamer.OneTimeSwitch; +import streamer.Pipeline; +import streamer.PipelineImpl; + +import common.ScreenDescription; + +public class VncInitializer extends OneTimeSwitch { + + // Pad names + public static final String CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD = "encodings"; + public static final String CLIENT_PIXEL_FORMAT_ADAPTER_PAD = "pixel_format"; + + protected byte sharedFlag = RfbConstants.EXCLUSIVE_ACCESS; + + /** + * Properties of remote screen . + */ + protected ScreenDescription screen; + + public VncInitializer(String id, boolean shared, ScreenDescription screen) { + super(id); + + setSharedFlag(shared); + this.screen = screen; + + declarePads(); + } + + @Override + protected void declarePads() { + super.declarePads(); + outputPads.put(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, null); + outputPads.put(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, null); + } + + public ScreenDescription getScreen() { + return screen; + } + + public void setScreen(ScreenDescription screen) { + this.screen = screen; + } + + public void setSharedFlag(boolean shared) { + if (shared) + sharedFlag = RfbConstants.SHARED_ACCESS; + else + sharedFlag = RfbConstants.EXCLUSIVE_ACCESS; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Server initialization message is at least 24 bytes long + length of + // desktop name + if (!cap(buf, 24, UNLIMITED, link, false)) + return; + + // Read server initialization message + // Read frame buffer size + int framebufferWidth = buf.readUnsignedShort(); + int framebufferHeight = buf.readUnsignedShort(); + + // Read pixel format + int bitsPerPixel = buf.readUnsignedByte(); + int depth = buf.readUnsignedByte(); + + int bigEndianFlag = buf.readUnsignedByte(); + int trueColorFlag = buf.readUnsignedByte(); + + int redMax = buf.readUnsignedShort(); + int greenMax = buf.readUnsignedShort(); + int blueMax = buf.readUnsignedShort(); + + int redShift = buf.readUnsignedByte(); + int greenShift = buf.readUnsignedByte(); + int blueShift = buf.readUnsignedByte(); + + // Skip padding + buf.skipBytes(3); + + // Read desktop name + int length = buf.readSignedInt(); + + // Consume exactly $length bytes, push back any extra bytes + if (!cap(buf, length, length, link, true)) + return; + + 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 + "."); + + // 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.setDesktopName(desktopName); + + // If sever screen has different parameters than ours, then change it + if (!screen.isRGB888_32_LE()) { + // Send client pixel format + sendClientPixelFormat(); + } + + // Send encodings supported by client + sendSupportedEncodings(); + + switchOff(); + + } + + @Override + protected void onStart() { + ByteBuffer buf = new ByteBuffer(new byte[] { sharedFlag }); + pushDataToOTOut(buf); + } + + private void sendClientPixelFormat() { + pushDataToPad(CLIENT_PIXEL_FORMAT_ADAPTER_PAD, new ByteBuffer(0)); + } + + private void sendSupportedEncodings() { + pushDataToPad(CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, new ByteBuffer(0)); + } + + public String toString() { + return "VncInit(" + id + ")"; + } + + /** + * 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"); + + final String desktopName = "test"; + + 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', + + // Tail + 1, 2, 3 + + }, + // Tail packet + new byte[] { 4, 5, 6 }); + } + }; + + ScreenDescription screen = new ScreenDescription(); + final VncInitializer init = new VncInitializer("init", true, screen); + Element initSink = new MockSink("initSink") { + { + // Expect shared flag + bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] { RfbConstants.SHARED_ACCESS }); + } + }; + Element mainSink = new MockSink("mainSink") { + { + // Expect two tail packets + bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] { 1, 2, 3 }, new byte[] { 4, 5, 6 }); + } + }; + ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}); + Element encodingsSink = new MockSink("encodings", emptyBuf); + Element pixelFormatSink = new MockSink("pixel_format", emptyBuf); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.addAndLink(source, init, mainSink); + pipeline.add(encodingsSink, pixelFormatSink, initSink); + pipeline.link("init >otout", "initSink"); + pipeline.link("init >" + CLIENT_SUPPORTED_ENCODINGS_ADAPTER_PAD, "encodings"); + pipeline.link("init >" + CLIENT_PIXEL_FORMAT_ADAPTER_PAD, "pixel_format"); + + pipeline.runMainLoop("source", STDOUT, false, false); + + if (!screen.isRGB888_32_LE()) + System.err.println("Screen description was read incorrectly: " + screen + "."); + if (!desktopName.equals(screen.getDesktopName())) + System.err.println("Screen desktop name was read incorrectly: \"" + screen.getDesktopName() + "\"."); + + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java new file mode 100644 index 00000000000..758000dd323 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/VncMessageHandler.java @@ -0,0 +1,419 @@ +// 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 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.BitmapOrder; +import common.BitmapRectangle; +import common.CopyRectOrder; +import common.ScreenDescription; + +public class VncMessageHandler extends BaseElement { + protected ScreenDescription screen = null; + + // Pad names + public static final String SERVER_BELL_ADAPTER_PAD = "bell"; + public static final String SERVER_CLIPBOARD_ADAPTER_PAD = "clipboard"; + public static final String PIXEL_ADAPTER_PAD = "pixels"; + 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"; + public static final String HEIGHT = "height"; + public static final String SOURCE_X = "srcX"; + public static final String SOURCE_Y = "srcY"; + public static final String PIXEL_FORMAT = "pixel_format"; + + private static final String NUM_OF_PROCESSED_RECTANGLES = "rects"; + private static final String SAVED_CURSOR_POSITION = "cursor"; + + // Pixel format: RGB888 LE 32 + public static final String RGB888LE32 = "RGB888LE32"; + + public VncMessageHandler(String id, ScreenDescription screen) { + super(id); + this.screen = screen; + declarePads(); + } + + private void declarePads() { + outputPads.put(SERVER_BELL_ADAPTER_PAD, null); + outputPads.put(SERVER_BELL_ADAPTER_PAD, null); + outputPads.put(SERVER_CLIPBOARD_ADAPTER_PAD, null); + outputPads.put(PIXEL_ADAPTER_PAD, null); + outputPads.put(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, null); + + inputPads.put("stdin", null); + } + + @Override + public void handleData(ByteBuffer buf, Link link) { + if (buf == null) + return; + + try { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + if (!cap(buf, 1, UNLIMITED, link, false)) + return; + + // Read server message type + int messageType = buf.readUnsignedByte(); + + // Invoke packet handler by packet type. + switch (messageType) { + + 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(); + + break; + } + + case RfbConstants.SERVER_BELL: { + if (!handleBell(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 + "."); + } + + // Cut tail, if any + cap(buf, 0, 0, link, true); + } finally { + + // Return processed buffer back to pool + buf.unref(); + } + } + + private boolean handleClipboard(ByteBuffer buf, Link link) { + if (!cap(buf, 3 + 4, UNLIMITED, link, true)) + return false; + + // Skip padding + buf.skipBytes(3); + + // Read text length + int length = buf.readSignedInt(); + + // We need full string to parse it + if (!cap(buf, length, UNLIMITED, link, true)) + return false; + + String content = buf.readString(length, RfbConstants.US_ASCII_CHARSET); + + // Send content in metadata + ByteBuffer outBuf = new ByteBuffer(0); + outBuf.putMetadata(CLIPBOARD_CONTENT, content); + + pushDataToPad(SERVER_CLIPBOARD_ADAPTER_PAD, outBuf); + + return true; + } + + private boolean handleBell(ByteBuffer buf, Link link) { + // Send empty packet to bell adapter to produce bell + pushDataToPad(SERVER_BELL_ADAPTER_PAD, new ByteBuffer(0)); + + return true; + } + + // FIXME: this method is too complex + private boolean handleFBU(ByteBuffer buf, Link link) { + + // We need at least 3 bytes here, 1 - padding, 2 - number of rectangles + if (!cap(buf, 3, UNLIMITED, link, true)) + return false; + + buf.skipBytes(1);// Skip padding + + // Read number of rectangles + int numberOfRectangles = buf.readUnsignedShort(); + + if (verbose) + System.out.println("[" + this + "] INFO: Frame buffer update. Number of rectangles: " + numberOfRectangles + "."); + + // Each rectangle must have header at least, header length is 12 bytes. + if (!cap(buf, 12 * numberOfRectangles, UNLIMITED, link, true)) + return false; + + // For all rectangles + + // Restore saved point, to avoid flickering and performance problems when + // frame buffer update is split between few incoming packets. + int numberOfProcessedRectangles = (buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) != null) ? (Integer) buf.getMetadata(NUM_OF_PROCESSED_RECTANGLES) : 0; + if (buf.getMetadata(SAVED_CURSOR_POSITION) != null) + 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 + "."); + + // For all new rectangles + for (int i = numberOfProcessedRectangles; i < numberOfRectangles; i++) { + + // We need coordinates of rectangle (2x4 bytes) and encoding type (4 + // bytes) + if (!cap(buf, 12, UNLIMITED, link, true)) + return false; + + // Read coordinates of rectangle + int x = buf.readUnsignedShort(); + int y = buf.readUnsignedShort(); + int width = buf.readUnsignedShort(); + int height = buf.readUnsignedShort(); + + // Read rectangle encoding + int encodingType = buf.readSignedInt(); + + // Process rectangle + switch (encodingType) { + + 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_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 + "."); + } + + // Update information about processed rectangles to avoid handling of same + // rectangle multiple times. + // TODO: push back partial rectangle only instead + buf.putMetadata(NUM_OF_PROCESSED_RECTANGLES, ++numberOfProcessedRectangles); + buf.putMetadata(SAVED_CURSOR_POSITION, buf.cursor); + } + + return true; + } + + private boolean handleScreenSizeChangeRect(ByteBuffer buf, Link link, int x, int y, int width, int height) { + // Remote screen size is changed + if (verbose) + System.out.println("[" + this + "] INFO: Screen size rect. Width: " + width + ", height: " + height + "."); + + screen.setFramebufferSize(width, height); + + return true; + } + + private boolean handleCopyRect(ByteBuffer buf, Link link, int x, int y, int width, int height) { + // Copy rectangle from one part of screen to another. + // Areas may overlap. Antialiasing may cause visible artifacts. + + // We need 4 bytes with coordinates of source rectangle + if (!cap(buf, 4, UNLIMITED, link, true)) + return false; + + CopyRectOrder order = new CopyRectOrder(); + + order.srcX = buf.readUnsignedShort(); + order.srcY = buf.readUnsignedShort(); + order.x = x; + order.y = y; + order.width = width; + 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 + "."); + + pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order)); + + return true; + } + + private boolean handleRawRectangle(ByteBuffer buf, Link link, int x, int y, int width, int height) { + // Raw rectangle is just array of pixels to draw on screen. + int rectDataLength = width * height * screen.getBytesPerPixel(); + + // We need at least rectDataLength bytes. Extra bytes may contain other + // rectangles. + if (!cap(buf, rectDataLength, UNLIMITED, link, true)) + return false; + + if (verbose) + 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; + rectangle.y = y; + rectangle.width = width; + rectangle.height = height; + rectangle.bufferWidth = width; + rectangle.bufferHeight = height; + rectangle.bitmapDataStream = buf.readBytes(rectDataLength); + rectangle.colorDepth=screen.getColorDeph(); + + BitmapOrder order = new BitmapOrder(); + order.rectangles = new BitmapRectangle[] { rectangle }; + + pushDataToPad(PIXEL_ADAPTER_PAD, new ByteBuffer(order)); + return true; + } + + public void onStart() { + // Send Frame Buffer Update request + sendFBUR(); + } + + private void sendFBUR() { + ByteBuffer buf = new ByteBuffer(0); + buf.putMetadata("incremental", true); + pushDataToPad(FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, buf); + } + + public String toString() { + return "VNCMessageHandler(" + id + ")"; + } + + /** + * 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"); + + Element source = new MockSource("source") { + { + // Split messages at random boundaries to check "pushback" logic + bufs = ByteBuffer.convertByteArraysToByteBuffers(new byte[] { + // Message type: server bell + RfbConstants.SERVER_BELL, + + // Message type: clipboard text + RfbConstants.SERVER_CUT_TEXT, + // Padding + 0, 0, 0, + // Length (test) + 0, 0, 0, 4, + + }, new byte[] { + // Clipboard text + 't', 'e', 's', 't', + + // Message type: frame buffer update + RfbConstants.SERVER_FRAMEBUFFER_UPDATE, + // Padding + 0, + // Number of rectangles + 0, 3, }, + + 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), }, + + 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@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; + } + }; + + final Element handler = new VncMessageHandler("handler", screen); + + ByteBuffer[] emptyBuf = ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}); + Element fburSink = new MockSink("fbur", ByteBuffer.convertByteArraysToByteBuffers(new byte[] {}, new byte[] {})); + 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 copyRectSink = new MockSink("copy_rect", emptyBuf); + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.addAndLink(source, handler); + pipeline.add(fburSink, bellSink, clipboardSink, desktopSizeChangeSink, pixelsSink, copyRectSink); + + pipeline.link("handler >" + FRAME_BUFFER_UPDATE_REQUEST_ADAPTER_PAD, "fbur"); + pipeline.link("handler >" + SERVER_BELL_ADAPTER_PAD, "bell"); + pipeline.link("handler >" + SERVER_CLIPBOARD_ADAPTER_PAD, "clipboard"); + pipeline.link("handler >" + PIXEL_ADAPTER_PAD, "pixels"); + + pipeline.runMainLoop("source", STDOUT, false, false); + + } + +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java new file mode 100644 index 00000000000..52d9976eece --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Authentication.java @@ -0,0 +1,291 @@ +// 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.security.spec.KeySpec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +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; + +public class Vnc_3_3_Authentication extends OneTimeSwitch { + + /** + * Password to use when authentication is required. + */ + protected String password = null; + + /** + * Authentication stage: + *
    + *
  • 0 - challenge received, response must be sent + *
  • 1 - authentication result received. + *
+ */ + protected int stage = 0; + + public Vnc_3_3_Authentication(String id) { + super(id); + } + + public Vnc_3_3_Authentication(String id, String password) { + super(id); + this.password = password; + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + switch (stage) { + case 0: // Read security with optional challenge and response + stage0(buf, link); + + break; + case 1: // Read authentication response + stage1(buf, link); + break; + } + + } + + /** + * Read security type. If connection type is @see + * RfbConstants.CONNECTION_FAILED, then throw exception. If connection type is @see + * RfbConstants.NO_AUTH, then switch off this element. If connection type is @see + * RfbConstants.VNC_AUTH, then read challenge, send encoded password, and read + * authentication response. + */ + private void stage0(ByteBuffer buf, Link link) { + // At least 4 bytes are necessary + if (!cap(buf, 4, UNLIMITED, link, true)) + return; + + // Read security type + int authType = buf.readSignedInt(); + + switch (authType) { + 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); + + 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.VNC_AUTH: { + // Read challenge and generate response + responseToChallenge(buf, link); + break; + } + + default: + throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + "."); + } + + } + + private void responseToChallenge(ByteBuffer buf, Link link) { + // Challenge is exactly 16 bytes long + if (!cap(buf, 16, 16, link, true)) + return; + + ByteBuffer challenge = buf.slice(buf.cursor, 16, true); + buf.unref(); + + // Encode challenge with password + ByteBuffer response; + try { + response = encodePassword(challenge, password); + challenge.unref(); + } catch (Exception e) { + throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage()); + } + + if (verbose) { + response.putMetadata("sender", this); + } + + // Send encoded challenge + nextStage(); + pushDataToOTOut(response); + + } + + private void nextStage() { + stage++; + + if (verbose) + System.out.println("[" + this + "] INFO: Next stage: " + stage + "."); + } + + /** + * Encode password using DES encryption with given challenge. + * + * @param challenge + * a random set of bytes. + * @param password + * a password + * @return DES hash of password and challenge + */ + public ByteBuffer encodePassword(ByteBuffer challenge, String password) { + if (challenge.length != 16) + throw new RuntimeException("Challenge must be exactly 16 bytes long."); + + // VNC password consist of up to eight ASCII characters. + byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding + byte[] passwordAsciiBytes = password.getBytes(RfbConstants.US_ASCII_CHARSET); + System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8)); + + // Flip bytes (reverse bits) in key + for (int i = 0; i < key.length; i++) { + key[i] = flipByte(key[i]); + } + + try { + KeySpec desKeySpec = new DESKeySpec(key); + SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES"); + SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec); + Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + ByteBuffer buf = new ByteBuffer(cipher.doFinal(challenge.data, challenge.offset, challenge.length)); + + return buf; + } catch (Exception e) { + throw new RuntimeException("Cannot encode password.", e); + } + } + + /** + * Reverse bits in byte, so least significant bit will be most significant + * bit. E.g. 01001100 will become 00110010. + * + * See also: http://www.vidarholen.net/contents/junk/vnc.html , + * http://bytecrafter .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html + * + * @param b + * a byte + * @return byte in reverse order + */ + private static byte flipByte(byte b) { + int b1_8 = (b & 0x1) << 7; + int b2_7 = (b & 0x2) << 5; + int b3_6 = (b & 0x4) << 3; + int b4_5 = (b & 0x8) << 1; + int b5_4 = (b & 0x10) >>> 1; + int b6_3 = (b & 0x20) >>> 3; + int b7_2 = (b & 0x40) >>> 5; + int b8_1 = (b & 0x80) >>> 7; + byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1); + return c; + } + + /** + * Read authentication result, send nothing. + */ + private void stage1(ByteBuffer buf, Link link) { + // Read authentication response + if (!cap(buf, 4, 4, link, false)) + return; + + 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_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."); + + default: + throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult); + } + + switchOff(); + + } + + public String toString() { + return "VNC3.3 Authentication(" + id + ")"; + } + + /** + * 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"); + + final String password = "test"; + + 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 }); + } + }; + + Element mainSink = new FakeSink("mainSink"); + final Vnc_3_3_Authentication auth = new Vnc_3_3_Authentication("auth", password); + Element initSink = new MockSink("initSink") { + { + // Expect encoded password + bufs = new ByteBuffer[] { auth.encodePassword(new ByteBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }), password) }; + } + }; + + Pipeline pipeline = new PipelineImpl("test"); + pipeline.addAndLink(source, auth, mainSink); + pipeline.add(initSink); + pipeline.link("auth >otout", "initSink"); + + pipeline.runMainLoop("source", STDOUT, false, false); + + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java new file mode 100644 index 00000000000..323380b047f --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/main/java/vncclient/Vnc_3_3_Hello.java @@ -0,0 +1,115 @@ +// 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import streamer.ByteBuffer; +import streamer.InputStreamSource; +import streamer.Link; +import streamer.OneTimeSwitch; +import streamer.OutputStreamSink; +import streamer.Pipeline; +import streamer.PipelineImpl; + +/** + * VNC server sends hello packet with RFB protocol version, e.g. + * "RFB 003.007\n". We need to send response packet with supported protocol + * version, e.g. "RFB 003.003\n". + */ +public class Vnc_3_3_Hello extends OneTimeSwitch { + + public Vnc_3_3_Hello(String id) { + super(id); + } + + @Override + protected void handleOneTimeData(ByteBuffer buf, Link link) { + if (verbose) + System.out.println("[" + this + "] INFO: Data received: " + buf + "."); + + // Initial packet is exactly 12 bytes long + if (!cap(buf, 12, 12, link, false)) + return; + + // Read protocol version + String rfbProtocol = new String(buf.data, buf.offset, buf.length, RfbConstants.US_ASCII_CHARSET); + buf.unref(); + + // Server should use RFB protocol 3.x + if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) + throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\"."); + + // Send response: we support RFB 3.3 only + String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n"; + + ByteBuffer outBuf = new ByteBuffer(ourProtocolString.getBytes(RfbConstants.US_ASCII_CHARSET)); + + if (verbose) { + outBuf.putMetadata("sender", this); + outBuf.putMetadata("version", RfbConstants.RFB_PROTOCOL_VERSION); + } + + pushDataToOTOut(outBuf); + + // Switch off this element from circuit + switchOff(); + + } + + public String toString() { + return "Vnc3.3 Hello(" + id + ")"; + } + + /** + * 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"); + + InputStream is = new ByteArrayInputStream("RFB 003.007\ntest".getBytes(RfbConstants.US_ASCII_CHARSET)); + ByteArrayOutputStream initOS = new ByteArrayOutputStream(); + ByteArrayOutputStream mainOS = new ByteArrayOutputStream(); + InputStreamSource inputStreamSource = new InputStreamSource("source", is); + OutputStreamSink outputStreamSink = new OutputStreamSink("mainSink", mainOS); + + Vnc_3_3_Hello hello = new Vnc_3_3_Hello("hello"); + + Pipeline pipeline = new PipelineImpl("test"); + + pipeline.addAndLink(inputStreamSource, hello, outputStreamSink); + pipeline.add(new OutputStreamSink("initSink", initOS)); + + pipeline.link("hello >" + OneTimeSwitch.OTOUT, "initSink"); + + pipeline.runMainLoop("source", STDOUT, false, false); + + String initOut = new String(initOS.toByteArray(), RfbConstants.US_ASCII_CHARSET); + String mainOut = new String(mainOS.toByteArray(), RfbConstants.US_ASCII_CHARSET); + + if (!"RFB 003.003\n".equals(initOut)) + System.err.println("Unexpected value for hello response: \"" + initOut + "\"."); + + if (!"test".equals(mainOut)) + System.err.println("Unexpected value for main data: \"" + mainOut + "\"."); + + } +} diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt new file mode 100644 index 00000000000..dd4168373b1 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/README.txt @@ -0,0 +1,32 @@ +// 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. + +To debug RDP sessions with Network Monitor or Wireshark, you need to +configure RDP server with custom private key. For Network Monitor +Decrypt Expert, you also will need to downgrade RDP server TLS protocol +to version 1.0. + +File dev-rdp-config.bat contains instructions to configure RDP to use custom +key, open firewall, disable NLA, downgrade TLS, and start RDP service. + +File rdp.pfx contains custom private key (password: test) for use with +rdp-config.bat and Network Monitor Decrypt Expert. If you will generate +your own key, you will need to alter rpd-file.bat to use it +fingerprints. + +File rdp-key.pem contains private key in PEM format for use with +Wireshark. 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 new file mode 100644 index 00000000000..14a7bbd0f0a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/dev-rdp-config.bat @@ -0,0 +1,126 @@ +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +rem +rem Configure and start RDP service. +rem Configure RPD service to use custom key instead of autogenerated for Wireshark and Network Monitor Decrypt Expert. +rem rdp.pfx is necessary because it fingerprints are hardcoded in this script. +rem + +rem Turn off firewall + +netsh advfirewall firewall set rule group="Remote Desktop" new enable=yes + +rem Enable TS connections +rem +rem Windows Registry Editor Version 5.00 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server] +rem "AllowTSConnections"=dword:00000001 +rem "fDenyTSConnections"=dword:00000000 + +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "AllowTSConnections" /t REG_DWORD /d 1 /f +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server" /v "fDenyTSConnections" /t REG_DWORD /d 0 /f + +rem Disable RDP NLA + +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v UserAuthentication /t REG_DWORD /d 0 /f + +rem Enable TS service + +sc config TermService start=auto + +rem Certificate Generation + +rem Make self-signed certificate + +rem makecert -r -pe -n "CN=%COMPUTERNAME%" -eku 1.3.6.1.5.5.7.3.1 -ss my -sr LocalMachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 + +rem Import certificate + +certutil -p test -importPFX "Remote Desktop" rdp.pfx + +rem Configure RDP server to use certificate: + +rem Windows Registry Editor Version 5.00 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp] +rem "SSLCertificateSHA1Hash"=hex:c1,70,84,70,bc,56,42,0a,bb,f4,35,35,ba,a6,09,b0,4e,98,4a,47 +reg add "HKLM\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" /v "SSLCertificateSHA1Hash" /t REG_HEX /d "" /f + +rem Grant permissions on certificate for everyone + +rem certutil -repairstore My "bcb40fb84ac891bd41068fe686864559" D:PAI(A;;GA;;;BA)(A;;GA;;;SY)(A;;GR;;;NS) +certutil -repairstore "Remote Desktop" "bcb40fb84ac891bd41068fe686864559" D:PAI(A;;GA;;;BA)(A;;GA;;;SY)(A;;GR;;;NS) + +rem confirm with + +rem certutil -store -v My +certutil -store -v "Remote Desktop" + +rem Disable TLS 1.1 (for Network Monitor Decrypt Expert) +rem +rem Windows Registry Editor Version 5.00 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client] +rem "Enabled"=dword:00000000 +rem "DisabledByDefault"=dword:00000001 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server] +rem "Enabled"=dword:00000000 +rem "DisabledByDefault"=dword:00000001 + +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" /v "Enabled" /t REG_DWORD /d 0 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Client" /v "DisabledByDefault" /t REG_DWORD /d 1 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" /v "Enabled" /t REG_DWORD /d 0 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.1\Server" /v "DisabledByDefault" /t REG_DWORD /d 1 /f + + +rem Disable TLS 1.2 (for Network Monitor Decrypt Expert) +rem +rem Windows Registry Editor Version 5.00 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client] +rem "Enabled"=dword:00000000 +rem "DisabledByDefault"=dword:00000001 +rem +rem [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server] +rem "Enabled"=dword:00000000 +rem "DisabledByDefault"=dword:00000001 + +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client" /v "Enabled" /t REG_DWORD /d 0 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client" /v "DisabledByDefault" /t REG_DWORD /d 1 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" /v "Enabled" /t REG_DWORD /d 0 /f +reg add "HKLM\System\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server" /v "DisabledByDefault" /t REG_DWORD /d 1 /f + +rem Start TS service + +net start Termservice + + +rem For Network Monitor Decrypt Expert. + +rem Install .Net 3.5 + +rem dism /online /enable-feature /featurename:NetFx3ServerFeatures +rem dism /online /enable-feature /featurename:NetFx3 + +rem PS. +rem Don't forget to set Windows profile as active in Network Monitor, so SSL traffic branch will appear under +rem svnchost.exe, so you will be able to decrypt it (don't forget to save and reopen captured traffic to file first). +rem + diff --git a/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem new file mode 100644 index 00000000000..cd050cd475a --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/test/doc/rdp-key.pem @@ -0,0 +1,23 @@ +Bag Attributes + Microsoft Local Key set: + localKeyID: 01 00 00 00 + friendlyName: 8fcf718d-921f-4bfc-9ae4-f63e9c66b6c7 + Microsoft CSP Name: Microsoft RSA SChannel Cryptographic Provider +Key Attributes + X509v3 Key Usage: 10 +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKrmMXjeoXRn6UFf +Hmw2HOnT/mEeSWQANzquJnKDBORIjD2rxL3h5FQ/DQUF4gm5JvBll8uWpDX11mVm +LlAZ2kA8KuJ2JuYEvu/GwuDyrP4D1sOiCwIjJnvmg5DjLB9sll5ohMbjMtiSFm5L +/YJNXop/pGJucvVzL4t0ZJ1zT2lZAgMBAAECgYAN/OJeuyyQeD+iXweaVTS5OzJ7 +PrBEgM03pQpp9zXx/P6LJUe1c2UUM8bvVGoJ+eW2HNkES/oSN2MLEKAVl7aCLWTe +7Ejc3JIRB7ZRdNt89w9XvxuRSn87pO082ciMsLvEqqDYahy3BxgI0J/GKbo28Zme +Z9f9QNCZ8TzbXJbDmwJBANVpBSfi09n5fUy3nGurGxE3chBnyik+Rft63fZp9eiD +lU5Q4l22+ZUTBChJUtLHztihcb4a4RQX6B4nH5Y1RtMCQQDNAVBKe2VfnFeEoIX7 +ooRnIKIVMxW08GENuJz64otshfH6jRaLL4E/QJLIpoNRFqafyuMkP5x8oZ3uvV1+ +nsujAkAd0Xez9ACP00lLn9gOPzEf/bRFUIsxqg7TLX64AGQoocIJ2ElYuMk0qByL +mHsnEl33bM9ctZq/WPvIwsSqEzWbAkAcb/k2S8W1LJfLUwUi8dlSAOna7Pou3kVo +RNqpxrE2faIicl3VMuLH5mo2ITsIDY9RjTBS/+vyMe0Zh/UnMlnnAkAAppLiJ15o +L3JlVbGRN+4kCP2HVtVRVIl3OlBoVSJZ5qe+s7HowTGurU/iYr1kmWd/C5sU0KPB +evwz8pdL08vr +-----END PRIVATE KEY----- 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 new file mode 100644 index 00000000000..cba01fd6a49 --- /dev/null +++ b/services/console-proxy-rdp/rdpconsole/src/test/java/rdpclient/MockServerTest.java @@ -0,0 +1,189 @@ +// 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 static rdpclient.MockServer.Packet.PacketType.CLIENT; +import static rdpclient.MockServer.Packet.PacketType.SERVER; +import static rdpclient.MockServer.Packet.PacketType.UPGRADE_TO_SSL; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +import junit.framework.TestCase; +import rdpclient.MockServer.Packet; + +public class MockServerTest extends TestCase { + + public void testIsMockServerCanRespond() throws Exception { + + final byte[] mockClientData = new byte[] { 0x01, 0x02, 0x03 }; + final byte[] mockServerData = new byte[] { 0x03, 0x02, 0x01 }; + + MockServer server = new MockServer(new Packet[] { new Packet("Client hello") { + { + type = CLIENT; + data = mockClientData; + } + }, new Packet("Server hello") { + { + type = SERVER; + data = mockServerData; + } + } }); + + server.start(); + + // Connect to server and send and receive mock data + + Socket socket = SocketFactory.getDefault().createSocket(); + try { + socket.connect(server.getAddress()); + + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + + // Write mock data to server + os.write(mockClientData); + + // Read data from server + byte actualData[] = new byte[mockServerData.length]; + int actualDataLength = is.read(actualData); + + // Compare mock data with actual data + assertEquals("Unexpected length of actual data read from server.", mockServerData.length, actualDataLength); + + for (int i = 0; i < actualDataLength; i++) { + assertEquals("Unexpected byte #" + i + " in response", mockServerData[i], actualData[i]); + } + + server.waitUntilShutdowned(1 * 1000 /* up to 1 second */); + + assertNull("Unexpected exception at mock server side.", server.getException()); + assertTrue("Server is not shutdowned at after conversation.", server.isShutdowned()); + + } finally { + socket.close(); + } + } + + public void testIsMockServerCanUpgradeConnectionToSsl() throws Exception { + + final byte[] mockClientData1 = new byte[] { 0x01, 0x02, 0x03 }; + final byte[] mockServerData1 = new byte[] { 0x03, 0x02, 0x01 }; + + final byte[] mockClientData2 = new byte[] { 0x02, 0x04, 0x02, 0x03 }; + final byte[] mockServerData2 = new byte[] { 0x02, 0x02, 0x01, 0x04 }; + + MockServer server = new MockServer(new Packet[] { new Packet("Client hello") { + { + type = CLIENT; + data = mockClientData1; + } + }, new Packet("Server hello") { + { + type = SERVER; + data = mockServerData1; + } + }, new Packet("Upgrade connection to SSL") { + { + type = UPGRADE_TO_SSL; + } + }, new Packet("Client data over SSL") { + { + type = CLIENT; + data = mockClientData2; + } + }, new Packet("Server data over SSL") { + { + type = SERVER; + data = mockServerData2; + } + } }); + + server.start(); + + // Connect to server and send and receive mock data + + Socket socket = SocketFactory.getDefault().createSocket(); + try { + InetSocketAddress address = server.getAddress(); + socket.connect(address); + + // Send hello data over plain connection + { + InputStream is = socket.getInputStream(); + OutputStream os = socket.getOutputStream(); + + // Write mock data to server + os.write(mockClientData1); + + // Read data from server + byte actualData[] = new byte[mockServerData1.length]; + int actualDataLength = is.read(actualData); + + // Compare mock data with actual data + assertEquals("Unexpected length of actual data read from server.", mockServerData1.length, actualDataLength); + + for (int i = 0; i < actualDataLength; i++) { + assertEquals("Unexpected byte #" + i + " in response", mockServerData1[i], actualData[i]); + } + } + + // Upgrade connection to SSL and send mock data + { + //System.setProperty("javax.net.debug", "ssl"); + + final SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(socket, address.getHostString(), address.getPort(), true); + sslSocket.setEnabledCipherSuites(sslSocket.getSupportedCipherSuites()); + sslSocket.startHandshake(); + + InputStream is = sslSocket.getInputStream(); + OutputStream os = sslSocket.getOutputStream(); + + // Write mock data to server + os.write(mockClientData2); + + // Read data from server + byte actualData[] = new byte[mockServerData2.length]; + int actualDataLength = is.read(actualData); + + // Compare mock data with actual data + assertEquals("Unexpected length of actual data read from server.", mockServerData2.length, actualDataLength); + + for (int i = 0; i < actualDataLength; i++) { + assertEquals("Unexpected byte #" + i + " in response", mockServerData2[i], actualData[i]); + } + + } + + server.waitUntilShutdowned(1 * 1000 /* up to 1 second */); + + assertNull("Unexpected exception at mock server side.", server.getException()); + assertTrue("Server is not shutdowned at after conversation.", server.isShutdowned()); + } finally { + socket.close(); + } + + } +} diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index 79550aee1bb..9c07a4494f6 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -1674,10 +1674,10 @@ INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('XenServer', '5.6 SP2', 50, 1); INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('XenServer', '6.0', 50, 1, 13); INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('XenServer', '6.0.2', 50, 1, 13); -INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('VMware', 'default', 128, 0); -INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('VMware', '4.0', 128, 0); -INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('VMware', '4.1', 128, 0); -INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('VMware', '5.0', 128, 0); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('VMware', 'default', 128, 0, 13); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('VMware', '4.0', 128, 0, 13); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('VMware', '4.1', 128, 0, 13); +INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit) VALUES ('VMware', '5.0', 128, 0, 13); INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('KVM', 'default', 50, 1); INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm', 'default', 25, 1); INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('Ovm', '2.3', 25, 1); diff --git a/setup/db/db/schema-421to430.sql b/setup/db/db/schema-421to430.sql index 1c99e3f79d0..dbfab4a5bb8 100644 --- a/setup/db/db/schema-421to430.sql +++ b/setup/db/db/schema-421to430.sql @@ -16,7 +16,7 @@ -- under the License. --; --- Schema upgrade from 4.2.0 to 4.3.0; +-- Schema upgrade from 4.2.1 to 4.3.0; --; -- Disable foreign key checking @@ -41,6 +41,8 @@ ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `power_state_update_count` INT DEFA ALTER TABLE `cloud`.`vm_instance` ADD COLUMN `power_host` bigint unsigned; ALTER TABLE `cloud`.`vm_instance` ADD CONSTRAINT `fk_vm_instance__power_host` FOREIGN KEY (`power_host`) REFERENCES `cloud`.`host`(`id`); +ALTER TABLE `cloud`.`load_balancing_rules` ADD COLUMN `lb_protocol` VARCHAR(40); + DROP TABLE IF EXISTS `cloud`.`vm_snapshot_details`; CREATE TABLE `cloud`.`vm_snapshot_details` ( `id` bigint unsigned UNIQUE NOT NULL, @@ -108,6 +110,9 @@ UPDATE `cloud`.`configuration` SET `default_value` = `value`; #Upgrade the offerings and template table to have actual remove and states ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `state` CHAR(40) NOT NULL DEFAULT 'Active' COMMENT 'state for disk offering'; +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)'; + +ALTER TABLE `cloud`.`volumes` ADD COLUMN `hv_ss_reserve` int(32) unsigned DEFAULT NULL COMMENT 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)'; UPDATE `cloud`.`disk_offering` SET `state`='Inactive' WHERE `removed` IS NOT NULL; UPDATE `cloud`.`disk_offering` SET `removed`=NULL; @@ -461,6 +466,107 @@ CREATE VIEW `cloud`.`storage_pool_view` AS and async_job.instance_type = 'StoragePool' and async_job.job_status = 0; + +CREATE TABLE `sslcerts` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `account_id` bigint(20) unsigned NOT NULL, + `domain_id` bigint(20) unsigned NOT NULL, + `certificate` text NOT NULL, + `fingerprint` varchar(62) NOT NULL, + `key` text NOT NULL, + `chain` text, + `password` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_sslcert__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_sslcert__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain` (`id`) ON DELETE CASCADE +); + +CREATE TABLE `load_balancer_cert_map` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `uuid` varchar(40) DEFAULT NULL, + `load_balancer_id` bigint(20) unsigned NOT NULL, + `certificate_id` bigint(20) unsigned NOT NULL, + `revoke` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + CONSTRAINT `fk_load_balancer_cert_map__certificate_id` FOREIGN KEY (`certificate_id`) REFERENCES `sslcerts` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_load_balancer_cert_map__load_balancer_id` FOREIGN KEY (`load_balancer_id`) REFERENCES `load_balancing_rules` (`id`) ON DELETE CASCADE); + +ALTER TABLE `cloud`.`host` ADD COLUMN `cpu_sockets` int(10) unsigned DEFAULT NULL COMMENT "the number of CPU sockets on the host" AFTER pod_id; +ALTER TABLE `cloud`.`physical_network_traffic_types` ADD COLUMN `hyperv_network_label` varchar(255) DEFAULT NULL COMMENT 'The network name label of the physical device dedicated to this traffic on a HyperV host'; + +DROP VIEW IF EXISTS `cloud`.`host_view`; +CREATE VIEW `cloud`.`host_view` AS + select + host.id, + host.uuid, + host.name, + host.status, + host.disconnected, + host.type, + host.private_ip_address, + host.version, + host.hypervisor_type, + host.hypervisor_version, + host.capabilities, + host.last_ping, + host.created, + host.removed, + host.resource_state, + host.mgmt_server_id, + host.cpu_sockets, + host.cpus, + host.speed, + host.ram, + cluster.id cluster_id, + cluster.uuid cluster_uuid, + cluster.name cluster_name, + cluster.cluster_type, + data_center.id data_center_id, + data_center.uuid data_center_uuid, + data_center.name data_center_name, + data_center.networktype data_center_type, + host_pod_ref.id pod_id, + host_pod_ref.uuid pod_uuid, + host_pod_ref.name pod_name, + host_tags.tag, + guest_os_category.id guest_os_category_id, + guest_os_category.uuid guest_os_category_uuid, + guest_os_category.name guest_os_category_name, + mem_caps.used_capacity memory_used_capacity, + mem_caps.reserved_capacity memory_reserved_capacity, + cpu_caps.used_capacity cpu_used_capacity, + cpu_caps.reserved_capacity cpu_reserved_capacity, + async_job.id job_id, + async_job.uuid job_uuid, + async_job.job_status job_status, + async_job.account_id job_account_id + from + `cloud`.`host` + left join + `cloud`.`cluster` ON host.cluster_id = cluster.id + left join + `cloud`.`data_center` ON host.data_center_id = data_center.id + left join + `cloud`.`host_pod_ref` ON host.pod_id = host_pod_ref.id + left join + `cloud`.`host_details` ON host.id = host_details.host_id + and host_details.name = 'guest.os.category.id' + left join + `cloud`.`guest_os_category` ON guest_os_category.id = CONVERT( host_details.value , UNSIGNED) + left join + `cloud`.`host_tags` ON host_tags.host_id = host.id + left join + `cloud`.`op_host_capacity` mem_caps ON host.id = mem_caps.host_id + and mem_caps.capacity_type = 0 + left join + `cloud`.`op_host_capacity` cpu_caps ON host.id = cpu_caps.host_id + and cpu_caps.capacity_type = 1 + left join + `cloud`.`async_job` ON async_job.instance_id = host.id + and async_job.instance_type = 'Host' + and async_job.job_status = 0; + CREATE TABLE `cloud`.`firewall_rule_details` ( `id` bigint unsigned NOT NULL auto_increment, `firewall_rule_id` bigint unsigned NOT NULL COMMENT 'Firewall rule id', @@ -487,3 +593,29 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag 'Sets the attribute for uniquemembers within a group','uniquemember',NULL,NULL,0); UPDATE `cloud`.`volumes` SET display_volume=1 where id>0; + +create table `cloud`.`monitoring_services` ( +`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id', +`uuid` varchar(40), `service` varchar(255) COMMENT 'Service name', +`process_name` varchar(255) COMMENT 'running process name', +`service_name` varchar(255) COMMENT 'exact name of the running service', +`service_path` varchar(255) COMMENT 'path of the service in system', +`pidfile` varchar(255) COMMENT 'path of the pid file of the service', +`isDefault` boolean COMMENT 'Default service', PRIMARY KEY (`id`) +); + +insert into cloud.monitoring_services(id, uuid, service, process_name, service_name, service_path, pidfile, isDefault) values(1, UUID(), 'ssh','sshd', 'ssh','/etc/init.d/ssh','/var/run/sshd.pid',true); +insert into cloud.monitoring_services(id, uuid, service, process_name, service_name, service_path, pidfile, isDefault) values(2, UUID(), 'dhcp','dnsmasq','dnsmasq','/etc/init.d/dnsmasq','/var/run/dnsmasq/dnsmasq.pid',false); +insert into cloud.monitoring_services(id, uuid, service, process_name, service_name, service_path, pidfile, isDefault) values(3, UUID(), 'loadbalancing','haproxy','haproxy','/etc/init.d/haproxy','/var/run/haproxy.pid',false); +insert into cloud.monitoring_services(id, uuid, service, process_name, service_name, service_path, pidfile, isDefault) values(4, UUID(), 'webserver','apache2','apache2','/etc/init.d/apache2','/var/run/apache2.pid', true); + +ALTER TABLE `cloud`.`service_offering` CHANGE COLUMN `cpu` `cpu` INT(10) UNSIGNED NULL COMMENT '# of cores' , CHANGE COLUMN `speed` `speed` INT(10) UNSIGNED NULL COMMENT 'speed per core in mhz' , CHANGE COLUMN `ram_size` `ram_size` BIGINT(20) UNSIGNED NULL ; + +CREATE TABLE `cloud`.`usage_event_details` ( + `id` bigint unsigned NOT NULL auto_increment, + `usage_event_id` bigint unsigned NOT NULL COMMENT 'usage event id', + `name` varchar(255) NOT NULL, + `value` varchar(1024) NOT NULL, + PRIMARY KEY (`id`), + CONSTRAINT `fk_usage_event_details__usage_event_id` FOREIGN KEY `fk_usage_event_details__usage_event_id`(`usage_event_id`) REFERENCES `usage_event`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/systemvm/patches/debian/config/opt/cloud/bin/monitor_service.sh b/systemvm/patches/debian/config/opt/cloud/bin/monitor_service.sh new file mode 100755 index 00000000000..a2b789cac8f --- /dev/null +++ b/systemvm/patches/debian/config/opt/cloud/bin/monitor_service.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +source /root/func.sh + +lock="biglock" +locked=$(getLockFile $lock) +if [ "$locked" != "1" ] +then + exit 1 +fi + +set -x +usage() { + printf "Usage: %s: -c config string \n" $(basename $0) >&2 +} + +configFile='/etc/monitor.conf' + +create_config() { +services=$1; +services_list=$(echo $services | cut -d, -f1- --output-delimiter=" "); + +echo "#Monitor services config" >$configFile + +for s in $services_list +do +service=$(echo $s | cut -d: -f1); +processname=$(echo $s | cut -d: -f2); +service_name=$(echo $s | cut -d: -f3); +pidfile=$(echo $s | cut -d: -f4); + +echo $service >> $configFile; +echo $processname >> $configFile +echo $service_name >> $configFile +echo $pidfile >> $configFile + + + +done + +} + +config=$2 + +#delete cron job before updating config file +crontab -l | grep -v monitorServices.py | crontab - + +create_config $config + +#add cron job +(crontab -l ; echo "*/3 * * * * python /root/monitorServices.py") | crontab - + + +unlock_exit 0 $lock $locked + diff --git a/systemvm/patches/debian/config/root/monitorServices.py b/systemvm/patches/debian/config/root/monitorServices.py new file mode 100755 index 00000000000..f0c2afe83a6 --- /dev/null +++ b/systemvm/patches/debian/config/root/monitorServices.py @@ -0,0 +1,263 @@ +#!/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. + + + + +__author__ = 'jayapalreddy' + +from ConfigParser import SafeConfigParser +from subprocess import * +from os import path +import time + +monitor_log='/var/log/monitor.log' +class StatusCodes: + SUCCESS = 0 + FAILED = 1 + INVALID_INP = 2 + RUNNING = 3 + STOPPED = 4 + STARTING = 5 + +class log: + INFO = 'INFO' + ALERT = 'ALERT' + CRIT = 'CRIT' + NOTIF = 'NOTIF' + + + + +def getConfig( config_file_path = "/etc/monitor.conf" ): + process_dict = {} + parser = SafeConfigParser() + parser.read( config_file_path ) + + #print 'Read values:\n' + + for section in parser.sections(): + # print section + process_dict[section] = {} + + for name, value in parser.items(section): + process_dict[section][name] = value +# print ' %s = %r' % (name, value) + + return process_dict + +def printd (msg): + return 0 + print msg + +def raisealert(severity, msg, process_name=None): + #timeStr=str(time.ctime()) + if process_name is not None: + log = '['+severity +']'+" " + '['+process_name+']' + " " + msg +"\n" + else: + log = '['+severity+']' + " " + msg +"\n" + + msg = 'logger -t monit '+ log + pout = Popen(msg, shell=True, stdout=PIPE) + + + #f= open(monitor_log,'r+') + #f.seek(0, 2) + #f.write(str(log)) + #f.close() + + +def isPidMatchPidFile(pidfile, pids): + + if pids is None or isinstance(pids,list) != True or len(pids) == 0: + print "Invalid Arguments" + return StatusCodes.FAILED + if not path.isfile(pidfile): + #It seems there is no pid file for this service + printd("The pid file "+pidfile+" is not there for this process") + return StatusCodes.FAILED + + fd=None + try: + fd = open(pidfile,'r') + except: + printd("pid file: "+ pidfile +" open failed") + return StatusCodes.FAILED + + + inp = fd.read() + printd("file content "+str(inp)) + printd(pids) + tocheck_pid = inp.strip() + for item in pids: + if str(tocheck_pid) == item.strip(): + printd("pid file matched") + return StatusCodes.SUCCESS + + fd.close() + return StatusCodes.FAILED + + + +def checkProcessStatus( process ): + process_name = process.get('processname') + service_name = process.get('servicename') + pidfile = process.get('pidfile') + #temp_out = None + restartFailed=0 + pidFileMatched=1 + cmd='' + if process_name is None: + print "\n Invalid Process Name" + return StatusCodes.INVALID_INP + else: + msg="checking the process " + process_name + printd(msg) + cmd = 'pidof ' + process_name + printd(cmd) + #cmd = 'service ' + process_name + ' status' + pout = Popen(cmd, shell=True, stdout=PIPE) + exitStatus = pout.wait() + temp_out = pout.communicate()[0] + + #check there is only one pid or not + if exitStatus == 0: + msg="pids: " +temp_out; + printd(msg) + pids = temp_out.split(' ') + + #there is more than one process so match the pid file + #if not matched set pidFileMatched=0 + printd("Checking pid file") + if isPidMatchPidFile(pidfile, pids) == StatusCodes.SUCCESS: + pidFileMatched = 1; + else: + pidFileMatched = 0; + + printd(pidFileMatched) + if exitStatus == 0 and pidFileMatched == 1: + printd("The process is running ....") + return StatusCodes.RUNNING + else: + printd('exit status:'+str(exitStatus)) + msg="The process " + process_name +" is not running trying recover " + printd(msg) + #Retry the process state for few seconds + for i in range(1,10): + pout = Popen(cmd, shell=True, stdout=PIPE) + exitStatus = pout.wait() + temp_out = pout.communicate()[0] + + if i < 5: # this is just for trying few more times + if exitStatus == 0: + pids = temp_out.split(' ') + + if isPidMatchPidFile(pidfile, pids) == StatusCodes.SUCCESS: + pidFileMatched = 1; + printd("pid file is matched ...") + raisealert(log.ALERT, "The process detected as running", process_name) + break + else: + printd("pid file is not matched ...") + pidFileMatched = 0; + continue + time.sleep(1) + else: + msg="The process " +process_name+" is not running trying recover " + raisealert(log.INFO,process_name,msg) + + if service_name == 'apache2': + # Killing apache2 process with this the main service will not start + for pid in pids: + cmd = 'kill -9 '+pid; + printd(cmd) + Popen(cmd, shell=True, stdout=PIPE) + + cmd = 'service ' + service_name + ' restart' + try: + time.sleep(1) + return_val= check_call(cmd , shell=True) + except CalledProcessError: + restartFailed=1 + msg="service "+ process_name +" restart failed" + printd(msg) + continue + + if return_val == 0: + printd("The process" + process_name +" recovered successfully ") + msg="The process " +process_name+" is recovered successfully " + raisealert(log.INFO,process_name,msg) + + break; + else: + #retry restarting the process for few tries + printd("process restart failing trying again ....") + restartFailed=1 + time.sleep(1) + continue + #for end here + + if restartFailed == 1: + msg="The process %s recover failed ", process_name; + raisealert(log.ALERT,process_name,msg) + + printd("Restart failed after number of retries") + return StatusCodes.STOPPED + + return StatusCodes.RUNNING + +def raiseAlert( process_name ): + print "process name %s is raised "%process_name + +def monitProcess( processes_info ): + if len( processes_info ) == 0: + print "Invalid Input" + return StatusCodes.INVALID_INP + for process,properties in processes_info.items(): + if checkProcessStatus( properties) != StatusCodes.RUNNING: + print "\n Process %s is not Running"%process + + +def main(): + ''' + Step1 : Get Config + ''' + + printd("monitoring started") + temp_dict = getConfig() + + ''' + Step2: Get Previous Run Log + ''' + + ''' + Step3: Monitor and Raise Alert + ''' + #raisealert(log.INFO, 'Monit started') + monitProcess( temp_dict ) + + +if __name__ == "__main__": + main() + + + + + + + diff --git a/systemvm/patches/debian/vpn/opt/cloud/bin/vpn_l2tp.sh b/systemvm/patches/debian/vpn/opt/cloud/bin/vpn_l2tp.sh index 86148a34624..5afe0096d9a 100755 --- a/systemvm/patches/debian/vpn/opt/cloud/bin/vpn_l2tp.sh +++ b/systemvm/patches/debian/vpn/opt/cloud/bin/vpn_l2tp.sh @@ -150,8 +150,12 @@ add_l2tp_ipsec_user() { local u=$1 local passwd=$2 - remove_l2tp_ipsec_user $u - echo "$u * $passwd *" >> /etc/ppp/chap-secrets + uptodate=$(grep "^$u \* $passwd \*$" /etc/ppp/chap-secrets) + if [ "$uptodate" == "" ] + then + remove_l2tp_ipsec_user $u + echo "$u * $passwd *" >> /etc/ppp/chap-secrets + fi } rflag= diff --git a/test/integration/component/maint/test_egress_rules_host_maintenance.py b/test/integration/component/maint/test_egress_rules_host_maintenance.py index 6f0f768d37c..2b817879c4b 100644 --- a/test/integration/component/maint/test_egress_rules_host_maintenance.py +++ b/test/integration/component/maint/test_egress_rules_host_maintenance.py @@ -22,7 +22,7 @@ import marvin from nose.plugins.attrib import attr from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/maint/test_high_availability.py b/test/integration/component/maint/test_high_availability.py index 7b0f78e2446..6ada659cd4f 100644 --- a/test/integration/component/maint/test_high_availability.py +++ b/test/integration/component/maint/test_high_availability.py @@ -26,7 +26,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/maint/test_vpc_host_maintenance.py b/test/integration/component/maint/test_vpc_host_maintenance.py index 0946cebdb7e..57dfb4be8d0 100644 --- a/test/integration/component/maint/test_vpc_host_maintenance.py +++ b/test/integration/component/maint/test_vpc_host_maintenance.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_accounts.py b/test/integration/component/test_accounts.py index 4c73c3acc84..650a595936e 100644 --- a/test/integration/component/test_accounts.py +++ b/test/integration/component/test_accounts.py @@ -22,7 +22,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from nose.plugins.attrib import attr from marvin.cloudstackException import cloudstackAPIException diff --git a/test/integration/component/test_affinity_groups.py b/test/integration/component/test_affinity_groups.py index ae53e399df9..7e4fabe5475 100644 --- a/test/integration/component/test_affinity_groups.py +++ b/test/integration/component/test_affinity_groups.py @@ -20,7 +20,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from nose.plugins.attrib import attr class Services: diff --git a/test/integration/component/test_asa1000v_fw.py b/test/integration/component/test_asa1000v_fw.py index 0d8cad09802..c8a11ab1701 100644 --- a/test/integration/component/test_asa1000v_fw.py +++ b/test/integration/component/test_asa1000v_fw.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_baremetal.py b/test/integration/component/test_baremetal.py index c6a877eca6e..2439d0d2132 100644 --- a/test/integration/component/test_baremetal.py +++ b/test/integration/component/test_baremetal.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/cpu_limits/test_domain_limits.py b/test/integration/component/test_cpu_domain_limits.py similarity index 100% rename from test/integration/component/cpu_limits/test_domain_limits.py rename to test/integration/component/test_cpu_domain_limits.py diff --git a/test/integration/component/cpu_limits/test_cpu_limits.py b/test/integration/component/test_cpu_limits.py similarity index 100% rename from test/integration/component/cpu_limits/test_cpu_limits.py rename to test/integration/component/test_cpu_limits.py diff --git a/test/integration/component/cpu_limits/test_maximum_limits.py b/test/integration/component/test_cpu_max_limits.py similarity index 100% rename from test/integration/component/cpu_limits/test_maximum_limits.py rename to test/integration/component/test_cpu_max_limits.py diff --git a/test/integration/component/cpu_limits/test_project_limits.py b/test/integration/component/test_cpu_project_limits.py similarity index 100% rename from test/integration/component/cpu_limits/test_project_limits.py rename to test/integration/component/test_cpu_project_limits.py diff --git a/test/integration/component/test_deploy_vm_userdata_reg.py b/test/integration/component/test_deploy_vm_userdata_reg.py index ca9179d77d2..b282a860816 100755 --- a/test/integration/component/test_deploy_vm_userdata_reg.py +++ b/test/integration/component/test_deploy_vm_userdata_reg.py @@ -23,7 +23,7 @@ from marvin.integration.lib.base import * from marvin.integration.lib.utils import * from marvin.integration.lib.common import * from nose.plugins.attrib import attr -from marvin import remoteSSHClient +from marvin.sshClient import SshClient import unittest import random import string diff --git a/test/integration/component/test_egress_fw_rules.py b/test/integration/component/test_egress_fw_rules.py index 253cc46cab9..0aed95742ec 100644 --- a/test/integration/component/test_egress_fw_rules.py +++ b/test/integration/component/test_egress_fw_rules.py @@ -39,7 +39,7 @@ from marvin.integration.lib.common import (get_domain, from marvin.cloudstackAPI.createEgressFirewallRule import createEgressFirewallRuleCmd from marvin.cloudstackAPI.deleteEgressFirewallRule import deleteEgressFirewallRuleCmd -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import time class Services: @@ -270,7 +270,7 @@ class TestEgressFWRules(cloudstackTestCase): fd.write(expect_script) fd.close() - ssh = remoteSSHClient(host=sourceip, + ssh = SshClient(host=sourceip, port=22, user='root', passwd=self.services["host_password"]) diff --git a/test/integration/component/test_egress_rules.py b/test/integration/component/test_egress_rules.py index f8e8e790248..34995ffd1a0 100644 --- a/test/integration/component/test_egress_rules.py +++ b/test/integration/component/test_egress_rules.py @@ -22,7 +22,7 @@ import marvin from nose.plugins.attrib import attr from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/test_eip_elb.py b/test/integration/component/test_eip_elb.py index 42a5148762e..d639d82d9f5 100644 --- a/test/integration/component/test_eip_elb.py +++ b/test/integration/component/test_eip_elb.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime @@ -314,7 +314,7 @@ class TestEIP(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -458,7 +458,7 @@ class TestEIP(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -657,7 +657,7 @@ class TestEIP(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -871,7 +871,7 @@ class TestEIP(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -1138,7 +1138,7 @@ class TestELB(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -1287,7 +1287,7 @@ class TestELB(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -1366,7 +1366,7 @@ class TestELB(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -1512,7 +1512,7 @@ class TestELB(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], diff --git a/test/integration/component/test_explicit_dedication.py b/test/integration/component/test_explicit_dedication.py index bd97551b5c6..7aefc21a1fb 100644 --- a/test/integration/component/test_explicit_dedication.py +++ b/test/integration/component/test_explicit_dedication.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/test_haproxy.py b/test/integration/component/test_haproxy.py index 799cfa3e925..c7340126b36 100644 --- a/test/integration/component/test_haproxy.py +++ b/test/integration/component/test_haproxy.py @@ -38,7 +38,7 @@ from marvin.integration.lib.common import (get_domain, random_gen ) from marvin.cloudstackAPI import createLBStickinessPolicy -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient class Services: @@ -383,7 +383,7 @@ class TestHAProxyStickyness(cloudstackTestCase): # If Round Robin Algorithm is chosen, # each ssh command should alternate between VMs - ssh_1 = remoteSSHClient( + ssh_1 = SshClient( ip_addr, 22, self.services["natrule"]["username"], diff --git a/test/integration/component/test_implicit_planner.py b/test/integration/component/test_implicit_planner.py index f233ba9791e..2b656d9f2c6 100644 --- a/test/integration/component/test_implicit_planner.py +++ b/test/integration/component/test_implicit_planner.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/memory_limits/test_memory_limits.py b/test/integration/component/test_memory_limits.py similarity index 95% rename from test/integration/component/memory_limits/test_memory_limits.py rename to test/integration/component/test_memory_limits.py index 18eab4c2383..7da85a5854f 100644 --- a/test/integration/component/memory_limits/test_memory_limits.py +++ b/test/integration/component/test_memory_limits.py @@ -55,7 +55,7 @@ class Services: "displaytext": "Tiny Instance", "cpunumber": 1, "cpuspeed": 100, # in MHz - "memory": 5120, # In MBs + "memory": 2048, # In MBs }, "virtual_machine": { "displayname": "TestVM", @@ -177,10 +177,10 @@ class TestMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_01_stop_start_instance(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 1. Create compute offering with specified RAM & Deploy VM as root admin # 2 .List Resource count for the root admin Memory usage # 3. Stop and start instance, resource count should list properly. @@ -229,10 +229,10 @@ class TestMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_02_migrate_instance(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 1. Create compute offering with specified RAM & Deploy VM as root admin # 2. List Resource count for the root admin Memory usage # 3. Migrate vm, resource count should list properly. @@ -268,10 +268,10 @@ class TestMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_03_delete_instance(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified GB RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM as root admin + # 1. Create compute offering with specified RAM & Deploy VM as root admin # 2. List Resource count for the root admin Memory usage # 3. Delete instance, resource count should be 0 after delete operation. @@ -306,11 +306,11 @@ class TestMemoryLimits(cloudstackTestCase): return @attr(tags=["advanced", "advancedns","simulator"]) - def test_04_deploy_multiple_vm_with_5gb_ram(self): - """Test Deploy multiple VM with 5 GB RAM & verify the usage""" + def test_04_deploy_multiple_vm(self): + """Test Deploy multiple VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM + # 1. Create compute offering with specified RAM # 2. Deploy multiple VMs with this service offering # 3. List Resource count for the root admin Memory usage # 4. Memory usage should list properly @@ -550,10 +550,10 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_02_migrate_instance(self): - """Test Deploy VM with 5 GB memory & verify the usage""" + """Test Deploy VM with specified memory & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB memory in child domains of root domain & Deploy VM + # 1. Create compute offering with specified memory in child domains of root domain & Deploy VM # 2. List Resource count # 3. Migrate instance to another host # 4. Resource count should list properly. @@ -608,10 +608,10 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_03_delete_instance(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM in child domains of root domain & Deploy VM + # 1. Create compute offering with specified RAM in child domains of root domain & Deploy VM # 2. List Resource count for the Memory usage # 3. Delete instance # 4. Resource count should list as 0 @@ -663,16 +663,16 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) @attr(configuration='max.account.memory') def test_04_deploy_multiple_vm(self): - """Test Deploy multiple VM with 5 GB memory & verify the usage""" - #keep the configuration value - max.account.memory = 20480 + """Test Deploy multiple VM with 2 GB memory & verify the usage""" + #keep the configuration value - max.account.memory = 8192 (maximum 4 instances per account with 2 GB RAM) # Validate the following - # 1. Create compute offering with 5 GB RAM + # 1. Create compute offering with 2 GB RAM # 2. Deploy multiple VMs with this service offering in child domains of root domain # 3. List Resource count for the root admin Memory usage # 4. Memory usage should list properly - self.debug("Creating service offering with 5 GB RAM") + self.debug("Creating service offering with 2 GB RAM") self.service_offering = ServiceOffering.create( self.apiclient, self.services["service_offering"] @@ -695,8 +695,8 @@ class TestDomainMemoryLimitsConfiguration(cloudstackTestCase): domainid = self.domain.id ) - if memory_account_gc[0].max != 20480: - self.skipTest("This test case requires configuration value max.account.memory to be 20480") + if memory_account_gc[0].max != 8192: + self.skipTest("This test case requires configuration value max.account.memory to be 8192") api_client = self.testClient.createUserApiClient( UserName=self.account.name, diff --git a/test/integration/component/memory_limits/test_domain_limits.py b/test/integration/component/test_mm_domain_limits.py similarity index 95% rename from test/integration/component/memory_limits/test_domain_limits.py rename to test/integration/component/test_mm_domain_limits.py index d87db84a470..c8560873560 100644 --- a/test/integration/component/memory_limits/test_domain_limits.py +++ b/test/integration/component/test_mm_domain_limits.py @@ -56,7 +56,7 @@ class Services: "displaytext": "Tiny Instance", "cpunumber": 1, "cpuspeed": 100, # in MHz - "memory": 5120, # In MBs + "memory": 2048, # In MBs }, "virtual_machine": { "displayname": "TestVM", @@ -183,7 +183,7 @@ class TestDomainMemoryLimits(cloudstackTestCase): Resources.updateLimit(self.apiclient, resourcetype=9, - max=15360, + max=6144, account=self.child_do_admin_1.name, domainid=self.child_do_admin_1.domainid) @@ -203,17 +203,17 @@ class TestDomainMemoryLimits(cloudstackTestCase): Resources.updateLimit(self.apiclient, resourcetype=9, - max=15360, + max=6144, account=self.child_do_admin_2.name, domainid=self.child_do_admin_2.domainid) return @attr(tags=["advanced", "advancedns","simulator"]) def test_01_change_service_offering(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 1. Create compute offering with specified RAM & Deploy VM in the created domain # 2. List Resource count for the root admin Memory usage # 3. Upgrade and downgrade service offering # 4. Resource count should list properly for the domain @@ -228,7 +228,7 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.domain = domain #Resetting memory count in service offering - self.services["service_offering"]["memory"] = 5120 + self.services["service_offering"]["memory"] = 2048 self.debug("Creating an instance with service offering: %s" % self.service_offering.name) @@ -266,24 +266,24 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.assertEqual(resource_count_after_stop, expected_resource_count, "Resource count should be same after stopping the instance") - self.debug("Creating service offering with 7 GB RAM") - self.services["service_offering"]["memory"] = 7168 - self.service_offering_7gb = ServiceOffering.create( + self.debug("Creating service offering with 5 GB RAM") + self.services["service_offering"]["memory"] = 5120 + self.service_offering_5gb = ServiceOffering.create( self.apiclient, self.services["service_offering"] ) # Adding to cleanup list after execution - self.cleanup.append(self.service_offering_7gb) + self.cleanup.append(self.service_offering_5gb) self.debug( "Upgrade service offering of instance %s from %s to %s" % (vm.name, self.service_offering.name, - self.service_offering_7gb.name)) + self.service_offering_5gb.name)) try: vm.change_service_offering(self.apiclient, - serviceOfferingId=self.service_offering_7gb.id) + serviceOfferingId=self.service_offering_5gb.id) except Exception as e: self.fail("Failed to change service offering of vm %s - %s" % (vm.name, e)) @@ -305,7 +305,7 @@ class TestDomainMemoryLimits(cloudstackTestCase): self.debug( "Down grade service offering of instance %s from %s to %s" % (vm.name, - self.service_offering_7gb.name, + self.service_offering_5gb.name, self.service_offering.name)) try: @@ -349,15 +349,15 @@ class TestDomainMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_02_migrate_vm(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 1. Create compute offering with specified RAM & Deploy VM in the created domain # 2. List Resource count for the root admin Memory usage # 3. Migrate vm to another host, resource count should list properly. #Resetting memory count in service offering - self.services["service_offering"]["memory"] = 5120 + self.services["service_offering"]["memory"] = 2048 self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -408,15 +408,15 @@ class TestDomainMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_03_delete_vm(self): - """Test Deploy VM with 5 GB RAM & verify the usage""" + """Test Deploy VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM & Deploy VM in the created domain + # 1. Create compute offering with specified RAM & Deploy VM in the created domain # 2. List Resource count for the root admin Memory usage # 3. Delete vm, resource count should list as 0 after delete operation. # Resetting the memory count of service offering - self.services["service_offering"]["memory"] = 5120 + self.services["service_offering"]["memory"] = 2048 self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -467,16 +467,16 @@ class TestDomainMemoryLimits(cloudstackTestCase): @attr(tags=["advanced", "advancedns","simulator"]) def test_04_deploy_multiple_vm(self): - """Test Deploy multiple VM with 5 GB RAM & verify the usage""" + """Test Deploy multiple VM with specified RAM & verify the usage""" # Validate the following - # 1. Create compute offering with 5 GB RAM + # 1. Create compute offering with specified RAM # 2. Deploy multiple VMs with this service offering # 3. List Resource count for the root admin Memory usage # 4. Memory usage should list properly # Resetting the memory count of service offering - self.services["service_offering"]["memory"] = 5120 + self.services["service_offering"]["memory"] = 2048 self.debug("Setting up account and domain hierarchy") self.setupAccounts() @@ -607,7 +607,7 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.domain.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=10240, + max=4096, account=self.parentd_admin.name, domainid=self.parentd_admin.domainid) self.debug("Creating a sub-domain under: %s" % self.parent_domain.name) @@ -631,14 +631,14 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.cdomain_1.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=5120, + max=2048, domainid=self.cadmin_1.domainid) self.debug("Updating the Memory resource count for account: %s" % self.cadmin_1.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=2148, + max=2048, account=self.cadmin_1.name, domainid=self.cadmin_1.domainid) @@ -653,14 +653,14 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): self.cdomain_2.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=5120, + max=2048, domainid=self.cadmin_2.domainid) self.debug("Updating the Memory resource count for domain: %s" % self.cadmin_2.name) Resources.updateLimit(self.apiclient, resourcetype=9, - max=2148, + max=2048, account=self.cadmin_2.name, domainid=self.cadmin_2.domainid) @@ -684,9 +684,8 @@ class TestMultipleChildDomainsMemory(cloudstackTestCase): """Test memory limits with multiple child domains""" # Validate the following - # 1. Create Domain1 with 10 GB RAM and 2 child domains with 5 GB - # each.Assign 2 GB for Domain1 admin1 & Domain1 User1 .Assign 2 - # GB for Domain2 admin1 & Domain2 User1 + # 1. Create Domain1 with 4 GB RAM and 2 child domains with 2 GB + # each. # 2. Deploy VM's by Domain1 admin1/user1/ Domain2 user1/Admin1 account # and verify the resource updates # 3. Deploy VM by admin account after reaching max parent domain limit diff --git a/test/integration/component/memory_limits/test_maximum_limits.py b/test/integration/component/test_mm_max_limits.py similarity index 100% rename from test/integration/component/memory_limits/test_maximum_limits.py rename to test/integration/component/test_mm_max_limits.py diff --git a/test/integration/component/memory_limits/test_project_limits.py b/test/integration/component/test_mm_project_limits.py similarity index 99% rename from test/integration/component/memory_limits/test_project_limits.py rename to test/integration/component/test_mm_project_limits.py index 1c0ed92d89e..ffeb20a5740 100644 --- a/test/integration/component/memory_limits/test_project_limits.py +++ b/test/integration/component/test_mm_project_limits.py @@ -55,7 +55,7 @@ class Services: "displaytext": "Tiny Instance", "cpunumber": 1, "cpuspeed": 100, # in MHz - "memory": 5120, # In MBs + "memory": 2048, # In MBs }, "virtual_machine": { "displayname": "TestVM", diff --git a/test/integration/component/test_netscaler_configs.py b/test/integration/component/test_netscaler_configs.py index c10f6882334..98c47485e34 100644 --- a/test/integration/component/test_netscaler_configs.py +++ b/test/integration/component/test_netscaler_configs.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime @@ -1745,7 +1745,7 @@ class TestGuestNetworkWithNetScaler(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1891,7 +1891,7 @@ class TestGuestNetworkWithNetScaler(cloudstackTestCase): listall=True ) nw = network_list[0] - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2051,7 +2051,7 @@ class TestGuestNetworkWithNetScaler(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2249,7 +2249,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2334,7 +2334,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): listall=True ) nw = network_list[0] - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2396,7 +2396,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): listall=True ) nw = network_list[0] - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2458,7 +2458,7 @@ class TestGuestNetworkShutDown(cloudstackTestCase): listall=True ) nw = network_list[0] - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], diff --git a/test/integration/component/test_netscaler_lb.py b/test/integration/component/test_netscaler_lb.py index 4b380e904e1..5a8d6a4f6b5 100644 --- a/test/integration/component/test_netscaler_lb.py +++ b/test/integration/component/test_netscaler_lb.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime @@ -1228,7 +1228,7 @@ class TestAddMultipleVmsLb(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], 22, self.services["netscaler"]["username"], @@ -2104,7 +2104,7 @@ class TestLoadBalancingRule(cloudstackTestCase): self.debug("SSH into Netscaler to verify other resources are deleted") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2430,7 +2430,7 @@ class TestVmWithLb(cloudstackTestCase): ) self.debug("SSH into Netscaler to verify other resources are deleted") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2523,7 +2523,7 @@ class TestVmWithLb(cloudstackTestCase): e)) self.debug("SSH into Netscaler to rules still persist") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2596,7 +2596,7 @@ class TestVmWithLb(cloudstackTestCase): e)) self.debug("SSH into Netscaler to rules still persist") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2709,7 +2709,7 @@ class TestVmWithLb(cloudstackTestCase): e)) self.debug("SSH into Netscaler to rules still persist") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2797,7 +2797,7 @@ class TestVmWithLb(cloudstackTestCase): e)) self.debug("SSH into Netscaler to rules still persist") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2880,7 +2880,7 @@ class TestVmWithLb(cloudstackTestCase): time.sleep(int(delay[0].value) + int(wait[0].value)) self.debug("SSH into Netscaler to rules still persist") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -2936,7 +2936,7 @@ class TestVmWithLb(cloudstackTestCase): ) self.debug("SSH into Netscaler to verify other resources are deleted") try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], diff --git a/test/integration/component/test_netscaler_lb_algo.py b/test/integration/component/test_netscaler_lb_algo.py index 4df7b897a5b..a5e1fe80b7b 100644 --- a/test/integration/component/test_netscaler_lb_algo.py +++ b/test/integration/component/test_netscaler_lb_algo.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime @@ -300,7 +300,7 @@ class TestLbWithRoundRobin(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -524,7 +524,7 @@ class TestLbWithLeastConn(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -739,7 +739,7 @@ class TestLbWithSourceIp(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -912,7 +912,7 @@ class TestLbAlgoRrLc(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -945,7 +945,7 @@ class TestLbAlgoRrLc(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1115,7 +1115,7 @@ class TestLbAlgoLcRr(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1147,7 +1147,7 @@ class TestLbAlgoLcRr(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1318,7 +1318,7 @@ class TestLbAlgoRrSb(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1351,7 +1351,7 @@ class TestLbAlgoRrSb(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1526,7 +1526,7 @@ class TestLbAlgoSbRr(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1560,7 +1560,7 @@ class TestLbAlgoSbRr(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1734,7 +1734,7 @@ class TestLbAlgoSbLc(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1767,7 +1767,7 @@ class TestLbAlgoSbLc(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1939,7 +1939,7 @@ class TestLbAlgoLcSb(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -1972,7 +1972,7 @@ class TestLbAlgoLcSb(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], diff --git a/test/integration/component/test_netscaler_lb_sticky.py b/test/integration/component/test_netscaler_lb_sticky.py index 56964a9fd9e..6c27a08052a 100644 --- a/test/integration/component/test_netscaler_lb_sticky.py +++ b/test/integration/component/test_netscaler_lb_sticky.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime @@ -267,7 +267,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -356,7 +356,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -445,7 +445,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -536,7 +536,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -627,7 +627,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -718,7 +718,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -809,7 +809,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -900,7 +900,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], @@ -991,7 +991,7 @@ class TestLbStickyPolicy(cloudstackTestCase): self.debug("SSH into netscaler: %s" % self.services["netscaler"]["ipaddress"]) try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( self.services["netscaler"]["ipaddress"], self.services["netscaler"]["port"], self.services["netscaler"]["username"], diff --git a/test/integration/component/test_netscaler_nw_off.py b/test/integration/component/test_netscaler_nw_off.py index b94d47ea164..3372612abab 100644 --- a/test/integration/component/test_netscaler_nw_off.py +++ b/test/integration/component/test_netscaler_nw_off.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_network_offering.py b/test/integration/component/test_network_offering.py index 335f8592ff0..04777b0c2a8 100644 --- a/test/integration/component/test_network_offering.py +++ b/test/integration/component/test_network_offering.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_portable_ip.py b/test/integration/component/test_portable_ip.py index 917e7f2d1a9..a532b362751 100644 --- a/test/integration/component/test_portable_ip.py +++ b/test/integration/component/test_portable_ip.py @@ -23,7 +23,7 @@ from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * from netaddr import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from nose.plugins.attrib import attr @@ -808,7 +808,7 @@ class TestAssociatePublicIp(cloudstackTestCase): self.debug("Trying to SSH to ip: %s" % portableip.ipaddress.ipaddress) - remoteSSHClient( + SshClient( portableip.ipaddress.ipaddress, self.services['natrule']["publicport"], self.virtual_machine.username, @@ -1533,7 +1533,7 @@ class TestPortableIpTransferAcrossNetworks(cloudstackTestCase): self.debug("Trying to SSH to ip: %s" % portableip.ipaddress.ipaddress) - remoteSSHClient( + SshClient( portableip.ipaddress.ipaddress, self.services['natrule']["publicport"], self.virtual_machine2.username, diff --git a/test/integration/component/test_project_configs.py b/test/integration/component/test_project_configs.py index a9654c82f57..be6cf1ce279 100644 --- a/test/integration/component/test_project_configs.py +++ b/test/integration/component/test_project_configs.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_project_resources.py b/test/integration/component/test_project_resources.py index 131a9e21050..9b9b2e6ac2e 100644 --- a/test/integration/component/test_project_resources.py +++ b/test/integration/component/test_project_resources.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_project_usage.py b/test/integration/component/test_project_usage.py index e836768591b..ba0a63c97e8 100644 --- a/test/integration/component/test_project_usage.py +++ b/test/integration/component/test_project_usage.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_projects.py b/test/integration/component/test_projects.py index e4c2180dd42..4b82fc2639c 100644 --- a/test/integration/component/test_projects.py +++ b/test/integration/component/test_projects.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_security_groups.py b/test/integration/component/test_security_groups.py index 5ec3ffb00d5..c90ccf6ee28 100644 --- a/test/integration/component/test_security_groups.py +++ b/test/integration/component/test_security_groups.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient #Import System modules import time @@ -361,7 +361,7 @@ class TestDefaultSecurityGroup(cloudstackTestCase): # SSH Attempt to VM should fail with self.assertRaises(Exception): self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) - ssh = remoteSSHClient.remoteSSHClient( + ssh = SshClient( self.virtual_machine.ssh_ip, self.virtual_machine.ssh_port, self.virtual_machine.username, @@ -656,7 +656,7 @@ class TestRevokeIngressRule(cloudstackTestCase): # SSH Attempt to VM should fail with self.assertRaises(Exception): self.debug("SSH into VM: %s" % self.virtual_machine.id) - remoteSSHClient.remoteSSHClient( + SshClient( self.virtual_machine.ssh_ip, self.virtual_machine.ssh_port, self.virtual_machine.username, diff --git a/test/integration/component/test_snapshot_limits.py b/test/integration/component/test_snapshot_limits.py index 2af77c3923d..e52a893587c 100644 --- a/test/integration/component/test_snapshot_limits.py +++ b/test/integration/component/test_snapshot_limits.py @@ -21,7 +21,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import is_snapshot_on_nfs import os diff --git a/test/integration/component/test_snapshots.py b/test/integration/component/test_snapshots.py index 1c2537c8e3a..0a82fc10600 100644 --- a/test/integration/component/test_snapshots.py +++ b/test/integration/component/test_snapshots.py @@ -5,9 +5,9 @@ # 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 @@ -17,13 +17,35 @@ """ P1 tests for Snapshots """ #Import Local Modules -from nose.plugins.attrib import attr -from marvin.cloudstackTestCase import * -from marvin.cloudstackAPI import * -from marvin.integration.lib.utils import * -from marvin.integration.lib.base import * -from marvin.integration.lib.common import * -from marvin.integration.lib.utils import is_snapshot_on_nfs +from nose.plugins.attrib import attr +from marvin.cloudstackTestCase import cloudstackTestCase, unittest + +from marvin.integration.lib.base import (Snapshot, + Template, + VirtualMachine, + Account, + ServiceOffering, + DiskOffering, + Volume) + +from marvin.integration.lib.common import (get_domain, + get_zone, + get_template, + list_events, + list_volumes, + list_snapshots, + list_templates, + list_virtual_machines, + get_hypervisor_type) + +from marvin.integration.lib.utils import (cleanup_resources, + format_volume_to_ext3, + random_gen, + is_snapshot_on_nfs) + +from marvin.cloudstackAPI import detachVolume +import time + class Services: @@ -104,7 +126,18 @@ class Services: "volume": { "diskname": "APP Data Volume", "size": 1, # in GBs - "diskdevice": ['/dev/xvdb', '/dev/sdb', '/dev/hdb', '/dev/vdb' ], # Data Disk + "xenserver": {"rootdiskdevice":"/dev/xvda", + "datadiskdevice_1": '/dev/xvdb', + "datadiskdevice_2": '/dev/xvdc', # Data Disk + }, + "kvm": {"rootdiskdevice": "/dev/vda", + "datadiskdevice_1": "/dev/vdb", + "datadiskdevice_2": "/dev/vdc" + }, + "vmware": {"rootdiskdevice": "/dev/hda", + "datadiskdevice_1": "/dev/hdb", + "datadiskdevice_2": "/dev/hdc" + } }, "paths": { "mount_dir": "/mnt/tmp", @@ -139,7 +172,7 @@ class TestSnapshots(cloudstackTestCase): cls.zone.id, cls.services["ostype"] ) - + cls.services["domainid"] = cls.domain.id cls.services["volume"]["zoneid"] = cls.services["server_with_disk"]["zoneid"] = cls.zone.id cls.services["server_with_disk"]["diskoffering"] = cls.disk_offering.id @@ -174,6 +207,9 @@ class TestSnapshots(cloudstackTestCase): mode=cls.services["mode"] ) + # Get Hypervisor Type + cls.hypervisor = (get_hypervisor_type(cls.api_client)).lower() + cls._cleanup = [ cls.service_offering, cls.disk_offering, @@ -259,15 +295,24 @@ class TestSnapshots(cloudstackTestCase): # Validate the following #1. Create a virtual machine and data volume #2. Attach data volume to VM - #3. Login to machine; create temp/test directories on data volume + #3. Login to machine; create temp/test directories on data volume and write some random data #4. Snapshot the Volume #5. Create another Volume from snapshot - #6. Mount/Attach volume to another server - #7. Compare data + #6. Mount/Attach volume to another virtual machine + #7. Compare data, data should match random_data_0 = random_gen(size=100) random_data_1 = random_gen(size=100) + self.debug("random_data_0 : %s" % random_data_0) + self.debug("random_data_1: %s" % random_data_1) + + try: + ssh_client = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH failed for VM: %s" % + self.virtual_machine.ipaddress) + volume = Volume.create( self.apiclient, self.services["volume"], @@ -285,22 +330,18 @@ class TestSnapshots(cloudstackTestCase): (volume.id, self.virtual_machine.id)) - try: - ssh_client = self.virtual_machine.get_ssh_client() - except Exception as e: - self.fail("SSH failed for VM: %s" % - self.virtual_machine.ipaddress) - self.debug("Formatting volume: %s to ext3" % volume.id) #Format partition using ext3 + # Note that this is the second data disk partition of virtual machine as it was already containing + # data disk before attaching the new volume, Hence datadiskdevice_2 format_volume_to_ext3( ssh_client, - self.services["volume"]["diskdevice"] + self.services["volume"][self.hypervisor]["datadiskdevice_2"] ) - cmds = [ + cmds = [ "fdisk -l", "mkdir -p %s" % self.services["paths"]["mount_dir"], - "mount %s1 %s" % ( - self.services["volume"]["diskdevice"], + "mount -t ext3 %s1 %s" % ( + self.services["volume"][self.hypervisor]["datadiskdevice_2"], self.services["paths"]["mount_dir"] ), "mkdir -p %s/%s/{%s,%s} " % ( @@ -323,10 +364,17 @@ class TestSnapshots(cloudstackTestCase): self.services["paths"]["sub_lvl_dir2"], self.services["paths"]["random_data"] ), + "cat %s/%s/%s/%s" % ( + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir1"], + self.services["paths"]["random_data"] + ) ] for c in cmds: self.debug("Command: %s" % c) - ssh_client.execute(c) + result = ssh_client.execute(c) + self.debug(result) # Unmount the Sec Storage cmds = [ @@ -340,7 +388,7 @@ class TestSnapshots(cloudstackTestCase): self.apiclient, virtualmachineid=self.virtual_machine.id, type='DATADISK', - listall=True + id=volume.id ) self.assertEqual( @@ -365,6 +413,15 @@ class TestSnapshots(cloudstackTestCase): account=self.account.name, domainid=self.account.domainid ) + + # Detach the volume from virtual machine + self.virtual_machine.detach_volume( + self.apiclient, + volume + ) + self.debug("Detached volume: %s from VM: %s" % + (volume.id, self.virtual_machine.id)) + self.debug("Created Volume: %s from Snapshot: %s" % ( volume_from_snapshot.id, snapshot.id)) @@ -399,7 +456,7 @@ class TestSnapshots(cloudstackTestCase): mode=self.services["mode"] ) self.debug("Deployed new VM for account: %s" % self.account.name) - self.cleanup.append(new_virtual_machine) + #self.cleanup.append(new_virtual_machine) self.debug("Attaching volume: %s to VM: %s" % ( volume_from_snapshot.id, @@ -411,21 +468,28 @@ class TestSnapshots(cloudstackTestCase): volume_from_snapshot ) + # Rebooting is required so that newly attached disks are detected + self.debug("Rebooting : %s" % new_virtual_machine.id) + + new_virtual_machine.reboot(self.apiclient) + try: #Login to VM to verify test directories and files ssh = new_virtual_machine.get_ssh_client() - cmds = [ + # Mount datadiskdevice_1 because this is the first data disk of the new virtual machine + cmds = ["fdisk -l", "mkdir -p %s" % self.services["paths"]["mount_dir"], - "mount %s1 %s" % ( - self.services["volume"]["diskdevice"], + "mount -t ext3 %s1 %s" % ( + self.services["volume"][self.hypervisor]["datadiskdevice_1"], self.services["paths"]["mount_dir"] ), ] for c in cmds: self.debug("Command: %s" % c) - ssh.execute(c) + result = ssh.execute(c) + self.debug(result) returned_data_0 = ssh.execute( "cat %s/%s/%s/%s" % ( @@ -442,8 +506,12 @@ class TestSnapshots(cloudstackTestCase): self.services["paths"]["random_data"] )) except Exception as e: - self.fail("SSH access failed for VM: %s" % - new_virtual_machine.ipaddress) + self.fail("SSH access failed for VM: %s, Exception: %s" % + (new_virtual_machine.ipaddress, e)) + + self.debug("returned_data_0: %s" % returned_data_0[0]) + self.debug("returned_data_1: %s" % returned_data_1[0]) + #Verify returned data self.assertEqual( random_data_0, @@ -512,7 +580,7 @@ class TestSnapshots(cloudstackTestCase): """ # Validate the following # 1. login in VM and write some data on data disk(use fdisk to - # partition datadisk,fdisk /dev/sdb, and make filesystem using + # partition datadisk,fdisk, and make filesystem using # mkfs.ext3) # 2. Detach the data disk and write some data on data disk # 3. perform the snapshot on the detached volume @@ -540,12 +608,12 @@ class TestSnapshots(cloudstackTestCase): #Format partition using ext3 format_volume_to_ext3( ssh_client, - self.services["volume"]["diskdevice"] + self.services["volume"][self.hypervisor]["datadiskdevice_1"] ) cmds = [ "mkdir -p %s" % self.services["paths"]["mount_dir"], "mount %s1 %s" % ( - self.services["volume"]["diskdevice"], + self.services["volume"][self.hypervisor]["datadiskdevice_1"], self.services["paths"]["mount_dir"] ), "pushd %s" % self.services["paths"]["mount_dir"], @@ -567,6 +635,7 @@ class TestSnapshots(cloudstackTestCase): self.services["paths"]["random_data"] ), "sync", + "umount %s" % (self.services["paths"]["mount_dir"]), ] for c in cmds: self.debug(ssh_client.execute(c)) @@ -641,7 +710,7 @@ class TestSnapshots(cloudstackTestCase): #3. Create Template from snapshot #4. Deploy Virtual machine using this template #5. Login to newly created virtual machine - #6. Compare data + #6. Compare data in the root disk with the one that was written on the volume, it should match random_data_0 = random_gen(size=100) random_data_1 = random_gen(size=100) @@ -653,7 +722,7 @@ class TestSnapshots(cloudstackTestCase): cmds = [ "mkdir -p %s" % self.services["paths"]["mount_dir"], "mount %s1 %s" % ( - self.services["volume"]["diskdevice"], + self.services["volume"][self.hypervisor]["rootdiskdevice"], self.services["paths"]["mount_dir"] ), "mkdir -p %s/%s/{%s,%s} " % ( @@ -767,7 +836,7 @@ class TestSnapshots(cloudstackTestCase): cmds = [ "mkdir -p %s" % self.services["paths"]["mount_dir"], "mount %s1 %s" % ( - self.services["volume"]["diskdevice"], + self.services["volume"][self.hypervisor]["rootdiskdevice"], self.services["paths"]["mount_dir"] ) ] @@ -813,8 +882,8 @@ class TestSnapshots(cloudstackTestCase): ssh_client.execute(c) except Exception as e: - self.fail("SSH failed for VM with IP address: %s" % - new_virtual_machine.ipaddress) + self.fail("SSH failed for VM with IP address: %s, Exception: %s" % + (new_virtual_machine.ipaddress, e)) return @@ -920,7 +989,7 @@ class TestCreateVMSnapshotTemplate(cloudstackTestCase): volume = volumes[0] # Create a snapshot from the ROOTDISK - snapshot = Snapshot.create(self.apiclient, volumes[0].id) + snapshot = Snapshot.create(self.apiclient, volume.id) self.debug("Snapshot created: ID - %s" % snapshot.id) self.cleanup.append(snapshot) @@ -1016,7 +1085,6 @@ class TestCreateVMSnapshotTemplate(cloudstackTestCase): self.assertTrue(is_snapshot_on_nfs(self.apiclient, self.dbclient, self.config, self.zone.id, snapshot_uuid)) return - class TestSnapshotEvents(cloudstackTestCase): @classmethod @@ -1087,7 +1155,7 @@ class TestSnapshotEvents(cloudstackTestCase): except Exception as e: raise Exception("Warning: Exception during cleanup : %s" % e) return - + @attr(speed = "slow") @attr(tags = ["advanced", "advancedns"]) def test_05_snapshot_events(self): @@ -1113,7 +1181,7 @@ class TestSnapshotEvents(cloudstackTestCase): volume = volumes[0] # Create a snapshot from the ROOTDISK - snapshot = Snapshot.create(self.apiclient, volumes[0].id) + snapshot = Snapshot.create(self.apiclient, volume.id) self.debug("Snapshot created with ID: %s" % snapshot.id) snapshots = list_snapshots( diff --git a/test/integration/component/test_stopped_vm.py b/test/integration/component/test_stopped_vm.py index 3be915166ee..5a5c2981e2f 100644 --- a/test/integration/component/test_stopped_vm.py +++ b/test/integration/component/test_stopped_vm.py @@ -22,7 +22,7 @@ import marvin from nose.plugins.attrib import attr from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/test_storage_motion.py b/test/integration/component/test_storage_motion.py index 36376f373bc..bae5acf5514 100644 --- a/test/integration/component/test_storage_motion.py +++ b/test/integration/component/test_storage_motion.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/test_templates.py b/test/integration/component/test_templates.py index ea4b27755ca..af86d32a3bd 100644 --- a/test/integration/component/test_templates.py +++ b/test/integration/component/test_templates.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import urllib from random import random #Import System modules diff --git a/test/integration/component/test_usage.py b/test/integration/component/test_usage.py index 75326f70a75..5979a0a495c 100644 --- a/test/integration/component/test_usage.py +++ b/test/integration/component/test_usage.py @@ -24,7 +24,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_vm_passwdenabled.py b/test/integration/component/test_vm_passwdenabled.py index a6b45acc9d9..5cfa52556f7 100644 --- a/test/integration/component/test_vm_passwdenabled.py +++ b/test/integration/component/test_vm_passwdenabled.py @@ -17,7 +17,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/component/test_vpc_network.py b/test/integration/component/test_vpc_network.py index b9b4f0049f6..9f5e6f623f0 100644 --- a/test/integration/component/test_vpc_network.py +++ b/test/integration/component/test_vpc_network.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime # For more info on ddt refer to http://ddt.readthedocs.org/en/latest/api.html#module-ddt from ddt import ddt, data diff --git a/test/integration/component/test_vpc_offerings.py b/test/integration/component/test_vpc_offerings.py index 109c8d13687..4f5802285f2 100644 --- a/test/integration/component/test_vpc_offerings.py +++ b/test/integration/component/test_vpc_offerings.py @@ -26,7 +26,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_vpc_routers.py b/test/integration/component/test_vpc_routers.py index 9b772e41c67..8ed99ca3c36 100644 --- a/test/integration/component/test_vpc_routers.py +++ b/test/integration/component/test_vpc_routers.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/component/test_vpc_vm_life_cycle.py b/test/integration/component/test_vpc_vm_life_cycle.py index 425c2848f96..cc65eedff8a 100644 --- a/test/integration/component/test_vpc_vm_life_cycle.py +++ b/test/integration/component/test_vpc_vm_life_cycle.py @@ -25,7 +25,8 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient +from marvin.codes import PASS import time @@ -1195,10 +1196,20 @@ class TestVMLifeCycleSharedNwVPC(cloudstackTestCase): ) self.debug("We should be allowed to ping virtual gateway") - self.debug("VM gateway: %s" % self.vm_1.nic[0].gateway) + self.debug("Finding the gateway corresponding to isolated network") + gateways = [nic.gateway for nic in self.vm_1.nic if nic.networkid == self.network_1.id] - res = ssh_1.execute("ping -c 1 %s" % self.vm_1.nic[0].gateway) - self.debug("ping -c 1 %s: %s" % (self.vm_1.nic[0].gateway, res)) + gateway_list_validation_result = validateList(gateways) + + self.assertEqual(gateway_list_validation_result[0], PASS, "gateway list validation failed due to %s" % + gateway_list_validation_result[2]) + + gateway = gateway_list_validation_result[1] + + self.debug("VM gateway: %s" % gateway) + + res = ssh_1.execute("ping -c 1 %s" % gateway) + self.debug("ping -c 1 %s: %s" % (gateway, res)) result = str(res) self.assertEqual( diff --git a/test/integration/component/test_vpc_vms_deployment.py b/test/integration/component/test_vpc_vms_deployment.py index b01137b1ccd..ebf3b317d2b 100644 --- a/test/integration/component/test_vpc_vms_deployment.py +++ b/test/integration/component/test_vpc_vms_deployment.py @@ -25,7 +25,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient import datetime diff --git a/test/integration/smoke/test_affinity_groups.py b/test/integration/smoke/test_affinity_groups.py index db11adabe16..c96a580f199 100644 --- a/test/integration/smoke/test_affinity_groups.py +++ b/test/integration/smoke/test_affinity_groups.py @@ -21,7 +21,7 @@ from marvin.cloudstackAPI import * from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from nose.plugins.attrib import attr class Services: diff --git a/test/integration/smoke/test_loadbalance.py b/test/integration/smoke/test_loadbalance.py index 3e48158442a..5f80c38303a 100644 --- a/test/integration/smoke/test_loadbalance.py +++ b/test/integration/smoke/test_loadbalance.py @@ -17,7 +17,7 @@ from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * @@ -197,7 +197,7 @@ class TestLoadBalance(cloudstackTestCase): # If Round Robin Algorithm is chosen, # each ssh command should alternate between VMs - ssh_1 = remoteSSHClient( + ssh_1 = SshClient( ip_addr, self.services['lbrule']["publicport"], self.vm_1.username, diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py index f2045959697..09d6437cd86 100644 --- a/test/integration/smoke/test_network.py +++ b/test/integration/smoke/test_network.py @@ -21,7 +21,7 @@ import marvin from marvin.cloudstackException import cloudstackAPIException from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * @@ -460,7 +460,7 @@ class TestPortForwarding(cloudstackTestCase): "SSHing into VM with IP address %s after NAT rule deletion" % self.virtual_machine.ipaddress) - remoteSSHClient( + SshClient( src_nat_ip_addr.ipaddress, self.virtual_machine.ssh_port, self.virtual_machine.username, @@ -578,7 +578,7 @@ class TestPortForwarding(cloudstackTestCase): "SSHing into VM with IP address %s after NAT rule deletion" % self.virtual_machine.ipaddress) - remoteSSHClient( + SshClient( ip_address.ipaddress.ipaddress, self.virtual_machine.ssh_port, self.virtual_machine.username, @@ -741,7 +741,7 @@ class TestRebootRouter(cloudstackTestCase): try: self.debug("SSH into VM (ID : %s ) after reboot" % self.vm_1.id) - remoteSSHClient( + SshClient( self.public_ip.ipaddress.ipaddress, self.services["natrule"]["publicport"], self.vm_1.username, @@ -883,7 +883,7 @@ class TestReleaseIP(cloudstackTestCase): # SSH Attempt though public IP should fail with self.assertRaises(Exception): - ssh_2 = remoteSSHClient( + ssh_2 = SshClient( self.ip_addr.ipaddress, self.services["natrule"]["publicport"], self.virtual_machine.username, diff --git a/test/integration/smoke/test_nic.py b/test/integration/smoke/test_nic.py index 583fc29c2c1..ac95685f37a 100644 --- a/test/integration/smoke/test_nic.py +++ b/test/integration/smoke/test_nic.py @@ -18,7 +18,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_pvlan.py b/test/integration/smoke/test_pvlan.py index 0a427ba229d..aeb47f953c4 100644 --- a/test/integration/smoke/test_pvlan.py +++ b/test/integration/smoke/test_pvlan.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_resource_detail.py b/test/integration/smoke/test_resource_detail.py index 93bc2ffc1f1..00a7b5c4171 100644 --- a/test/integration/smoke/test_resource_detail.py +++ b/test/integration/smoke/test_resource_detail.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_routers.py b/test/integration/smoke/test_routers.py index 0f32e274338..7d324136e89 100644 --- a/test/integration/smoke/test_routers.py +++ b/test/integration/smoke/test_routers.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_ssvm.py b/test/integration/smoke/test_ssvm.py index 9fa59a94d3f..a2b9eabc13c 100644 --- a/test/integration/smoke/test_ssvm.py +++ b/test/integration/smoke/test_ssvm.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_templates.py b/test/integration/smoke/test_templates.py index 9478440f77e..e371ad436a8 100644 --- a/test/integration/smoke/test_templates.py +++ b/test/integration/smoke/test_templates.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index b60b70e86ed..816ae03f267 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -21,7 +21,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackException import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.integration.lib.utils import * from marvin.integration.lib.base import * from marvin.integration.lib.common import * diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index aa73b2a74de..d58252ca61b 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -79,6 +79,7 @@ known_categories = { 'ServiceOffering': 'Service Offering', 'DiskOffering': 'Disk Offering', 'LoadBalancer': 'Load Balancer', + 'SslCert': 'Load Balancer', 'Router': 'Router', 'SystemVm': 'System VM', 'Configuration': 'Configuration', diff --git a/tools/marvin/marvin/cloudstackTestCase.py b/tools/marvin/marvin/cloudstackTestCase.py index 85ef5423091..6456bb1fe61 100644 --- a/tools/marvin/marvin/cloudstackTestCase.py +++ b/tools/marvin/marvin/cloudstackTestCase.py @@ -16,6 +16,8 @@ # under the License. import unittest +from marvin.integration.lib.utils import verifyElementInList +from marvin.codes import PASS def user(Name, DomainName, AcctType): @@ -35,6 +37,18 @@ def user(Name, DomainName, AcctType): class cloudstackTestCase(unittest.case.TestCase): clstestclient = None + def assertElementInList(inp, toverify, responsevar=None, pos=0, + assertmsg="TC Failed for reason"): + ''' + @Name: assertElementInList + @desc:Uses the utility function verifyElementInList and + asserts based upon PASS\FAIL value of the output. + Takes one additional argument of what message to assert with + when failed + ''' + out = verifyElementInList(inp, toverify, responsevar, pos) + unittest.TestCase.assertEquals(out[0], PASS, "msg:%s" % out[1]) + @classmethod def getClsTestClient(cls): return cls.clstestclient diff --git a/tools/marvin/marvin/codes.py b/tools/marvin/marvin/codes.py index b6580d0d199..f409c7c8d13 100644 --- a/tools/marvin/marvin/codes.py +++ b/tools/marvin/marvin/codes.py @@ -40,3 +40,5 @@ EMPTY_LIST = "EMPTY_LIST" FAIL = 0 PASS = 1 MATCH_NOT_FOUND = "ELEMENT NOT FOUND IN THE INPUT" +SUCCESS = "SUCCESS" +EXCEPTION_OCCURRED = "Exception Occurred" diff --git a/tools/marvin/marvin/integration/lib/common.py b/tools/marvin/marvin/integration/lib/common.py index 164ef2052dd..6c2bb2aced1 100644 --- a/tools/marvin/marvin/integration/lib/common.py +++ b/tools/marvin/marvin/integration/lib/common.py @@ -19,9 +19,10 @@ #Import Local Modules from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from utils import * from base import * +from marvin.codes import PASS #Import System modules import time @@ -198,13 +199,25 @@ def get_template(apiclient, zoneid, ostype, services=None): ostypeid) return +def get_hypervisor_type(apiclient): + + """Return the hypervisor type of the hosts in setup""" + + hosts = list_hosts(apiclient, type='Routing', listall=True) + + hosts_list_validation_result = validateList(hosts) + + assert hosts_list_validation_result[0] == PASS, "host list validation failed" + + return hosts_list_validation_result[1].hypervisor + def download_systemplates_sec_storage(server, services): """Download System templates on sec storage""" try: # Login to management server - ssh = remoteSSHClient( + ssh = SshClient( server["ipaddress"], server["port"], server["username"], diff --git a/tools/marvin/marvin/integration/lib/utils.py b/tools/marvin/marvin/integration/lib/utils.py index d53c1ae301d..0fe3c26adbf 100644 --- a/tools/marvin/marvin/integration/lib/utils.py +++ b/tools/marvin/marvin/integration/lib/utils.py @@ -29,7 +29,7 @@ import socket import urlparse import datetime from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from marvin.codes import * @@ -113,19 +113,28 @@ def cleanup_resources(api_client, resources): obj.delete(api_client) -def is_server_ssh_ready(ipaddress, port, username, password, retries=10, timeout=30, keyPairFileLocation=None): - """Return ssh handle else wait till sshd is running""" +def is_server_ssh_ready(ipaddress, port, username, password, retries=20, retryinterv=30, timeout=10.0, keyPairFileLocation=None): + ''' + @Name: is_server_ssh_ready + @Input: timeout: tcp connection timeout flag, + others information need to be added + @Output:object for SshClient + Name of the function is little misnomer and is not + verifying anything as such mentioned + ''' + try: - ssh = remoteSSHClient( + ssh = SshClient( host=ipaddress, port=port, user=username, passwd=password, - keyPairFileLocation=keyPairFileLocation, + keyPairFiles=keyPairFileLocation, retries=retries, - delay=timeout) + delay=retryinterv, + timeout=timeout) except Exception, e: - raise Exception("Failed to bring up ssh service in time. Waited %ss. Error is %s" % (retries * timeout, e)) + raise Exception("SSH connection has Failed. Waited %ss. Error is %s" % (retries * retryinterv, str(e))) else: return ssh @@ -181,7 +190,7 @@ def get_process_status(hostip, port, username, password, linklocalip, process, h """Double hop and returns a process status""" #SSH to the machine - ssh = remoteSSHClient(hostip, port, username, password) + ssh = SshClient(hostip, port, username, password) if str(hypervisor).lower() == 'vmware': ssh_command = "ssh -i /var/cloudstack/management/.ssh/id_rsa -ostricthostkeychecking=no " else: @@ -287,7 +296,7 @@ def is_snapshot_on_nfs(apiclient, dbconn, config, zoneid, snapshotid): mgtSvr, user, passwd = config.mgtSvr[0].mgtSvrIp, config.mgtSvr[0].user, config.mgtSvr[0].passwd try: - ssh_client = remoteSSHClient( + ssh_client = SshClient( mgtSvr, 22, user, @@ -320,11 +329,10 @@ def is_snapshot_on_nfs(apiclient, dbconn, config, zoneid, snapshotid): (config.mgtSvr[0].mgtSvrIp, e)) return 'snapshot exists' in result - def validateList(inp): - ''' - @name: validateList - @Description: 1. A utility function to validate + """ + @name: validateList + @Description: 1. A utility function to validate whether the input passed is a list 2. The list is empty or not 3. If it is list and not empty, return PASS and first element @@ -341,55 +349,63 @@ def validateList(inp): default to None. INVALID_INPUT EMPTY_LIST - ''' - ret = [FAIL, None, None] - if inp is None: - ret[2] = INVALID_INPUT - return ret - if not isinstance(inp, list): - ret[2] = INVALID_INPUT - return ret - if len(inp) == 0: - ret[2] = EMPTY_LIST - return ret - return [PASS, inp[0], None] + """ + ret = [FAIL, None, None] + if inp is None: + ret[2] = INVALID_INPUT + return ret + if not isinstance(inp, list): + ret[2] = INVALID_INPUT + return ret + if len(inp) == 0: + ret[2] = EMPTY_LIST + return ret + return [PASS, inp[0], None] -def verifyElementInList(inp, toverify, pos = 0): - ''' - @name: verifyElementInList - @Description: - 1. A utility function to validate - whether the input passed is a list. - The list is empty or not. - If it is list and not empty, verify - whether a given element is there in that list or not - at a given pos - @Input: - I : Input to be verified whether its a list or not +def verifyElementInList(inp, toverify, responsevar=None, pos=0): + ''' + @name: verifyElementInList + @Description: + 1. A utility function to validate + whether the input passed is a list. + The list is empty or not. + If it is list and not empty, verify + whether a given element is there in that list or not + at a given pos + @Input: + I : Input to be verified whether its a list or not II : Element to verify whether it exists in the list - III : Position in the list at which the input element to verify - default to 0 - @output: List, containing [ Result,Reason ] - Ist Argument('Result') : FAIL : If it is not a list - If it is list but empty - PASS : If it is list and not empty + III : variable name in response object to verify + default to None, if None, we will verify for the complete + first element EX: state of response object object + IV : Position in the list at which the input element to verify + default to 0 + @output: List, containing [ Result,Reason ] + Ist Argument('Result') : FAIL : If it is not a list + If it is list but empty + PASS : If it is list and not empty and matching element was found - IIrd Argument( 'Reason' ): Reason for failure ( FAIL ), - default to None. - INVALID_INPUT - EMPTY_LIST - MATCH_NOT_FOUND - ''' - if toverify is None or toverify == '' \ - or pos is None or pos < -1 or pos == '': - return [FAIL, INVALID_INPUT] - out = validateList(inp) - if out[0] == FAIL: - return [FAIL, out[2]] - if out[0] == PASS: - if len(inp) > pos and inp[pos] == toverify: - return [PASS, None] - else: - return [FAIL, MATCH_NOT_FOUND] - + IIrd Argument( 'Reason' ): Reason for failure ( FAIL ), + default to None. + INVALID_INPUT + EMPTY_LIST + MATCH_NOT_FOUND + ''' + if toverify is None or toverify == '' \ + or pos is None or pos < -1 or pos == '': + return [FAIL, INVALID_INPUT] + out = validateList(inp) + if out[0] == FAIL: + return [FAIL, out[2]] + if len(inp) > pos: + if responsevar is None: + if inp[pos] == toverify: + return [PASS, None] + else: + if responsevar in inp[pos].__dict__ and getattr(inp[pos], responsevar) == toverify: + return [PASS, None] + else: + return [FAIL, MATCH_NOT_FOUND] + else: + return [FAIL, MATCH_NOT_FOUND] diff --git a/tools/marvin/marvin/remoteSSHClient.py b/tools/marvin/marvin/remoteSSHClient.py deleted file mode 100644 index fea9b125d19..00000000000 --- a/tools/marvin/marvin/remoteSSHClient.py +++ /dev/null @@ -1,108 +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. - -import paramiko -import time -import cloudstackException -import contextlib -import logging -from contextlib import closing - - -class remoteSSHClient(object): - def __init__(self, host, port, user, passwd, retries=10, delay=30, - log_lvl=logging.INFO, keyPairFileLocation=None): - self.host = host - self.port = port - self.user = user - self.passwd = passwd - self.keyPairFile = keyPairFileLocation - self.ssh = paramiko.SSHClient() - self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - self.logger = logging.getLogger('sshClient') - ch = logging.StreamHandler() - ch.setLevel(log_lvl) - self.logger.addHandler(ch) - - retry_count = retries - while retry_count >= 0: - try: - if keyPairFileLocation is None: - self.ssh.connect(str(host), int(port), user, passwd) - self.logger.debug("SSH connect: %s@%s with passwd %s" % - (user, str(host), passwd)) - else: - self.ssh.connect(hostname=str(host), - port=int(port), - username=str(user), - key_filename=str(keyPairFileLocation), - look_for_keys=False - ) - self.logger.debug( - "connecting to server %s with user %s key %s" % - (str(host), user, keyPairFileLocation)) - self.logger.debug("SSH connect: %s@%s with passwd %s" % - (user, str(host), passwd)) - #except paramiko.AuthenticationException, authEx: - # raise cloudstackException. \ - # InvalidParameterException("Invalid credentials to " - # + "login to %s on port %s" % - # (str(host), port)) - except Exception as se: - if retry_count == 0: - raise cloudstackException. \ - InvalidParameterException(repr(se)) - else: - return - - retry_count = retry_count - 1 - time.sleep(delay) - - def execute(self, command): - stdin, stdout, stderr = self.ssh.exec_command(command) - output = stdout.readlines() - errors = stderr.readlines() - results = [] - if output is not None and len(output) == 0: - if errors is not None and len(errors) > 0: - for error in errors: - results.append(error.rstrip()) - - else: - for strOut in output: - results.append(strOut.rstrip()) - self.logger.debug("{Cmd: %s via Host: %s} {returns: %s}" % - (command, str(self.host), results)) - return results - - def scp(self, srcFile, destPath): - transport = paramiko.Transport((self.host, int(self.port))) - transport.connect(username=self.user, password=self.passwd) - sftp = paramiko.SFTPClient.from_transport(transport) - try: - sftp.put(srcFile, destPath) - except IOError, e: - raise e - - def close(self): - self.ssh.close() - - -if __name__ == "__main__": - with contextlib.closing(remoteSSHClient("10.223.75.10", 22, "root", - "password")) as ssh: - print ssh.execute("ls -l") diff --git a/tools/marvin/marvin/sandbox/demo/live/testSshDeployVM.py b/tools/marvin/marvin/sandbox/demo/live/testSshDeployVM.py index 5438e40d6c8..38351861de7 100644 --- a/tools/marvin/marvin/sandbox/demo/live/testSshDeployVM.py +++ b/tools/marvin/marvin/sandbox/demo/live/testSshDeployVM.py @@ -20,7 +20,7 @@ import marvin from marvin.cloudstackTestCase import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient @UserName('demo', 'ROOT', '0') @@ -84,7 +84,7 @@ class TestSshDeployVm(cloudstackTestCase): # SSH login and compare hostname self.debug("Attempting to SSH into %s over %s of %s"%(nattedip, "22", vm.name)) - ssh_client = remoteSSHClient(nattedip, "22", "root", "password") + ssh_client = SshClient(nattedip, "22", "root", "password") stdout = ssh_client.execute("hostname") self.assertEqual(hostname, stdout[0], "cloudstack VM name and hostname \ diff --git a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/common.py b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/common.py index fc184016d43..e0c452741b1 100644 --- a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/common.py +++ b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/common.py @@ -22,7 +22,7 @@ #Import Local Modules from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -import marvin.remoteSSHClient +import marvin.sshClient from utils import * from base import * @@ -102,7 +102,7 @@ def download_systemplates_sec_storage(server, services): try: # Login to management server - ssh = marvin.remoteSSHClient.remoteSSHClient( + ssh = marvin.sshClient.SshClient( server["ipaddress"], server["port"], server["username"], diff --git a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/utils.py b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/utils.py index 8abed18b99d..f26d2c0212b 100644 --- a/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/utils.py +++ b/tools/marvin/marvin/sandbox/demo/simulator/testcase/libs/utils.py @@ -20,10 +20,10 @@ """ import time -import marvin.remoteSSHClient +import marvin.sshClient from marvin.cloudstackAPI import * import marvin.cloudstackConnection -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient #from marvin.cloudstackConnection import cloudConnection import marvin.configGenerator import logging @@ -44,7 +44,7 @@ def is_server_ssh_ready(ipaddress, port, username, password, retries=50): loop_cnt = retries while True: try: - ssh = marvin.remoteSSHClient.remoteSSHClient( + ssh = marvin.sshClient.SshClient( ipaddress, port, username, @@ -89,7 +89,7 @@ def get_process_status(hostip, port, username, password, linklocalip, process): """Double hop and returns a process status""" #SSH to the machine - ssh = marvin.remoteSSHClient.remoteSSHClient( + ssh = marvin.sshClient.SshClient( hostip, port, username, diff --git a/tools/marvin/marvin/sandbox/demo/simulator/testcase/test_vm_life_cycle.py b/tools/marvin/marvin/sandbox/demo/simulator/testcase/test_vm_life_cycle.py index 9bc7e149f97..2e612021f6c 100644 --- a/tools/marvin/marvin/sandbox/demo/simulator/testcase/test_vm_life_cycle.py +++ b/tools/marvin/marvin/sandbox/demo/simulator/testcase/test_vm_life_cycle.py @@ -22,7 +22,7 @@ import marvin from marvin.cloudstackTestCase import * from marvin.cloudstackAPI import * -from marvin.remoteSSHClient import remoteSSHClient +from marvin.sshClient import SshClient from testcase.libs.utils import * from testcase.libs.base import * diff --git a/tools/marvin/marvin/sshClient.py b/tools/marvin/marvin/sshClient.py new file mode 100644 index 00000000000..fd8726cee37 --- /dev/null +++ b/tools/marvin/marvin/sshClient.py @@ -0,0 +1,182 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +import paramiko +import time +import cloudstackException +import contextlib +import logging +from marvin.codes import ( + SUCCESS, FAIL, INVALID_INPUT, EXCEPTION_OCCURRED + ) +from contextlib import closing + + +class SshClient(object): + ''' + Added timeout flag for ssh connect calls.Default to 3.0 seconds + ''' + def __init__(self, host, port, user, passwd, retries=20, delay=30, + log_lvl=logging.INFO, keyPairFiles=None, timeout=10.0): + self.host = None + self.port = 22 + self.user = user + self.passwd = passwd + self.keyPairFiles = keyPairFiles + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.logger = logging.getLogger('sshClient') + self.retryCnt = 0 + self.delay = 0 + self.timeout = 3.0 + ch = logging.StreamHandler() + ch.setLevel(log_lvl) + self.logger.addHandler(ch) + + #Check invalid host value and raise exception + #Atleast host is required for connection + if host is not None and host != '': + self.host = host + if retries is not None and retries > 0: + self.retryCnt = retries + if delay is not None and delay > 0: + self.delay = delay + if timeout is not None and timeout > 0: + self.timeout = timeout + if port is not None or port >= 0: + self.port = port + if self.createConnection() == FAIL: + raise cloudstackException.\ + internalError("Connection Failed") + + def execute(self, command): + stdin, stdout, stderr = self.ssh.exec_command(command) + output = stdout.readlines() + errors = stderr.readlines() + results = [] + if output is not None and len(output) == 0: + if errors is not None and len(errors) > 0: + for error in errors: + results.append(error.rstrip()) + + else: + for strOut in output: + results.append(strOut.rstrip()) + self.logger.debug("{Cmd: %s via Host: %s} {returns: %s}" % + (command, str(self.host), results)) + return results + + def createConnection(self): + ''' + @Name: createConnection + @Desc: Creates an ssh connection for + retries mentioned,along with sleep mentioned + @Output: SUCCESS on successful connection + FAIL If connection through ssh failed + ''' + ret = FAIL + while self.retryCnt >= 0: + try: + self.logger.debug("SSH Connection: Host:%s User:%s\ + Port:%s" % + (self.host, self.user, str(self.port) + )) + if self.keyPairFiles is None: + self.ssh.connect(hostname=self.host, + port=self.port, + username=self.user, + password=self.passwd, + timeout=self.timeout) + else: + self.ssh.connect(hostname=self.host, + port=self.port, + username=self.user, + password=self.passwd, + key_filename=self.keyPairFiles, + timeout=self.timeout, + look_for_keys=False + ) + ret = SUCCESS + break + except Exception as se: + self.retryCnt = self.retryCnt - 1 + if self.retryCnt == 0: + break + time.sleep(self.delay) + return ret + + def runCommand(self, command): + ''' + @Name: runCommand + @Desc: Runs a command over ssh and + returns the result along with status code + @Input: command to execute + @Output: 1: status of command executed. + Default to None + SUCCESS : If command execution is successful + FAIL : If command execution has failed + EXCEPTION_OCCURRED: Exception occurred while executing + command + INVALID_INPUT : If invalid value for command is passed + 2: stdin,stdout,stderr values of command output + ''' + excep_msg = '' + ret = {"status": None, "stdin": None, "stdout": None, "stderr": None} + if command is None or command == '': + ret["status"] = INVALID_INPUT + return ret + try: + status_check = 1 + stdin, stdout, stderr = self.ssh.exec_command(command) + output = stdout.readlines() + errors = stderr.readlines() + inp = stdin.readlines() + ret["stdin"] = inp + ret["stdout"] = output + ret["stderr"] = errors + if stdout is not None: + status_check = stdout.channel.recv_exit_status() + if status_check == 0: + ret["status"] = SUCCESS + else: + ret["status"] = FAIL + except Exception as e: + excep_msg = str(e) + ret["status"] = EXCEPTION_OCCURRED + finally: + self.logger.debug(" Host: %s Cmd: %s Output:%s Exception: %s" % + (self.host, command, str(ret), excep_msg)) + return ret + + def scp(self, srcFile, destPath): + transport = paramiko.Transport((self.host, int(self.port))) + transport.connect(username=self.user, password=self.passwd) + sftp = paramiko.SFTPClient.from_transport(transport) + try: + sftp.put(srcFile, destPath) + except IOError, e: + raise e + + def close(self): + if self.ssh is not None: + self.ssh.close() + + +if __name__ == "__main__": + with contextlib.closing(SshClient("10.223.75.10", 22, "root", + "password")) as ssh: + print ssh.execute("ls -l") diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css index ba2439a3ca7..2acc6b44043 100644 --- a/ui/css/cloudstack3.css +++ b/ui/css/cloudstack3.css @@ -63,7 +63,7 @@ body.install-wizard { #main-area { width: 1024px; - height: 700px; + height: 729px; margin: auto; border: 1px solid #D4D4D4; /*+box-shadow:0px -5px 11px #B7B7B7;*/ @@ -76,7 +76,7 @@ body.install-wizard { #container { /*[empty]width:;*/ - height: 700px; + height: 100%; margin: auto; position: relative; } @@ -318,6 +318,7 @@ table th div.ui-resizable-handle { body.login { background: url(../images/overlay-pattern.png) repeat center, #106CA9 url(../images/bg-login.jpg) no-repeat center; background-size: auto, cover; + overflow: hidden; } .login { @@ -1670,19 +1671,18 @@ div.list-view td.state.off span { .project-view .ui-tabs ul li.ui-state-default a { background: #6D747D; - border-top: 1px solid #90969E; - /*+box-shadow:inset -1px 1px 1px #575D64;*/ - -moz-box-shadow: inset -1px 1px 1px #575D64; - -webkit-box-shadow: inset -1px 1px 1px #575D64; - -o-box-shadow: inset -1px 1px 1px #575D64; - box-shadow: inset -1px 1px 1px #575D64; - border-right: 1px solid #90969E; + /*+box-shadow:inset -1px -2px 12px #596066;*/ + -moz-box-shadow: inset -1px -2px 12px #596066; + -webkit-box-shadow: inset -1px -2px 12px #596066; + -o-box-shadow: inset -1px -2px 12px #596066; + box-shadow: inset -1px -2px 12px #596066; color: #FFFFFF; - /*+text-shadow:0px 1px 1px #000000;*/ - -moz-text-shadow: 0px 1px 1px #000000; - -webkit-text-shadow: 0px 1px 1px #000000; - -o-text-shadow: 0px 1px 1px #000000; - text-shadow: 0px 1px 1px #000000; + font-weight: bold; + /*+text-shadow:0px -1px 1px #3A3E42;*/ + -moz-text-shadow: 0px -1px 1px #3A3E42; + -webkit-text-shadow: 0px -1px 1px #3A3E42; + -o-text-shadow: 0px -1px 1px #3A3E42; + text-shadow: 0px -1px 1px #3A3E42; } .ui-tabs ul li.ui-state-hover a { @@ -2460,7 +2460,6 @@ div.detail-group.actions td { /*Navigation*/ #navigation, #browser { - height: 728px; } #navigation { @@ -2494,6 +2493,7 @@ div.detail-group.actions td { .project-view #navigation ul { border-right: 1px solid #464C53; + background: #6D747D; } #navigation ul li { @@ -2505,6 +2505,7 @@ div.detail-group.actions td { .project-view #navigation ul li { background-image: url(../images/bg-nav-item-project-view.png); background-position: 0px 0px; + border: none; } .project-view #navigation ul li span { @@ -2700,11 +2701,10 @@ div.detail-group.actions td { /*Browser*/ #browser { width: 794px; - height: 700px; + height: 100%; max-width: 794px; position: relative; float: left; - background: #FFFFFF; overflow: hidden; } @@ -2717,7 +2717,6 @@ div.detail-group.actions td { #browser div.panel { height: 100%; - background: #FFFFFF; border-right: 1px solid #A5A5A5; overflow: visible; background-color: #F7F7F7; @@ -2757,7 +2756,6 @@ div.detail-group.actions td { } .ui-tabs div.ui-tabs-panel { - background: #FFFFFF; position: relative; } @@ -3396,7 +3394,7 @@ div.toolbar div.filters select { } .project-view #breadcrumbs ul li { - background: url(../images/bg-breadcrumb-project-view.png) 0px -2px; + color: #FFFFFF !important; } #breadcrumbs ul li, @@ -8531,26 +8529,26 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t } .network-chart li.firewall { - /*+placement:shift 206px 211px;*/ + /*+placement:shift 282px 188px;*/ position: relative; - left: 206px; - top: 211px; + left: 282px; + top: 188px; position: absolute; } .network-chart li.loadBalancing { - /*+placement:shift 92px 308px;*/ + /*+placement:shift 167px 342px;*/ position: relative; - left: 92px; - top: 308px; + left: 167px; + top: 342px; position: absolute; } .network-chart li.portForwarding { - /*+placement:shift 332px 308px;*/ + /*+placement:shift 401px 342px;*/ position: relative; - left: 332px; - top: 308px; + left: 401px; + top: 342px; position: absolute; } @@ -8579,20 +8577,44 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t } .network-chart li .view-details { - /*+placement:anchor-bottom-right 11px 11px;*/ + /*+placement:anchor-bottom-right 34px 19px;*/ position: absolute; - right: 11px; - bottom: 11px; + right: 34px; + bottom: 19px; cursor: pointer; - color: #FFFFFF; - background: url(../images/buttons.png) no-repeat -457px -503px; + background: #F7F7F7; + background: rgb(247, 247, 247); + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIxJSIgc3RvcC1jb2xvcj0iI2Y3ZjdmNyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlYWVhZWEiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(top, rgba(247,247,247,1) 1%, rgba(234,234,234,1) 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(1%,rgba(247,247,247,1)), color-stop(100%,rgba(234,234,234,1))); + background: -webkit-linear-gradient(top, rgba(247,247,247,1) 1%,rgba(234,234,234,1) 100%); + background: -o-linear-gradient(top, rgba(247,247,247,1) 1%,rgba(234,234,234,1) 100%); + background: -ms-linear-gradient(top, rgba(247,247,247,1) 1%,rgba(234,234,234,1) 100%); + background: linear-gradient(to bottom, rgba(247,247,247,1) 1%,rgba(234,234,234,1) 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7f7f7', endColorstr='#eaeaea',GradientType=0 ); font-size: 11px; - padding: 6px 17px 6px 9px; - /*+text-shadow:0px 1px 1px #395065;*/ - -moz-text-shadow: 0px 1px 1px #395065; - -webkit-text-shadow: 0px 1px 1px #395065; - -o-text-shadow: 0px 1px 1px #395065; - text-shadow: 0px 1px 1px #395065; + padding: 8px 20px; + color: #000000; + border: 1px solid #A2A2A2; + /*+border-radius:4px;*/ + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; +} + +.network-chart li .view-details:hover { + background: #D5D5D5; + /*+text-shadow:0px 1px 1px #FFFFFF;*/ + -moz-text-shadow: 0px 1px 1px #FFFFFF; + -webkit-text-shadow: 0px 1px 1px #FFFFFF; + -o-text-shadow: 0px 1px 1px #FFFFFF; + text-shadow: 0px 1px 1px #FFFFFF; + /*+box-shadow:inset 0px 0px 4px #000000;*/ + -moz-box-shadow: inset 0px 0px 4px #000000; + -webkit-box-shadow: inset 0px 0px 4px #000000; + -o-box-shadow: inset 0px 0px 4px #000000; + box-shadow: inset 0px 0px 4px #000000; } .network-chart li.disabled .view-details { @@ -8643,6 +8665,21 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t box-shadow: 0px 0px 1px #FFFFFF; } +.project-view .system-dashboard .head { + color: #FFFFFF; + /*+text-shadow:0px -1px #000000;*/ + -moz-text-shadow: 0px -1px #000000; + -webkit-text-shadow: 0px -1px #000000; + -o-text-shadow: 0px -1px #000000; + text-shadow: 0px -1px #000000; + /*+box-shadow:none;*/ + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; + box-shadow: none; + padding-top: 14px; +} + .system-dashboard .view-more, .system-dashboard .view-all { float: right; @@ -9404,6 +9441,7 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t width: 510px; height: 230px; float: left; + background: #777E88; } .project-dashboard-view .compute-and-storage .system-dashboard ul, @@ -9414,9 +9452,10 @@ div.ui-dialog div.multi-edit-add-list div.view div.data-table table.body tbody t .project-dashboard-view .compute-and-storage .system-dashboard li, .project-dashboard-view .users .system-dashboard li { - width: 159px; + width: 156px; height: 161px; - background-position: 300px -1018px; + background: #3D4045; + color: #FFFFFF; } .project-dashboard-view .compute-and-storage .system-dashboard li .icon, diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index e2bef0f8dd7..d3bfc37c021 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -23,7 +23,7 @@ under the License. <% long now = System.currentTimeMillis(); %> - + + + - diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index 42805973881..9c051690a41 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -1241,6 +1241,7 @@ var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]'); var $minIops = $form.find('.form-item[rel=minIops]'); var $maxIops = $form.find('.form-item[rel=maxIops]'); + var $hypervisorSnapshotReserve = $form.find('.form-item[rel=hypervisorSnapshotReserve]'); var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]'); var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]'); var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]'); @@ -1256,17 +1257,20 @@ $isCustomizedIops.css('display', 'inline-block'); - if ($isCustomizedIops == true) { + if ($isCustomizedIops.find('input[type=checkbox]').is(':checked')) { $minIops.hide(); $maxIops.hide(); } else { $minIops.css('display', 'inline-block'); $maxIops.css('display', 'inline-block'); } + + $hypervisorSnapshotReserve.css('display', 'inline-block'); } else if (qosId == 'hypervisor') { // Hypervisor Qos $isCustomizedIops.hide(); $minIops.hide(); $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); $diskBytesReadRate.css('display', 'inline-block'); $diskBytesWriteRate.css('display', 'inline-block'); @@ -1280,6 +1284,7 @@ $isCustomizedIops.hide(); $minIops.hide(); $maxIops.hide(); + $hypervisorSnapshotReserve.hide(); } }); } @@ -1309,6 +1314,14 @@ number: true } }, + hypervisorSnapshotReserve: { + label: 'label.hypervisor.snapshot.reserve', + docID: 'helpDiskOfferingHypervisorSnapshotReserve', + validation: { + required: false, + number: true + } + }, diskBytesReadRate: { label: 'label.disk.bytes.read.rate', docID: 'helpDiskOfferingDiskBytesReadRate', @@ -1416,6 +1429,12 @@ }); } } + + if (args.data.hypervisorSnapshotReserve != null && args.data.hypervisorSnapshotReserve.length > 0) { + $.extend(data, { + hypervisorsnapshotreserve: args.data.hypervisorSnapshotReserve + }); + } } else if (args.data.qosType == 'hypervisor') { if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) { $.extend(data, { diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index 3a4f8ca604f..a3151b18297 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -326,6 +326,10 @@ cloudStack.docs = { desc: 'Appears only if Custom IOPS is not selected. Define the maximum volume IOPS.', externalLink: '' }, + helpDiskOfferingHypervisorSnapshotReserve: { + desc: 'Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware) (Ex. The value 25 means 25%.)).', + externalLink: '' + }, helpDiskOfferingStorageTags: { desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".', externalLink: '' @@ -770,6 +774,75 @@ cloudStack.docs = { desc: 'Number of guest networks/accounts that will share this device', externalLink: '' }, + // Add Palo Alto + helpPaloAltoIPAddress: { + desc: 'The IP address of the device', + externalLink: '' + }, + helpPaloAltoUsername: { + desc: 'A user ID with valid authentication credentials that provide to access the device', + externalLink: '' + }, + helpPaloAltoPassword: { + desc: 'The password for the user ID provided in Username', + externalLink: '' + }, + helpPaloAltoType: { + desc: 'The type of device that is being added', + externalLink: '' + }, + helpPaloAltoPublicInterface: { + desc: 'Interface of device that is configured to be part of the public network. For example, ge-0/0/2', + externalLink: '' + }, + helpPaloAltoPrivateInterface: { + desc: 'Interface of device that is configured to be part of the private network. For example, ge-0/0/1', + externalLink: '' + }, + helpPaloAltoUsageInterface: { + desc: 'Interface used to meter traffic. If you don\'t want to use the public interface, specify a different interface name here.', + externalLink: '' + }, + helpPaloAltoRetries: { + desc: 'Number of times to attempt a command on the device before considering the operation failed. Default is 2.', + externalLink: '' + }, + helpPaloAltoTimeout: { + desc: 'The time to wait for a command on the Palo Alto before considering it failed. Default is 300 seconds.', + externalLink: '' + }, + helpPaloAltoMode: { + desc: 'Side by side mode is supported for the Palo Alto.', + externalLink: '' + }, + helpPaloAltoPublicNetwork: { + desc: 'The name of the public network on the Palo Alto. For example, trust.', + externalLink: '' + }, + helpPaloAltoPrivateNetwork: { + desc: 'The name of the private network on the Palo Alto. For example, untrust.', + externalLink: '' + }, + helpPaloAltoVirtualRouter: { + desc: 'The name of the virtual router on the Palo Alto.', + externalLink: '' + }, + helpPaloAltoThreatProfile: { + desc: 'The threat profile name/group to associate with allow firewall policies.', + externalLink: '' + }, + helpPaloAltoLogProfile: { + desc: 'The log profile name/group to associate with allow firewall policies.', + externalLink: '' + }, + helpPaloAltoDedicated: { + desc: 'Check this box to dedicate the device to a single account. The value in the Capacity field will be ignored.', + externalLink: '' + }, + helpPaloAltoCapacity: { + desc: 'Number of guest networks/accounts that will share this device', + externalLink: '' + }, // Add system service offering helpSystemOfferingName: { desc: 'Any desired name for the offering', diff --git a/ui/scripts/domains.js b/ui/scripts/domains.js index 7306a38d2d7..5d8fd689bfb 100644 --- a/ui/scripts/domains.js +++ b/ui/scripts/domains.js @@ -213,6 +213,17 @@ }); } + if (args.data.networkLimit != null) { + $.ajax({ + url: createURL("updateResourceLimit&domainid=" + args.context.domains[0].id + "&resourceType=6&max=" + args.data.networkLimit), + dataType: "json", + async: false, + success: function(json) { + domainObj["networkLimit"] = args.data.networkLimit; + } + }); + } + if (args.data.primaryStorageLimit != null) { $.ajax({ url: createURL("updateResourceLimit&domainid=" + args.context.domains[0].id + "&resourceType=10&max=" + args.data.primaryStorageLimit), @@ -432,6 +443,15 @@ return true; } }, + networkLimit: { + label: 'label.network.limits', + isEditable: function(args) { + if (args.domains[0].id == g_domainid) //disallow to update the field on the domain you log in as + return false; + else + return true; + } + }, primaryStorageLimit: { label: 'label.primary.storage.limits', isEditable: function(args) { @@ -560,6 +580,9 @@ case "4": domainObj["templateLimit"] = limit.max; break; + case "6": + domainObj["networkLimit"] = limit.max; + break; case "7": domainObj["vpcLimit"] = limit.max; break; diff --git a/ui/scripts/network.js b/ui/scripts/network.js index 4196b108cf3..d2b2c4da26d 100755 --- a/ui/scripts/network.js +++ b/ui/scripts/network.js @@ -3368,6 +3368,46 @@ requireValidation: true, buttonLabel: 'label.configure', action: cloudStack.uiCustom.autoscaler(cloudStack.autoscaler) + }, + isHidden: function(args) { + if (!('vpc' in args.context)) { //from Guest Network section + var lbProviderIsNetscaler = false; + $.ajax({ + url: createURL('listNetworkOfferings'), + data: { + id: args.context.networks[0].networkofferingid + }, + async: false, + success: function(json) { + var networkOffering = json.listnetworkofferingsresponse.networkoffering[0]; + var services = networkOffering.service; + if (services != null) { + for (var i = 0; i < services.length; i++) { + if (services[i].name == 'Lb') { + var providers = services[i].provider; + if (providers != null) { + for (var k = 0; k < providers.length; k++) { + if (providers[k].name == 'Netscaler') { + lbProviderIsNetscaler = true; + break; + } + } + } + break; + } + } + } + } + }); + if (lbProviderIsNetscaler == true) { //AutoScale is only supported on Netscaler (but not on any other provider like VirtualRouter) + return false; //show AutoScale button + } else { + return 2; //hide Autoscale button (both header and form) + } + } else { //from VPC section + //VPC doesn't support autoscale + return 2; + } } }, diff --git a/ui/scripts/sharedFunctions.js b/ui/scripts/sharedFunctions.js index de8e6933b69..95fc5583ff6 100644 --- a/ui/scripts/sharedFunctions.js +++ b/ui/scripts/sharedFunctions.js @@ -46,7 +46,6 @@ var md5HashedLogin = false; //page size for API call (e.g."listXXXXXXX&pagesize=N" ) var pageSize = 30; - var rootAccountId = 1; //async action @@ -923,11 +922,16 @@ cloudStack.converters = { } return localDate; }, - toBooleanText: function(booleanValue) { - if (booleanValue == true) - return "Yes"; - - return "No"; + toBooleanText: function(booleanValue) { + var text1; + if (booleanValue == true) { + text1 = "Yes"; + } else if (booleanValue == false) { + text1 = "No"; + } else { //booleanValue == undefined + text1 = ""; + } + return text1; }, convertHz: function(hz) { if (hz == null) diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 15959483367..8f943e2e751 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -380,9 +380,64 @@ } }, - socketInfo: function(data) { + socketInfo: function(data) { + var socketCount = 0; + $.ajax({ + url: createURL('listHypervisors'), + async: false, + success: function(json) { + args.response.success({ + data: $(json.listhypervisorsresponse.hypervisor).map(function(index, hypervisor) { + var totalHostCount = 0; + var currentPage = 1; + var returnedHostCount = 0; + var returnedHostCpusocketsSum = 0; + var returnedHostHavingCpusockets = true; + + var callListHostsWithPage = function(setTotalHostCount) { + $.ajax({ + url: createURL('listHosts'), + async: false, + data: { + hypervisortype: hypervisor.name, + page: currentPage, + pagesize: pageSize //global variable + }, + success: function(json) { + if (setTotalHostCount) { + totalHostCount = json.listhostsresponse.count; + } + returnedHostCount += json.listhostsresponse.host.length; + + var items = json.listhostsresponse.host; + for (var i = 0; i < items.length; i++) { + if (items[i].cpusockets != undefined && isNaN(items[i].cpusockets) == false) { + returnedHostCpusocketsSum += items[i].cpusockets; + } else { + returnedHostHavingCpusockets = false; + } + } + + if (returnedHostCount < totalHostCount) { + currentPage++; + callListHostsWithPage(false); + } + } + }); + } + + callListHostsWithPage(true); + + if (returnedHostHavingCpusockets) { + socketCount += returnedHostCpusocketsSum; + } + }) + }); + } + }); + complete($.extend(data, { - socketCount: 0 + socketCount: socketCount })); } }; @@ -4889,6 +4944,288 @@ } }, + // Palo Alto provider detailView + pa: { + type: 'detailView', + id: 'paProvider', + label: 'label.PA', + viewAll: { + label: 'label.devices', + path: '_zone.paDevices' + }, + tabs: { + details: { + title: 'label.details', + fields: [{ + name: { + label: 'label.name' + } + }, { + state: { + label: 'label.state' + } + }], + dataProvider: function (args) { + refreshNspData("PaloAlto"); + var providerObj; + $(nspHardcodingArray).each(function () { + if (this.id == "pa") { + providerObj = this; + return false; //break each loop + } + }); + args.response.success({ + data: providerObj, + actionFilter: networkProviderActionFilter('pa') + }); + } + } + }, + actions: { + add: { + label: 'label.add.PA.device', + createForm: { + title: 'label.add.PA.device', + fields: { + ip: { + label: 'label.ip.address', + docID: 'helpPaloAltoIPAddress' + }, + username: { + label: 'label.username', + docID: 'helpPaloAltoUsername' + }, + password: { + label: 'label.password', + isPassword: true, + docID: 'helpPaloAltoPassword' + }, + networkdevicetype: { + label: 'label.type', + docID: 'helpPaloAltoType', + select: function (args) { + var items = []; + items.push({ + id: "PaloAltoFirewall", + description: "Palo Alto Firewall" + }); + args.response.success({ + data: items + }); + } + }, + publicinterface: { + label: 'label.public.interface', + docID: 'helpPaloAltoPublicInterface' + }, + privateinterface: { + label: 'label.private.interface', + docID: 'helpPaloAltoPrivateInterface' + }, + //usageinterface: { + // label: 'Usage interface', + // docID: 'helpPaloAltoUsageInterface' + //}, + numretries: { + label: 'label.numretries', + defaultValue: '2', + docID: 'helpPaloAltoRetries' + }, + timeout: { + label: 'label.timeout', + defaultValue: '300', + docID: 'helpPaloAltoTimeout' + }, + // inline: { + // label: 'Mode', + // docID: 'helpPaloAltoMode', + // select: function(args) { + // var items = []; + // items.push({id: "false", description: "side by side"}); + // items.push({id: "true", description: "inline"}); + // args.response.success({data: items}); + // } + // }, + publicnetwork: { + label: 'label.public.network', + defaultValue: 'untrust', + docID: 'helpPaloAltoPublicNetwork' + }, + privatenetwork: { + label: 'label.private.network', + defaultValue: 'trust', + docID: 'helpPaloAltoPrivateNetwork' + }, + pavr: { + label: 'label.virtual.router', + docID: 'helpPaloAltoVirtualRouter' + }, + patp: { + label: 'label.PA.threat.profile', + docID: 'helpPaloAltoThreatProfile' + }, + palp: { + label: 'label.PA.log.profile', + docID: 'helpPaloAltoLogProfile' + }, + capacity: { + label: 'label.capacity', + validation: { + required: false, + number: true + }, + docID: 'helpPaloAltoCapacity' + }, + dedicated: { + label: 'label.dedicated', + isBoolean: true, + isChecked: false, + docID: 'helpPaloAltoDedicated' + } + } + }, + action: function (args) { + if (nspMap["pa"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=PaloAlto&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + dataType: "json", + async: true, + success: function (json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addPaloAltoProviderIntervalID = setInterval(function () { + $.ajax({ + url: createURL("queryAsyncJobResult&jobId=" + jobId), + dataType: "json", + success: function (json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } else { + clearInterval(addPaloAltoProviderIntervalID); + if (result.jobstatus == 1) { + nspMap["pa"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + errorMsg); + } + }); + }, 3000); + } + }); + } else { + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } + }, + messages: { + notification: function (args) { + return 'label.add.PA.device'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + enable: { + label: 'label.enable.provider', + action: function (args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["pa"].id + "&state=Enabled"), + dataType: "json", + success: function (json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function (json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.enable.provider'; + }, + notification: function () { + return 'label.enable.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + disable: { + label: 'label.disable.provider', + action: function (args) { + $.ajax({ + url: createURL("updateNetworkServiceProvider&id=" + nspMap["pa"].id + "&state=Disabled"), + dataType: "json", + success: function (json) { + var jid = json.updatenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid, + getUpdatedItem: function (json) { + $(window).trigger('cloudStack.fullRefresh'); + } + } + }); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.disable.provider'; + }, + notification: function () { + return 'label.disable.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + }, + destroy: { + label: 'label.shutdown.provider', + action: function (args) { + $.ajax({ + url: createURL("deleteNetworkServiceProvider&id=" + nspMap["pa"].id), + dataType: "json", + success: function (json) { + var jid = json.deletenetworkserviceproviderresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + + $(window).trigger('cloudStack.fullRefresh'); + } + }); + }, + messages: { + confirm: function (args) { + return 'message.confirm.shutdown.provider'; + }, + notification: function (args) { + return 'label.shutdown.provider'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + } + }, + // Security groups detail view securityGroups: { id: 'securityGroup-providers', @@ -7057,41 +7394,57 @@ }, virtualRouters: function() { var listView = $.extend(true, {}, cloudStack.sections.system.subsections.virtualRouters.listView, { - dataProvider: function(args) { - var searchByArgs = args.filterBy.search.value.length ? - '&keyword=' + args.filterBy.search.value : ''; - + dataProvider: function(args) { + var data = {}; + listViewDataProvider(args, data); + var routers = []; + + //get account-owned routers $.ajax({ - url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + searchByArgs), - async: true, + url: createURL('listRouters'), + data: $.extend(data,{ + listAll: true + }), + async: false, success: function(json) { - var items = json.listroutersresponse.router ? - json.listroutersresponse.router : []; - + var items = json.listroutersresponse.router ? json.listroutersresponse.router : []; $(items).map(function(index, item) { routers.push(item); }); - - // Get project routers - $.ajax({ - url: createURL("listRouters&listAll=true&page=" + args.page + "&pagesize=" + pageSize + "&projectid=-1"), - async: true, - success: function(json) { - var items = json.listroutersresponse.router ? - json.listroutersresponse.router : []; - - $(items).map(function(index, item) { - routers.push(item); - }); - args.response.success({ - actionFilter: routerActionfilter, - data: $(routers).map(mapRouterType) - }); - } - }); + + //get project-owned routers + var toSearchByProjectid = true; + if (args.filterBy != null) { + if (args.filterBy.advSearch != null && typeof(args.filterBy.advSearch) == "object") { //advanced search + if ('account' in args.filterBy.advSearch && args.filterBy.advSearch.account.length > 0) { //if account is specified in advanced search, don't search project-owned routers + toSearchByProjectid = false; //since account and projectid can't be specified together + } + } + } + if (toSearchByProjectid) { + $.ajax({ + url: createURL('listRouters'), + data: $.extend(data,{ + listAll: true, + projectid: -1 + }), + async: false, + success: function(json) { + var items = json.listroutersresponse.router ? json.listroutersresponse.router : []; + $(items).map(function(index, item) { + routers.push(item); + }); + } + }); + } } - }); + }); + + args.response.success({ + actionFilter: routerActionfilter, + data: $(routers).map(mapRouterType) + }); }, detailView: { @@ -7125,20 +7478,60 @@ var listView = { id: 'sockets', fields: { - hypervisor: { label: 'label.hypervisor' }, - sockets: { label: 'label.sockets' }, - hosts: { label: 'label.hosts' } + hypervisor: { label: 'label.hypervisor' }, + hosts: { label: 'label.hosts' }, + sockets: { label: 'label.sockets' } }, dataProvider: function(args) { $.ajax({ url: createURL('listHypervisors'), success: function(json) { args.response.success({ - data: $(json.listhypervisorsresponse.hypervisor).map(function(index, hypervisor) { + data: $(json.listhypervisorsresponse.hypervisor).map(function(index, hypervisor) { + var totalHostCount = 0; + var currentPage = 1; + var returnedHostCount = 0; + var returnedHostCpusocketsSum = 0; + var returnedHostHavingCpusockets = true; + + var callListHostsWithPage = function(setTotalHostCount) { + $.ajax({ + url: createURL('listHosts'), + async: false, + data: { + hypervisortype: hypervisor.name, + page: currentPage, + pagesize: pageSize //global variable + }, + success: function(json) { + if (setTotalHostCount) { + totalHostCount = json.listhostsresponse.count; + } + returnedHostCount += json.listhostsresponse.host.length; + + var items = json.listhostsresponse.host; + for (var i = 0; i < items.length; i++) { + if (items[i].cpusockets != undefined && isNaN(items[i].cpusockets) == false) { + returnedHostCpusocketsSum += items[i].cpusockets; + } else { + returnedHostHavingCpusockets = false; + } + } + + if (returnedHostCount < totalHostCount) { + currentPage++; + callListHostsWithPage(false); + } + } + }); + } + + callListHostsWithPage(true); + return { hypervisor: hypervisor.name, - sockets: 0, - hosts: 0 + hosts: totalHostCount, + sockets: (returnedHostHavingCpusockets? returnedHostCpusocketsSum : 'unknown') }; }) }); @@ -7181,6 +7574,110 @@ } } }, + + advSearchFields: { + name: { + label: 'Name' + }, + zoneid: { + label: 'Zone', + select: function(args) { + $.ajax({ + url: createURL('listZones'), + data: { + listAll: true + }, + success: function(json) { + var zones = json.listzonesresponse.zone ? json.listzonesresponse.zone : []; + + args.response.success({ + data: $.map(zones, function(zone) { + return { + id: zone.id, + description: zone.name + }; + }) + }); + } + }); + } + }, + podid: { + label: 'Pod', + dependsOn: 'zoneid', + select: function (args) { + $.ajax({ + url: createURL("listPods&zoneid=" + args.zoneid), + dataType: "json", + async: true, + success: function (json) { + var pods = json.listpodsresponse.pod ? json.listpodsresponse.pod : []; + args.response.success({ + data: $.map(pods, function(pod) { + return { + id: pod.id, + description: pod.name + }; + }) + }); + } + }); + } + }, + domainid: { + label: 'Domain', + select: function(args) { + if (isAdmin() || isDomainAdmin()) { + $.ajax({ + url: createURL('listDomains'), + data: { + listAll: true, + details: 'min' + }, + success: function(json) { + var array1 = [{ + id: '', + description: '' + }]; + var domains = json.listdomainsresponse.domain; + if (domains != null && domains.length > 0) { + for (var i = 0; i < domains.length; i++) { + array1.push({ + id: domains[i].id, + description: domains[i].path + }); + } + } + args.response.success({ + data: array1 + }); + } + }); + } else { + args.response.success({ + data: null + }); + } + }, + isHidden: function(args) { + if (isAdmin() || isDomainAdmin()) + return false; + else + return true; + } + }, + + account: { + label: 'Account', + isHidden: function(args) { + if (isAdmin() || isDomainAdmin()) + return false; + else + return true; + } + } + }, + dataProvider: function(args) { var array1 = []; if (args.filterBy != null) { @@ -7593,6 +8090,13 @@ state: { label: 'label.state' }, + version: { + label: 'label.version' + }, + requiresupgrade: { + label: 'Requires Upgrade', + converter: cloudStack.converters.toBooleanText + }, guestnetworkid: { label: 'label.network.id' }, @@ -7641,7 +8145,7 @@ dataType: 'json', async: true, success: function(json) { - var jsonObj = json.listroutersresponse.router[0]; + var jsonObj = json.listroutersresponse.router[0]; addExtraPropertiesToRouterInstanceObject(jsonObj); args.response.success({ actionFilter: routerActionfilter, @@ -9058,6 +9562,250 @@ } } }, + + //Palo Alto devices listView + paDevices: { + id: 'paDevices', + title: 'label.devices', + listView: { + id: 'paDevices', + fields: { + ipaddress: { + label: 'label.ip.address' + }, + fwdevicestate: { + label: 'label.status' + }, + fwdevicename: { + label: 'label.type' + } + }, + actions: { + add: { + label: 'label.add.PA.device', + createForm: { + title: 'label.add.PA.device', + fields: { + ip: { + label: 'label.ip.address' + }, + username: { + label: 'label.username' + }, + password: { + label: 'label.password', + isPassword: true + }, + networkdevicetype: { + label: 'label.type', + select: function (args) { + var items = []; + items.push({ + id: "PaloAltoFirewall", + description: "Palo Alto Firewall" + }); + args.response.success({ + data: items + }); + } + }, + publicinterface: { + label: 'label.public.interface' + }, + privateinterface: { + label: 'label.private.interface' + }, + //usageinterface: { + // label: 'label.usage.interface' + //}, + numretries: { + label: 'label.numretries', + defaultValue: '2' + }, + timeout: { + label: 'label.timeout', + defaultValue: '300' + }, + // inline: { + // label: 'Mode', + // select: function(args) { + // var items = []; + // items.push({id: "false", description: "side by side"}); + // items.push({id: "true", description: "inline"}); + // args.response.success({data: items}); + // } + // }, + publicnetwork: { + label: 'label.public.network', + defaultValue: 'untrust' + }, + privatenetwork: { + label: 'label.private.network', + defaultValue: 'trust' + }, + pavr: { + label: 'label.virtual.router' + }, + patp: { + label: 'label.PA.threat.profile' + }, + palp: { + label: 'label.PA.log.profile' + }, + capacity: { + label: 'label.capacity', + validation: { + required: false, + number: true + } + }, + dedicated: { + label: 'label.dedicated', + isBoolean: true, + isChecked: false + } + } + }, + action: function (args) { + if (nspMap["pa"] == null) { + $.ajax({ + url: createURL("addNetworkServiceProvider&name=PaloAlto&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + dataType: "json", + async: true, + success: function (json) { + var jobId = json.addnetworkserviceproviderresponse.jobid; + var addPaloAltoProviderIntervalID = setInterval(function () { + $.ajax({ + url: createURL("queryAsyncJobResult&jobId=" + jobId), + dataType: "json", + success: function (json) { + var result = json.queryasyncjobresultresponse; + if (result.jobstatus == 0) { + return; //Job has not completed + } else { + clearInterval(addPaloAltoProviderIntervalID); + if (result.jobstatus == 1) { + nspMap["pa"] = json.queryasyncjobresultresponse.jobresult.networkserviceprovider; + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } else if (result.jobstatus == 2) { + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + _s(result.jobresult.errortext)); + } + } + }, + error: function (XMLHttpResponse) { + var errorMsg = parseXMLHttpResponse(XMLHttpResponse); + alert("addNetworkServiceProvider&name=Palo Alto failed. Error: " + errorMsg); + } + }); + }, 3000); + } + }); + } else { + addExternalFirewall(args, selectedPhysicalNetworkObj, "addPaloAltoFirewall", "addpaloaltofirewallresponse", "pafirewall"); + } + }, + messages: { + notification: function (args) { + return 'label.add.PA.device'; + } + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + dataProvider: function (args) { + $.ajax({ + url: createURL("listPaloAltoFirewalls&physicalnetworkid=" + selectedPhysicalNetworkObj.id), + data: { + page: args.page, + pageSize: pageSize + }, + dataType: "json", + async: false, + success: function (json) { + var items = json.listpaloaltofirewallresponse.paloaltofirewall; + args.response.success({ + data: items + }); + } + }); + }, + detailView: { + name: 'Palo Alto details', + actions: { + 'remove': { + label: 'label.delete.PA', + messages: { + confirm: function (args) { + return 'message.confirm.delete.PA'; + }, + notification: function (args) { + return 'label.delete.PA'; + } + }, + action: function (args) { + $.ajax({ + url: createURL("deletePaloAltoFirewall&fwdeviceid=" + args.context.paDevices[0].fwdeviceid), + dataType: "json", + async: true, + success: function (json) { + var jid = json.deletepaloaltofirewallresponse.jobid; + args.response.success({ + _custom: { + jobId: jid + } + }); + } + }); + }, + notification: { + poll: pollAsyncJobResult + } + } + }, + tabs: { + details: { + title: 'label.details', + fields: [{ + fwdeviceid: { + label: 'label.id' + }, + ipaddress: { + label: 'label.ip.address' + }, + fwdevicestate: { + label: 'label.status' + }, + fwdevicename: { + label: 'label.type' + }, + fwdevicecapacity: { + label: 'label.capacity' + }, + timeout: { + label: 'label.timeout' + } + }], + dataProvider: function (args) { + $.ajax({ + url: createURL("listPaloAltoFirewalls&fwdeviceid=" + args.context.paDevices[0].fwdeviceid), + dataType: "json", + async: true, + success: function (json) { + var item = json.listpaloaltofirewallresponse.paloaltofirewall[0]; + args.response.success({ + data: item + }); + } + }); + } + } + } + } + } + }, + // FIXME convert to nicira detailview // NiciraNvp devices listView niciraNvpDevices: { @@ -11406,6 +12154,13 @@ data: data, success: function(json) { var item = json.updateconfigurationresponse.configuration; + + if (args.data.jsonObj.name == 'cpu.overprovisioning.factor' || args.data.jsonObj.name == 'mem.overprovisioning.factor') { + cloudStack.dialog.notice({ + message: 'Please note - if you are changing the over provisioning factor for a cluster with vms running, please refer to the admin guide to understand the capacity calculation.' + }); + } + args.response.success({ data: item }); @@ -12323,6 +13078,12 @@ }, type: { label: 'label.type' + }, + hypervisor: { + label: 'label.hypervisor' + }, + hypervisorversion: { + label: 'label.hypervisor.version' }, hosttags: { label: 'label.host.tags', @@ -12370,10 +13131,7 @@ }, ipaddress: { label: 'label.ip.address' - }, - version: { - label: 'label.version' - }, + }, disconnected: { label: 'label.last.disconnected' } @@ -15665,6 +16423,44 @@ } url.push("fwdevicededicated=" + dedicated.toString()); + // START - Palo Alto Specific Fields + var externalVirtualRouter = args.data.pavr; + if(externalVirtualRouter != null && externalVirtualRouter.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("pavr=" + encodeURIComponent(externalVirtualRouter)); + } + + var externalThreatProfile = args.data.patp; + if(externalThreatProfile != null && externalThreatProfile.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("patp=" + encodeURIComponent(externalThreatProfile)); + } + + var externalLogProfile = args.data.palp; + if(externalLogProfile != null && externalLogProfile.length > 0) { + if(isQuestionMarkAdded == false) { + url.push("?"); + isQuestionMarkAdded = true; + } + else { + url.push("&"); + } + url.push("palp=" + encodeURIComponent(externalLogProfile)); + } + // END - Palo Alto Specific Fields + array1.push("&url=" + todb(url.join(""))); //construct URL ends here @@ -16340,10 +17136,11 @@ } var addExtraPropertiesToRouterInstanceObject = function(jsonObj) { - if (jsonObj.isredundantrouter == true) + if (jsonObj.isredundantrouter == true) { jsonObj["redundantRouterState"] = jsonObj.redundantstate; - else + } else { jsonObj["redundantRouterState"] = ""; + } } var refreshNspData = function(nspName) { @@ -16389,6 +17186,9 @@ case "JuniperSRX": nspMap["srx"] = items[i]; break; + case "PaloAlto": + nspMap["pa"] = items[i]; + break; case "SecurityGroupProvider": nspMap["securityGroups"] = items[i]; break; @@ -16470,6 +17270,11 @@ name: 'SRX', state: nspMap.srx ? nspMap.srx.state : 'Disabled' }); + nspHardcodingArray.push({ + id: 'pa', + name: 'Palo Alto', + state: nspMap.pa ? nspMap.pa.state : 'Disabled' + }); } }; diff --git a/ui/scripts/ui/widgets/multiEdit.js b/ui/scripts/ui/widgets/multiEdit.js index 5d973aad949..79bb0d4f869 100755 --- a/ui/scripts/ui/widgets/multiEdit.js +++ b/ui/scripts/ui/widgets/multiEdit.js @@ -85,6 +85,12 @@ return true; } + var isHidden = $multi.find('th.' + fieldName).hasClass('always-hide'); + + if (isHidden) { + return true; + } + var $td = $('').addClass(fieldName).appendTo($tr); var $input, val; var $addButton = $multi.find('form .button.add-vm:not(.custom-action)').clone(); @@ -264,7 +270,7 @@ // Align width to main header _medit.refreshItemWidths($multi); - + if (data._hideFields && $.inArray(fieldName, data._hideFields) > -1) { $td.addClass('disabled'); @@ -891,7 +897,16 @@ $td.attr('rel', fieldName); $td.appendTo($inputForm); - if (field.isHidden) { + var isHidden = $.isFunction(field.isHidden) ? + field.isHidden({ context: context }) : field.isHidden; + + if (isHidden) { + // return true == hide only header and form column + // return 2 == hide header and form, as well as returned item column + if (isHidden === 2) { + $th.addClass('always-hide'); + } + $th.hide(); $td.hide(); } diff --git a/ui/scripts/vpc.js b/ui/scripts/vpc.js index 2dc916244a0..9775f606de8 100644 --- a/ui/scripts/vpc.js +++ b/ui/scripts/vpc.js @@ -2858,13 +2858,13 @@ id: { label: 'label.id' }, - - //s2svpngatewayid: { label: 'VPN gateway ID' }, + passive: { + label: 'Passive', + converter: cloudStack.converters.toBooleanText + }, publicip: { label: 'label.ip.address' - }, - - //s2scustomergatewayid: { label: 'Customer gateway ID' }, + }, gateway: { label: 'label.gateway' }, diff --git a/utils/src/com/cloud/maint/Version.java b/utils/src/com/cloud/maint/Version.java index 73175474edc..f900b56b4c5 100644 --- a/utils/src/com/cloud/maint/Version.java +++ b/utils/src/com/cloud/maint/Version.java @@ -58,6 +58,14 @@ public class Version { return "0"; return tokens[0] + "." + tokens[1]+ "." + tokens[2]; } + + public static String trimRouterVersion(String version){ + String[] tokens = version.split(" "); + if(tokens.length >= 3){ + return tokens[2]; + } + return "0"; + } public static void main(String[] args) { System.out.println("Result is " + compare(args[0], args[1])); diff --git a/utils/src/com/cloud/utils/FileUtil.java b/utils/src/com/cloud/utils/FileUtil.java index a6f0c9167ef..96ad3afe8c3 100644 --- a/utils/src/com/cloud/utils/FileUtil.java +++ b/utils/src/com/cloud/utils/FileUtil.java @@ -17,28 +17,14 @@ package com.cloud.utils; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; + +import org.apache.commons.io.FileUtils; public class FileUtil { - public static void copyfile(File f1, File f2) throws IOException { - InputStream in = new FileInputStream(f1); - OutputStream out = new FileOutputStream(f2); - - try { - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } finally { - in.close(); - out.close(); - } + public static void copyfile(File source, File destination) throws IOException { + FileUtils.copyFile(source, destination); } } diff --git a/utils/src/com/cloud/utils/db/DbProperties.java b/utils/src/com/cloud/utils/db/DbProperties.java index a5ad383118d..55df23da08e 100644 --- a/utils/src/com/cloud/utils/db/DbProperties.java +++ b/utils/src/com/cloud/utils/db/DbProperties.java @@ -65,12 +65,15 @@ public class DbProperties { if ( is == null ) { is = PropertiesUtil.openStreamFromURL("db.properties"); } + if ( is == null ) { System.err.println("Failed to find db.properties"); log.error("Failed to find db.properties"); } - dbProps.load(is); + if ( is != null ) { + dbProps.load(is); + } EncryptionSecretKeyChecker checker = new EncryptionSecretKeyChecker(); checker.check(dbProps); diff --git a/utils/src/com/cloud/utils/db/EntityManager.java b/utils/src/com/cloud/utils/db/EntityManager.java index 49f4f720698..d613d7d5f9a 100644 --- a/utils/src/com/cloud/utils/db/EntityManager.java +++ b/utils/src/com/cloud/utils/db/EntityManager.java @@ -43,6 +43,15 @@ public interface EntityManager { */ public T findByUuid(Class entityType, String uuid); + /** + * Finds a unique entity by uuid string, including those removed entries + * @param entity class + * @param entityType type of entity you're looking for. + * @param uuid the unique id + * @return T if found, null if not. + */ + public T findByUuidIncludingRemoved(Class entityType, String uuid); + /** * Finds an entity by external id which is always String * @param entity class diff --git a/utils/src/com/cloud/utils/net/NetUtils.java b/utils/src/com/cloud/utils/net/NetUtils.java index f5904250bb4..8a351097845 100755 --- a/utils/src/com/cloud/utils/net/NetUtils.java +++ b/utils/src/com/cloud/utils/net/NetUtils.java @@ -52,6 +52,7 @@ import com.cloud.utils.script.Script; public class NetUtils { protected final static Logger s_logger = Logger.getLogger(NetUtils.class); public final static String HTTP_PORT = "80"; + public final static String HTTPS_PORT = "443"; public final static int VPN_PORT = 500; public final static int VPN_NATT_PORT = 4500; public final static int VPN_L2TP_PORT = 1701; @@ -61,6 +62,8 @@ public class NetUtils { public final static String ANY_PROTO = "any"; public final static String ICMP_PROTO = "icmp"; public final static String ALL_PROTO = "all"; + public final static String HTTP_PROTO = "http"; + public final static String SSL_PROTO = "ssl"; public final static String ALL_CIDRS = "0.0.0.0/0"; public final static int PORT_RANGE_MIN = 0; diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java index 108a3ecb194..c4836a8ad1d 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java @@ -913,6 +913,7 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost { summary.setCpuCount(hardwareSummary.getNumCpuThreads()); summary.setMemoryBytes(hardwareSummary.getMemorySize()); summary.setCpuSpeed(hardwareSummary.getCpuMhz()); + summary.setCpuSockets((int)hardwareSummary.getNumCpuPkgs()); if(s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - getHyperHostResourceSummary() done"); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java index 3a70744aa95..cd2c3e8746c 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HypervisorHostHelper.java @@ -937,7 +937,7 @@ public class HypervisorHostHelper { if(vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) ) { createGCTag = true; - vid = Integer.parseInt(BroadcastDomainType.getValue(vlanId)); + vid = Integer.parseInt(vlanId); } } @@ -1205,7 +1205,7 @@ public class HypervisorHostHelper { return false; } - public static VirtualMachineMO createWorkerVM(VmwareHypervisorHost hyperHost, + public static VirtualMachineMO createWorkerVM(VmwareHypervisorHost hyperHost, DatastoreMO dsMo, String vmName) throws Exception { // Allow worker VM to float within cluster so that we will have better chance to @@ -1247,7 +1247,7 @@ public class HypervisorHostHelper { if(workingVM != null) { workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER, "true"); - String workerTag = String.format("%d-%s", System.currentTimeMillis(), + String workerTag = String.format("%d-%s", System.currentTimeMillis(), hyperHost.getContext().getStockObject("noderuninfo")); workingVM.setCustomFieldValue(CustomFieldConstants.CLOUD_WORKER_TAG, workerTag); } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java index f7288f1b149..c2c326a9751 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHost.java @@ -21,7 +21,6 @@ import com.vmware.vim25.ComputeResourceSummary; import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.ObjectContent; import com.vmware.vim25.VirtualMachineConfigSpec; - import com.cloud.hypervisor.vmware.util.VmwareContext; /** diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHostResourceSummary.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHostResourceSummary.java index fa11dc3488c..323cb35e362 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHostResourceSummary.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VmwareHypervisorHostResourceSummary.java @@ -20,6 +20,7 @@ public class VmwareHypervisorHostResourceSummary { private long memoryBytes; private long cpuCount; private long cpuSpeed; + private Integer cpuSockets; public VmwareHypervisorHostResourceSummary() { } @@ -47,4 +48,12 @@ public class VmwareHypervisorHostResourceSummary { public void setCpuSpeed(long cpuSpeed) { this.cpuSpeed = cpuSpeed; } + + public Integer getCpuSockets() { + return cpuSockets; + } + + public void setCpuSockets(Integer cpuSockets) { + this.cpuSockets = cpuSockets; + } } diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java index d1adbc2dc30..13172fbea23 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareClient.java @@ -132,8 +132,8 @@ public class VmwareClient { ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url); ctxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true); - ctxt.put("com.sun.xml.internal.ws.request.timeout", 600000); - ctxt.put("com.sun.xml.internal.ws.connect.timeout", 600000); + ctxt.put("com.sun.xml.internal.ws.request.timeout", 1200000); + ctxt.put("com.sun.xml.internal.ws.connect.timeout", 1200000); ServiceContent serviceContent = vimPort.retrieveServiceContent(SVC_INST_REF); diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java index 6ee0f753d79..b8c3e8dc58a 100755 --- a/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/util/VmwareGuestOsMapper.java @@ -121,6 +121,14 @@ public class VmwareGuestOsMapper { s_mapper.put("CentOS 5.6 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); s_mapper.put("CentOS 6.0 (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); s_mapper.put("CentOS 6.0 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); + s_mapper.put("CentOS 6.1 (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); + s_mapper.put("CentOS 6.1 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); + s_mapper.put("CentOS 6.2 (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); + s_mapper.put("CentOS 6.2 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); + s_mapper.put("CentOS 6.3 (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); + s_mapper.put("CentOS 6.3 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); + s_mapper.put("CentOS 6.4 (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); + s_mapper.put("CentOS 6.4 (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST); s_mapper.put("Other CentOS (32-bit)", VirtualMachineGuestOsIdentifier.CENTOS_GUEST); s_mapper.put("Other CentOS (64-bit)", VirtualMachineGuestOsIdentifier.CENTOS_64_GUEST);